Fix identity alias
This commit is contained in:
parent
76b3713b2e
commit
8caab33ba8
87
iam/identity/auth0.go
Normal file
87
iam/identity/auth0.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package identity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/datarhei/core/v16/iam/jwks"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Auth0Tenant struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
Audience string `json:"audience"`
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Auth0Tenant) key() string {
|
||||||
|
return t.Domain + t.Audience
|
||||||
|
}
|
||||||
|
|
||||||
|
type auth0Tenant struct {
|
||||||
|
domain string
|
||||||
|
issuer string
|
||||||
|
audience string
|
||||||
|
clientIDs []string
|
||||||
|
certs jwks.JWKS
|
||||||
|
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAuth0Tenant(tenant Auth0Tenant) (*auth0Tenant, error) {
|
||||||
|
t := &auth0Tenant{
|
||||||
|
domain: tenant.Domain,
|
||||||
|
issuer: "https://" + tenant.Domain + "/",
|
||||||
|
audience: tenant.Audience,
|
||||||
|
clientIDs: []string{tenant.ClientID},
|
||||||
|
certs: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
url := t.issuer + ".well-known/jwks.json"
|
||||||
|
certs, err := jwks.NewFromURL(url, jwks.Config{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.certs = certs
|
||||||
|
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *auth0Tenant) Cancel() {
|
||||||
|
a.certs.Cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *auth0Tenant) AddClientID(clientid string) {
|
||||||
|
a.lock.Lock()
|
||||||
|
defer a.lock.Unlock()
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, id := range a.clientIDs {
|
||||||
|
if id == clientid {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
a.clientIDs = append(a.clientIDs, clientid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *auth0Tenant) RemoveClientID(clientid string) {
|
||||||
|
a.lock.Lock()
|
||||||
|
defer a.lock.Unlock()
|
||||||
|
|
||||||
|
clientids := []string{}
|
||||||
|
|
||||||
|
for _, id := range a.clientIDs {
|
||||||
|
if id == clientid {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
clientids = append(clientids, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.clientIDs = clientids
|
||||||
|
}
|
||||||
@ -7,7 +7,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
enctoken "github.com/datarhei/core/v16/encoding/token"
|
enctoken "github.com/datarhei/core/v16/encoding/token"
|
||||||
"github.com/datarhei/core/v16/iam/jwks"
|
|
||||||
"github.com/datarhei/core/v16/log"
|
"github.com/datarhei/core/v16/log"
|
||||||
"github.com/datarhei/core/v16/slices"
|
"github.com/datarhei/core/v16/slices"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@ -94,8 +93,8 @@ func (u *User) clone() User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Verifier interface {
|
type Verifier interface {
|
||||||
// Name returns the name of the identity, if an alias is available the alias will be returned.
|
Name() string // Name returns the name of the identity.
|
||||||
Name() string
|
Alias() string // Alias returns the alias of the identity, or an empty string if no alias has been set.
|
||||||
|
|
||||||
VerifyJWT(jwt string) (bool, error)
|
VerifyJWT(jwt string) (bool, error)
|
||||||
|
|
||||||
@ -127,13 +126,13 @@ type identity struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *identity) Name() string {
|
func (i *identity) Name() string {
|
||||||
if len(i.user.Alias) != 0 {
|
|
||||||
return i.user.Alias
|
|
||||||
}
|
|
||||||
|
|
||||||
return i.user.Name
|
return i.user.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *identity) Alias() string {
|
||||||
|
return i.user.Alias
|
||||||
|
}
|
||||||
|
|
||||||
func (i *identity) VerifyAPIPassword(password string) (bool, error) {
|
func (i *identity) VerifyAPIPassword(password string) (bool, error) {
|
||||||
i.lock.RLock()
|
i.lock.RLock()
|
||||||
defer i.lock.RUnlock()
|
defer i.lock.RUnlock()
|
||||||
@ -503,7 +502,6 @@ type identityManager struct {
|
|||||||
root *identity
|
root *identity
|
||||||
|
|
||||||
identities map[string]*identity
|
identities map[string]*identity
|
||||||
aliases map[string]*identity
|
|
||||||
tenants map[string]*auth0Tenant
|
tenants map[string]*auth0Tenant
|
||||||
|
|
||||||
auth0UserIdentityMap map[string]string
|
auth0UserIdentityMap map[string]string
|
||||||
@ -529,7 +527,6 @@ type Config struct {
|
|||||||
func New(config Config) (Manager, error) {
|
func New(config Config) (Manager, error) {
|
||||||
im := &identityManager{
|
im := &identityManager{
|
||||||
identities: map[string]*identity{},
|
identities: map[string]*identity{},
|
||||||
aliases: map[string]*identity{},
|
|
||||||
tenants: map[string]*auth0Tenant{},
|
tenants: map[string]*auth0Tenant{},
|
||||||
auth0UserIdentityMap: map[string]string{},
|
auth0UserIdentityMap: map[string]string{},
|
||||||
adapter: config.Adapter,
|
adapter: config.Adapter,
|
||||||
@ -571,7 +568,6 @@ func (im *identityManager) Close() {
|
|||||||
im.adapter = nil
|
im.adapter = nil
|
||||||
im.auth0UserIdentityMap = map[string]string{}
|
im.auth0UserIdentityMap = map[string]string{}
|
||||||
im.identities = map[string]*identity{}
|
im.identities = map[string]*identity{}
|
||||||
im.aliases = map[string]*identity{}
|
|
||||||
im.root = nil
|
im.root = nil
|
||||||
|
|
||||||
for _, t := range im.tenants {
|
for _, t := range im.tenants {
|
||||||
@ -606,26 +602,10 @@ func (im *identityManager) Reload() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
if im.root != nil && u.Name == im.root.user.Name {
|
if ok, _ := im.isNameAvailable(u.Name, u.Alias); !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ok := im.identities[u.Name]
|
|
||||||
if ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(u.Alias) != 0 {
|
|
||||||
if im.root != nil && im.root.user.Alias == u.Alias {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
_, ok := im.aliases[u.Alias]
|
|
||||||
if ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := u.Validate(); err != nil {
|
if err := u.Validate(); err != nil {
|
||||||
return fmt.Errorf("invalid user from adapter: %s, %w", u.Name, err)
|
return fmt.Errorf("invalid user from adapter: %s, %w", u.Name, err)
|
||||||
}
|
}
|
||||||
@ -637,13 +617,47 @@ func (im *identityManager) Reload() error {
|
|||||||
|
|
||||||
im.identities[identity.user.Name] = identity
|
im.identities[identity.user.Name] = identity
|
||||||
if len(identity.user.Alias) != 0 {
|
if len(identity.user.Alias) != 0 {
|
||||||
im.aliases[identity.user.Alias] = identity
|
im.identities[identity.user.Alias] = identity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (im *identityManager) isNameAvailable(name, alias string) (bool, error) {
|
||||||
|
if im.root == nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if name == im.root.user.Name {
|
||||||
|
return false, fmt.Errorf("name already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
if name == im.root.user.Alias {
|
||||||
|
return false, fmt.Errorf("name already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := im.identities[name]; ok {
|
||||||
|
return false, fmt.Errorf("name already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(alias) != 0 {
|
||||||
|
if alias == im.root.user.Name {
|
||||||
|
return false, fmt.Errorf("alias already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
if alias == im.root.user.Alias {
|
||||||
|
return false, fmt.Errorf("alias already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := im.identities[alias]; ok {
|
||||||
|
return false, fmt.Errorf("alias already exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (im *identityManager) Create(u User) error {
|
func (im *identityManager) Create(u User) error {
|
||||||
if err := u.Validate(); err != nil {
|
if err := u.Validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -652,24 +666,8 @@ func (im *identityManager) Create(u User) error {
|
|||||||
im.lock.Lock()
|
im.lock.Lock()
|
||||||
defer im.lock.Unlock()
|
defer im.lock.Unlock()
|
||||||
|
|
||||||
if im.root != nil && im.root.user.Name == u.Name {
|
if ok, err := im.isNameAvailable(u.Name, u.Alias); !ok {
|
||||||
return fmt.Errorf("identity name already exists")
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
_, ok := im.identities[u.Name]
|
|
||||||
if ok {
|
|
||||||
return fmt.Errorf("identity name already exists")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(u.Alias) != 0 {
|
|
||||||
if im.root != nil && im.root.user.Alias == u.Alias {
|
|
||||||
return fmt.Errorf("identity alias already exists")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, ok := im.aliases[u.Alias]
|
|
||||||
if ok {
|
|
||||||
return fmt.Errorf("identity alias already exists")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
identity, err := im.create(u)
|
identity, err := im.create(u)
|
||||||
@ -679,7 +677,7 @@ func (im *identityManager) Create(u User) error {
|
|||||||
|
|
||||||
im.identities[identity.user.Name] = identity
|
im.identities[identity.user.Name] = identity
|
||||||
if len(identity.user.Alias) != 0 {
|
if len(identity.user.Alias) != 0 {
|
||||||
im.aliases[identity.user.Alias] = identity
|
im.identities[identity.user.Alias] = identity
|
||||||
}
|
}
|
||||||
|
|
||||||
if im.autosave {
|
if im.autosave {
|
||||||
@ -731,45 +729,41 @@ func (im *identityManager) Update(name string, u User) error {
|
|||||||
im.lock.Lock()
|
im.lock.Lock()
|
||||||
defer im.lock.Unlock()
|
defer im.lock.Unlock()
|
||||||
|
|
||||||
if im.root.user.Name == name {
|
if im.root.user.Name == name || im.root.user.Alias == name {
|
||||||
return fmt.Errorf("this identity can't be updated")
|
return fmt.Errorf("this identity cannot be updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
oldidentity, ok := im.identities[name]
|
oldidentity, ok := im.identities[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("not found")
|
return fmt.Errorf("identity not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldidentity.user.Name != u.Name {
|
delete(im.identities, oldidentity.user.Name)
|
||||||
_, err := im.getIdentity(u.Name)
|
delete(im.identities, oldidentity.user.Alias)
|
||||||
if err == nil {
|
|
||||||
return fmt.Errorf("identity already exist")
|
ok, err := im.isNameAvailable(u.Name, u.Alias)
|
||||||
}
|
|
||||||
|
im.identities[oldidentity.user.Name] = oldidentity
|
||||||
|
if len(oldidentity.user.Alias) != 0 {
|
||||||
|
im.identities[oldidentity.user.Alias] = oldidentity
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(u.Alias) != 0 {
|
if !ok {
|
||||||
if oldidentity.user.Alias != u.Alias {
|
return err
|
||||||
_, err := im.getIdentityFromAlias(u.Alias)
|
|
||||||
if err == nil {
|
|
||||||
return fmt.Errorf("identity alias already exist")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := im.delete(name)
|
err = im.delete(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
identity, err := im.create(u)
|
identity, err := im.create(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if identity, err := im.create(oldidentity.user); err != nil {
|
// restore old identity
|
||||||
return err
|
im.create(oldidentity.user)
|
||||||
} else {
|
im.identities[oldidentity.user.Name] = oldidentity
|
||||||
im.identities[identity.user.Name] = identity
|
if len(oldidentity.user.Alias) != 0 {
|
||||||
if len(identity.user.Alias) != 0 {
|
im.identities[oldidentity.user.Alias] = oldidentity
|
||||||
im.aliases[identity.user.Alias] = identity
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@ -777,7 +771,7 @@ func (im *identityManager) Update(name string, u User) error {
|
|||||||
|
|
||||||
im.identities[identity.user.Name] = identity
|
im.identities[identity.user.Name] = identity
|
||||||
if len(identity.user.Alias) != 0 {
|
if len(identity.user.Alias) != 0 {
|
||||||
im.aliases[identity.user.Alias] = identity
|
im.identities[identity.user.Alias] = identity
|
||||||
}
|
}
|
||||||
|
|
||||||
im.logger.Debug().WithFields(log.Fields{
|
im.logger.Debug().WithFields(log.Fields{
|
||||||
@ -805,17 +799,17 @@ func (im *identityManager) Delete(name string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (im *identityManager) delete(name string) error {
|
func (im *identityManager) delete(name string) error {
|
||||||
if im.root.user.Name == name {
|
if im.root.user.Name == name || im.root.user.Alias == name {
|
||||||
return fmt.Errorf("this identity can't be removed")
|
return fmt.Errorf("this identity can't be removed")
|
||||||
}
|
}
|
||||||
|
|
||||||
identity, ok := im.identities[name]
|
identity, ok := im.identities[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("not found")
|
return fmt.Errorf("identity not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(im.identities, name)
|
delete(im.identities, identity.user.Name)
|
||||||
delete(im.aliases, identity.user.Alias)
|
delete(im.identities, identity.user.Alias)
|
||||||
|
|
||||||
identity.lock.Lock()
|
identity.lock.Lock()
|
||||||
identity.valid = false
|
identity.valid = false
|
||||||
@ -880,14 +874,14 @@ func (im *identityManager) delete(name string) error {
|
|||||||
func (im *identityManager) getIdentity(name string) (*identity, error) {
|
func (im *identityManager) getIdentity(name string) (*identity, error) {
|
||||||
var identity *identity = nil
|
var identity *identity = nil
|
||||||
|
|
||||||
if im.root.user.Name == name {
|
if im.root.user.Name == name || im.root.user.Alias == name {
|
||||||
identity = im.root
|
identity = im.root
|
||||||
} else {
|
} else {
|
||||||
identity = im.identities[name]
|
identity = im.identities[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
if identity == nil {
|
if identity == nil {
|
||||||
return nil, fmt.Errorf("not found")
|
return nil, fmt.Errorf("identity not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
identity.jwtRealm = im.jwtRealm
|
identity.jwtRealm = im.jwtRealm
|
||||||
@ -896,32 +890,13 @@ func (im *identityManager) getIdentity(name string) (*identity, error) {
|
|||||||
return identity, nil
|
return identity, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im *identityManager) getIdentityFromAlias(alias string) (*identity, error) {
|
|
||||||
var identity *identity = nil
|
|
||||||
|
|
||||||
if im.root.user.Alias == alias {
|
|
||||||
identity = im.root
|
|
||||||
} else {
|
|
||||||
identity = im.aliases[alias]
|
|
||||||
}
|
|
||||||
|
|
||||||
if identity == nil {
|
|
||||||
return nil, fmt.Errorf("not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return im.getIdentity(identity.user.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (im *identityManager) Get(name string) (User, error) {
|
func (im *identityManager) Get(name string) (User, error) {
|
||||||
im.lock.RLock()
|
im.lock.RLock()
|
||||||
defer im.lock.RUnlock()
|
defer im.lock.RUnlock()
|
||||||
|
|
||||||
identity, err := im.getIdentity(name)
|
identity, err := im.getIdentity(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
identity, err = im.getIdentityFromAlias(name)
|
return User{}, err
|
||||||
if err != nil {
|
|
||||||
return User{}, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
user := identity.user.clone()
|
user := identity.user.clone()
|
||||||
@ -933,12 +908,7 @@ func (im *identityManager) GetVerifier(name string) (Verifier, error) {
|
|||||||
im.lock.RLock()
|
im.lock.RLock()
|
||||||
defer im.lock.RUnlock()
|
defer im.lock.RUnlock()
|
||||||
|
|
||||||
identity, err := im.getIdentity(name)
|
return im.getIdentity(name)
|
||||||
if err != nil {
|
|
||||||
identity, err = im.getIdentityFromAlias(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return identity, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im *identityManager) GetVerifierFromAuth0(name string) (Verifier, error) {
|
func (im *identityManager) GetVerifierFromAuth0(name string) (Verifier, error) {
|
||||||
@ -1058,83 +1028,3 @@ func (im *identityManager) CreateJWT(name string) (string, string, error) {
|
|||||||
|
|
||||||
return at, rt, nil
|
return at, rt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Auth0Tenant struct {
|
|
||||||
Domain string `json:"domain"`
|
|
||||||
Audience string `json:"audience"`
|
|
||||||
ClientID string `json:"client_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Auth0Tenant) key() string {
|
|
||||||
return t.Domain + t.Audience
|
|
||||||
}
|
|
||||||
|
|
||||||
type auth0Tenant struct {
|
|
||||||
domain string
|
|
||||||
issuer string
|
|
||||||
audience string
|
|
||||||
clientIDs []string
|
|
||||||
certs jwks.JWKS
|
|
||||||
|
|
||||||
lock sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAuth0Tenant(tenant Auth0Tenant) (*auth0Tenant, error) {
|
|
||||||
t := &auth0Tenant{
|
|
||||||
domain: tenant.Domain,
|
|
||||||
issuer: "https://" + tenant.Domain + "/",
|
|
||||||
audience: tenant.Audience,
|
|
||||||
clientIDs: []string{tenant.ClientID},
|
|
||||||
certs: nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
url := t.issuer + ".well-known/jwks.json"
|
|
||||||
certs, err := jwks.NewFromURL(url, jwks.Config{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
t.certs = certs
|
|
||||||
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *auth0Tenant) Cancel() {
|
|
||||||
a.certs.Cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *auth0Tenant) AddClientID(clientid string) {
|
|
||||||
a.lock.Lock()
|
|
||||||
defer a.lock.Unlock()
|
|
||||||
|
|
||||||
found := false
|
|
||||||
for _, id := range a.clientIDs {
|
|
||||||
if id == clientid {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if found {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
a.clientIDs = append(a.clientIDs, clientid)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *auth0Tenant) RemoveClientID(clientid string) {
|
|
||||||
a.lock.Lock()
|
|
||||||
defer a.lock.Unlock()
|
|
||||||
|
|
||||||
clientids := []string{}
|
|
||||||
|
|
||||||
for _, id := range a.clientIDs {
|
|
||||||
if id == clientid {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
clientids = append(clientids, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
a.clientIDs = clientids
|
|
||||||
}
|
|
||||||
|
|||||||
@ -63,7 +63,7 @@ func TestIdentity(t *testing.T) {
|
|||||||
require.Equal(t, "foobar", identity.Name())
|
require.Equal(t, "foobar", identity.Name())
|
||||||
|
|
||||||
identity.user.Alias = "raboof"
|
identity.user.Alias = "raboof"
|
||||||
require.Equal(t, "raboof", identity.Name())
|
require.Equal(t, "raboof", identity.Alias())
|
||||||
|
|
||||||
require.False(t, identity.isValid())
|
require.False(t, identity.isValid())
|
||||||
identity.valid = true
|
identity.valid = true
|
||||||
@ -364,6 +364,9 @@ func TestAlias(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, "foobaz", identity.Name)
|
require.Equal(t, "foobaz", identity.Name)
|
||||||
require.Equal(t, "alias", identity.Alias)
|
require.Equal(t, "alias", identity.Alias)
|
||||||
|
|
||||||
|
err = im.Create(User{Name: "alias", Alias: "foobaz"})
|
||||||
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateUserAuth0(t *testing.T) {
|
func TestCreateUserAuth0(t *testing.T) {
|
||||||
@ -624,7 +627,8 @@ func TestUpdateUserAlias(t *testing.T) {
|
|||||||
identity, err = im.GetVerifier("fooboz")
|
identity, err = im.GetVerifier("fooboz")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, identity)
|
require.NotNil(t, identity)
|
||||||
require.Equal(t, "alias", identity.Name())
|
require.Equal(t, "fooboz", identity.Name())
|
||||||
|
require.Equal(t, "alias", identity.Alias())
|
||||||
|
|
||||||
err = im.Create(User{Name: "barfoo", Alias: "alias2"})
|
err = im.Create(User{Name: "barfoo", Alias: "alias2"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
12
maps/copy.go
Normal file
12
maps/copy.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package maps
|
||||||
|
|
||||||
|
// Copy returns a shallow copy of a map
|
||||||
|
func Copy[A comparable, B any](a map[A]B) map[A]B {
|
||||||
|
b := map[A]B{}
|
||||||
|
|
||||||
|
for k, v := range a {
|
||||||
|
b[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
18
maps/copy_test.go
Normal file
18
maps/copy_test.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package maps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCopy(t *testing.T) {
|
||||||
|
a := map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": "foo",
|
||||||
|
}
|
||||||
|
|
||||||
|
b := Copy(a)
|
||||||
|
|
||||||
|
require.Equal(t, a, b)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user