package proxycfgglue import ( "context" "sync" "testing" "time" "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/require" "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/acl/resolver" "github.com/hashicorp/consul/agent/consul/state" "github.com/hashicorp/consul/agent/consul/stream" "github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/submatview" "github.com/hashicorp/consul/proto/pbsubscribe" "github.com/hashicorp/consul/sdk/testutil" ) func TestServerIntentions(t *testing.T) { const ( serviceName = "web" index = 1 ) logger := hclog.NewNullLogger() ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) store := submatview.NewStore(logger) go store.Run(ctx) publisher := stream.NewEventPublisher(10 * time.Second) publisher.RegisterHandler(pbsubscribe.Topic_ServiceIntentions, func(stream.SubscribeRequest, stream.SnapshotAppender) (uint64, error) { return index, nil }, false) go publisher.Run(ctx) intentions := ServerIntentions(ServerDataSourceDeps{ ACLResolver: newStaticResolver(acl.ManageAll()), ViewStore: store, EventPublisher: publisher, Logger: logger, }) eventCh := make(chan proxycfg.UpdateEvent) require.NoError(t, intentions.Notify(ctx, &structs.ServiceSpecificRequest{ ServiceName: serviceName, EnterpriseMeta: *acl.DefaultEnterpriseMeta(), }, "", eventCh)) testutil.RunStep(t, "initial snapshot", func(t *testing.T) { getEventResult[structs.Intentions](t, eventCh) }) testutil.RunStep(t, "publishing an explicit intention", func(t *testing.T) { publisher.Publish([]stream.Event{ { Topic: pbsubscribe.Topic_ServiceIntentions, Index: index + 1, Payload: state.EventPayloadConfigEntry{ Op: pbsubscribe.ConfigEntryUpdate_Upsert, Value: &structs.ServiceIntentionsConfigEntry{ Name: serviceName, Sources: []*structs.SourceIntention{ {Name: "db", Action: structs.IntentionActionAllow, Precedence: 1}, }, }, }, }, }) result := getEventResult[structs.Intentions](t, eventCh) require.Len(t, result, 1) intention := result[0] require.Equal(t, intention.DestinationName, serviceName) require.Equal(t, intention.SourceName, "db") }) testutil.RunStep(t, "publishing a wildcard intention", func(t *testing.T) { publisher.Publish([]stream.Event{ { Topic: pbsubscribe.Topic_ServiceIntentions, Index: index + 2, Payload: state.EventPayloadConfigEntry{ Op: pbsubscribe.ConfigEntryUpdate_Upsert, Value: &structs.ServiceIntentionsConfigEntry{ Name: structs.WildcardSpecifier, Sources: []*structs.SourceIntention{ {Name: structs.WildcardSpecifier, Action: structs.IntentionActionAllow, Precedence: 0}, }, }, }, }, }) result := getEventResult[structs.Intentions](t, eventCh) require.Len(t, result, 2) a := result[0] require.Equal(t, a.DestinationName, serviceName) require.Equal(t, a.SourceName, "db") b := result[1] require.Equal(t, b.DestinationName, structs.WildcardSpecifier) require.Equal(t, b.SourceName, structs.WildcardSpecifier) }) testutil.RunStep(t, "publishing a delete event", func(t *testing.T) { publisher.Publish([]stream.Event{ { Topic: pbsubscribe.Topic_ServiceIntentions, Index: index + 3, Payload: state.EventPayloadConfigEntry{ Op: pbsubscribe.ConfigEntryUpdate_Delete, Value: &structs.ServiceIntentionsConfigEntry{ Name: serviceName, }, }, }, }) result := getEventResult[structs.Intentions](t, eventCh) require.Len(t, result, 1) }) } type staticResolver struct { mu sync.Mutex authorizer acl.Authorizer } func newStaticResolver(authz acl.Authorizer) *staticResolver { resolver := new(staticResolver) resolver.SwapAuthorizer(authz) return resolver } func (r *staticResolver) SwapAuthorizer(authz acl.Authorizer) { r.mu.Lock() defer r.mu.Unlock() r.authorizer = authz } func (r *staticResolver) ResolveTokenAndDefaultMeta(_ string, entMeta *acl.EnterpriseMeta, authzContext *acl.AuthorizerContext) (resolver.Result, error) { entMeta.FillAuthzContext(authzContext) r.mu.Lock() defer r.mu.Unlock() return resolver.Result{Authorizer: r.authorizer}, nil }