From dfb5543a209764ea259f4a8968fb2d6391d69911 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Thu, 28 Nov 2024 16:34:33 +0100 Subject: [PATCH 1/2] feat: emit admin recovery code event --- .../strategy/code/strategy_recovery_admin.go | 9 ++- .../strategy/link/strategy_recovery.go | 20 ++++--- x/events/events.go | 56 +++++++++++-------- 3 files changed, 52 insertions(+), 33 deletions(-) diff --git a/selfservice/strategy/code/strategy_recovery_admin.go b/selfservice/strategy/code/strategy_recovery_admin.go index d1626f8a3987..a8ab0f8a421a 100644 --- a/selfservice/strategy/code/strategy_recovery_admin.go +++ b/selfservice/strategy/code/strategy_recovery_admin.go @@ -8,13 +8,12 @@ import ( "net/http" "net/url" "time" - "github.com/gobuffalo/pop/v6" - + "go.opentelemetry.io/otel/trace" + "github.com/ory/kratos/x/events" "github.com/gofrs/uuid" "github.com/julienschmidt/httprouter" "github.com/pkg/errors" - "github.com/ory/herodot" "github.com/ory/kratos/identity" "github.com/ory/kratos/selfservice/flow" @@ -223,6 +222,10 @@ func (s *Strategy) createRecoveryCodeForIdentity(w http.ResponseWriter, r *http. return } + trace.SpanFromContext(r.Context()).AddEvent( + events.NewRecoveryCodeCreatedByAdmin(ctx, recoveryFlow.ID, id.ID, flowType.String(), "code"), + ) + s.deps.Audit(). WithField("identity_id", id.ID). WithSensitiveField("recovery_code", rawCode). diff --git a/selfservice/strategy/link/strategy_recovery.go b/selfservice/strategy/link/strategy_recovery.go index 6c92082e47ef..e3a9b4b80066 100644 --- a/selfservice/strategy/link/strategy_recovery.go +++ b/selfservice/strategy/link/strategy_recovery.go @@ -9,21 +9,19 @@ import ( "net/http" "net/url" "time" - + "go.opentelemetry.io/otel/trace" + "github.com/ory/kratos/x/events" "github.com/gobuffalo/pop/v6" - "github.com/gofrs/uuid" "github.com/julienschmidt/httprouter" "github.com/pkg/errors" "go.opentelemetry.io/otel/attribute" - "github.com/ory/herodot" "github.com/ory/x/decoderx" "github.com/ory/x/otelx" "github.com/ory/x/sqlcon" "github.com/ory/x/sqlxx" "github.com/ory/x/urlx" - "github.com/ory/kratos/identity" "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" @@ -146,13 +144,15 @@ type recoveryLinkForIdentity struct { // 404: errorGeneric // default: errorGeneric func (s *Strategy) createRecoveryLinkForIdentity(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + ctx := r.Context() + var p createRecoveryLinkForIdentityBody if err := s.dx.Decode(r, &p, decoderx.HTTPJSONDecoder()); err != nil { s.d.Writer().WriteError(w, r, err) return } - expiresIn := s.d.Config().SelfServiceLinkMethodLifespan(r.Context()) + expiresIn := s.d.Config().SelfServiceLinkMethodLifespan(ctx) if len(p.ExpiresIn) > 0 { var err error expiresIn, err = time.ParseDuration(p.ExpiresIn) @@ -173,7 +173,7 @@ func (s *Strategy) createRecoveryLinkForIdentity(w http.ResponseWriter, r *http. return } - id, err := s.d.IdentityPool().GetIdentity(r.Context(), p.IdentityID, identity.ExpandDefault) + id, err := s.d.IdentityPool().GetIdentity(ctx, p.IdentityID, identity.ExpandDefault) if errors.Is(err, sqlcon.ErrNoRows) { s.d.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf("The requested identity id does not exist.").WithWrap(err))) return @@ -183,7 +183,7 @@ func (s *Strategy) createRecoveryLinkForIdentity(w http.ResponseWriter, r *http. } token := NewAdminRecoveryToken(id.ID, req.ID, expiresIn) - if err := s.d.TransactionalPersisterProvider().Transaction(r.Context(), func(ctx context.Context, c *pop.Connection) error { + if err := s.d.TransactionalPersisterProvider().Transaction(ctx, func(ctx context.Context, c *pop.Connection) error { if err := s.d.RecoveryFlowPersister().CreateRecoveryFlow(ctx, req); err != nil { return err } @@ -194,6 +194,10 @@ func (s *Strategy) createRecoveryLinkForIdentity(w http.ResponseWriter, r *http. return } + trace.SpanFromContext(ctx).AddEvent( + events.NewRecoveryCodeCreatedByAdmin(ctx, req.ID, id.ID, req.Type.String(), "link"), + ) + s.d.Audit(). WithField("identity_id", id.ID). WithSensitiveField("recovery_link_token", token). @@ -202,7 +206,7 @@ func (s *Strategy) createRecoveryLinkForIdentity(w http.ResponseWriter, r *http. s.d.Writer().Write(w, r, &recoveryLinkForIdentity{ ExpiresAt: req.ExpiresAt.UTC(), RecoveryLink: urlx.CopyWithQuery( - urlx.AppendPaths(s.d.Config().SelfPublicURL(r.Context()), recovery.RouteSubmitFlow), + urlx.AppendPaths(s.d.Config().SelfPublicURL(ctx), recovery.RouteSubmitFlow), url.Values{ "token": {token.Token}, "flow": {req.ID.String()}, diff --git a/x/events/events.go b/x/events/events.go index 95b0b856a4a9..672366b710c5 100644 --- a/x/events/events.go +++ b/x/events/events.go @@ -19,28 +19,29 @@ import ( ) const ( - SessionIssued semconv.Event = "SessionIssued" - SessionChanged semconv.Event = "SessionChanged" - SessionLifespanExtended semconv.Event = "SessionLifespanExtended" - SessionRevoked semconv.Event = "SessionRevoked" - SessionChecked semconv.Event = "SessionChecked" - SessionTokenizedAsJWT semconv.Event = "SessionTokenizedAsJWT" - RegistrationFailed semconv.Event = "RegistrationFailed" - RegistrationSucceeded semconv.Event = "RegistrationSucceeded" - LoginFailed semconv.Event = "LoginFailed" - LoginSucceeded semconv.Event = "LoginSucceeded" - SettingsFailed semconv.Event = "SettingsFailed" - SettingsSucceeded semconv.Event = "SettingsSucceeded" - RecoveryFailed semconv.Event = "RecoveryFailed" - RecoverySucceeded semconv.Event = "RecoverySucceeded" - VerificationFailed semconv.Event = "VerificationFailed" - 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" + SessionIssued semconv.Event = "SessionIssued" + SessionChanged semconv.Event = "SessionChanged" + SessionLifespanExtended semconv.Event = "SessionLifespanExtended" + SessionRevoked semconv.Event = "SessionRevoked" + SessionChecked semconv.Event = "SessionChecked" + SessionTokenizedAsJWT semconv.Event = "SessionTokenizedAsJWT" + RegistrationFailed semconv.Event = "RegistrationFailed" + RegistrationSucceeded semconv.Event = "RegistrationSucceeded" + LoginFailed semconv.Event = "LoginFailed" + LoginSucceeded semconv.Event = "LoginSucceeded" + SettingsFailed semconv.Event = "SettingsFailed" + SettingsSucceeded semconv.Event = "SettingsSucceeded" + RecoveryFailed semconv.Event = "RecoveryFailed" + RecoverySucceeded semconv.Event = "RecoverySucceeded" + RecoveryCodeCreatedByAdmin semconv.Event = "RecoveryCodeCreatedByAdmin" + VerificationFailed semconv.Event = "VerificationFailed" + 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" ) const ( @@ -223,6 +224,17 @@ func NewRecoverySucceeded(ctx context.Context, flowID, identityID uuid.UUID, flo )...) } +func NewRecoveryCodeCreatedByAdmin(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) { + return RecoveryCodeCreatedByAdmin.String(), + trace.WithAttributes(append( + semconv.AttributesFromContext(ctx), + attrSelfServiceFlowType(flowType), + semconv.AttrIdentityID(identityID), + attrSelfServiceMethodUsed(method), + attrFlowID(flowID), + )...) +} + func NewSettingsSucceeded(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) { return SettingsSucceeded.String(), trace.WithAttributes(append( From 798ff0a6ed961c9aaaf5a88cf21c0905465b64d1 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Thu, 28 Nov 2024 19:32:59 +0100 Subject: [PATCH 2/2] chore: synchronize workspaces --- .../strategy/code/strategy_recovery_admin.go | 8 +-- .../strategy/link/strategy_recovery.go | 18 ++++--- x/events/events.go | 50 +++++++++---------- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/selfservice/strategy/code/strategy_recovery_admin.go b/selfservice/strategy/code/strategy_recovery_admin.go index a8ab0f8a421a..b64eb7b66e02 100644 --- a/selfservice/strategy/code/strategy_recovery_admin.go +++ b/selfservice/strategy/code/strategy_recovery_admin.go @@ -8,12 +8,13 @@ import ( "net/http" "net/url" "time" + "github.com/gobuffalo/pop/v6" - "go.opentelemetry.io/otel/trace" - "github.com/ory/kratos/x/events" "github.com/gofrs/uuid" "github.com/julienschmidt/httprouter" "github.com/pkg/errors" + "go.opentelemetry.io/otel/trace" + "github.com/ory/herodot" "github.com/ory/kratos/identity" "github.com/ory/kratos/selfservice/flow" @@ -22,6 +23,7 @@ import ( "github.com/ory/kratos/text" "github.com/ory/kratos/ui/node" "github.com/ory/kratos/x" + "github.com/ory/kratos/x/events" "github.com/ory/x/decoderx" "github.com/ory/x/sqlcon" "github.com/ory/x/urlx" @@ -223,7 +225,7 @@ func (s *Strategy) createRecoveryCodeForIdentity(w http.ResponseWriter, r *http. } trace.SpanFromContext(r.Context()).AddEvent( - events.NewRecoveryCodeCreatedByAdmin(ctx, recoveryFlow.ID, id.ID, flowType.String(), "code"), + events.NewRecoveryInitiatedByAdmin(ctx, recoveryFlow.ID, id.ID, flowType.String(), "code"), ) s.deps.Audit(). diff --git a/selfservice/strategy/link/strategy_recovery.go b/selfservice/strategy/link/strategy_recovery.go index e3a9b4b80066..23d40758980e 100644 --- a/selfservice/strategy/link/strategy_recovery.go +++ b/selfservice/strategy/link/strategy_recovery.go @@ -9,19 +9,15 @@ import ( "net/http" "net/url" "time" - "go.opentelemetry.io/otel/trace" - "github.com/ory/kratos/x/events" + "github.com/gobuffalo/pop/v6" "github.com/gofrs/uuid" "github.com/julienschmidt/httprouter" "github.com/pkg/errors" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "github.com/ory/herodot" - "github.com/ory/x/decoderx" - "github.com/ory/x/otelx" - "github.com/ory/x/sqlcon" - "github.com/ory/x/sqlxx" - "github.com/ory/x/urlx" "github.com/ory/kratos/identity" "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" @@ -31,6 +27,12 @@ import ( "github.com/ory/kratos/text" "github.com/ory/kratos/ui/node" "github.com/ory/kratos/x" + "github.com/ory/kratos/x/events" + "github.com/ory/x/decoderx" + "github.com/ory/x/otelx" + "github.com/ory/x/sqlcon" + "github.com/ory/x/sqlxx" + "github.com/ory/x/urlx" ) const ( @@ -195,7 +197,7 @@ func (s *Strategy) createRecoveryLinkForIdentity(w http.ResponseWriter, r *http. } trace.SpanFromContext(ctx).AddEvent( - events.NewRecoveryCodeCreatedByAdmin(ctx, req.ID, id.ID, req.Type.String(), "link"), + events.NewRecoveryInitiatedByAdmin(ctx, req.ID, id.ID, req.Type.String(), "link"), ) s.d.Audit(). diff --git a/x/events/events.go b/x/events/events.go index 672366b710c5..ca7108e74abf 100644 --- a/x/events/events.go +++ b/x/events/events.go @@ -19,29 +19,29 @@ import ( ) const ( - SessionIssued semconv.Event = "SessionIssued" - SessionChanged semconv.Event = "SessionChanged" - SessionLifespanExtended semconv.Event = "SessionLifespanExtended" - SessionRevoked semconv.Event = "SessionRevoked" - SessionChecked semconv.Event = "SessionChecked" - SessionTokenizedAsJWT semconv.Event = "SessionTokenizedAsJWT" - RegistrationFailed semconv.Event = "RegistrationFailed" - RegistrationSucceeded semconv.Event = "RegistrationSucceeded" - LoginFailed semconv.Event = "LoginFailed" - LoginSucceeded semconv.Event = "LoginSucceeded" - SettingsFailed semconv.Event = "SettingsFailed" - SettingsSucceeded semconv.Event = "SettingsSucceeded" - RecoveryFailed semconv.Event = "RecoveryFailed" - RecoverySucceeded semconv.Event = "RecoverySucceeded" - RecoveryCodeCreatedByAdmin semconv.Event = "RecoveryCodeCreatedByAdmin" - VerificationFailed semconv.Event = "VerificationFailed" - 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" + SessionIssued semconv.Event = "SessionIssued" + SessionChanged semconv.Event = "SessionChanged" + SessionLifespanExtended semconv.Event = "SessionLifespanExtended" + SessionRevoked semconv.Event = "SessionRevoked" + SessionChecked semconv.Event = "SessionChecked" + SessionTokenizedAsJWT semconv.Event = "SessionTokenizedAsJWT" + RegistrationFailed semconv.Event = "RegistrationFailed" + RegistrationSucceeded semconv.Event = "RegistrationSucceeded" + LoginFailed semconv.Event = "LoginFailed" + LoginSucceeded semconv.Event = "LoginSucceeded" + SettingsFailed semconv.Event = "SettingsFailed" + SettingsSucceeded semconv.Event = "SettingsSucceeded" + RecoveryFailed semconv.Event = "RecoveryFailed" + RecoverySucceeded semconv.Event = "RecoverySucceeded" + RecoveryInitiatedByAdmin semconv.Event = "RecoveryInitiatedByAdmin" + VerificationFailed semconv.Event = "VerificationFailed" + 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" ) const ( @@ -224,8 +224,8 @@ func NewRecoverySucceeded(ctx context.Context, flowID, identityID uuid.UUID, flo )...) } -func NewRecoveryCodeCreatedByAdmin(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) { - return RecoveryCodeCreatedByAdmin.String(), +func NewRecoveryInitiatedByAdmin(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) { + return RecoveryInitiatedByAdmin.String(), trace.WithAttributes(append( semconv.AttributesFromContext(ctx), attrSelfServiceFlowType(flowType),