Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: emit admin recovery code event #4230

Merged
merged 2 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions selfservice/strategy/code/strategy_recovery_admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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).
Expand Down
20 changes: 12 additions & 8 deletions selfservice/strategy/link/strategy_recovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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
}
Expand All @@ -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).
Expand All @@ -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()},
Expand Down
56 changes: 34 additions & 22 deletions x/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe avoid "code" when it is used for both code and link?

Suggested change
RecoveryCodeCreatedByAdmin semconv.Event = "RecoveryCodeCreatedByAdmin"
RecoveryCreatedByAdmin semconv.Event = "RecoveryCreatedByAdmin"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair point.

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 (
Expand Down Expand Up @@ -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(
Expand Down
Loading