From 60e65f4400745b667d16c08faccdce32c0e78e76 Mon Sep 17 00:00:00 2001 From: Sam Gardner Date: Tue, 16 Nov 2021 10:50:52 +0100 Subject: [PATCH] Add ability to import user password credentials and removed dependency to driver/config from hash --- cmd/hashers/argon2/root.go | 8 +- driver/config/config.go | 31 ++-- driver/registry.go | 2 +- driver/registry_default.go | 14 +- hash/hash_comparator.go | 6 +- hash/hasher.go | 44 +++++- hash/hasher_argon2.go | 6 +- hash/hasher_bcrypt.go | 10 +- identity/handler.go | 94 +++++++++++- internal/httpclient/.openapi-generator/FILES | 2 + internal/httpclient/README.md | 1 + internal/httpclient/api/openapi.yaml | 33 ++++- internal/httpclient/api_v0alpha2.go | 14 +- .../docs/AdminCreateIdentityBody.md | 52 +++++++ .../httpclient/docs/PasswordCredential.md | 72 +++++++++ .../model_admin_create_identity_body.go | 73 +++++++++ .../httpclient/model_password_credential.go | 138 ++++++++++++++++++ schema/{ => errors}/errors.go | 2 +- selfservice/flow/login/error_test.go | 7 +- selfservice/flow/login/handler.go | 7 +- selfservice/flow/recovery/error_test.go | 7 +- selfservice/flow/recovery/handler.go | 8 +- selfservice/flow/registration/error_test.go | 7 +- selfservice/flow/registration/handler.go | 6 +- selfservice/flow/registration/hook.go | 5 +- selfservice/flow/settings/error_test.go | 9 +- selfservice/flow/settings/handler.go | 4 +- selfservice/flow/settings/hook.go | 5 +- selfservice/flow/verification/error_test.go | 7 +- selfservice/flow/verification/handler.go | 5 +- .../strategy/link/strategy_recovery.go | 5 +- .../strategy/link/strategy_verification.go | 5 +- selfservice/strategy/lookup/login.go | 9 +- selfservice/strategy/lookup/strategy.go | 2 +- selfservice/strategy/password/login.go | 11 +- selfservice/strategy/password/login_test.go | 7 +- selfservice/strategy/password/registration.go | 11 +- selfservice/strategy/password/settings.go | 7 +- selfservice/strategy/password/strategy.go | 4 +- selfservice/strategy/password/types.go | 8 +- selfservice/strategy/totp/login.go | 7 +- selfservice/strategy/totp/settings.go | 8 +- selfservice/strategy/totp/strategy.go | 2 +- selfservice/strategy/webauthn/login.go | 7 +- selfservice/strategy/webauthn/strategy.go | 2 +- spec/api.json | 31 +++- spec/swagger.json | 31 +++- ui/container/container.go | 5 +- ui/container/container_test.go | 5 +- 49 files changed, 705 insertions(+), 141 deletions(-) create mode 100644 internal/httpclient/docs/PasswordCredential.md create mode 100644 internal/httpclient/model_password_credential.go rename schema/{ => errors}/errors.go (99%) diff --git a/cmd/hashers/argon2/root.go b/cmd/hashers/argon2/root.go index ad8fa1dc68d7..0498e886fb1c 100644 --- a/cmd/hashers/argon2/root.go +++ b/cmd/hashers/argon2/root.go @@ -6,6 +6,8 @@ import ( "reflect" "strings" + "github.com/ory/kratos/hash" + "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -76,7 +78,7 @@ func configProvider(cmd *cobra.Command, flagConf *argon2Config) (*argon2Config, _, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Unable to initialize the config provider: %s\n", err) return nil, cmdx.FailSilently(cmd) } - conf.localConfig = *conf.config.HasherArgon2() + conf.localConfig = (config.Argon2)(*conf.config.HasherArgon2()) if cmd.Flags().Changed(FlagIterations) { conf.localConfig.Iterations = flagConf.localConfig.Iterations @@ -162,6 +164,10 @@ func (c *argon2Config) Config(_ context.Context) *config.Config { return c.config } +func (c *argon2Config) HashConfig(ctx context.Context) hash.HashConfigProvider { + return c.Config(ctx) +} + func (c *argon2Config) HasherArgon2() (*config.Argon2, error) { if c.localConfig.Memory == 0 { c.localConfig.Memory = config.Argon2DefaultMemory diff --git a/driver/config/config.go b/driver/config/config.go index 43c21979fd4b..b3ad01d670df 100644 --- a/driver/config/config.go +++ b/driver/config/config.go @@ -15,6 +15,8 @@ import ( "testing" "time" + "github.com/ory/kratos/hash" + "golang.org/x/net/publicsuffix" "github.com/duo-labs/webauthn/protocol" @@ -172,19 +174,9 @@ const ( const DefaultSessionCookieName = "ory_kratos_session" type ( - Argon2 struct { - Memory bytesize.ByteSize `json:"memory"` - Iterations uint32 `json:"iterations"` - Parallelism uint8 `json:"parallelism"` - SaltLength uint32 `json:"salt_length"` - KeyLength uint32 `json:"key_length"` - ExpectedDuration time.Duration `json:"expected_duration"` - ExpectedDeviation time.Duration `json:"expected_deviation"` - DedicatedMemory bytesize.ByteSize `json:"dedicated_memory"` - } - Bcrypt struct { - Cost uint32 `json:"cost"` - } + Argon2 hash.Argon2Config + Bcrypt hash.BcryptConfig + SelfServiceHook struct { Name string `json:"hook"` Config json.RawMessage `json:"config"` @@ -215,6 +207,7 @@ type ( Provider interface { Config(ctx context.Context) *Config + hash.ConfigProvider } ) @@ -394,10 +387,10 @@ func (p *Config) SessionName() string { return stringsx.Coalesce(p.p.String(ViperKeySessionName), DefaultSessionCookieName) } -func (p *Config) HasherArgon2() *Argon2 { +func (p *Config) HasherArgon2() *hash.Argon2Config { // warn about usage of default values and point to the docs // warning will require https://github.com/ory/viper/issues/19 - return &Argon2{ + return &hash.Argon2Config{ Memory: p.p.ByteSizeF(ViperKeyHasherArgon2ConfigMemory, Argon2DefaultMemory), Iterations: uint32(p.p.IntF(ViperKeyHasherArgon2ConfigIterations, int(Argon2DefaultIterations))), Parallelism: uint8(p.p.IntF(ViperKeyHasherArgon2ConfigParallelism, int(Argon2DefaultParallelism))), @@ -409,7 +402,7 @@ func (p *Config) HasherArgon2() *Argon2 { } } -func (p *Config) HasherBcrypt() *Bcrypt { +func (p *Config) HasherBcrypt() *hash.BcryptConfig { // warn about usage of default values and point to the docs // warning will require https://github.com/ory/viper/issues/19 cost := uint32(p.p.IntF(ViperKeyHasherBcryptCost, int(BcryptDefaultCost))) @@ -417,7 +410,7 @@ func (p *Config) HasherBcrypt() *Bcrypt { cost = BcryptDefaultCost } - return &Bcrypt{Cost: cost} + return &hash.BcryptConfig{Cost: cost} } func (p *Config) listenOn(key string) string { @@ -1040,6 +1033,10 @@ func (p *Config) HasherPasswordHashingAlgorithm() string { } } +func (p *Config) Hasher(provider hash.ConfigProvider) hash.Hasher { + return hash.NewHasher(p.HasherPasswordHashingAlgorithm(), provider) +} + func (p *Config) CipherAlgorithm() string { configValue := p.p.StringF(ViperKeyCipherAlgorithm, DefaultCipherAlgorithm) switch configValue { diff --git a/driver/registry.go b/driver/registry.go index ad701b7ec7e0..376d4cd048f0 100644 --- a/driver/registry.go +++ b/driver/registry.go @@ -79,7 +79,7 @@ type Registry interface { errorx.HandlerProvider errorx.PersistenceProvider - hash.HashProvider + hash.Generator identity.HandlerProvider identity.ValidationProvider diff --git a/driver/registry_default.go b/driver/registry_default.go index fc59c3e145fd..c439b36e90ea 100644 --- a/driver/registry_default.go +++ b/driver/registry_default.go @@ -7,12 +7,10 @@ import ( "sync" "time" + "github.com/gobuffalo/pop/v6" "github.com/hashicorp/go-retryablehttp" - "github.com/ory/x/httpx" - "github.com/gobuffalo/pop/v6" - "github.com/ory/nosurf" "github.com/ory/kratos/selfservice/strategy/webauthn" @@ -271,6 +269,10 @@ func (m *RegistryDefault) SMTPConfig(ctx context.Context) courier.SMTPConfig { return m.Config(ctx) } +func (m *RegistryDefault) HashConfig(ctx context.Context) hash.HashConfigProvider { + return m.Config(ctx) +} + func (m *RegistryDefault) selfServiceStrategies() []interface{} { if len(m.selfserviceStrategies) == 0 { m.selfserviceStrategies = []interface{}{ @@ -404,11 +406,7 @@ func (m *RegistryDefault) Cipher() cipher.Cipher { func (m *RegistryDefault) Hasher() hash.Hasher { if m.passwordHasher == nil { - if m.c.HasherPasswordHashingAlgorithm() == "bcrypt" { - m.passwordHasher = hash.NewHasherBcrypt(m) - } else { - m.passwordHasher = hash.NewHasherArgon2(m) - } + m.passwordHasher = m.c.Hasher(m) } return m.passwordHasher } diff --git a/hash/hash_comparator.go b/hash/hash_comparator.go index 00a11a89934d..27c4cffaeb2b 100644 --- a/hash/hash_comparator.go +++ b/hash/hash_comparator.go @@ -12,8 +12,6 @@ import ( "golang.org/x/crypto/argon2" "golang.org/x/crypto/bcrypt" "golang.org/x/crypto/pbkdf2" - - "github.com/ory/kratos/driver/config" ) var ErrUnknownHashAlgorithm = errors.New("unknown hash algorithm") @@ -102,7 +100,7 @@ func IsPbkdf2Hash(hash []byte) bool { return isPbkdf2Hash.Match(hash) } -func decodeArgon2idHash(encodedHash string) (p *config.Argon2, salt, hash []byte, err error) { +func decodeArgon2idHash(encodedHash string) (p *Argon2Config, salt, hash []byte, err error) { parts := strings.Split(encodedHash, "$") if len(parts) != 6 { return nil, nil, nil, ErrInvalidHash @@ -117,7 +115,7 @@ func decodeArgon2idHash(encodedHash string) (p *config.Argon2, salt, hash []byte return nil, nil, nil, ErrIncompatibleVersion } - p = new(config.Argon2) + p = new(Argon2Config) _, err = fmt.Sscanf(parts[3], "m=%d,t=%d,p=%d", &p.Memory, &p.Iterations, &p.Parallelism) if err != nil { return nil, nil, nil, err diff --git a/hash/hasher.go b/hash/hasher.go index 8398ae293393..c54757d1abf1 100644 --- a/hash/hasher.go +++ b/hash/hasher.go @@ -1,6 +1,11 @@ package hash -import "context" +import ( + "context" + "time" + + "github.com/inhies/go-bytesize" +) // Hasher provides methods for generating and comparing password hashes. type Hasher interface { @@ -11,6 +16,41 @@ type Hasher interface { Understands(hash []byte) bool } -type HashProvider interface { +// Generator is the interface that objects that can construct hashers must implement +type Generator interface { Hasher() Hasher } + +// HashConfigProvider is the interface that objects that can generate configuration for the implemented hashers must +// implement +type HashConfigProvider interface { + HasherBcrypt() *BcryptConfig + HasherArgon2() *Argon2Config +} + +type BcryptConfig struct { + Cost uint32 `json:"cost"` +} + +type Argon2Config struct { + Memory bytesize.ByteSize `json:"memory"` + Iterations uint32 `json:"iterations"` + Parallelism uint8 `json:"parallelism"` + SaltLength uint32 `json:"salt_length"` + KeyLength uint32 `json:"key_length"` + ExpectedDuration time.Duration `json:"expected_duration"` + ExpectedDeviation time.Duration `json:"expected_deviation"` + DedicatedMemory bytesize.ByteSize `json:"dedicated_memory"` +} + +type ConfigProvider interface { + HashConfig(ctx context.Context) HashConfigProvider +} + +func NewHasher(algorithm string, provider ConfigProvider) Hasher { + if algorithm == "bcrypt" { + return NewHasherBcrypt(provider) + } else { + return NewHasherArgon2(provider) + } +} diff --git a/hash/hasher_argon2.go b/hash/hasher_argon2.go index 6280518df860..92b490664924 100644 --- a/hash/hasher_argon2.go +++ b/hash/hasher_argon2.go @@ -11,8 +11,6 @@ import ( "github.com/pkg/errors" "golang.org/x/crypto/argon2" - - "github.com/ory/kratos/driver/config" ) var ( @@ -26,7 +24,7 @@ type Argon2 struct { } type Argon2Configuration interface { - config.Provider + ConfigProvider } func NewHasherArgon2(c Argon2Configuration) *Argon2 { @@ -38,7 +36,7 @@ func toKB(mem bytesize.ByteSize) uint32 { } func (h *Argon2) Generate(ctx context.Context, password []byte) ([]byte, error) { - p := h.c.Config(ctx).HasherArgon2() + p := h.c.HashConfig(ctx).HasherArgon2() salt := make([]byte, p.SaltLength) if _, err := rand.Read(salt); err != nil { diff --git a/hash/hasher_bcrypt.go b/hash/hasher_bcrypt.go index 4f23a58e4246..0be10467dd88 100644 --- a/hash/hasher_bcrypt.go +++ b/hash/hasher_bcrypt.go @@ -3,11 +3,9 @@ package hash import ( "context" - "github.com/ory/kratos/schema" - "golang.org/x/crypto/bcrypt" - "github.com/ory/kratos/driver/config" + "github.com/ory/kratos/schema/errors" ) type Bcrypt struct { @@ -15,7 +13,7 @@ type Bcrypt struct { } type BcryptConfiguration interface { - config.Provider + ConfigProvider } func NewHasherBcrypt(c BcryptConfiguration) *Bcrypt { @@ -27,7 +25,7 @@ func (h *Bcrypt) Generate(ctx context.Context, password []byte) ([]byte, error) return nil, err } - hash, err := bcrypt.GenerateFromPassword(password, int(h.c.Config(ctx).HasherBcrypt().Cost)) + hash, err := bcrypt.GenerateFromPassword(password, int(h.c.HashConfig(ctx).HasherBcrypt().Cost)) if err != nil { return nil, err } @@ -40,7 +38,7 @@ func validateBcryptPasswordLength(password []byte) error { // so if password is longer than 72 bytes, function returns an error // See https://en.wikipedia.org/wiki/Bcrypt#User_input if len(password) > 72 { - return schema.NewPasswordPolicyViolationError( + return errors.NewPasswordPolicyViolationError( "#/password", "passwords are limited to a maximum length of 72 characters", ) diff --git a/identity/handler.go b/identity/handler.go index 92a9adcf6039..749ac202d013 100644 --- a/identity/handler.go +++ b/identity/handler.go @@ -22,8 +22,11 @@ import ( "github.com/ory/kratos/driver/config" ) -const RouteCollection = "/identities" -const RouteItem = RouteCollection + "/:id" +const ( + RouteCollection = "/identities" + RouteItem = RouteCollection + "/:id" + Bcrypt = "bcrypt" +) type ( handlerDependencies interface { @@ -188,6 +191,30 @@ type adminCreateIdentity struct { Body AdminCreateIdentityBody } +// CredentialsConfig is the struct that is being used as part of the identity credentials. +type CredentialsConfig struct { + // HashedPassword is a hash-representation of the password. + HashedPassword string `json:"hashed_password"` +} + +// User's password +// +// The password of the user. It represents a cleartext or hashed user password. +// +// swagger:model passwordCredential +type PasswordCredential struct { + // Value represents the value of the password in clear text or hashed + // + // required: true + Value string `json:"value"` + + // This field must be true if the value represents a hashed password. Otherwise the value is assumed to be a + // cleartext password and it will be hashed before storing. + // + // required: true + IsHashed bool `json:"is_hashed"` +} + // swagger:model adminCreateIdentityBody type AdminCreateIdentityBody struct { // SchemaID is the ID of the JSON Schema to be used for validating the identity's traits. @@ -202,19 +229,61 @@ type AdminCreateIdentityBody struct { // required: true Traits json.RawMessage `json:"traits"` + // Password for the user. This will only be used if the json schema actually defines the + // password as a valid type. + // + // required: false + Password *PasswordCredential `json:"password"` + // State is the identity's state. // // required: false State State `json:"state"` + + // VerifiableAddresses contains all the addresses that can be verified by the user. + // + // required: false + VerifiableAddresses []VerifiableAddress `json:"verifiable_addresses"` +} + +func (h *Handler) parsePwdCredential(i *Identity, cr AdminCreateIdentityBody, ctx context.Context) error { + if cr.Password != nil { + // Create the credentials + hpw := []byte(cr.Password.Value) + if !cr.Password.IsHashed { + var err error + + if hpw, err = h.r.Config(ctx).Hasher(h.r).Generate(ctx, hpw); err != nil { + return errors.WithStack(herodot.ErrBadRequest.WithReasonf("%s", err).WithWrap(err)) + } + } + + config, err := json.Marshal(&CredentialsConfig{ + HashedPassword: string(hpw), + }) + if err != nil { + return errors.WithStack(herodot.ErrBadRequest.WithReasonf("Unable to encode password options to JSON: %s", err).WithWrap(err)) + } + + i.Credentials = map[CredentialsType]Credentials{ + CredentialsTypePassword: { + Type: CredentialsTypePassword, + Identifiers: []string{}, + Config: config, + }, + } + } + return nil } // swagger:route POST /identities v0alpha2 adminCreateIdentity // // Create an Identity // -// This endpoint creates an identity. It is NOT possible to set an identity's credentials (password, ...) -// using this method! A way to achieve that will be introduced in the future. -// +// This endpoint creates an identity. Optionally a password can be set for the newly created identity. Do to so +// a password field must be included on the creation request. The password field can be in cleartext or hashed with +// any of the supported hashing algorithms. Using already hashed values can be useful when moving from another user +// authentication mechanism to Kratos. // Learn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model). // // Consumes: @@ -249,7 +318,20 @@ func (h *Handler) create(w http.ResponseWriter, r *http.Request, _ httprouter.Pa } state = cr.State } - i := &Identity{SchemaID: cr.SchemaID, Traits: []byte(cr.Traits), State: state, StateChangedAt: &stateChangedAt} + + i := &Identity{ + SchemaID: cr.SchemaID, + Traits: []byte(cr.Traits), + State: state, + StateChangedAt: &stateChangedAt, + VerifiableAddresses: cr.VerifiableAddresses, + } + + if err := h.parsePwdCredential(i, cr, r.Context()); err != nil { + h.r.Writer().WriteError(w, r, err) + return + } + if err := h.r.IdentityManager().Create(r.Context(), i); err != nil { h.r.Writer().WriteError(w, r, err) return diff --git a/internal/httpclient/.openapi-generator/FILES b/internal/httpclient/.openapi-generator/FILES index 9fbe63f65eed..37a4fa4049be 100644 --- a/internal/httpclient/.openapi-generator/FILES +++ b/internal/httpclient/.openapi-generator/FILES @@ -27,6 +27,7 @@ docs/JsonError.md docs/MetadataApi.md docs/NeedsPrivilegedSessionError.md docs/Pagination.md +docs/PasswordCredential.md docs/RecoveryAddress.md docs/RevokedSessions.md docs/SelfServiceBrowserLocationChangeRequiredError.md @@ -104,6 +105,7 @@ model_inline_response_503.go model_json_error.go model_needs_privileged_session_error.go model_pagination.go +model_password_credential.go model_recovery_address.go model_revoked_sessions.go model_self_service_browser_location_change_required_error.go diff --git a/internal/httpclient/README.md b/internal/httpclient/README.md index e4b3ee919897..4aae727fb3ac 100644 --- a/internal/httpclient/README.md +++ b/internal/httpclient/README.md @@ -148,6 +148,7 @@ Class | Method | HTTP request | Description - [JsonError](docs/JsonError.md) - [NeedsPrivilegedSessionError](docs/NeedsPrivilegedSessionError.md) - [Pagination](docs/Pagination.md) + - [PasswordCredential](docs/PasswordCredential.md) - [RecoveryAddress](docs/RecoveryAddress.md) - [RevokedSessions](docs/RevokedSessions.md) - [SelfServiceBrowserLocationChangeRequiredError](docs/SelfServiceBrowserLocationChangeRequiredError.md) diff --git a/internal/httpclient/api/openapi.yaml b/internal/httpclient/api/openapi.yaml index dea8f28c55cf..293871270882 100644 --- a/internal/httpclient/api/openapi.yaml +++ b/internal/httpclient/api/openapi.yaml @@ -147,9 +147,10 @@ paths: - v0alpha2 post: description: |- - This endpoint creates an identity. It is NOT possible to set an identity's credentials (password, ...) - using this method! A way to achieve that will be introduced in the future. - + This endpoint creates an identity. Optionally a password can be set for the newly created identity. Do to so + a password field must be included on the creation request. The password field can be in cleartext or hashed with + any of the supported hashing algorithms. Using already hashed values can be useful when moving from another user + authentication mechanism to Kratos. Learn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model). operationId: adminCreateIdentity requestBody: @@ -2641,6 +2642,8 @@ components: type: string adminCreateIdentityBody: properties: + password: + $ref: '#/components/schemas/passwordCredential' schema_id: description: SchemaID is the ID of the JSON Schema to be used for validating the identity's traits. @@ -2653,6 +2656,12 @@ components: in a self-service manner. The input will always be validated against the JSON Schema defined in `schema_url`. type: object + verifiable_addresses: + description: VerifiableAddresses contains all the addresses that can be + verified by the user. + items: + $ref: '#/components/schemas/verifiableIdentityAddress' + type: array required: - schema_id - traits @@ -3092,6 +3101,24 @@ components: minimum: 1 type: integer type: object + passwordCredential: + description: The password of the user. It represents a cleartext or hashed user + password. + properties: + is_hashed: + description: |- + This field must be true if the value represents a hashed password. Otherwise the value is assumed to be a + cleartext password and it will be hashed before storing. + type: boolean + value: + description: Value represents the value of the password in clear text or + hashed + type: string + required: + - is_hashed + - value + title: User's password + type: object revokedSessions: example: count: 0 diff --git a/internal/httpclient/api_v0alpha2.go b/internal/httpclient/api_v0alpha2.go index ba815408f974..58af8b917af2 100644 --- a/internal/httpclient/api_v0alpha2.go +++ b/internal/httpclient/api_v0alpha2.go @@ -30,9 +30,10 @@ type V0alpha2Api interface { /* * AdminCreateIdentity Create an Identity - * This endpoint creates an identity. It is NOT possible to set an identity's credentials (password, ...) - using this method! A way to achieve that will be introduced in the future. - + * This endpoint creates an identity. Optionally a password can be set for the newly created identity. Do to so + a password field must be included on the creation request. The password field can be in cleartext or hashed with + any of the supported hashing algorithms. Using already hashed values can be useful when moving from another user + authentication mechanism to Kratos. Learn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model). * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @return V0alpha2ApiApiAdminCreateIdentityRequest @@ -1101,9 +1102,10 @@ func (r V0alpha2ApiApiAdminCreateIdentityRequest) Execute() (*Identity, *http.Re /* * AdminCreateIdentity Create an Identity - * This endpoint creates an identity. It is NOT possible to set an identity's credentials (password, ...) -using this method! A way to achieve that will be introduced in the future. - + * This endpoint creates an identity. Optionally a password can be set for the newly created identity. Do to so +a password field must be included on the creation request. The password field can be in cleartext or hashed with +any of the supported hashing algorithms. Using already hashed values can be useful when moving from another user +authentication mechanism to Kratos. Learn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model). * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @return V0alpha2ApiApiAdminCreateIdentityRequest diff --git a/internal/httpclient/docs/AdminCreateIdentityBody.md b/internal/httpclient/docs/AdminCreateIdentityBody.md index c56a9147263b..b9d290bff7bd 100644 --- a/internal/httpclient/docs/AdminCreateIdentityBody.md +++ b/internal/httpclient/docs/AdminCreateIdentityBody.md @@ -4,9 +4,11 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- +**Password** | Pointer to [**PasswordCredential**](PasswordCredential.md) | | [optional] **SchemaId** | **string** | SchemaID is the ID of the JSON Schema to be used for validating the identity's traits. | **State** | Pointer to [**IdentityState**](IdentityState.md) | | [optional] **Traits** | **map[string]interface{}** | Traits represent an identity's traits. The identity is able to create, modify, and delete traits in a self-service manner. The input will always be validated against the JSON Schema defined in `schema_url`. | +**VerifiableAddresses** | Pointer to [**[]VerifiableIdentityAddress**](VerifiableIdentityAddress.md) | VerifiableAddresses contains all the addresses that can be verified by the user. | [optional] ## Methods @@ -27,6 +29,31 @@ NewAdminCreateIdentityBodyWithDefaults instantiates a new AdminCreateIdentityBod This constructor will only assign default values to properties that have it defined, but it doesn't guarantee that properties required by API are set +### GetPassword + +`func (o *AdminCreateIdentityBody) GetPassword() PasswordCredential` + +GetPassword returns the Password field if non-nil, zero value otherwise. + +### GetPasswordOk + +`func (o *AdminCreateIdentityBody) GetPasswordOk() (*PasswordCredential, bool)` + +GetPasswordOk returns a tuple with the Password field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetPassword + +`func (o *AdminCreateIdentityBody) SetPassword(v PasswordCredential)` + +SetPassword sets Password field to given value. + +### HasPassword + +`func (o *AdminCreateIdentityBody) HasPassword() bool` + +HasPassword returns a boolean if a field has been set. + ### GetSchemaId `func (o *AdminCreateIdentityBody) GetSchemaId() string` @@ -92,6 +119,31 @@ and a boolean to check if the value has been set. SetTraits sets Traits field to given value. +### GetVerifiableAddresses + +`func (o *AdminCreateIdentityBody) GetVerifiableAddresses() []VerifiableIdentityAddress` + +GetVerifiableAddresses returns the VerifiableAddresses field if non-nil, zero value otherwise. + +### GetVerifiableAddressesOk + +`func (o *AdminCreateIdentityBody) GetVerifiableAddressesOk() (*[]VerifiableIdentityAddress, bool)` + +GetVerifiableAddressesOk returns a tuple with the VerifiableAddresses field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetVerifiableAddresses + +`func (o *AdminCreateIdentityBody) SetVerifiableAddresses(v []VerifiableIdentityAddress)` + +SetVerifiableAddresses sets VerifiableAddresses field to given value. + +### HasVerifiableAddresses + +`func (o *AdminCreateIdentityBody) HasVerifiableAddresses() bool` + +HasVerifiableAddresses returns a boolean if a field has been set. + [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/internal/httpclient/docs/PasswordCredential.md b/internal/httpclient/docs/PasswordCredential.md new file mode 100644 index 000000000000..715440947a05 --- /dev/null +++ b/internal/httpclient/docs/PasswordCredential.md @@ -0,0 +1,72 @@ +# PasswordCredential + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**IsHashed** | **bool** | This field must be true if the value represents a hashed password. Otherwise the value is assumed to be a cleartext password and it will be hashed before storing. | +**Value** | **string** | Value represents the value of the password in clear text or hashed | + +## Methods + +### NewPasswordCredential + +`func NewPasswordCredential(isHashed bool, value string, ) *PasswordCredential` + +NewPasswordCredential instantiates a new PasswordCredential object +This constructor will assign default values to properties that have it defined, +and makes sure properties required by API are set, but the set of arguments +will change when the set of required properties is changed + +### NewPasswordCredentialWithDefaults + +`func NewPasswordCredentialWithDefaults() *PasswordCredential` + +NewPasswordCredentialWithDefaults instantiates a new PasswordCredential object +This constructor will only assign default values to properties that have it defined, +but it doesn't guarantee that properties required by API are set + +### GetIsHashed + +`func (o *PasswordCredential) GetIsHashed() bool` + +GetIsHashed returns the IsHashed field if non-nil, zero value otherwise. + +### GetIsHashedOk + +`func (o *PasswordCredential) GetIsHashedOk() (*bool, bool)` + +GetIsHashedOk returns a tuple with the IsHashed field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetIsHashed + +`func (o *PasswordCredential) SetIsHashed(v bool)` + +SetIsHashed sets IsHashed field to given value. + + +### GetValue + +`func (o *PasswordCredential) GetValue() string` + +GetValue returns the Value field if non-nil, zero value otherwise. + +### GetValueOk + +`func (o *PasswordCredential) GetValueOk() (*string, bool)` + +GetValueOk returns a tuple with the Value field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetValue + +`func (o *PasswordCredential) SetValue(v string)` + +SetValue sets Value field to given value. + + + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/internal/httpclient/model_admin_create_identity_body.go b/internal/httpclient/model_admin_create_identity_body.go index 5f85e9262bb9..5a94a8921a76 100644 --- a/internal/httpclient/model_admin_create_identity_body.go +++ b/internal/httpclient/model_admin_create_identity_body.go @@ -17,11 +17,14 @@ import ( // AdminCreateIdentityBody struct for AdminCreateIdentityBody type AdminCreateIdentityBody struct { + Password *PasswordCredential `json:"password,omitempty"` // SchemaID is the ID of the JSON Schema to be used for validating the identity's traits. SchemaId string `json:"schema_id"` State *IdentityState `json:"state,omitempty"` // Traits represent an identity's traits. The identity is able to create, modify, and delete traits in a self-service manner. The input will always be validated against the JSON Schema defined in `schema_url`. Traits map[string]interface{} `json:"traits"` + // VerifiableAddresses contains all the addresses that can be verified by the user. + VerifiableAddresses []VerifiableIdentityAddress `json:"verifiable_addresses,omitempty"` } // NewAdminCreateIdentityBody instantiates a new AdminCreateIdentityBody object @@ -43,6 +46,38 @@ func NewAdminCreateIdentityBodyWithDefaults() *AdminCreateIdentityBody { return &this } +// GetPassword returns the Password field value if set, zero value otherwise. +func (o *AdminCreateIdentityBody) GetPassword() PasswordCredential { + if o == nil || o.Password == nil { + var ret PasswordCredential + return ret + } + return *o.Password +} + +// GetPasswordOk returns a tuple with the Password field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminCreateIdentityBody) GetPasswordOk() (*PasswordCredential, bool) { + if o == nil || o.Password == nil { + return nil, false + } + return o.Password, true +} + +// HasPassword returns a boolean if a field has been set. +func (o *AdminCreateIdentityBody) HasPassword() bool { + if o != nil && o.Password != nil { + return true + } + + return false +} + +// SetPassword gets a reference to the given PasswordCredential and assigns it to the Password field. +func (o *AdminCreateIdentityBody) SetPassword(v PasswordCredential) { + o.Password = &v +} + // GetSchemaId returns the SchemaId field value func (o *AdminCreateIdentityBody) GetSchemaId() string { if o == nil { @@ -123,8 +158,43 @@ func (o *AdminCreateIdentityBody) SetTraits(v map[string]interface{}) { o.Traits = v } +// GetVerifiableAddresses returns the VerifiableAddresses field value if set, zero value otherwise. +func (o *AdminCreateIdentityBody) GetVerifiableAddresses() []VerifiableIdentityAddress { + if o == nil || o.VerifiableAddresses == nil { + var ret []VerifiableIdentityAddress + return ret + } + return o.VerifiableAddresses +} + +// GetVerifiableAddressesOk returns a tuple with the VerifiableAddresses field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminCreateIdentityBody) GetVerifiableAddressesOk() ([]VerifiableIdentityAddress, bool) { + if o == nil || o.VerifiableAddresses == nil { + return nil, false + } + return o.VerifiableAddresses, true +} + +// HasVerifiableAddresses returns a boolean if a field has been set. +func (o *AdminCreateIdentityBody) HasVerifiableAddresses() bool { + if o != nil && o.VerifiableAddresses != nil { + return true + } + + return false +} + +// SetVerifiableAddresses gets a reference to the given []VerifiableIdentityAddress and assigns it to the VerifiableAddresses field. +func (o *AdminCreateIdentityBody) SetVerifiableAddresses(v []VerifiableIdentityAddress) { + o.VerifiableAddresses = v +} + func (o AdminCreateIdentityBody) MarshalJSON() ([]byte, error) { toSerialize := map[string]interface{}{} + if o.Password != nil { + toSerialize["password"] = o.Password + } if true { toSerialize["schema_id"] = o.SchemaId } @@ -134,6 +204,9 @@ func (o AdminCreateIdentityBody) MarshalJSON() ([]byte, error) { if true { toSerialize["traits"] = o.Traits } + if o.VerifiableAddresses != nil { + toSerialize["verifiable_addresses"] = o.VerifiableAddresses + } return json.Marshal(toSerialize) } diff --git a/internal/httpclient/model_password_credential.go b/internal/httpclient/model_password_credential.go new file mode 100644 index 000000000000..c7bf680798ec --- /dev/null +++ b/internal/httpclient/model_password_credential.go @@ -0,0 +1,138 @@ +/* + * Ory Kratos API + * + * Documentation for all public and administrative Ory Kratos APIs. Public and administrative APIs are exposed on different ports. Public APIs can face the public internet without any protection while administrative APIs should never be exposed without prior authorization. To protect the administative API port you should use something like Nginx, Ory Oathkeeper, or any other technology capable of authorizing incoming requests. + * + * API version: 1.0.0 + * Contact: hi@ory.sh + */ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +import ( + "encoding/json" +) + +// PasswordCredential The password of the user. It represents a cleartext or hashed user password. +type PasswordCredential struct { + // This field must be true if the value represents a hashed password. Otherwise the value is assumed to be a cleartext password and it will be hashed before storing. + IsHashed bool `json:"is_hashed"` + // Value represents the value of the password in clear text or hashed + Value string `json:"value"` +} + +// NewPasswordCredential instantiates a new PasswordCredential object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewPasswordCredential(isHashed bool, value string) *PasswordCredential { + this := PasswordCredential{} + this.IsHashed = isHashed + this.Value = value + return &this +} + +// NewPasswordCredentialWithDefaults instantiates a new PasswordCredential object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewPasswordCredentialWithDefaults() *PasswordCredential { + this := PasswordCredential{} + return &this +} + +// GetIsHashed returns the IsHashed field value +func (o *PasswordCredential) GetIsHashed() bool { + if o == nil { + var ret bool + return ret + } + + return o.IsHashed +} + +// GetIsHashedOk returns a tuple with the IsHashed field value +// and a boolean to check if the value has been set. +func (o *PasswordCredential) GetIsHashedOk() (*bool, bool) { + if o == nil { + return nil, false + } + return &o.IsHashed, true +} + +// SetIsHashed sets field value +func (o *PasswordCredential) SetIsHashed(v bool) { + o.IsHashed = v +} + +// GetValue returns the Value field value +func (o *PasswordCredential) GetValue() string { + if o == nil { + var ret string + return ret + } + + return o.Value +} + +// GetValueOk returns a tuple with the Value field value +// and a boolean to check if the value has been set. +func (o *PasswordCredential) GetValueOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Value, true +} + +// SetValue sets field value +func (o *PasswordCredential) SetValue(v string) { + o.Value = v +} + +func (o PasswordCredential) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["is_hashed"] = o.IsHashed + } + if true { + toSerialize["value"] = o.Value + } + return json.Marshal(toSerialize) +} + +type NullablePasswordCredential struct { + value *PasswordCredential + isSet bool +} + +func (v NullablePasswordCredential) Get() *PasswordCredential { + return v.value +} + +func (v *NullablePasswordCredential) Set(val *PasswordCredential) { + v.value = val + v.isSet = true +} + +func (v NullablePasswordCredential) IsSet() bool { + return v.isSet +} + +func (v *NullablePasswordCredential) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullablePasswordCredential(val *PasswordCredential) *NullablePasswordCredential { + return &NullablePasswordCredential{value: val, isSet: true} +} + +func (v NullablePasswordCredential) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullablePasswordCredential) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/schema/errors.go b/schema/errors/errors.go similarity index 99% rename from schema/errors.go rename to schema/errors/errors.go index cf91fdbcb7c6..e6f2f6f5364f 100644 --- a/schema/errors.go +++ b/schema/errors/errors.go @@ -1,4 +1,4 @@ -package schema +package errors import ( "fmt" diff --git a/selfservice/flow/login/error_test.go b/selfservice/flow/login/error_test.go index b8580e41825a..a0d645921aea 100644 --- a/selfservice/flow/login/error_test.go +++ b/selfservice/flow/login/error_test.go @@ -8,6 +8,8 @@ import ( "testing" "time" + "github.com/ory/kratos/schema/errors" + "github.com/ory/kratos/identity" "github.com/gofrs/uuid" @@ -28,7 +30,6 @@ import ( "github.com/ory/kratos/driver/config" "github.com/ory/kratos/internal" "github.com/ory/kratos/internal/testhelpers" - "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/login" "github.com/ory/kratos/text" @@ -160,7 +161,7 @@ func TestHandleError(t *testing.T) { t.Cleanup(reset) loginFlow = newFlow(t, time.Minute, tc.t) - flowError = schema.NewInvalidCredentialsError() + flowError = errors.NewInvalidCredentialsError() ct = node.PasswordGroup res, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+"/error")) @@ -221,7 +222,7 @@ func TestHandleError(t *testing.T) { t.Cleanup(reset) loginFlow = newFlow(t, time.Minute, flow.TypeBrowser) - flowError = schema.NewInvalidCredentialsError() + flowError = errors.NewInvalidCredentialsError() ct = node.PasswordGroup lf, _ := expectLoginUI(t) diff --git a/selfservice/flow/login/handler.go b/selfservice/flow/login/handler.go index 83537da08f97..a2a870316950 100644 --- a/selfservice/flow/login/handler.go +++ b/selfservice/flow/login/handler.go @@ -4,6 +4,8 @@ import ( "net/http" "time" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/gofrs/uuid" "github.com/ory/herodot" @@ -13,7 +15,6 @@ import ( "github.com/ory/nosurf" "github.com/ory/kratos/identity" - "github.com/ory/kratos/schema" "github.com/ory/kratos/ui/node" "github.com/ory/x/decoderx" @@ -594,13 +595,13 @@ continueLogin: } if i == nil { - h.d.LoginFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, errors.WithStack(schema.NewNoLoginStrategyResponsible())) + h.d.LoginFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, errors.WithStack(errors2.NewNoLoginStrategyResponsible())) return } if err := h.d.LoginHookExecutor().PostLoginHook(w, r, f, i, sess); err != nil { if errors.Is(err, ErrAddressNotVerified) { - h.d.LoginFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, errors.WithStack(schema.NewAddressNotVerifiedError())) + h.d.LoginFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, errors.WithStack(errors2.NewAddressNotVerifiedError())) return } diff --git a/selfservice/flow/recovery/error_test.go b/selfservice/flow/recovery/error_test.go index 7ef7b2bc5f5f..b9b72e4413f1 100644 --- a/selfservice/flow/recovery/error_test.go +++ b/selfservice/flow/recovery/error_test.go @@ -7,6 +7,8 @@ import ( "testing" "time" + "github.com/ory/kratos/schema/errors" + "github.com/gofrs/uuid" "github.com/ory/x/jsonx" @@ -27,7 +29,6 @@ import ( "github.com/ory/kratos/driver/config" "github.com/ory/kratos/internal" "github.com/ory/kratos/internal/testhelpers" - "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/recovery" "github.com/ory/kratos/text" @@ -145,7 +146,7 @@ func TestHandleError(t *testing.T) { t.Cleanup(reset) recoveryFlow = newFlow(t, time.Minute, tc.t) - flowError = schema.NewInvalidCredentialsError() + flowError = errors.NewInvalidCredentialsError() methodName = recovery.StrategyRecoveryLinkName res, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+"/error")) @@ -206,7 +207,7 @@ func TestHandleError(t *testing.T) { t.Cleanup(reset) recoveryFlow = newFlow(t, time.Minute, flow.TypeBrowser) - flowError = schema.NewInvalidCredentialsError() + flowError = errors.NewInvalidCredentialsError() methodName = node.RecoveryLinkGroup lf, _ := expectRecoveryUI(t) diff --git a/selfservice/flow/recovery/handler.go b/selfservice/flow/recovery/handler.go index 92f4efd3f53a..4581954bea17 100644 --- a/selfservice/flow/recovery/handler.go +++ b/selfservice/flow/recovery/handler.go @@ -4,14 +4,14 @@ import ( "net/http" "time" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/ory/nosurf" - "github.com/ory/kratos/schema" + "github.com/ory/kratos/ui/node" "github.com/ory/x/sqlcon" - "github.com/ory/kratos/ui/node" - "github.com/ory/herodot" "github.com/julienschmidt/httprouter" @@ -385,7 +385,7 @@ func (h *Handler) submitFlow(w http.ResponseWriter, r *http.Request, ps httprout } if !found { - h.d.RecoveryFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, errors.WithStack(schema.NewNoRecoveryStrategyResponsible())) + h.d.RecoveryFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, errors.WithStack(errors2.NewNoRecoveryStrategyResponsible())) return } diff --git a/selfservice/flow/registration/error_test.go b/selfservice/flow/registration/error_test.go index a211d7678788..32fed1ac089e 100644 --- a/selfservice/flow/registration/error_test.go +++ b/selfservice/flow/registration/error_test.go @@ -8,6 +8,8 @@ import ( "testing" "time" + "github.com/ory/kratos/schema/errors" + "github.com/gofrs/uuid" "github.com/ory/kratos/ui/node" @@ -26,7 +28,6 @@ import ( "github.com/ory/kratos/driver/config" "github.com/ory/kratos/internal" "github.com/ory/kratos/internal/testhelpers" - "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/registration" "github.com/ory/kratos/text" @@ -148,7 +149,7 @@ func TestHandleError(t *testing.T) { t.Cleanup(reset) registrationFlow = newFlow(t, time.Minute, tc.t) - flowError = schema.NewInvalidCredentialsError() + flowError = errors.NewInvalidCredentialsError() group = node.PasswordGroup res, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+"/error")) @@ -209,7 +210,7 @@ func TestHandleError(t *testing.T) { t.Cleanup(reset) registrationFlow = newFlow(t, time.Minute, flow.TypeBrowser) - flowError = schema.NewInvalidCredentialsError() + flowError = errors.NewInvalidCredentialsError() group = node.PasswordGroup lf, _ := expectRegistrationUI(t) diff --git a/selfservice/flow/registration/handler.go b/selfservice/flow/registration/handler.go index bb794f136de2..bb15523774a8 100644 --- a/selfservice/flow/registration/handler.go +++ b/selfservice/flow/registration/handler.go @@ -4,12 +4,12 @@ import ( "net/http" "time" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/ory/kratos/text" "github.com/ory/nosurf" - "github.com/ory/kratos/schema" - "github.com/ory/kratos/identity" "github.com/ory/kratos/ui/node" @@ -453,7 +453,7 @@ func (h *Handler) submitFlow(w http.ResponseWriter, r *http.Request, _ httproute } if s == nil { - h.d.RegistrationFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, errors.WithStack(schema.NewNoRegistrationStrategyResponsible())) + h.d.RegistrationFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, errors.WithStack(errors2.NewNoRegistrationStrategyResponsible())) return } diff --git a/selfservice/flow/registration/hook.go b/selfservice/flow/registration/hook.go index 12b278a4152a..ff002e7229bf 100644 --- a/selfservice/flow/registration/hook.go +++ b/selfservice/flow/registration/hook.go @@ -6,13 +6,14 @@ import ( "net/http" "time" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/pkg/errors" "github.com/ory/x/sqlcon" "github.com/ory/kratos/driver/config" "github.com/ory/kratos/identity" - "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/session" "github.com/ory/kratos/x" @@ -120,7 +121,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque // would imply that the identity has to exist already. } else if err := e.d.IdentityManager().Create(r.Context(), i); err != nil { if errors.Is(err, sqlcon.ErrUniqueViolation) { - return schema.NewDuplicateCredentialsError() + return errors2.NewDuplicateCredentialsError() } return err } diff --git a/selfservice/flow/settings/error_test.go b/selfservice/flow/settings/error_test.go index 63a724e96783..81f7fffdf664 100644 --- a/selfservice/flow/settings/error_test.go +++ b/selfservice/flow/settings/error_test.go @@ -8,6 +8,8 @@ import ( "testing" "time" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/pkg/errors" "github.com/gofrs/uuid" @@ -30,7 +32,6 @@ import ( "github.com/ory/kratos/identity" "github.com/ory/kratos/internal" "github.com/ory/kratos/internal/testhelpers" - "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/settings" "github.com/ory/kratos/session" @@ -166,7 +167,7 @@ func TestHandleError(t *testing.T) { t.Cleanup(reset) settingsFlow = newFlow(t, time.Minute, tc.t) - flowError = schema.NewInvalidCredentialsError() + flowError = errors2.NewInvalidCredentialsError() flowMethod = settings.StrategyProfile res, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+"/error")) @@ -343,7 +344,7 @@ func TestHandleError(t *testing.T) { t.Cleanup(reset) settingsFlow = newFlow(t, time.Minute, flow.TypeBrowser) - flowError = schema.NewInvalidCredentialsError() + flowError = errors2.NewInvalidCredentialsError() flowMethod = settings.StrategyProfile lf, _ := expectSettingsUI(t) @@ -360,7 +361,7 @@ func TestHandleError(t *testing.T) { id.SchemaURL = "http://some.random.url" settingsFlow = newFlow(t, time.Minute, flow.TypeBrowser) - flowError = schema.NewInvalidCredentialsError() + flowError = errors2.NewInvalidCredentialsError() flowMethod = settings.StrategyProfile lf, _ := expectSettingsUI(t) diff --git a/selfservice/flow/settings/handler.go b/selfservice/flow/settings/handler.go index 1a2f78f0ba9c..c26c8326bff2 100644 --- a/selfservice/flow/settings/handler.go +++ b/selfservice/flow/settings/handler.go @@ -4,6 +4,8 @@ import ( "net/http" "time" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/ory/kratos/text" "github.com/ory/kratos/ui/node" @@ -533,7 +535,7 @@ func (h *Handler) submitSettingsFlow(w http.ResponseWriter, r *http.Request, ps if updateContext == nil { c := &UpdateContext{Session: ss, Flow: f} - h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, f, c.GetIdentityToUpdate(), errors.WithStack(schema.NewNoSettingsStrategyResponsible())) + h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, f, c.GetIdentityToUpdate(), errors.WithStack(errors2.NewNoSettingsStrategyResponsible())) return } diff --git a/selfservice/flow/settings/hook.go b/selfservice/flow/settings/hook.go index d23cfdf60819..7ca7cb6c8667 100644 --- a/selfservice/flow/settings/hook.go +++ b/selfservice/flow/settings/hook.go @@ -6,10 +6,11 @@ import ( "net/http" "time" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/ory/kratos/text" "github.com/ory/kratos/ui/node" - "github.com/ory/kratos/schema" "github.com/ory/x/sqlcon" "github.com/pkg/errors" @@ -153,7 +154,7 @@ func (e *HookExecutor) PostSettingsHook(w http.ResponseWriter, r *http.Request, return errors.WithStack(NewFlowNeedsReAuth()) } if errors.Is(err, sqlcon.ErrUniqueViolation) { - return schema.NewDuplicateCredentialsError() + return errors2.NewDuplicateCredentialsError() } return err } diff --git a/selfservice/flow/verification/error_test.go b/selfservice/flow/verification/error_test.go index 793f13b13f43..cd28b3859797 100644 --- a/selfservice/flow/verification/error_test.go +++ b/selfservice/flow/verification/error_test.go @@ -7,6 +7,8 @@ import ( "testing" "time" + "github.com/ory/kratos/schema/errors" + "github.com/gofrs/uuid" "github.com/ory/x/jsonx" @@ -27,7 +29,6 @@ import ( "github.com/ory/kratos/driver/config" "github.com/ory/kratos/internal" "github.com/ory/kratos/internal/testhelpers" - "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/verification" "github.com/ory/kratos/text" @@ -145,7 +146,7 @@ func TestHandleError(t *testing.T) { t.Cleanup(reset) verificationFlow = newFlow(t, time.Minute, tc.t) - flowError = schema.NewInvalidCredentialsError() + flowError = errors.NewInvalidCredentialsError() methodName = verification.StrategyVerificationLinkName res, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+"/error")) @@ -206,7 +207,7 @@ func TestHandleError(t *testing.T) { t.Cleanup(reset) verificationFlow = newFlow(t, time.Minute, flow.TypeBrowser) - flowError = schema.NewInvalidCredentialsError() + flowError = errors.NewInvalidCredentialsError() methodName = node.VerificationLinkGroup lf, _ := expectVerificationUI(t) diff --git a/selfservice/flow/verification/handler.go b/selfservice/flow/verification/handler.go index de2c3243209e..65b8b518a0bb 100644 --- a/selfservice/flow/verification/handler.go +++ b/selfservice/flow/verification/handler.go @@ -4,9 +4,10 @@ import ( "net/http" "time" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/ory/nosurf" - "github.com/ory/kratos/schema" "github.com/ory/kratos/ui/node" "github.com/ory/x/sqlcon" @@ -367,7 +368,7 @@ func (h *Handler) submitFlow(w http.ResponseWriter, r *http.Request, ps httprout } if !found { - h.d.VerificationFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, errors.WithStack(schema.NewNoVerificationStrategyResponsible())) + h.d.VerificationFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, errors.WithStack(errors2.NewNoVerificationStrategyResponsible())) return } diff --git a/selfservice/strategy/link/strategy_recovery.go b/selfservice/strategy/link/strategy_recovery.go index 6bc97c988709..5f8a4ff203bb 100644 --- a/selfservice/strategy/link/strategy_recovery.go +++ b/selfservice/strategy/link/strategy_recovery.go @@ -5,6 +5,8 @@ import ( "net/url" "time" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/gofrs/uuid" "github.com/julienschmidt/httprouter" "github.com/pkg/errors" @@ -16,7 +18,6 @@ import ( "github.com/ory/x/urlx" "github.com/ory/kratos/identity" - "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/recovery" "github.com/ory/kratos/selfservice/strategy" @@ -407,7 +408,7 @@ func (s *Strategy) recoveryHandleFormSubmission(w http.ResponseWriter, r *http.R } if len(body.Email) == 0 { - return s.HandleRecoveryError(w, r, f, body, schema.NewRequiredError("#/email", "email")) + return s.HandleRecoveryError(w, r, f, body, errors2.NewRequiredError("#/email", "email")) } if err := flow.EnsureCSRF(s.d, r, f.Type, s.d.Config(r.Context()).DisableAPIFlowEnforcement(), s.d.GenerateCSRFToken, body.CSRFToken); err != nil { diff --git a/selfservice/strategy/link/strategy_verification.go b/selfservice/strategy/link/strategy_verification.go index b83c7e69cfa5..394b7692c5f6 100644 --- a/selfservice/strategy/link/strategy_verification.go +++ b/selfservice/strategy/link/strategy_verification.go @@ -5,10 +5,11 @@ import ( "net/url" "time" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/pkg/errors" "github.com/ory/kratos/identity" - "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/verification" "github.com/ory/kratos/text" @@ -149,7 +150,7 @@ func (s *Strategy) verificationHandleFormSubmission(w http.ResponseWriter, r *ht } if len(body.Email) == 0 { - return s.handleVerificationError(w, r, f, body, schema.NewRequiredError("#/email", "email")) + return s.handleVerificationError(w, r, f, body, errors2.NewRequiredError("#/email", "email")) } if err := flow.EnsureCSRF(s.d, r, f.Type, s.d.Config(r.Context()).DisableAPIFlowEnforcement(), s.d.GenerateCSRFToken, body.CSRFToken); err != nil { diff --git a/selfservice/strategy/lookup/login.go b/selfservice/strategy/lookup/login.go index c4ef436bb7ff..eff1802ff96c 100644 --- a/selfservice/strategy/lookup/login.go +++ b/selfservice/strategy/lookup/login.go @@ -5,6 +5,8 @@ import ( "net/http" "time" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/ory/x/sqlcon" "github.com/ory/x/sqlxx" @@ -13,7 +15,6 @@ import ( "github.com/ory/herodot" "github.com/ory/kratos/identity" - "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/login" "github.com/ory/kratos/session" @@ -108,7 +109,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, i, c, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(r.Context(), s.ID(), ss.IdentityID.String()) if errors.Is(err, sqlcon.ErrNoRows) { - return nil, s.handleLoginError(r, f, errors.WithStack(schema.NewNoLookupDefined())) + return nil, s.handleLoginError(r, f, errors.WithStack(errors2.NewNoLookupDefined())) } else if err != nil { return nil, s.handleLoginError(r, f, err) } @@ -125,13 +126,13 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, o.RecoveryCodes[k].UsedAt = sqlxx.NullTime(time.Now().UTC().Round(time.Second)) found = true } else { - return nil, s.handleLoginError(r, f, errors.WithStack(schema.NewLookupAlreadyUsed())) + return nil, s.handleLoginError(r, f, errors.WithStack(errors2.NewLookupAlreadyUsed())) } } } if !found { - return nil, s.handleLoginError(r, f, errors.WithStack(schema.NewErrorValidationLookupInvalid())) + return nil, s.handleLoginError(r, f, errors.WithStack(errors2.NewErrorValidationLookupInvalid())) } toUpdate, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(r.Context(), ss.IdentityID) diff --git a/selfservice/strategy/lookup/strategy.go b/selfservice/strategy/lookup/strategy.go index 8412d5421560..4c0870f0fe5b 100644 --- a/selfservice/strategy/lookup/strategy.go +++ b/selfservice/strategy/lookup/strategy.go @@ -34,7 +34,7 @@ type registrationStrategyDependencies interface { continuity.ManagementProvider errorx.ManagementProvider - hash.HashProvider + hash.Generator registration.HandlerProvider registration.HooksProvider diff --git a/selfservice/strategy/password/login.go b/selfservice/strategy/password/login.go index 641b3fec182a..46580d2f13dd 100644 --- a/selfservice/strategy/password/login.go +++ b/selfservice/strategy/password/login.go @@ -7,6 +7,8 @@ import ( "net/http" "time" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/gofrs/uuid" "github.com/ory/kratos/session" @@ -18,7 +20,6 @@ import ( "github.com/ory/kratos/hash" "github.com/ory/kratos/identity" - "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/login" "github.com/ory/kratos/text" @@ -65,17 +66,17 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, i, c, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(r.Context(), s.ID(), p.Identifier) if err != nil { time.Sleep(x.RandomDelay(s.d.Config(r.Context()).HasherArgon2().ExpectedDuration, s.d.Config(r.Context()).HasherArgon2().ExpectedDeviation)) - return nil, s.handleLoginError(w, r, f, &p, errors.WithStack(schema.NewInvalidCredentialsError())) + return nil, s.handleLoginError(w, r, f, &p, errors.WithStack(errors2.NewInvalidCredentialsError())) } - var o CredentialsConfig + var o identity.CredentialsConfig d := json.NewDecoder(bytes.NewBuffer(c.Config)) if err := d.Decode(&o); err != nil { return nil, herodot.ErrInternalServerError.WithReason("The password credentials could not be decoded properly").WithDebug(err.Error()).WithWrap(err) } if err := hash.Compare(r.Context(), []byte(p.Password), []byte(o.HashedPassword)); err != nil { - return nil, s.handleLoginError(w, r, f, &p, errors.WithStack(schema.NewInvalidCredentialsError())) + return nil, s.handleLoginError(w, r, f, &p, errors.WithStack(errors2.NewInvalidCredentialsError())) } if !s.d.Hasher().Understands([]byte(o.HashedPassword)) { @@ -101,7 +102,7 @@ func (s *Strategy) migratePasswordHash(ctx context.Context, identifier uuid.UUID if err != nil { return err } - co, err := json.Marshal(&CredentialsConfig{HashedPassword: string(hpw)}) + co, err := json.Marshal(&identity.CredentialsConfig{HashedPassword: string(hpw)}) if err != nil { return errors.Wrap(err, "unable to encode password configuration to JSON") } diff --git a/selfservice/strategy/password/login_test.go b/selfservice/strategy/password/login_test.go index c984c4fb4f50..54cf686a04cd 100644 --- a/selfservice/strategy/password/login_test.go +++ b/selfservice/strategy/password/login_test.go @@ -12,6 +12,8 @@ import ( "testing" "time" + "github.com/ory/kratos/schema/errors" + "github.com/ory/kratos/selfservice/flow" "github.com/gofrs/uuid" @@ -35,7 +37,6 @@ import ( "github.com/ory/kratos/identity" "github.com/ory/kratos/internal" "github.com/ory/kratos/internal/testhelpers" - "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow/login" "github.com/ory/kratos/text" "github.com/ory/kratos/x" @@ -427,7 +428,7 @@ func TestCompleteLogin(t *testing.T) { ensureFieldsExist(t, []byte(body)) assert.Equal(t, - errorsx.Cause(schema.NewInvalidCredentialsError()).(*schema.ValidationError).Messages[0].Text, + errorsx.Cause(errors.NewInvalidCredentialsError()).(*errors.ValidationError).Messages[0].Text, gjson.Get(body, "ui.messages.0.text").String(), "%s", body, ) @@ -709,7 +710,7 @@ func TestCompleteLogin(t *testing.T) { ensureFieldsExist(t, []byte(body)) assert.Equal(t, - errorsx.Cause(schema.NewAddressNotVerifiedError()).(*schema.ValidationError).Messages[0].Text, + errorsx.Cause(errors.NewAddressNotVerifiedError()).(*errors.ValidationError).Messages[0].Text, gjson.Get(body, "ui.messages.0.text").String(), "%s", body, ) diff --git a/selfservice/strategy/password/registration.go b/selfservice/strategy/password/registration.go index 7c09e0309064..5252a51267a6 100644 --- a/selfservice/strategy/password/registration.go +++ b/selfservice/strategy/password/registration.go @@ -5,6 +5,8 @@ import ( "encoding/json" "net/http" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/ory/kratos/text" "github.com/pkg/errors" @@ -12,7 +14,6 @@ import ( "github.com/ory/herodot" "github.com/ory/kratos/identity" - "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/registration" "github.com/ory/kratos/ui/container" @@ -98,7 +99,7 @@ func (s *Strategy) Register(w http.ResponseWriter, r *http.Request, f *registrat } if len(p.Password) == 0 { - return s.handleRegistrationError(w, r, f, &p, schema.NewRequiredError("#/password", "password")) + return s.handleRegistrationError(w, r, f, &p, errors2.NewRequiredError("#/password", "password")) } if len(p.Traits) == 0 { @@ -110,7 +111,7 @@ func (s *Strategy) Register(w http.ResponseWriter, r *http.Request, f *registrat return s.handleRegistrationError(w, r, f, &p, err) } - co, err := json.Marshal(&CredentialsConfig{HashedPassword: string(hpw)}) + co, err := json.Marshal(&identity.CredentialsConfig{HashedPassword: string(hpw)}) if err != nil { return s.handleRegistrationError(w, r, f, &p, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Unable to encode password options to JSON: %s", err))) } @@ -135,7 +136,7 @@ func (s *Strategy) validateCredentials(ctx context.Context, i *identity.Identity // This should never happen return errors.WithStack(x.PseudoPanic.WithReasonf("identity object did not provide the %s CredentialType unexpectedly", identity.CredentialsTypePassword)) } else if len(c.Identifiers) == 0 { - return schema.NewMissingIdentifierError() + return errors2.NewMissingIdentifierError() } for _, id := range c.Identifiers { @@ -143,7 +144,7 @@ func (s *Strategy) validateCredentials(ctx context.Context, i *identity.Identity if _, ok := errorsx.Cause(err).(*herodot.DefaultError); ok { return err } - return schema.NewPasswordPolicyViolationError("#/password", err.Error()) + return errors2.NewPasswordPolicyViolationError("#/password", err.Error()) } } diff --git a/selfservice/strategy/password/settings.go b/selfservice/strategy/password/settings.go index 45d6aa60d4db..63a12c6d6d29 100644 --- a/selfservice/strategy/password/settings.go +++ b/selfservice/strategy/password/settings.go @@ -5,6 +5,8 @@ import ( "net/http" "time" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/ory/kratos/text" "github.com/ory/kratos/ui/node" @@ -16,7 +18,6 @@ import ( "github.com/ory/herodot" "github.com/ory/kratos/identity" - "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/settings" "github.com/ory/kratos/x" @@ -116,7 +117,7 @@ func (s *Strategy) continueSettingsFlow( } if len(p.Password) == 0 { - return schema.NewRequiredError("#/password", "password") + return errors2.NewRequiredError("#/password", "password") } hpw, err := s.d.Hasher().Generate(r.Context(), []byte(p.Password)) @@ -124,7 +125,7 @@ func (s *Strategy) continueSettingsFlow( return err } - co, err := json.Marshal(&CredentialsConfig{HashedPassword: string(hpw)}) + co, err := json.Marshal(&identity.CredentialsConfig{HashedPassword: string(hpw)}) if err != nil { return errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Unable to encode password options to JSON: %s", err)) } diff --git a/selfservice/strategy/password/strategy.go b/selfservice/strategy/password/strategy.go index 2b5f2211ee63..5dd8ba7ee804 100644 --- a/selfservice/strategy/password/strategy.go +++ b/selfservice/strategy/password/strategy.go @@ -38,7 +38,7 @@ type registrationStrategyDependencies interface { errorx.ManagementProvider ValidationProvider - hash.HashProvider + hash.Generator registration.HandlerProvider registration.HooksProvider @@ -81,7 +81,7 @@ func NewStrategy(d registrationStrategyDependencies) *Strategy { func (s *Strategy) CountActiveCredentials(cc map[identity.CredentialsType]identity.Credentials) (count int, err error) { for _, c := range cc { if c.Type == s.ID() && len(c.Config) > 0 { - var conf CredentialsConfig + var conf identity.CredentialsConfig if err = json.Unmarshal(c.Config, &conf); err != nil { return 0, errors.WithStack(err) } diff --git a/selfservice/strategy/password/types.go b/selfservice/strategy/password/types.go index e8d8d924310c..82c1b9650b07 100644 --- a/selfservice/strategy/password/types.go +++ b/selfservice/strategy/password/types.go @@ -5,10 +5,10 @@ import ( ) // CredentialsConfig is the struct that is being used as part of the identity credentials. -type CredentialsConfig struct { - // HashedPassword is a hash-representation of the password. - HashedPassword string `json:"hashed_password"` -} +//type CredentialsConfig struct { +// // HashedPassword is a hash-representation of the password. +// HashedPassword string `json:"hashed_password"` +//} // submitSelfServiceLoginFlowWithPasswordMethodBody is used to decode the login form payload. // diff --git a/selfservice/strategy/totp/login.go b/selfservice/strategy/totp/login.go index a3eb8747ac39..96d676b50918 100644 --- a/selfservice/strategy/totp/login.go +++ b/selfservice/strategy/totp/login.go @@ -4,13 +4,14 @@ import ( "encoding/json" "net/http" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/pkg/errors" "github.com/pquerna/otp" "github.com/pquerna/otp/totp" "github.com/ory/herodot" "github.com/ory/kratos/identity" - "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/login" "github.com/ory/kratos/session" @@ -105,7 +106,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, i, c, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(r.Context(), s.ID(), ss.IdentityID.String()) if err != nil { - return nil, s.handleLoginError(r, f, errors.WithStack(schema.NewNoTOTPDeviceRegistered())) + return nil, s.handleLoginError(r, f, errors.WithStack(errors2.NewNoTOTPDeviceRegistered())) } var o CredentialsConfig @@ -119,7 +120,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, } if !totp.Validate(p.TOTPCode, key.Secret()) { - return nil, s.handleLoginError(r, f, errors.WithStack(schema.NewTOTPVerifierWrongError("#/"))) + return nil, s.handleLoginError(r, f, errors.WithStack(errors2.NewTOTPVerifierWrongError("#/"))) } f.Active = s.ID() diff --git a/selfservice/strategy/totp/settings.go b/selfservice/strategy/totp/settings.go index 56bff7759c32..c6fe52c625cd 100644 --- a/selfservice/strategy/totp/settings.go +++ b/selfservice/strategy/totp/settings.go @@ -6,14 +6,14 @@ import ( "net/http" "time" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/pquerna/otp" "github.com/pquerna/otp/totp" "github.com/tidwall/gjson" "github.com/tidwall/sjson" "github.com/ory/herodot" - "github.com/ory/kratos/schema" - "github.com/ory/kratos/text" "github.com/ory/kratos/ui/node" @@ -174,11 +174,11 @@ func (s *Strategy) continueSettingsFlowAddTOTP(w http.ResponseWriter, r *http.Re } if p.ValidationTOTP == "" { - return nil, schema.NewRequiredError("#/totp_code", "totp_code") + return nil, errors2.NewRequiredError("#/totp_code", "totp_code") } if !totp.Validate(p.ValidationTOTP, key.Secret()) { - return nil, schema.NewTOTPVerifierWrongError("#/totp_code") + return nil, errors2.NewTOTPVerifierWrongError("#/totp_code") } co, err := json.Marshal(&CredentialsConfig{TOTPURL: key.URL()}) diff --git a/selfservice/strategy/totp/strategy.go b/selfservice/strategy/totp/strategy.go index 35ed16f41d49..ee75535b1cad 100644 --- a/selfservice/strategy/totp/strategy.go +++ b/selfservice/strategy/totp/strategy.go @@ -35,7 +35,7 @@ type registrationStrategyDependencies interface { continuity.ManagementProvider errorx.ManagementProvider - hash.HashProvider + hash.Generator registration.HandlerProvider registration.HooksProvider diff --git a/selfservice/strategy/webauthn/login.go b/selfservice/strategy/webauthn/login.go index b8947813b65f..9bf5576a77dc 100644 --- a/selfservice/strategy/webauthn/login.go +++ b/selfservice/strategy/webauthn/login.go @@ -5,6 +5,8 @@ import ( "net/http" "strings" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/ory/x/urlx" "github.com/duo-labs/webauthn/protocol" @@ -16,7 +18,6 @@ import ( "github.com/ory/herodot" "github.com/ory/kratos/identity" - "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/login" "github.com/ory/kratos/session" @@ -147,7 +148,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, i, c, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(r.Context(), s.ID(), ss.IdentityID.String()) if err != nil { - return nil, s.handleLoginError(r, f, errors.WithStack(schema.NewNoWebAuthnRegistered())) + return nil, s.handleLoginError(r, f, errors.WithStack(errors2.NewNoWebAuthnRegistered())) } var o CredentialsConfig @@ -171,7 +172,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, } if _, err := web.ValidateLogin(&wrappedUser{id: i.ID, c: o.Credentials.ToWebAuthn()}, webAuthnSess, webAuthnResponse); err != nil { - return nil, s.handleLoginError(r, f, errors.WithStack(schema.NewWebAuthnVerifierWrongError("#/"))) + return nil, s.handleLoginError(r, f, errors.WithStack(errors2.NewWebAuthnVerifierWrongError("#/"))) } // Remove the WebAuthn URL from the internal context now that it is set! diff --git a/selfservice/strategy/webauthn/strategy.go b/selfservice/strategy/webauthn/strategy.go index bfec17a541aa..0e605906bc19 100644 --- a/selfservice/strategy/webauthn/strategy.go +++ b/selfservice/strategy/webauthn/strategy.go @@ -37,7 +37,7 @@ type registrationStrategyDependencies interface { continuity.ManagementProvider errorx.ManagementProvider - hash.HashProvider + hash.Generator registration.HandlerProvider registration.HooksProvider diff --git a/spec/api.json b/spec/api.json index 894aaa787813..c1b6a1b6bd62 100755 --- a/spec/api.json +++ b/spec/api.json @@ -77,6 +77,9 @@ }, "adminCreateIdentityBody": { "properties": { + "password": { + "$ref": "#/components/schemas/passwordCredential" + }, "schema_id": { "description": "SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.", "type": "string" @@ -87,6 +90,13 @@ "traits": { "description": "Traits represent an identity's traits. The identity is able to create, modify, and delete traits\nin a self-service manner. The input will always be validated against the JSON Schema defined\nin `schema_url`.", "type": "object" + }, + "verifiable_addresses": { + "description": "VerifiableAddresses contains all the addresses that can be verified by the user.", + "items": { + "$ref": "#/components/schemas/verifiableIdentityAddress" + }, + "type": "array" } }, "required": [ @@ -491,6 +501,25 @@ }, "type": "object" }, + "passwordCredential": { + "description": "The password of the user. It represents a cleartext or hashed user password.", + "properties": { + "is_hashed": { + "description": "This field must be true if the value represents a hashed password. Otherwise the value is assumed to be a\ncleartext password and it will be hashed before storing.", + "type": "boolean" + }, + "value": { + "description": "Value represents the value of the password in clear text or hashed", + "type": "string" + } + }, + "required": [ + "value", + "is_hashed" + ], + "title": "User's password", + "type": "object" + }, "revokedSessions": { "properties": { "count": { @@ -2151,7 +2180,7 @@ ] }, "post": { - "description": "This endpoint creates an identity. It is NOT possible to set an identity's credentials (password, ...)\nusing this method! A way to achieve that will be introduced in the future.\n\nLearn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model).", + "description": "This endpoint creates an identity. Optionally a password can be set for the newly created identity. Do to so\na password field must be included on the creation request. The password field can be in cleartext or hashed with\nany of the supported hashing algorithms. Using already hashed values can be useful when moving from another user\nauthentication mechanism to Kratos.\nLearn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model).", "operationId": "adminCreateIdentity", "requestBody": { "content": { diff --git a/spec/swagger.json b/spec/swagger.json index f52b0d92db11..41d21384731c 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -159,7 +159,7 @@ "oryAccessToken": [] } ], - "description": "This endpoint creates an identity. It is NOT possible to set an identity's credentials (password, ...)\nusing this method! A way to achieve that will be introduced in the future.\n\nLearn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model).", + "description": "This endpoint creates an identity. Optionally a password can be set for the newly created identity. Do to so\na password field must be included on the creation request. The password field can be in cleartext or hashed with\nany of the supported hashing algorithms. Using already hashed values can be useful when moving from another user\nauthentication mechanism to Kratos.\nLearn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model).", "consumes": [ "application/json" ], @@ -2333,6 +2333,9 @@ "traits" ], "properties": { + "password": { + "$ref": "#/definitions/passwordCredential" + }, "schema_id": { "description": "SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.", "type": "string" @@ -2343,6 +2346,13 @@ "traits": { "description": "Traits represent an identity's traits. The identity is able to create, modify, and delete traits\nin a self-service manner. The input will always be validated against the JSON Schema defined\nin `schema_url`.", "type": "object" + }, + "verifiable_addresses": { + "description": "VerifiableAddresses contains all the addresses that can be verified by the user.", + "type": "array", + "items": { + "$ref": "#/definitions/verifiableIdentityAddress" + } } } }, @@ -2728,6 +2738,25 @@ } } }, + "passwordCredential": { + "description": "The password of the user. It represents a cleartext or hashed user password.", + "type": "object", + "title": "User's password", + "required": [ + "value", + "is_hashed" + ], + "properties": { + "is_hashed": { + "description": "This field must be true if the value represents a hashed password. Otherwise the value is assumed to be a\ncleartext password and it will be hashed before storing.", + "type": "boolean" + }, + "value": { + "description": "Value represents the value of the password in clear text or hashed", + "type": "string" + } + } + }, "revokedSessions": { "type": "object", "properties": { diff --git a/ui/container/container.go b/ui/container/container.go index 0602b595fd5c..6c42e62f2ba1 100644 --- a/ui/container/container.go +++ b/ui/container/container.go @@ -8,6 +8,8 @@ import ( "net/http" "strings" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/ory/kratos/ui/node" "github.com/ory/x/sqlxx" @@ -18,7 +20,6 @@ import ( "github.com/ory/x/jsonx" "github.com/ory/x/stringslice" - "github.com/ory/kratos/schema" "github.com/ory/kratos/text" ) @@ -156,7 +157,7 @@ func (c *Container) ParseError(group node.Group, err error) error { return nil } return err - } else if e := new(schema.ValidationError); errors.As(err, &e) { + } else if e := new(errors2.ValidationError); errors.As(err, &e) { pointer, _ := jsonschemax.JSONPointerToDotNotation(e.InstancePtr) for i := range e.Messages { c.AddMessage(group, &e.Messages[i], pointer) diff --git a/ui/container/container_test.go b/ui/container/container_test.go index 6cc733733b2f..3103544fb21e 100644 --- a/ui/container/container_test.go +++ b/ui/container/container_test.go @@ -9,6 +9,8 @@ import ( "sort" "testing" + errors2 "github.com/ory/kratos/schema/errors" + "github.com/ory/kratos/ui/node" "github.com/pkg/errors" @@ -21,7 +23,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/ory/kratos/schema" "github.com/ory/kratos/text" ) @@ -214,7 +215,7 @@ func TestContainer(t *testing.T) { {err: errors.New("foo"), expectErr: true}, {err: &herodot.ErrNotFound, expectErr: true}, {err: herodot.ErrBadRequest.WithReason("tests"), expect: Container{Nodes: node.Nodes{}, Messages: text.Messages{*text.NewValidationErrorGeneric("tests")}}}, - {err: schema.NewInvalidCredentialsError(), expect: Container{Nodes: node.Nodes{}, Messages: text.Messages{*text.NewErrorValidationInvalidCredentials()}}}, + {err: errors2.NewInvalidCredentialsError(), expect: Container{Nodes: node.Nodes{}, Messages: text.Messages{*text.NewErrorValidationInvalidCredentials()}}}, {err: &jsonschema.ValidationError{Message: "test", InstancePtr: "#/foo/bar/baz"}, expect: Container{Nodes: node.Nodes{ &node.Node{Group: node.DefaultGroup, Type: node.Input, Attributes: &node.InputAttributes{Name: "foo.bar.baz", Type: node.InputAttributeTypeText}, Messages: text.Messages{*text.NewValidationErrorGeneric("test")}, Meta: new(node.Meta)}, }}},