Files
upstream-ory-kratos/identity/credentials_migrate.go
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

149 lines
3.4 KiB
Go

// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package identity
import (
"encoding/json"
"fmt"
"slices"
"strings"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
"github.com/ory/kratos/x"
"github.com/pkg/errors"
)
func UpgradeWebAuthnCredentials(i *Identity, c *Credentials) (err error) {
if c.Type != CredentialsTypeWebAuthn {
return nil
}
version := c.Version
if version == 0 {
if gjson.GetBytes(c.Config, "user_handle").String() == "" {
id, err := json.Marshal(i.ID[:])
if err != nil {
return errors.WithStack(err)
}
c.Config, err = sjson.SetRawBytes(c.Config, "user_handle", id)
if err != nil {
return errors.WithStack(err)
}
}
index := -1
var err error
gjson.GetBytes(c.Config, "credentials").ForEach(func(_, value gjson.Result) bool {
index++
if value.Get("is_passwordless").Exists() {
return true
}
c.Config, err = sjson.SetBytes(c.Config, fmt.Sprintf("credentials.%d.is_passwordless", index), false)
return err == nil
})
if err != nil {
return errors.WithStack(err)
}
c.Version = 1
}
return nil
}
// UpgradeCredentials migrates a set of older WebAuthn, Code, and
// Password credentials to newer ones.
func UpgradeCredentials(i *Identity) error {
for k := range i.Credentials {
c := i.Credentials[k]
if err := UpgradeWebAuthnCredentials(i, &c); err != nil {
return errors.WithStack(err)
}
if err := UpgradeCodeCredentials(&c); err != nil {
return errors.WithStack(err)
}
if err := UpgradePasswordCredentials(&c); err != nil {
return errors.WithStack(err)
}
i.Credentials[k] = c
}
return nil
}
func UpgradeCodeCredentials(c *Credentials) (err error) {
if c.Type != CredentialsTypeCodeAuth {
return nil
}
version := c.Version
if version == 0 {
addressType := strings.ToLower(strings.TrimSpace(gjson.GetBytes(c.Config, "address_type").String()))
channel, err := NewCodeChannel(addressType)
if err != nil {
// We know that in some cases the address type can be empty. In this case, we default to email
// as sms is a new addition to the address_type introduced in this PR.
channel = CodeChannelEmail
}
c.Config, err = sjson.DeleteBytes(c.Config, "used_at")
if err != nil {
return errors.WithStack(err)
}
c.Config, err = sjson.DeleteBytes(c.Config, "address_type")
if err != nil {
return errors.WithStack(err)
}
for _, id := range c.Identifiers {
if id == "" {
continue
}
c.Config, err = sjson.SetBytes(c.Config, "addresses.-1", &CredentialsCodeAddress{
Address: id,
Channel: channel,
})
if err != nil {
return errors.WithStack(err)
}
}
// This is needed because sjson adds spaces which can trip string comparisons.
c.Config, err = json.Marshal(json.RawMessage(c.Config))
if err != nil {
return errors.WithStack(err)
}
c.Version = 1
}
return nil
}
// UpgradePasswordCredentials will make a graceful attempt to normalize
// the format of a credential's identifier. Version is always bumped to v1.
func UpgradePasswordCredentials(c *Credentials) (err error) {
if c.Type != CredentialsTypePassword {
return nil
}
if c.Version == 0 {
// iterate over identifier values
// and normalize format
for i := range c.Identifiers {
c.Identifiers[i] = x.GracefulNormalization(c.Identifiers[i])
}
slices.Sort(c.Identifiers)
c.Identifiers = slices.Compact(c.Identifiers)
c.Version = 1
}
return nil
}