Files
upstream-mirror c60cc7ca75
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
2026-09-15 21:19:12 +05:00

272 lines
11 KiB
Go

// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package identity
import (
"context"
"github.com/ory/kratos/x"
"github.com/ory/pop/v6"
"github.com/ory/x/crdbx"
"github.com/ory/x/pagination/keysetpagination"
"github.com/ory/x/sqlxx"
"github.com/gofrs/uuid"
)
func NewUpdateIdentityOptions(opts []UpdateIdentityModifier) UpdateIdentityOptions {
var o UpdateIdentityOptions
for _, opt := range opts {
opt(&o)
}
return o
}
// NewCreateIdentitiesOptions parses CreateIdentities modifiers.
func NewCreateIdentitiesOptions(opts []CreateIdentitiesModifier) *CreateIdentitiesOptions {
o := &CreateIdentitiesOptions{}
for _, opt := range opts {
opt(o)
}
return o
}
// WithExtraColumns appends fixed (key, value) columns to a batch insert.
func WithExtraColumns(cols []ExtraColumn) CreateIdentitiesModifier {
return func(o *CreateIdentitiesOptions) {
o.ExtraColumns = append(o.ExtraColumns, cols...)
}
}
// NewUpdateCredentialsConfigOptions parses UpdateCredentialsConfig modifiers.
func NewUpdateCredentialsConfigOptions(opts []UpdateCredentialsConfigModifier) *UpdateCredentialsConfigOptions {
o := &UpdateCredentialsConfigOptions{}
for _, opt := range opts {
opt(o)
}
return o
}
// WithDerivedIdentifiers syncs the credential's identifier rows to the set
// derived from the post-mutation config, inside the same locked transaction.
// Like mutate, derive must be pure: it may run more than once on database
// retries.
func WithDerivedIdentifiers(derive func(newConfig []byte) ([]string, error)) UpdateCredentialsConfigModifier {
return func(o *UpdateCredentialsConfigOptions) {
o.DeriveIdentifiers = derive
}
}
// DiffAgainst instructs UpdateIdentity to attempt a minimal update of the
// identity's data in the database by computing a diff against `existing` and
// only updating what is necessary, rather than bulk-replacing everything. Use
// with caution. If `existing` is different from what is stored in the database
// at the time of the update, the results are undefined. An error is returned if
// `existing` has a mismatching IdentityID or NID.
func DiffAgainst(existing *Identity) UpdateIdentityModifier {
return func(o *UpdateIdentityOptions) {
o.fromDatabase = existing
}
}
func (o UpdateIdentityOptions) FromDatabase() *Identity {
return o.fromDatabase
}
// WithoutCredentialTypes excludes the given credential types from the
// credential-association diff: their rows are neither deleted, recreated,
// nor updated by UpdateIdentity, and the returned identity carries their
// database state instead of the in-memory one. Use it for credential types
// whose rows are persisted through UpdateCredentialsConfig.
func WithoutCredentialTypes(cts ...CredentialsType) UpdateIdentityModifier {
return func(o *UpdateIdentityOptions) {
o.excludedCredentialTypes = append(o.excludedCredentialTypes, cts...)
}
}
func (o UpdateIdentityOptions) ExcludedCredentialTypes() []CredentialsType {
return o.excludedCredentialTypes
}
// WithUpdateExtraColumns appends fixed (key, value) columns to the inserts
// that UpdateIdentity performs for associated rows (addresses, credentials).
func WithUpdateExtraColumns(cols []ExtraColumn) UpdateIdentityModifier {
return func(o *UpdateIdentityOptions) {
o.extraColumns = append(o.extraColumns, cols...)
}
}
func (o UpdateIdentityOptions) ExtraColumns() []ExtraColumn {
return o.extraColumns
}
type (
ListIdentityParameters struct {
Expand Expandables
IdsFilter []uuid.UUID
CredentialsIdentifier string
CredentialsIdentifierSimilar string
DeclassifyCredentials []CredentialsType
KeySetPagination []keysetpagination.Option
OrganizationID uuid.UUID
ConsistencyLevel crdbx.ConsistencyLevel
StatementTransformer func(string) string
// ColumnsTransformer rewrites the SELECT column list to add extra
// columns the persister scans. Must be set together with RowScanner;
// the persister rejects the call if only one is provided.
ColumnsTransformer func(string) string
// RowScanner replaces the default scan into []Identity to consume
// the extra columns added by ColumnsTransformer. Must be set together
// with ColumnsTransformer.
RowScanner func(con *pop.Connection, query string, args []any) ([]Identity, error)
// DEPRECATED
PagePagination *x.Page
}
UpdateIdentityModifier func(*UpdateIdentityOptions)
UpdateIdentityOptions struct {
fromDatabase *Identity
excludedCredentialTypes []CredentialsType
extraColumns []ExtraColumn
}
// ExtraColumn carries a (key, value) pair for an extra SQL column on a
// batch insert (e.g. crdb_region) without coupling Identity to it.
ExtraColumn struct {
K string
V any
}
CreateIdentitiesOptions struct {
ExtraColumns []ExtraColumn
}
CreateIdentitiesModifier func(*CreateIdentitiesOptions)
UpdateCredentialsConfigOptions struct {
ExtraColumns []ExtraColumn
DeriveIdentifiers func(newConfig []byte) ([]string, error)
}
UpdateCredentialsConfigModifier func(*UpdateCredentialsConfigOptions)
Pool interface {
// ListIdentities lists all identities in the store given the page and itemsPerPage.
ListIdentities(ctx context.Context, params ListIdentityParameters) ([]Identity, *keysetpagination.Paginator, error)
// CountIdentities counts the number of identities in the store.
CountIdentities(ctx context.Context) (int64, error)
// GetIdentity returns an identity by its id. Will return an error if the identity does not exist or backend
// connectivity is broken.
GetIdentity(context.Context, uuid.UUID, sqlxx.Expandables) (*Identity, error)
// FindVerifiableAddressByValue returns a matching address or sql.ErrNoRows if no address could be found.
FindVerifiableAddressByValue(ctx context.Context, via, address string) (*VerifiableAddress, error)
// FindRecoveryAddressByValue returns a matching address or sql.ErrNoRows if no address could be found.
FindRecoveryAddressByValue(ctx context.Context, via, address string) (*RecoveryAddress, error)
// FindAllRecoveryAddressValuesForIdentityByRecoveryAddressValue finds the values of all recovery addresses for an identity if at least one of its recovery addresses matches the provided value.
FindAllRecoveryAddressValuesForIdentityByRecoveryAddressValue(ctx context.Context, anyRecoveryAddress string) ([]string, error)
}
PoolProvider interface {
IdentityPool() Pool
}
PrivilegedPoolProvider interface {
PrivilegedIdentityPool() PrivilegedPool
}
PrivilegedPool interface {
Pool
// FindByCredentialsIdentifier returns an identity by querying for it's credential identifiers.
FindByCredentialsIdentifier(ctx context.Context, ct CredentialsType, match string) (*Identity, *Credentials, error)
// DeleteIdentity removes an identity by its id. Will return an error
// if identity does not exists, or backend connectivity is broken.
DeleteIdentity(context.Context, uuid.UUID) error
// DeleteIdentities removes identities by its id. Will return an error
// if any identity does not exists, or backend connectivity is broken.
DeleteIdentities(context.Context, []uuid.UUID) error
// UpdateVerifiableAddress updates an identity's verifiable address.
UpdateVerifiableAddress(ctx context.Context, address *VerifiableAddress, updateColumns ...string) error
// CreateIdentity creates an identity. It is capable of setting credentials without encoding. Will return an error
// if identity exists, backend connectivity is broken, or trait validation fails.
CreateIdentity(context.Context, *Identity) error
// CreateIdentities creates multiple identities. It is capable of setting credentials without encoding. Will return an error
// if identity exists, backend connectivity is broken, or trait validation fails.
CreateIdentities(ctx context.Context, identities []*Identity, opts ...CreateIdentitiesModifier) error
// UpdateIdentity updates an identity including its confidential / privileged / protected data.
UpdateIdentity(context.Context, *Identity, ...UpdateIdentityModifier) error
// UpdateIdentityColumns updates targeted columns of an identity.
UpdateIdentityColumns(ctx context.Context, i *Identity, columns ...string) error
// UpdateCredentialsConfig atomically read-modify-writes a single
// identity_credentials row's config under an exclusive row lock:
// concurrent updates serialize and mutate always observes the latest
// committed config. Use it for credential-content decisions that must
// be mutually exclusive (PIN lockout counter, webauthn clone counter);
// wrap mutate with UpdateConfig for typed configs.
// mutate must be pure (it may run more than once on database retries);
// a structurally unchanged result skips the write; the version column
// (the config's schema version) is untouched. Calls inside a
// surrounding transaction are rejected.
// opts may narrow the row set with ExtraColumns (the cloud
// multi-region persister pins crdb_region) and sync the credential's
// identifier rows to the post-mutation config with
// WithDerivedIdentifiers.
UpdateCredentialsConfig(ctx context.Context, identityID uuid.UUID, ct CredentialsType, mutate func(config []byte) ([]byte, error), opts ...UpdateCredentialsConfigModifier) error
// GetIdentityConfidential returns the identity including it's raw credentials.
//
// This should only be used internally. Please be aware that this method uses HydrateIdentityAssociations
// internally, which must not be executed as part of a transaction.
GetIdentityConfidential(context.Context, uuid.UUID) (*Identity, error)
// ListVerifiableAddresses lists all tracked verifiable addresses, regardless of whether they are already verified
// or not.
ListVerifiableAddresses(ctx context.Context, page, itemsPerPage int) ([]VerifiableAddress, error)
// ListRecoveryAddresses lists all tracked recovery addresses.
ListRecoveryAddresses(ctx context.Context, page, itemsPerPage int) ([]RecoveryAddress, error)
// HydrateIdentityAssociations hydrates the associations of an identity.
//
// Please be aware that this method must not be called within a transaction if more than one element is expanded.
// It may error with "conn busy" otherwise.
HydrateIdentityAssociations(ctx context.Context, i *Identity, expandables Expandables) error
// InjectTraitsSchemaURL sets the identity's traits JSON schema URL from the schema's ID.
InjectTraitsSchemaURL(ctx context.Context, i *Identity) error
// FindIdentityByCredentialIdentifier returns an identity by matching the identifier to any of the identity's credentials.
FindIdentityByCredentialIdentifier(ctx context.Context, identifier string, caseSensitive bool, expandables Expandables) (*Identity, error)
// FindIdentityByWebauthnUserHandle returns an identity matching a webauthn user handle.
FindIdentityByWebauthnUserHandle(ctx context.Context, userHandle []byte) (*Identity, error)
// FindIdentityByCredentialsIdentifier returns an identity by its external ID.
FindIdentityByExternalID(ctx context.Context, externalID string, expand sqlxx.Expandables) (*Identity, error)
}
)
func (p ListIdentityParameters) TransformStatement(statement string) string {
if p.StatementTransformer != nil {
return p.StatementTransformer(statement)
}
return statement
}