diff --git a/config/policy.go b/config/policy.go index 8d6d13ed1..2ec8d837b 100644 --- a/config/policy.go +++ b/config/policy.go @@ -21,16 +21,27 @@ type Policy struct { // in Except. in other words, Allow=true means that Except is a deny-list // and Allow=false means that Except is an allow-list. Except []string - - // Publish determines whether or not peers are allowed to publish + // Publish is the default Allow policy when a provider has no policy in + // PublisherForProviders. It determines if any peers are allowed to publish // advertisements for a provider with a differen peer ID. Publish bool - // PublisherExcept is a list of peer IDs that are exceptions to the Publish - // policy. If Publish is false, then all allowed peers cannot publish - // advertisements for providers with a different peer ID, unless listed in - // PublishExcept. If Publish is true, then all allowed peers can publish - // advertisements for any provider, unless listed in PublishExcept. + // PublisherExcept is a list of peer IDs that are exceptions to the default + // Publish policy. PublishExcept []string + // PublishersForProvider is a list of policies specifying which publishers + // are allowed to publish advertisements on behalf of a specified provider. + PublishersForProvider []PublishersPolicy +} + +type PublishersPolicy struct { + // Provider is the provider peer ID this policy pertains to. + Provider string + // Allow determines whether a peer is allowed or blocked from publishing + // advertisements on behalf of a provider with a differen peer ID. + Allow bool + // Except is a list of peer IDs that are exceptions to the Allow + // policy. + Except []string } // NewPolicy returns Policy with values set to their defaults. diff --git a/internal/registry/policy/policy.go b/internal/registry/policy/policy.go index 58944cc1e..ab748031f 100644 --- a/internal/registry/policy/policy.go +++ b/internal/registry/policy/policy.go @@ -12,7 +12,9 @@ import ( type Policy struct { allow peerutil.Policy publish peerutil.Policy - rwmutex sync.RWMutex + // publishForProvider contains a publish policy for a specific provider. + publishForProvider map[peer.ID]peerutil.Policy + rwmutex sync.RWMutex } func New(cfg config.Policy) (*Policy, error) { @@ -26,9 +28,26 @@ func New(cfg config.Policy) (*Policy, error) { return nil, fmt.Errorf("bad publish policy: %s", err) } + var pubForProvider map[peer.ID]peerutil.Policy + if len(cfg.PublishersForProvider) != 0 { + pubForProvider = make(map[peer.ID]peerutil.Policy, len(cfg.PublishersForProvider)) + for i, pubPolicyCfg := range cfg.PublishersForProvider { + providerID, err := peer.Decode(pubPolicyCfg.Provider) + if err != nil { + return nil, fmt.Errorf("error decoding provider id in publisher policy %d: %s", i+1, err) + } + pubPolicy, err := peerutil.NewPolicyStrings(pubPolicyCfg.Allow, pubPolicyCfg.Except) + if err != nil { + return nil, fmt.Errorf("bad publisher policy for provider %s: %s", pubPolicyCfg.Provider, err) + } + pubForProvider[providerID] = pubPolicy + } + } + return &Policy{ - allow: allow, - publish: publish, + allow: allow, + publish: publish, + publishForProvider: pubForProvider, }, nil } @@ -54,7 +73,13 @@ func (p *Policy) PublishAllowed(publisherID, providerID peer.ID) bool { if !p.allow.Eval(providerID) { return false } - return p.publish.Eval(publisherID) + + pubPolicy, found := p.publishForProvider[providerID] + if !found { + // Use default publish policy. + pubPolicy = p.publish + } + return pubPolicy.Eval(publisherID) } // Allow alters the policy to allow the specified peer. Returns true if the @@ -93,8 +118,14 @@ func (p *Policy) Copy(other *Policy) { defer p.rwmutex.Unlock() other.rwmutex.RLock() - p.allow = other.allow - p.publish = other.publish + p.allow = peerutil.NewPolicy(other.allow.Default(), other.allow.Except()...) + p.publish = peerutil.NewPolicy(other.publish.Default(), other.publish.Except()...) + if len(other.publishForProvider) != 0 { + p.publishForProvider = make(map[peer.ID]peerutil.Policy, len(other.publishForProvider)) + for k, v := range other.publishForProvider { + p.publishForProvider[k] = peerutil.NewPolicy(v.Default(), v.Except()...) + } + } other.rwmutex.RUnlock() } diff --git a/internal/registry/policy/policy_test.go b/internal/registry/policy/policy_test.go index 25641c4ab..dcf613b82 100644 --- a/internal/registry/policy/policy_test.go +++ b/internal/registry/policy/policy_test.go @@ -119,3 +119,59 @@ func TestPolicyAccess(t *testing.T) { require.NoError(t, err) require.True(t, p.NoneAllowed(), "expected inaccessible policy") } + +func TestPublishersForProvider(t *testing.T) { + prov1Str := "12D3KooWPMGfQs5CaJKG4yCxVWizWBRtB85gEUwiX2ekStvYvqgp" + prov1, err := peer.Decode(prov1Str) + if err != nil { + panic(err) + } + prov2Str := "12D3KooWNH73Z4oxej8u6NzYswQxH7Cd92U2aUYUsxX9mKr5SMuj" + prov2, err := peer.Decode(prov2Str) + if err != nil { + panic(err) + } + + policyCfg := config.Policy{ + Allow: true, + Publish: false, + PublishersForProvider: []config.PublishersPolicy{ + { + Provider: prov1Str, + Allow: false, + Except: []string{exceptIDStr}, + }, + { + Provider: prov2Str, + Allow: true, + Except: []string{exceptIDStr}, + }, + }, + } + + p, err := New(policyCfg) + require.NoError(t, err) + + require.True(t, p.PublishAllowed(prov1, prov1), "should be allowed to publish to self") + require.False(t, p.PublishAllowed(exceptID, otherID)) + + // Test publishing for prov1. + require.False(t, p.PublishAllowed(otherID, prov1)) + require.True(t, p.PublishAllowed(exceptID, prov1)) + require.False(t, p.PublishAllowed(prov2, prov1)) + + // Test publishing for prov1. + require.True(t, p.PublishAllowed(otherID, prov2)) + require.False(t, p.PublishAllowed(exceptID, prov2)) + require.True(t, p.PublishAllowed(prov1, prov2)) + + // Test with default policy set to default allow true. + policyCfg.Publish = true + p, err = New(policyCfg) + require.NoError(t, err) + + require.True(t, p.PublishAllowed(exceptID, otherID)) + require.False(t, p.PublishAllowed(exceptID, prov2)) + require.True(t, p.PublishAllowed(otherID, prov2)) + require.False(t, p.PublishAllowed(otherID, prov1)) +} diff --git a/internal/registry/registry_test.go b/internal/registry/registry_test.go index b613325b0..6f029f6ce 100644 --- a/internal/registry/registry_test.go +++ b/internal/registry/registry_test.go @@ -569,7 +569,9 @@ func TestPollProviderOverrides(t *testing.T) { func TestRegistry_RegisterOrUpdateToleratesEmptyPublisherAddrs(t *testing.T) { ctx := context.Background() - subject, err := New(ctx, config.NewDiscovery(), datastore.NewMapDatastore()) + cfg := config.NewDiscovery() + cfg.Policy.Publish = true + subject, err := New(ctx, cfg, datastore.NewMapDatastore()) require.NoError(t, err) t.Cleanup(func() { subject.Close() }) @@ -607,6 +609,7 @@ func TestRegistry_RegisterOrUpdateToleratesEmptyPublisherAddrs(t *testing.T) { func TestFilterIPs(t *testing.T) { cfg := config.NewDiscovery() + cfg.Policy.Publish = true ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel()