diff --git a/audit/format.go b/audit/format.go index 8bbf1b1cc..cbc7f8a06 100644 --- a/audit/format.go +++ b/audit/format.go @@ -283,6 +283,7 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config Metadata: auth.Metadata, RemainingUses: req.ClientTokenRemainingUses, EntityID: auth.EntityID, + EntityCreated: auth.EntityCreated, TokenType: auth.TokenType.String(), TokenTTL: int64(auth.TTL.Seconds()), }, @@ -415,6 +416,7 @@ type AuditAuth struct { NumUses int `json:"num_uses,omitempty"` RemainingUses int `json:"remaining_uses,omitempty"` EntityID string `json:"entity_id,omitempty"` + EntityCreated bool `json:"entity_created,omitempty"` TokenType string `json:"token_type,omitempty"` TokenTTL int64 `json:"token_ttl,omitempty"` TokenIssueTime string `json:"token_issue_time,omitempty"` diff --git a/changelog/15487.txt b/changelog/15487.txt new file mode 100644 index 000000000..4ce85a1f1 --- /dev/null +++ b/changelog/15487.txt @@ -0,0 +1,3 @@ +```release-note:improvement +audit: added entity_created boolean to audit log, set when login operations create an entity +``` \ No newline at end of file diff --git a/sdk/logical/auth.go b/sdk/logical/auth.go index 9e9524a22..62707e819 100644 --- a/sdk/logical/auth.go +++ b/sdk/logical/auth.go @@ -108,6 +108,9 @@ type Auth struct { // MFARequirement MFARequirement *MFARequirement `json:"mfa_requirement"` + + // EntityCreated is set to true if an entity is created as part of a login request + EntityCreated bool `json:"entity_created"` } func (a *Auth) GoString() string { diff --git a/vault/core_metrics_test.go b/vault/core_metrics_test.go index 543e970c7..6fb3b179f 100644 --- a/vault/core_metrics_test.go +++ b/vault/core_metrics_test.go @@ -269,7 +269,7 @@ func TestCoreMetrics_EntityGauges(t *testing.T) { Name: "githubuser", } - entity, err := is.CreateOrFetchEntity(ctx, alias1) + entity, _, err := is.CreateOrFetchEntity(ctx, alias1) if err != nil { t.Fatal(err) } diff --git a/vault/identity_store.go b/vault/identity_store.go index 8d1c743aa..b3b01bb5e 100644 --- a/vault/identity_store.go +++ b/vault/identity_store.go @@ -1099,37 +1099,38 @@ func (i *IdentityStore) CreateEntity(ctx context.Context) (*identity.Entity, err // CreateOrFetchEntity creates a new entity. This is used by core to // associate each login attempt by an alias to a unified entity in Vault. -func (i *IdentityStore) CreateOrFetchEntity(ctx context.Context, alias *logical.Alias) (*identity.Entity, error) { +func (i *IdentityStore) CreateOrFetchEntity(ctx context.Context, alias *logical.Alias) (*identity.Entity, bool, error) { defer metrics.MeasureSince([]string{"identity", "create_or_fetch_entity"}, time.Now()) var entity *identity.Entity var err error var update bool + var entityCreated bool if alias == nil { - return nil, fmt.Errorf("alias is nil") + return nil, false, fmt.Errorf("alias is nil") } if alias.Name == "" { - return nil, fmt.Errorf("empty alias name") + return nil, false, fmt.Errorf("empty alias name") } mountValidationResp := i.router.ValidateMountByAccessor(alias.MountAccessor) if mountValidationResp == nil { - return nil, fmt.Errorf("invalid mount accessor %q", alias.MountAccessor) + return nil, false, fmt.Errorf("invalid mount accessor %q", alias.MountAccessor) } if mountValidationResp.MountType != alias.MountType { - return nil, fmt.Errorf("mount accessor %q is not a mount of type %q", alias.MountAccessor, alias.MountType) + return nil, false, fmt.Errorf("mount accessor %q is not a mount of type %q", alias.MountAccessor, alias.MountType) } // Check if an entity already exists for the given alias entity, err = i.entityByAliasFactors(alias.MountAccessor, alias.Name, true) if err != nil { - return nil, err + return nil, false, err } if entity != nil && changedAliasIndex(entity, alias) == -1 { - return entity, nil + return entity, false, nil } i.lock.Lock() @@ -1142,12 +1143,12 @@ func (i *IdentityStore) CreateOrFetchEntity(ctx context.Context, alias *logical. // Check if an entity was created before acquiring the lock entity, err = i.entityByAliasFactorsInTxn(txn, alias.MountAccessor, alias.Name, true) if err != nil { - return nil, err + return nil, false, err } if entity != nil { idx := changedAliasIndex(entity, alias) if idx == -1 { - return entity, nil + return entity, false, nil } a := entity.Aliases[idx] a.Metadata = alias.Metadata @@ -1160,7 +1161,7 @@ func (i *IdentityStore) CreateOrFetchEntity(ctx context.Context, alias *logical. entity = new(identity.Entity) err = i.sanitizeEntity(ctx, entity) if err != nil { - return nil, err + return nil, false, err } // Create a new alias @@ -1176,7 +1177,7 @@ func (i *IdentityStore) CreateOrFetchEntity(ctx context.Context, alias *logical. err = i.sanitizeAlias(ctx, newAlias) if err != nil { - return nil, err + return nil, false, err } i.logger.Debug("creating a new entity", "alias", newAlias) @@ -1202,16 +1203,18 @@ func (i *IdentityStore) CreateOrFetchEntity(ctx context.Context, alias *logical. {"auth_method", newAlias.MountType}, {"mount_point", newAlias.MountPath}, }) + entityCreated = true } // Update MemDB and persist entity object err = i.upsertEntityInTxn(ctx, txn, entity, nil, true) if err != nil { - return nil, err + return nil, false, err } txn.Commit() - return entity.Clone() + clonedEntity, err := entity.Clone() + return clonedEntity, entityCreated, err } // changedAliasIndex searches an entity for changed alias metadata. diff --git a/vault/identity_store_test.go b/vault/identity_store_test.go index d62a209f1..a5e335f94 100644 --- a/vault/identity_store_test.go +++ b/vault/identity_store_test.go @@ -182,7 +182,7 @@ func TestIdentityStore_EntityIDPassthrough(t *testing.T) { } // Create an entity with GitHub alias - entity, err := is.CreateOrFetchEntity(ctx, alias) + entity, _, err := is.CreateOrFetchEntity(ctx, alias) if err != nil { t.Fatal(err) } @@ -258,7 +258,7 @@ func TestIdentityStore_CreateOrFetchEntity(t *testing.T) { }, } - entity, err := is.CreateOrFetchEntity(ctx, alias) + entity, _, err := is.CreateOrFetchEntity(ctx, alias) if err != nil { t.Fatal(err) } @@ -274,7 +274,7 @@ func TestIdentityStore_CreateOrFetchEntity(t *testing.T) { t.Fatalf("bad: alias name; expected: %q, actual: %q", alias.Name, entity.Aliases[0].Name) } - entity, err = is.CreateOrFetchEntity(ctx, alias) + entity, _, err = is.CreateOrFetchEntity(ctx, alias) if err != nil { t.Fatal(err) } @@ -309,7 +309,7 @@ func TestIdentityStore_CreateOrFetchEntity(t *testing.T) { t.Fatalf("err:%v resp:%#v", err, resp) } - entity, err = is.CreateOrFetchEntity(ctx, alias) + entity, _, err = is.CreateOrFetchEntity(ctx, alias) if err != nil { t.Fatal(err) } @@ -335,7 +335,7 @@ func TestIdentityStore_CreateOrFetchEntity(t *testing.T) { "foo": "zzzz", } - entity, err = is.CreateOrFetchEntity(ctx, alias) + entity, _, err = is.CreateOrFetchEntity(ctx, alias) if err != nil { t.Fatal(err) } @@ -599,7 +599,7 @@ func TestIdentityStore_MergeConflictingAliases(t *testing.T) { t.Fatal(err) } - newEntity, err := c.identityStore.CreateOrFetchEntity(namespace.RootContext(nil), &logical.Alias{ + newEntity, _, err := c.identityStore.CreateOrFetchEntity(namespace.RootContext(nil), &logical.Alias{ MountAccessor: meGH.Accessor, MountType: "github", Name: "githubuser", @@ -792,14 +792,14 @@ func TestIdentityStore_NewEntityCounter(t *testing.T) { }, } - _, err = is.CreateOrFetchEntity(ctx, alias) + _, _, err = is.CreateOrFetchEntity(ctx, alias) if err != nil { t.Fatal(err) } expectSingleCount(t, sink, "identity.entity.creation") - _, err = is.CreateOrFetchEntity(ctx, alias) + _, _, err = is.CreateOrFetchEntity(ctx, alias) if err != nil { t.Fatal(err) } diff --git a/vault/request_handling.go b/vault/request_handling.go index 74f6957ae..e7f679231 100644 --- a/vault/request_handling.go +++ b/vault/request_handling.go @@ -488,7 +488,6 @@ func (c *Core) handleCancelableRequest(ctx context.Context, req *logical.Request req.Operation == logical.PatchOperation) { return logical.ErrorResponse("cannot write to a path ending in '/'"), nil } - waitGroup, err := waitForReplicationState(ctx, c, req) if err != nil { return nil, err @@ -1437,7 +1436,7 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re var err error // Fetch the entity for the alias, or create an entity if one // doesn't exist. - entity, err = c.identityStore.CreateOrFetchEntity(ctx, auth.Alias) + entity, entityCreated, err := c.identityStore.CreateOrFetchEntity(ctx, auth.Alias) if err != nil { switch auth.Alias.Local { case true: @@ -1446,8 +1445,12 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re resp.AddWarning("primary cluster doesn't yet issue entities for local auth mounts; falling back to not issuing entities for local auth mounts") goto CREATE_TOKEN } + // If the entity creation via forwarding was successful, update the bool flag + if entity != nil && err == nil { + entityCreated = true + } default: - entity, err = possiblyForwardAliasCreation(ctx, c, err, auth, entity) + entity, entityCreated, err = possiblyForwardAliasCreation(ctx, c, err, auth, entity) } } if err != nil { @@ -1462,6 +1465,7 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re } auth.EntityID = entity.ID + auth.EntityCreated = entityCreated validAliases, err := c.identityStore.refreshExternalGroupMembershipsByEntityID(ctx, auth.EntityID, auth.GroupAliases, req.MountAccessor) if err != nil { return nil, nil, err diff --git a/vault/request_handling_util.go b/vault/request_handling_util.go index ff0a291aa..a08709d5c 100644 --- a/vault/request_handling_util.go +++ b/vault/request_handling_util.go @@ -50,8 +50,8 @@ func getAuthRegisterFunc(c *Core) (RegisterAuthFunc, error) { return c.RegisterAuth, nil } -func possiblyForwardAliasCreation(ctx context.Context, c *Core, inErr error, auth *logical.Auth, entity *identity.Entity) (*identity.Entity, error) { - return entity, inErr +func possiblyForwardAliasCreation(ctx context.Context, c *Core, inErr error, auth *logical.Auth, entity *identity.Entity) (*identity.Entity, bool, error) { + return entity, false, inErr } var errCreateEntityUnimplemented = "create entity unimplemented in the server" diff --git a/vault/token_store.go b/vault/token_store.go index 3f5d9c0ac..331e18925 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -2591,7 +2591,7 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque } // Create or fetch entity from entity alias - entity, err := ts.core.identityStore.CreateOrFetchEntity(ctx, alias) + entity, _, err := ts.core.identityStore.CreateOrFetchEntity(ctx, alias) if err != nil { return nil, err }