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
149 lines
3.4 KiB
Go
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
|
|
}
|