Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow configuration of per-provider publishers policy #2526

Merged
merged 2 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions config/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
43 changes: 37 additions & 6 deletions internal/registry/policy/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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
}

Expand All @@ -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
Expand Down Expand Up @@ -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()
}

Expand Down
56 changes: 56 additions & 0 deletions internal/registry/policy/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
5 changes: 4 additions & 1 deletion internal/registry/registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() })

Expand Down Expand Up @@ -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()
Expand Down
Loading