From fee85dac797690b565dc4d1f5f1b21471fad3178 Mon Sep 17 00:00:00 2001 From: Piotr Kazmierczak <470696+pkazmierczak@users.noreply.github.com> Date: Mon, 21 Nov 2022 10:06:05 +0100 Subject: [PATCH] acl: sso auth method event stream (#15280) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR implements SSO auth method support in the event stream. This PR is part of the SSO work captured under ☂️ ticket #13120. --- nomad/state/events.go | 26 +++++++++++++++++++ nomad/state/events_test.go | 53 ++++++++++++++++++++++++++++++++++++++ nomad/structs/event.go | 31 ++++++++++++++-------- 3 files changed, 99 insertions(+), 11 deletions(-) diff --git a/nomad/state/events.go b/nomad/state/events.go index d83d92461..1dee631f1 100644 --- a/nomad/state/events.go +++ b/nomad/state/events.go @@ -29,6 +29,8 @@ var MsgTypeEvents = map[structs.MessageType]string{ structs.ACLPolicyUpsertRequestType: structs.TypeACLPolicyUpserted, structs.ACLRolesDeleteByIDRequestType: structs.TypeACLRoleDeleted, structs.ACLRolesUpsertRequestType: structs.TypeACLRoleUpserted, + structs.ACLAuthMethodsUpsertRequestType: structs.TypeACLAuthMethodUpserted, + structs.ACLAuthMethodsDeleteRequestType: structs.TypeACLAuthMethodDeleted, structs.ServiceRegistrationUpsertRequestType: structs.TypeServiceRegistration, structs.ServiceRegistrationDeleteByIDRequestType: structs.TypeServiceDeregistration, structs.ServiceRegistrationDeleteByNodeIDRequestType: structs.TypeServiceDeregistration, @@ -91,6 +93,18 @@ func eventFromChange(change memdb.Change) (structs.Event, bool) { ACLRole: before, }, }, true + case TableACLAuthMethods: + before, ok := change.Before.(*structs.ACLAuthMethod) + if !ok { + return structs.Event{}, false + } + return structs.Event{ + Topic: structs.TopicACLAuthMethod, + Key: before.Name, + Payload: &structs.ACLAuthMethodEvent{ + AuthMethod: before, + }, + }, true case "nodes": before, ok := change.Before.(*structs.Node) if !ok { @@ -163,6 +177,18 @@ func eventFromChange(change memdb.Change) (structs.Event, bool) { ACLRole: after, }, }, true + case TableACLAuthMethods: + after, ok := change.After.(*structs.ACLAuthMethod) + if !ok { + return structs.Event{}, false + } + return structs.Event{ + Topic: structs.TopicACLAuthMethod, + Key: after.Name, + Payload: &structs.ACLAuthMethodEvent{ + AuthMethod: after, + }, + }, true case "evals": after, ok := change.After.(*structs.Evaluation) if !ok { diff --git a/nomad/state/events_test.go b/nomad/state/events_test.go index d2b6c3e39..5d3a69f36 100644 --- a/nomad/state/events_test.go +++ b/nomad/state/events_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/nomad/helper/uuid" "github.com/hashicorp/nomad/nomad/mock" "github.com/hashicorp/nomad/nomad/structs" + "github.com/shoenig/test/must" "github.com/stretchr/testify/require" ) @@ -1051,6 +1052,58 @@ func Test_eventsFromChanges_ACLRole(t *testing.T) { require.Equal(t, aclRole, eventPayload.ACLRole) } +func Test_eventsFromChanges_ACLAuthMethod(t *testing.T) { + ci.Parallel(t) + testState := TestStateStoreCfg(t, TestStateStorePublisher(t)) + defer testState.StopEventBroker() + + // Generate a test ACL auth method + authMethod := mock.ACLAuthMethod() + + // Upsert the auth method straight into state + writeTxn := testState.db.WriteTxn(10) + updated, err := testState.upsertACLAuthMethodTxn(10, writeTxn, authMethod) + must.True(t, updated) + must.NoError(t, err) + writeTxn.Txn.Commit() + + // Pull the events from the stream. + upsertChange := Changes{Changes: writeTxn.Changes(), Index: 10, MsgType: structs.ACLAuthMethodsUpsertRequestType} + receivedChange := eventsFromChanges(writeTxn, upsertChange) + must.NotNil(t, receivedChange) + + // Check the event, and its payload are what we are expecting. + must.Len(t, 1, receivedChange.Events) + must.Eq(t, structs.TopicACLAuthMethod, receivedChange.Events[0].Topic) + must.Eq(t, authMethod.Name, receivedChange.Events[0].Key) + must.Eq(t, structs.TypeACLAuthMethodUpserted, receivedChange.Events[0].Type) + must.Eq(t, uint64(10), receivedChange.Events[0].Index) + + eventPayload := receivedChange.Events[0].Payload.(*structs.ACLAuthMethodEvent) + must.Eq(t, authMethod, eventPayload.AuthMethod) + + // Delete the previously upserted auth method + deleteTxn := testState.db.WriteTxn(20) + must.NoError(t, testState.deleteACLAuthMethodTxn(deleteTxn, authMethod.Name)) + must.NoError(t, deleteTxn.Insert(tableIndex, &IndexEntry{TableACLAuthMethods, 20})) + deleteTxn.Txn.Commit() + + // Pull the events from the stream. + deleteChange := Changes{Changes: deleteTxn.Changes(), Index: 20, MsgType: structs.ACLAuthMethodsDeleteRequestType} + receivedDeleteChange := eventsFromChanges(deleteTxn, deleteChange) + must.NotNil(t, receivedDeleteChange) + + // Check the event, and its payload are what we are expecting. + must.Len(t, 1, receivedDeleteChange.Events) + must.Eq(t, structs.TopicACLAuthMethod, receivedDeleteChange.Events[0].Topic) + must.Eq(t, authMethod.Name, receivedDeleteChange.Events[0].Key) + must.Eq(t, structs.TypeACLAuthMethodDeleted, receivedDeleteChange.Events[0].Type) + must.Eq(t, uint64(20), receivedDeleteChange.Events[0].Index) + + eventPayload = receivedChange.Events[0].Payload.(*structs.ACLAuthMethodEvent) + must.Eq(t, authMethod, eventPayload.AuthMethod) +} + func requireNodeRegistrationEventEqual(t *testing.T, want, got structs.Event) { t.Helper() diff --git a/nomad/structs/event.go b/nomad/structs/event.go index 34c6ad981..ad6f49e0c 100644 --- a/nomad/structs/event.go +++ b/nomad/structs/event.go @@ -16,16 +16,17 @@ type EventStreamWrapper struct { type Topic string const ( - TopicDeployment Topic = "Deployment" - TopicEvaluation Topic = "Evaluation" - TopicAllocation Topic = "Allocation" - TopicJob Topic = "Job" - TopicNode Topic = "Node" - TopicACLPolicy Topic = "ACLPolicy" - TopicACLToken Topic = "ACLToken" - TopicACLRole Topic = "ACLRole" - TopicService Topic = "Service" - TopicAll Topic = "*" + TopicDeployment Topic = "Deployment" + TopicEvaluation Topic = "Evaluation" + TopicAllocation Topic = "Allocation" + TopicJob Topic = "Job" + TopicNode Topic = "Node" + TopicACLPolicy Topic = "ACLPolicy" + TopicACLToken Topic = "ACLToken" + TopicACLRole Topic = "ACLRole" + TopicACLAuthMethod Topic = "ACLAuthMethod" + TopicService Topic = "Service" + TopicAll Topic = "*" TypeNodeRegistration = "NodeRegistration" TypeNodeDeregistration = "NodeDeregistration" @@ -49,6 +50,8 @@ const ( TypeACLPolicyUpserted = "ACLPolicyUpserted" TypeACLRoleDeleted = "ACLRoleDeleted" TypeACLRoleUpserted = "ACLRoleUpserted" + TypeACLAuthMethodUpserted = "ACLAuthMethodUpserted" + TypeACLAuthMethodDeleted = "ACLAuthMethodDeleted" TypeServiceRegistration = "ServiceRegistration" TypeServiceDeregistration = "ServiceDeregistration" ) @@ -155,8 +158,14 @@ type ACLPolicyEvent struct { ACLPolicy *ACLPolicy } -// ACLRoleStreamEvent holds a newly updated or delete ACL role to be used as an +// ACLRoleStreamEvent holds a newly updated or deleted ACL role to be used as an // event within the event stream. type ACLRoleStreamEvent struct { ACLRole *ACLRole } + +// ACLAuthMethodEvent holds a newly updated or deleted ACL auth method to be +// used as an event in the event stream. +type ACLAuthMethodEvent struct { + AuthMethod *ACLAuthMethod +}