From 198e79bac83fc1c34a7bd851e08a560e062d61f5 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:54:28 +0000 Subject: [PATCH 1/4] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 819db890172c..8475a7af238b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-08-26)](#2024-08-26) +- [ (2024-09-16)](#2024-09-16) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Documentation](#documentation) @@ -331,10 +331,36 @@ -# [](https://github.com/ory/kratos/compare/v1.2.0...v) (2024-08-26) +# [](https://github.com/ory/kratos/compare/v1.2.0...v) (2024-09-16) ## Breaking Changes +Please note that the `via` parameter is deprecated when performing SMS 2FA. It +will be removed in a future version. If the parameter is not included in the +request, the user will see all their phone/email addresses from which to perform +the flow. + +Before upgrading, ensure that your identity schema has the appropriate code +configuration when using the code method for passwordless or 2fa login. + +If you are using the code method for 2FA login already, or you are using it for +1FA login but have not yet configured the code identifier, set +`selfservice.methods.code.config.missing_credential_fallback_enabled` to `true` +to prevent users from being locked out. + +Please note that the `via` parameter is deprecated when performing SMS 2FA. It +will be removed in a future version. If the parameter is not included in the +request, the user will see all their phone/email addresses from which to perform +the flow. + +Before upgrading, ensure that your identity schema has the appropriate code +configuration when using the code method for passwordless or 2fa login. + +If you are using the code method for 2FA login already, or you are using it for +1FA login but have not yet configured the code identifier, set +`selfservice.methods.code.config.missing_credential_fallback_enabled` to `true` +to prevent users from being locked out. + Going forward, the `/admin/session/.../extend` endpoint will return 204 no content for new Ory Network projects. We will deprecate returning 200 + session body in the future. @@ -365,9 +391,15 @@ body in the future. - Add missing JS triggers ([7597bc6](https://github.com/ory/kratos/commit/7597bc6345848b66161d5a9b7a42307bbc85c978)) +- Add PKCE config key to config schema + ([#4098](https://github.com/ory/kratos/issues/4098)) + ([2c7ff3c](https://github.com/ory/kratos/commit/2c7ff3c8baab6aaa105e2d733a483fc07537470f)) - Concurrent map update for webhook header ([#4055](https://github.com/ory/kratos/issues/4055)) ([6ceb2f1](https://github.com/ory/kratos/commit/6ceb2f1213e1b28d3aa72380661e4aa985bfa437)) +- Do not populate `id_first` first step for account linking flows + ([#4074](https://github.com/ory/kratos/issues/4074)) + ([6ab2637](https://github.com/ory/kratos/commit/6ab2637652013e0ff377f52355e2025d68c7b3d3)) - Downgrade go-webauthn ([#4035](https://github.com/ory/kratos/issues/4035)) ([4d1954a](https://github.com/ory/kratos/commit/4d1954ac74dee358f9a08e619848dfe94e4934ce)) - Emit SelfServiceMethodUsed in SettingsSucceeded event @@ -375,6 +407,16 @@ body in the future. ([76af303](https://github.com/ory/kratos/commit/76af303b20ae5dffb932169a73667a55be3f3f80)) - Filter web hook headers ([#4048](https://github.com/ory/kratos/issues/4048)) ([ddb838e](https://github.com/ory/kratos/commit/ddb838e0e8f7d752cd1708c505e80b6c0ccc0b8a)) +- Improve OIDC account linking UI + ([#4036](https://github.com/ory/kratos/issues/4036)) + ([2b4a618](https://github.com/ory/kratos/commit/2b4a618485c9d79762243f59b35f142083f5492c)) +- Include duplicate credentials in account linking message + ([#4079](https://github.com/ory/kratos/issues/4079)) + ([122b63d](https://github.com/ory/kratos/commit/122b63d68a3ff2ad78107300869c5a6d2aa43354)) +- Incorrect append of code credential identifier + ([#4102](https://github.com/ory/kratos/issues/4102)) + ([3215792](https://github.com/ory/kratos/commit/3215792df4cab494c05ef09e969b2fa0ed95a98b)), + closes [#4076](https://github.com/ory/kratos/issues/4076) - Jsonnet timeouts ([#3979](https://github.com/ory/kratos/issues/3979)) ([7c5299f](https://github.com/ory/kratos/commit/7c5299f1f832ebbe0622d0920b7a91253d26b06c)) - Move password migration hook config @@ -391,6 +433,14 @@ body in the future. migrate_hook: ... ``` +- Normalize code credentials and deprecate via parameter + ([c417b4a](https://github.com/ory/kratos/commit/c417b4aa76a76d3aebb4474999d7bb072615bd9f)): + + Before this, code credentials for passwordless and mfa login were incorrectly + stored and normalized. This could cause issues where the system would not + detect the user's phone number, and where SMS/email MFA would not properly + work with the `highest_available` setting. + - Password migration hook config ([#4001](https://github.com/ory/kratos/issues/4001)) ([50deedf](https://github.com/ory/kratos/commit/50deedfeecf7adbc948521371b181306a0c26cf1)): @@ -408,14 +458,38 @@ body in the future. - Replace submit with continue button for recovery and verification and add maxlength ([04850f4](https://github.com/ory/kratos/commit/04850f45cfbdc89223366ffa3b540d579a3b44be)) +- Return credentials in FindByCredentialsIdentifier + ([#4068](https://github.com/ory/kratos/issues/4068)) + ([f949173](https://github.com/ory/kratos/commit/f949173b3ed3d45167bb4af8b95440d5e4a39636)): + + Instead of re-fetching the credentials later (expensive), we load them only + once. + +- **security:** Code credential does not respect `highest_available` setting + ([b0111d4](https://github.com/ory/kratos/commit/b0111d4bd561d0f0e2f5883f30fac36fcf7135d5)): + + This patch fixes a security vulnerability which prevents the `code` method to + properly report it's credentials count to the `highest_available` mechanism. + + For more details on this issue please refer to the + [security advisory](https://github.com/ory/kratos/security/advisories/GHSA-wc43-73w7-x2f5). + - Timestamp precision on mysql ([9a1f171](https://github.com/ory/kratos/commit/9a1f171c1a4a8d20dc2103073bdc11ee3fdc70af)) +- Transient_payload is lost when verification flow started as part of + registration ([#3983](https://github.com/ory/kratos/issues/3983)) + ([192f10f](https://github.com/ory/kratos/commit/192f10f4ad9eb44a612baaccfc71765d52c7e1ed)) - Trigger oidc web hook on sign in after registration ([#4027](https://github.com/ory/kratos/issues/4027)) ([ad5fb09](https://github.com/ory/kratos/commit/ad5fb09687f863e7c5d45868d0b8f5ec2d965372)) - Typo in login link CLI error messages ([#3995](https://github.com/ory/kratos/issues/3995)) ([8350625](https://github.com/ory/kratos/commit/835062542077b9dd8d6a30836d0455adb015265d)) +- Validate page tokens for better error codes + ([#4021](https://github.com/ory/kratos/issues/4021)) + ([32737dc](https://github.com/ory/kratos/commit/32737dc708c1ecf0ec0ceaa4bbc0ac09286186fd)) +- Whoami latency ([#4070](https://github.com/ory/kratos/issues/4070)) + ([ff6ed5b](https://github.com/ory/kratos/commit/ff6ed5b70b7f715fc38a41cedd17b5323aebd79e)) ### Documentation @@ -474,6 +548,40 @@ body in the future. - Clarify session extend behavior ([#3962](https://github.com/ory/kratos/issues/3962)) ([af5ea35](https://github.com/ory/kratos/commit/af5ea35759e74d7a1637823abcc21dc8e3e39a9d)) +- Client-side PKCE take 3 ([#4078](https://github.com/ory/kratos/issues/4078)) + ([f7c1024](https://github.com/ory/kratos/commit/f7c102456a71b226d8353b9d59cc03fb2ba0af40)): + + - feat: client-side PKCE + + This change introduces a new configuration for OIDC providers: pkce with + values auto (default), never, force. + + When auto is specified or the field is omitted, Kratos will perform + autodiscovery and perform PKCE when the server advertises support for it. This + requires the issuer_url to be set for the provider. + + never completely disables PKCE support. This is only theoretically useful: + when a provider advertises PKCE support but doesn't actually implement it. + + force always sends a PKCE challenge in the initial redirect URL, regardless of + what the provider advertises. This setting is useful when the provider offers + PKCE but doesn't advertise it in his ./well-known/openid-configuration. + + Important: When setting pkce: force, you must whitelist a different return URL + for your OAuth2 client in the provider's configuration. Instead of + /self-service/methods/oidc/callback/, you must use + /self-service/methods/oidc/callback (note missing last path + segment). This is to enable the use of the same OAuth client ID+secret when + configuring several Kratos OIDC providers, without having to whitelist + individual redirect_uris for each Kratos provider config. + + - chore: regenerate SDK, bump DB versions, cleanup tool install + + - chore: get final organization ID from provider config during registration + and login + + - chore: fixup OIDC function signatures and improve tests + - Identifier first auth ([1bdc19a](https://github.com/ory/kratos/commit/1bdc19ae3e1a3df38234cb892f65de4a2c95f041)) - Identifier first login for all first factor login methods @@ -568,6 +676,8 @@ body in the future. - Improve stability of refresh test ([#4037](https://github.com/ory/kratos/issues/4037)) ([68693a4](https://github.com/ory/kratos/commit/68693a43e4e1e3028f17789e72d0b79f6298d139)) +- Resolve CI failures ([#4067](https://github.com/ory/kratos/issues/4067)) + ([dbf7274](https://github.com/ory/kratos/commit/dbf7274f7a4be56c33b06559875c42725bf4a351)) - Resolve issues and update snapshots for all selfservice strategies ([e2e81ac](https://github.com/ory/kratos/commit/e2e81ac16726b180d33c57913e3cac099daf946b)) - Update incorrect usage of Auth0 in Salesforce tests @@ -578,6 +688,24 @@ body in the future. ### Unclassified +- Merge commit from fork + ([123e807](https://github.com/ory/kratos/commit/123e80782b392095631ee2e0d1bd6ec337c1fb79)): + + - fix(security): code credential does not respect `highest_available` setting + + This patch fixes a security vulnerability which prevents the `code` method to + properly report it's credentials count to the `highest_available` mechanism. + + For more details on this issue please refer to the + [security advisory](https://github.com/ory/kratos/security/advisories/GHSA-wc43-73w7-x2f5). + + - fix: normalize code credentials and deprecate via parameter + + Before this, code credentials for passwordless and mfa login were incorrectly + stored and normalized. This could cause issues where the system would not + detect the user's phone number, and where SMS/email MFA would not properly + work with the `highest_available` setting. + - Update .github/workflows/ci.yaml ([2d60772](https://github.com/ory/kratos/commit/2d60772062a684c3a27f28b8836c3548f5b8cea9)) - Update Code QL action to v2 From 9aefc0a0b3b890d9a0b796e81474499db7636525 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Mon, 16 Sep 2024 17:45:10 +0200 Subject: [PATCH 2/4] chore: refactor API in package cipher for easier dependency injection (#4103) --- cipher/aes.go | 16 +++++----------- cipher/chacha20.go | 15 +++++---------- cipher/cipher.go | 4 ++++ cipher/cipher_test.go | 6 +++--- cipher/noop.go | 19 +++++-------------- driver/config/config.go | 5 ++++- driver/registry_default.go | 6 +++--- identity/identity_test.go | 2 +- 8 files changed, 30 insertions(+), 43 deletions(-) diff --git a/cipher/aes.go b/cipher/aes.go index f4bae7e98001..7c34651d5e3f 100644 --- a/cipher/aes.go +++ b/cipher/aes.go @@ -12,19 +12,13 @@ import ( "github.com/ory/herodot" "github.com/pkg/errors" - - "github.com/ory/kratos/driver/config" ) type AES struct { - c AESConfiguration -} - -type AESConfiguration interface { - config.Provider + c SecretsProvider } -func NewCryptAES(c AESConfiguration) *AES { +func NewCryptAES(c SecretsProvider) *AES { return &AES{c: c} } @@ -36,11 +30,11 @@ func (a *AES) Encrypt(ctx context.Context, message []byte) (string, error) { return "", nil } - if len(a.c.Config().SecretsCipher(ctx)) == 0 { + if len(a.c.SecretsCipher(ctx)) == 0 { return "", errors.WithStack(herodot.ErrInternalServerError.WithReason("Unable to encrypt message because no cipher secrets were configured.")) } - ciphertext, err := cryptopasta.Encrypt(message, &a.c.Config().SecretsCipher(ctx)[0]) + ciphertext, err := cryptopasta.Encrypt(message, &a.c.SecretsCipher(ctx)[0]) return hex.EncodeToString(ciphertext), errors.WithStack(err) } @@ -52,7 +46,7 @@ func (a *AES) Decrypt(ctx context.Context, ciphertext string) ([]byte, error) { return nil, nil } - secrets := a.c.Config().SecretsCipher(ctx) + secrets := a.c.SecretsCipher(ctx) if len(secrets) == 0 { return nil, errors.WithStack(herodot.ErrInternalServerError.WithReason("Unable to decipher the encrypted message because no AES secrets were configured.")) } diff --git a/cipher/chacha20.go b/cipher/chacha20.go index 9c35e4237369..6ad71746c895 100644 --- a/cipher/chacha20.go +++ b/cipher/chacha20.go @@ -14,18 +14,13 @@ import ( "golang.org/x/crypto/chacha20poly1305" "github.com/ory/herodot" - "github.com/ory/kratos/driver/config" ) -type ChaCha20Configuration interface { - config.Provider -} - type XChaCha20Poly1305 struct { - c ChaCha20Configuration + c SecretsProvider } -func NewCryptChaCha20(c ChaCha20Configuration) *XChaCha20Poly1305 { +func NewCryptChaCha20(c SecretsProvider) *XChaCha20Poly1305 { return &XChaCha20Poly1305{c: c} } @@ -35,11 +30,11 @@ func (c *XChaCha20Poly1305) Encrypt(ctx context.Context, message []byte) (string return "", nil } - if len(c.c.Config().SecretsCipher(ctx)) == 0 { + if len(c.c.SecretsCipher(ctx)) == 0 { return "", errors.WithStack(herodot.ErrInternalServerError.WithReason("Unable to encrypt message because no cipher secrets were configured.")) } - aead, err := chacha20poly1305.NewX(c.c.Config().SecretsCipher(ctx)[0][:]) + aead, err := chacha20poly1305.NewX(c.c.SecretsCipher(ctx)[0][:]) if err != nil { return "", herodot.ErrInternalServerError.WithWrap(err).WithReason("Unable to generate key") } @@ -65,7 +60,7 @@ func (c *XChaCha20Poly1305) Decrypt(ctx context.Context, ciphertext string) ([]b return nil, nil } - secrets := c.c.Config().SecretsCipher(ctx) + secrets := c.c.SecretsCipher(ctx) if len(secrets) == 0 { return nil, errors.WithStack(herodot.ErrInternalServerError.WithReason("Unable to decipher the encrypted message because no cipher secrets were configured.")) } diff --git a/cipher/cipher.go b/cipher/cipher.go index 27fc316c2cfa..73c2a0b3b5c3 100644 --- a/cipher/cipher.go +++ b/cipher/cipher.go @@ -23,3 +23,7 @@ type Cipher interface { type Provider interface { Cipher(ctx context.Context) Cipher } + +type SecretsProvider interface { + SecretsCipher(ctx context.Context) [][32]byte +} diff --git a/cipher/cipher_test.go b/cipher/cipher_test.go index 90e02ff0de45..1680622517d8 100644 --- a/cipher/cipher_test.go +++ b/cipher/cipher_test.go @@ -29,8 +29,8 @@ func TestCipher(t *testing.T) { _, reg := internal.NewFastRegistryWithMocks(t, configx.WithValue(config.ViperKeySecretsDefault, goodSecret)) ciphers := []cipher.Cipher{ - cipher.NewCryptAES(reg), - cipher.NewCryptChaCha20(reg), + cipher.NewCryptAES(reg.Config()), + cipher.NewCryptChaCha20(reg.Config()), } for _, c := range ciphers { @@ -78,7 +78,7 @@ func TestCipher(t *testing.T) { }) } - c := cipher.NewNoop(reg) + c := cipher.NewNoop() t.Run(fmt.Sprintf("cipher=%T", c), func(t *testing.T) { t.Parallel() testAllWork(ctx, t, c) diff --git a/cipher/noop.go b/cipher/noop.go index 2b2a353beb23..481d4c8bcba2 100644 --- a/cipher/noop.go +++ b/cipher/noop.go @@ -6,30 +6,21 @@ package cipher import ( "context" "encoding/hex" - - "github.com/ory/kratos/driver/config" ) // Noop is default cipher implementation witch does not do encryption +type Noop struct{} -type NoopConfiguration interface { - config.Provider -} - -type Noop struct { - c NoopConfiguration -} - -func NewNoop(c NoopConfiguration) *Noop { - return &Noop{c: c} +func NewNoop() *Noop { + return &Noop{} } // Encrypt encode message to hex -func (c *Noop) Encrypt(_ context.Context, message []byte) (string, error) { +func (*Noop) Encrypt(_ context.Context, message []byte) (string, error) { return hex.EncodeToString(message), nil } // Decrypt decode the hex message -func (c *Noop) Decrypt(_ context.Context, ciphertext string) ([]byte, error) { +func (*Noop) Decrypt(_ context.Context, ciphertext string) ([]byte, error) { return hex.DecodeString(ciphertext) } diff --git a/driver/config/config.go b/driver/config/config.go index 52762356fcc2..d1e28d166b98 100644 --- a/driver/config/config.go +++ b/driver/config/config.go @@ -859,6 +859,10 @@ func (p *Config) SecretsSession(ctx context.Context) [][]byte { func (p *Config) SecretsCipher(ctx context.Context) [][32]byte { secrets := p.GetProvider(ctx).Strings(ViperKeySecretsCipher) + return ToCipherSecrets(secrets) +} + +func ToCipherSecrets(secrets []string) [][32]byte { var cleanSecrets []string for k := range secrets { if len(secrets[k]) == 32 { @@ -1615,7 +1619,6 @@ func (p *Config) DefaultConsistencyLevel(ctx context.Context) crdbx.ConsistencyL } func (p *Config) PasswordMigrationHook(ctx context.Context) *PasswordMigrationHook { - hook := &PasswordMigrationHook{ Enabled: p.GetProvider(ctx).BoolF(ViperKeyPasswordMigrationHook+".enabled", false), } diff --git a/driver/registry_default.go b/driver/registry_default.go index 8eef98ed28db..9f88c7375dd0 100644 --- a/driver/registry_default.go +++ b/driver/registry_default.go @@ -479,11 +479,11 @@ func (m *RegistryDefault) Cipher(ctx context.Context) cipher.Cipher { if m.crypter == nil { switch m.c.CipherAlgorithm(ctx) { case "xchacha20-poly1305": - m.crypter = cipher.NewCryptChaCha20(m) + m.crypter = cipher.NewCryptChaCha20(m.Config()) case "aes": - m.crypter = cipher.NewCryptAES(m) + m.crypter = cipher.NewCryptAES(m.Config()) default: - m.crypter = cipher.NewNoop(m) + m.crypter = cipher.NewNoop() m.l.Logger.Warning("No encryption configuration found. The default algorithm (noop) will be used, resulting in sensitive data being stored in plaintext") } } diff --git a/identity/identity_test.go b/identity/identity_test.go index d14388feacd4..3a84b7624abe 100644 --- a/identity/identity_test.go +++ b/identity/identity_test.go @@ -314,7 +314,7 @@ func TestVerifiableAddresses(t *testing.T) { type cipherProvider struct{} func (c *cipherProvider) Cipher(ctx context.Context) cipher.Cipher { - return cipher.NewNoop(nil) + return cipher.NewNoop() } func TestWithDeclassifiedCredentials(t *testing.T) { From c910b4ec18b173adac9b86468da0a09293ec549c Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Mon, 16 Sep 2024 15:46:41 +0000 Subject: [PATCH 3/4] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 20156f651f2faa0a79842de8d2fb4a09ee7094c1 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Tue, 17 Sep 2024 15:09:57 +0200 Subject: [PATCH 4/4] feat: emit events in identity persister (#4107) --- identity/manager.go | 10 ------ .../sql/identity/persister_identity.go | 35 +++++++++++++++---- x/events/events.go | 11 ++++++ 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/identity/manager.go b/identity/manager.go index f41f0b1a33bb..f35fd1468710 100644 --- a/identity/manager.go +++ b/identity/manager.go @@ -10,10 +10,7 @@ import ( "slices" "sort" - "go.opentelemetry.io/otel/trace" - "github.com/ory/kratos/schema" - "github.com/ory/kratos/x/events" "github.com/ory/x/sqlcon" "github.com/ory/x/otelx" @@ -101,7 +98,6 @@ func (m *Manager) Create(ctx context.Context, i *Identity, opts ...ManagerOption return err } - trace.SpanFromContext(ctx).AddEvent(events.NewIdentityCreated(ctx, i.ID)) return nil } @@ -346,10 +342,6 @@ func (m *Manager) CreateIdentities(ctx context.Context, identities []*Identity, return err } - for _, i := range identities { - trace.SpanFromContext(ctx).AddEvent(events.NewIdentityCreated(ctx, i.ID)) - } - return nil } @@ -416,7 +408,6 @@ func (m *Manager) UpdateSchemaID(ctx context.Context, id uuid.UUID, schemaID str return err } - trace.SpanFromContext(ctx).AddEvent(events.NewIdentityUpdated(ctx, id)) return m.r.PrivilegedIdentityPool().UpdateIdentity(ctx, original) } @@ -477,7 +468,6 @@ func (m *Manager) UpdateTraits(ctx context.Context, id uuid.UUID, traits Traits, return err } - trace.SpanFromContext(ctx).AddEvent(events.NewIdentityUpdated(ctx, id)) return m.r.PrivilegedIdentityPool().UpdateIdentity(ctx, updated) } diff --git a/persistence/sql/identity/persister_identity.go b/persistence/sql/identity/persister_identity.go index 807d3d67779d..127167613b98 100644 --- a/persistence/sql/identity/persister_identity.go +++ b/persistence/sql/identity/persister_identity.go @@ -13,6 +13,8 @@ import ( "sync" "time" + "github.com/ory/kratos/x/events" + "github.com/ory/x/crdbx" "github.com/gobuffalo/pop/v6" @@ -538,7 +540,7 @@ func (p *IdentityPersister) CreateIdentity(ctx context.Context, ident *identity. func (p *IdentityPersister) CreateIdentities(ctx context.Context, identities ...*identity.Identity) (err error) { ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.CreateIdentities", trace.WithAttributes( - attribute.Int("num_identities", len(identities)), + attribute.Int("identities.count", len(identities)), attribute.Stringer("network.id", p.NetworkID(ctx)))) defer otelx.End(span, &err) @@ -568,7 +570,7 @@ func (p *IdentityPersister) CreateIdentities(ctx context.Context, identities ... } } - return p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error { + if err := p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error { conn := &batch.TracerConnection{ Tracer: p.r.Tracer(ctx), Connection: tx, @@ -590,7 +592,15 @@ func (p *IdentityPersister) CreateIdentities(ctx context.Context, identities ... return sqlcon.HandleError(err) } return nil - }) + }); err != nil { + return err + } + + for _, ident := range identities { + span.AddEvent(events.NewIdentityCreated(ctx, ident.ID)) + } + + return nil } func (p *IdentityPersister) HydrateIdentityAssociations(ctx context.Context, i *identity.Identity, expand identity.Expandables) (err error) { @@ -960,10 +970,15 @@ func (p *IdentityPersister) UpdateIdentityColumns(ctx context.Context, i *identi attribute.Stringer("network.id", p.NetworkID(ctx)))) defer otelx.End(span, &err) - return p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error { + if err := p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error { _, err := tx.Where("id = ? AND nid = ?", i.ID, p.NetworkID(ctx)).UpdateQuery(i, columns...) return sqlcon.HandleError(err) - }) + }); err != nil { + return err + } + + span.AddEvent(events.NewIdentityUpdated(ctx, i.ID)) + return nil } func (p *IdentityPersister) UpdateIdentity(ctx context.Context, i *identity.Identity) (err error) { @@ -978,7 +993,7 @@ func (p *IdentityPersister) UpdateIdentity(ctx context.Context, i *identity.Iden } i.NID = p.NetworkID(ctx) - return sqlcon.HandleError(p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error { + if err := sqlcon.HandleError(p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error { // This returns "ErrNoRows" if the identity does not exist if err := update.Generic(WithTransaction(ctx, tx), tx, p.r.Tracer(ctx).Tracer(), i); err != nil { return err @@ -1003,7 +1018,12 @@ func (p *IdentityPersister) UpdateIdentity(ctx context.Context, i *identity.Iden } return sqlcon.HandleError(p.createIdentityCredentials(ctx, tx, i)) - })) + })); err != nil { + return err + } + + span.AddEvent(events.NewIdentityUpdated(ctx, i.ID)) + return nil } func (p *IdentityPersister) DeleteIdentity(ctx context.Context, id uuid.UUID) (err error) { @@ -1028,6 +1048,7 @@ func (p *IdentityPersister) DeleteIdentity(ctx context.Context, id uuid.UUID) (e if count == 0 { return errors.WithStack(sqlcon.ErrNoRows) } + span.AddEvent(events.NewIdentityDeleted(ctx, id)) return nil } diff --git a/x/events/events.go b/x/events/events.go index 13ec16955539..862a35a39e98 100644 --- a/x/events/events.go +++ b/x/events/events.go @@ -34,6 +34,7 @@ const ( VerificationSucceeded semconv.Event = "VerificationSucceeded" IdentityCreated semconv.Event = "IdentityCreated" IdentityUpdated semconv.Event = "IdentityUpdated" + IdentityDeleted semconv.Event = "IdentityDeleted" WebhookDelivered semconv.Event = "WebhookDelivered" WebhookSucceeded semconv.Event = "WebhookSucceeded" WebhookFailed semconv.Event = "WebhookFailed" @@ -262,6 +263,16 @@ func NewIdentityCreated(ctx context.Context, identityID uuid.UUID) (string, trac ) } +func NewIdentityDeleted(ctx context.Context, identityID uuid.UUID) (string, trace.EventOption) { + return IdentityDeleted.String(), + trace.WithAttributes( + append( + semconv.AttributesFromContext(ctx), + semconv.AttrIdentityID(identityID), + )..., + ) +} + func NewIdentityUpdated(ctx context.Context, identityID uuid.UUID) (string, trace.EventOption) { return IdentityUpdated.String(), trace.WithAttributes(