WIP: identity management and testing

This commit is contained in:
Ingo Oppermann
2023-02-16 21:10:35 +01:00
parent acc4d7ec5a
commit ca2fc5fe26
2 changed files with 1031 additions and 37 deletions

View File

@@ -86,6 +86,15 @@ func (u *User) marshalIdentity() *identity {
return i
}
func (u *User) clone() User {
user := *u
user.Auth.Services.Token = make([]string, len(u.Auth.Services.Token))
copy(user.Auth.Services.Token, u.Auth.Services.Token)
return user
}
type IdentityVerifier interface {
Name() string
@@ -238,12 +247,30 @@ func (i *identity) auth0KeyFunc(token *jwtgo.Token) (interface{}, error) {
}
func (i *identity) VerifyJWT(jwt string) (bool, error) {
i.lock.RLock()
defer i.lock.RUnlock()
if !i.isValid() {
return false, fmt.Errorf("invalid identity")
}
p := &jwtgo.Parser{}
token, _, err := p.ParseUnverified(jwt, jwtgo.MapClaims{})
if err != nil {
return false, err
}
var subject string
if claims, ok := token.Claims.(jwtgo.MapClaims); ok {
if sub, ok := claims["sub"]; ok {
subject = sub.(string)
}
}
if subject != i.user.Name {
return false, fmt.Errorf("wrong subject")
}
var issuer string
if claims, ok := token.Claims.(jwtgo.MapClaims); ok {
if sub, ok := claims["iss"]; ok {
@@ -346,17 +373,19 @@ func (i *identity) IsSuperuser() bool {
type IdentityManager interface {
Create(identity User) error
Update(name string, identity User) error
Remove(name string) error
Get(name string) (User, error)
GetVerifier(name string) (IdentityVerifier, error)
GetVerifierByAuth0(name string) (IdentityVerifier, error)
GetDefaultVerifier() (IdentityVerifier, error)
Rename(oldname, newname string) error
Update(name string, identity User) error
Validators() []string
CreateJWT(name string) (string, string, error)
Save() error
Autosave(bool)
Close()
}
@@ -370,6 +399,7 @@ type identityManager struct {
fs fs.Filesystem
filePath string
autosave bool
logger log.Logger
jwtRealm string
@@ -446,6 +476,10 @@ func (im *identityManager) Create(u User) error {
im.lock.Lock()
defer im.lock.Unlock()
if im.root != nil && im.root.user.Name == u.Name {
return fmt.Errorf("identity already exists")
}
_, ok := im.identities[u.Name]
if ok {
return fmt.Errorf("identity already exists")
@@ -458,10 +492,15 @@ func (im *identityManager) Create(u User) error {
im.identities[identity.user.Name] = identity
if im.autosave {
im.save(im.filePath)
}
return nil
}
func (im *identityManager) create(u User) (*identity, error) {
u = u.clone()
identity := u.marshalIdentity()
if identity.user.Auth.API.Auth0.Enable {
@@ -471,7 +510,7 @@ func (im *identityManager) create(u User) (*identity, error) {
auth0Key := identity.user.Auth.API.Auth0.Tenant.key()
if _, ok := im.tenants[auth0Key]; !ok {
if tenant, ok := im.tenants[auth0Key]; !ok {
tenant, err := newAuth0Tenant(identity.user.Auth.API.Auth0.Tenant)
if err != nil {
return nil, err
@@ -479,7 +518,12 @@ func (im *identityManager) create(u User) (*identity, error) {
im.tenants[auth0Key] = tenant
identity.tenant = tenant
} else {
tenant.AddClientID(identity.user.Auth.API.Auth0.Tenant.ClientID)
identity.tenant = tenant
}
im.auth0UserIdentityMap[identity.user.Auth.API.Auth0.User] = u.Name
}
identity.valid = true
@@ -487,7 +531,52 @@ func (im *identityManager) create(u User) (*identity, error) {
return identity, nil
}
func (im *identityManager) Update(name string, identity User) error {
func (im *identityManager) Update(name string, u User) error {
if err := u.validate(); err != nil {
return err
}
im.lock.Lock()
defer im.lock.Unlock()
if im.root.user.Name == name {
return fmt.Errorf("this identity can't be updated")
}
oldidentity, ok := im.identities[name]
if !ok {
return fmt.Errorf("not found")
}
if name != u.Name {
_, err := im.getIdentity(u.Name)
if err == nil {
return fmt.Errorf("identity already exist")
}
}
err := im.remove(name)
if err != nil {
return err
}
identity, err := im.create(u)
if err != nil {
if identity, err := im.create(oldidentity.user); err != nil {
return err
} else {
im.identities[identity.user.Name] = identity
}
return err
}
im.identities[identity.user.Name] = identity
if im.autosave {
im.save(im.filePath)
}
return nil
}
@@ -495,16 +584,75 @@ func (im *identityManager) Remove(name string) error {
im.lock.Lock()
defer im.lock.Unlock()
user, ok := im.identities[name]
return im.remove(name)
}
func (im *identityManager) remove(name string) error {
if im.root.user.Name == name {
return fmt.Errorf("this identity can't be removed")
}
identity, ok := im.identities[name]
if !ok {
return nil
return fmt.Errorf("not found")
}
delete(im.identities, name)
user.lock.Lock()
user.valid = false
user.lock.Unlock()
identity.lock.Lock()
identity.valid = false
identity.lock.Unlock()
if !identity.user.Auth.API.Auth0.Enable {
if im.autosave {
im.save(im.filePath)
}
return nil
}
delete(im.auth0UserIdentityMap, identity.user.Auth.API.Auth0.User)
// find out if the tenant is still used somewhere else
found := false
for _, i := range im.identities {
if i.tenant == identity.tenant {
found = true
break
}
}
if !found {
identity.tenant.Cancel()
delete(im.tenants, identity.user.Auth.API.Auth0.Tenant.key())
if im.autosave {
im.save(im.filePath)
}
return nil
}
// find out if the tenant's clientid is still used somewhere else
found = false
for _, i := range im.identities {
if !i.user.Auth.API.Auth0.Enable {
continue
}
if i.user.Auth.API.Auth0.Tenant.ClientID == identity.user.Auth.API.Auth0.Tenant.ClientID {
found = true
break
}
}
if !found {
identity.tenant.RemoveClientID(identity.user.Auth.API.Auth0.Tenant.ClientID)
}
if im.autosave {
im.save(im.filePath)
}
return nil
}
@@ -516,7 +664,6 @@ func (im *identityManager) getIdentity(name string) (*identity, error) {
identity = im.root
} else {
identity = im.identities[name]
}
if identity == nil {
@@ -529,6 +676,20 @@ func (im *identityManager) getIdentity(name string) (*identity, error) {
return identity, nil
}
func (im *identityManager) Get(name string) (User, error) {
im.lock.RLock()
defer im.lock.RUnlock()
identity, err := im.getIdentity(name)
if err != nil {
return User{}, err
}
user := identity.user.clone()
return user, nil
}
func (im *identityManager) GetVerifier(name string) (IdentityVerifier, error) {
im.lock.RLock()
defer im.lock.RUnlock()
@@ -552,27 +713,6 @@ func (im *identityManager) GetDefaultVerifier() (IdentityVerifier, error) {
return im.root, nil
}
func (im *identityManager) Rename(oldname, newname string) error {
im.lock.Lock()
defer im.lock.Unlock()
identity, ok := im.identities[oldname]
if !ok {
return nil
}
if _, ok := im.identities[newname]; ok {
return fmt.Errorf("the new name already exists")
}
delete(im.identities, oldname)
identity.user.Name = newname
im.identities[newname] = identity
return nil
}
func (im *identityManager) load(filePath string) error {
if _, err := im.fs.Stat(filePath); os.IsNotExist(err) {
return nil
@@ -601,6 +741,9 @@ func (im *identityManager) load(filePath string) error {
}
func (im *identityManager) Save() error {
im.lock.RLock()
defer im.lock.RUnlock()
return im.save(im.filePath)
}
@@ -609,9 +752,6 @@ func (im *identityManager) save(filePath string) error {
return fmt.Errorf("invalid file path, file path cannot be empty")
}
im.lock.RLock()
defer im.lock.RUnlock()
users := []User{}
for _, u := range im.identities {
@@ -628,6 +768,13 @@ func (im *identityManager) save(filePath string) error {
return err
}
func (im *identityManager) Autosave(auto bool) {
im.lock.Lock()
defer im.lock.Unlock()
im.autosave = auto
}
func (im *identityManager) Validators() []string {
validators := []string{"localjwt"}
@@ -644,6 +791,14 @@ func (im *identityManager) Validators() []string {
}
func (im *identityManager) CreateJWT(name string) (string, string, error) {
im.lock.RLock()
defer im.lock.RUnlock()
identity, err := im.getIdentity(name)
if err != nil {
return "", "", err
}
now := time.Now()
accessExpires := now.Add(time.Minute * 10)
refreshExpires := now.Add(time.Hour * 24)
@@ -651,7 +806,7 @@ func (im *identityManager) CreateJWT(name string) (string, string, error) {
// Create access token
accessToken := jwtgo.NewWithClaims(jwtgo.SigningMethodHS256, jwtgo.MapClaims{
"iss": im.jwtRealm,
"sub": name,
"sub": identity.Name(),
"usefor": "access",
"iat": now.Unix(),
"exp": accessExpires.Unix(),
@@ -668,7 +823,7 @@ func (im *identityManager) CreateJWT(name string) (string, string, error) {
// Create refresh token
refreshToken := jwtgo.NewWithClaims(jwtgo.SigningMethodHS256, jwtgo.MapClaims{
"iss": im.jwtRealm,
"sub": name,
"sub": identity.Name(),
"usefor": "refresh",
"iat": now.Unix(),
"exp": refreshExpires.Unix(),
@@ -701,6 +856,8 @@ type auth0Tenant struct {
audience string
clientIDs []string
certs jwks.JWKS
lock sync.Mutex
}
func newAuth0Tenant(tenant Auth0Tenant) (*auth0Tenant, error) {
@@ -712,7 +869,7 @@ func newAuth0Tenant(tenant Auth0Tenant) (*auth0Tenant, error) {
certs: nil,
}
url := t.issuer + "/.well-known/jwks.json"
url := t.issuer + ".well-known/jwks.json"
certs, err := jwks.NewFromURL(url, jwks.Config{})
if err != nil {
return nil, err
@@ -726,3 +883,39 @@ func newAuth0Tenant(tenant Auth0Tenant) (*auth0Tenant, error) {
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
}

View File

@@ -1 +1,802 @@
package iam
import (
"testing"
"github.com/datarhei/core/v16/io/fs"
"github.com/stretchr/testify/require"
)
func TestUserName(t *testing.T) {
user := User{}
err := user.validate()
require.Error(t, err)
user.Name = "foobar_5"
err = user.validate()
require.NoError(t, err)
user.Name = "$foob:ar"
err = user.validate()
require.Error(t, err)
}
func TestUserAuth(t *testing.T) {
user := User{
Name: "foobar",
}
err := user.validate()
require.NoError(t, err)
user.Auth.API.Userpass.Enable = true
err = user.validate()
require.Error(t, err)
user.Auth.API.Userpass.Password = "secret"
err = user.validate()
require.NoError(t, err)
user.Auth.API.Auth0.Enable = true
err = user.validate()
require.Error(t, err)
user.Auth.API.Auth0.User = "auth0|123456"
err = user.validate()
require.NoError(t, err)
user.Auth.Services.Basic.Enable = true
err = user.validate()
require.Error(t, err)
user.Auth.Services.Basic.Password = "secret"
err = user.validate()
require.NoError(t, err)
}
func TestIdentity(t *testing.T) {
user := User{
Name: "foobar",
}
identity := user.marshalIdentity()
require.Equal(t, "foobar", identity.Name())
require.False(t, identity.isValid())
identity.valid = true
require.True(t, identity.isValid())
require.False(t, identity.IsSuperuser())
identity.user.Superuser = true
require.True(t, identity.IsSuperuser())
}
func TestDefaultIdentity(t *testing.T) {
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
require.NoError(t, err)
im, err := NewIdentityManager(IdentityConfig{
FS: dummyfs,
Superuser: User{Name: "foobar"},
JWTRealm: "test-realm",
JWTSecret: "abc123",
Logger: nil,
})
require.NoError(t, err)
require.NotNil(t, im)
identity, err := im.GetDefaultVerifier()
require.NoError(t, err)
require.NotNil(t, identity)
require.Equal(t, "foobar", identity.Name())
}
func TestIdentityAPIAuth(t *testing.T) {
user := User{
Name: "foobar",
}
identity := user.marshalIdentity()
ok, err := identity.VerifyAPIPassword("secret")
require.False(t, ok)
require.Error(t, err)
identity.user.Auth.API.Userpass.Enable = true
identity.user.Auth.API.Userpass.Password = "secret"
ok, err = identity.VerifyAPIPassword("secret")
require.False(t, ok)
require.Error(t, err)
identity.valid = true
ok, err = identity.VerifyAPIPassword("secret")
require.True(t, ok)
require.NoError(t, err)
identity.user.Auth.API.Userpass.Enable = false
ok, err = identity.VerifyAPIPassword("secret")
require.False(t, ok)
require.Error(t, err)
identity.user.Auth.API.Userpass.Enable = true
identity.user.Auth.API.Userpass.Password = "terces"
ok, err = identity.VerifyAPIPassword("secret")
require.False(t, ok)
require.NoError(t, err)
}
func TestIdentityServiceBasicAuth(t *testing.T) {
user := User{
Name: "foobar",
}
identity := user.marshalIdentity()
ok, err := identity.VerifyServiceBasicAuth("secret")
require.False(t, ok)
require.Error(t, err)
identity.user.Auth.Services.Basic.Enable = true
identity.user.Auth.Services.Basic.Password = "secret"
ok, err = identity.VerifyServiceBasicAuth("secret")
require.False(t, ok)
require.Error(t, err)
identity.valid = true
ok, err = identity.VerifyServiceBasicAuth("secret")
require.True(t, ok)
require.NoError(t, err)
identity.user.Auth.Services.Basic.Enable = false
ok, err = identity.VerifyServiceBasicAuth("secret")
require.False(t, ok)
require.Error(t, err)
identity.user.Auth.Services.Basic.Enable = true
identity.user.Auth.Services.Basic.Password = "terces"
ok, err = identity.VerifyServiceBasicAuth("secret")
require.False(t, ok)
require.NoError(t, err)
password := identity.GetServiceBasicAuth()
require.Equal(t, "terces", password)
}
func TestIdentityServiceTokenAuth(t *testing.T) {
user := User{
Name: "foobar",
}
identity := user.marshalIdentity()
ok, err := identity.VerifyServiceToken("secret")
require.False(t, ok)
require.Error(t, err)
identity.user.Auth.Services.Token = []string{"secret"}
ok, err = identity.VerifyServiceToken("secret")
require.False(t, ok)
require.Error(t, err)
identity.valid = true
ok, err = identity.VerifyServiceToken("secret")
require.True(t, ok)
require.NoError(t, err)
identity.user.Auth.Services.Token = []string{"terces"}
ok, err = identity.VerifyServiceToken("secret")
require.False(t, ok)
require.NoError(t, err)
token := identity.GetServiceToken()
require.Equal(t, "foobar:terces", token)
}
func TestJWT(t *testing.T) {
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
require.NoError(t, err)
im, err := NewIdentityManager(IdentityConfig{
FS: dummyfs,
Superuser: User{Name: "foobar"},
JWTRealm: "test-realm",
JWTSecret: "abc123",
Logger: nil,
})
require.NoError(t, err)
require.NotNil(t, im)
access, refresh, err := im.CreateJWT("foobaz")
require.Error(t, err)
require.Equal(t, "", access)
require.Equal(t, "", refresh)
access, refresh, err = im.CreateJWT("foobar")
require.NoError(t, err)
identity, err := im.GetVerifier("foobar")
require.NoError(t, err)
require.NotNil(t, identity)
ok, err := identity.VerifyJWT("something")
require.Error(t, err)
require.False(t, ok)
ok, err = identity.VerifyJWT(access)
require.NoError(t, err)
require.True(t, ok)
ok, err = identity.VerifyJWT(refresh)
require.NoError(t, err)
require.True(t, ok)
err = im.Create(User{Name: "foobaz"})
require.NoError(t, err)
access, refresh, err = im.CreateJWT("foobaz")
require.NoError(t, err)
ok, err = identity.VerifyJWT(access)
require.Error(t, err)
require.False(t, ok)
ok, err = identity.VerifyJWT(refresh)
require.Error(t, err)
require.False(t, ok)
}
func TestCreateUser(t *testing.T) {
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
require.NoError(t, err)
im, err := NewIdentityManager(IdentityConfig{
FS: dummyfs,
Superuser: User{Name: "foobar"},
JWTRealm: "test-realm",
JWTSecret: "abc123",
Logger: nil,
})
require.NoError(t, err)
require.NotNil(t, im)
err = im.Create(User{Name: "foobar"})
require.Error(t, err)
err = im.Create(User{Name: "foobaz"})
require.NoError(t, err)
err = im.Create(User{Name: "foobaz"})
require.Error(t, err)
}
func TestCreateUserAuth0(t *testing.T) {
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
require.NoError(t, err)
im, err := NewIdentityManager(IdentityConfig{
FS: dummyfs,
Superuser: User{Name: "foobar"},
JWTRealm: "test-realm",
JWTSecret: "abc123",
Logger: nil,
})
require.NoError(t, err)
require.NotNil(t, im)
require.ElementsMatch(t, []string{"localjwt"}, im.Validators())
err = im.Create(User{
Name: "foobaz",
Superuser: false,
Auth: UserAuth{
API: UserAuthAPI{
Auth0: UserAuthAPIAuth0{
Enable: true,
User: "auth0|123456",
Tenant: Auth0Tenant{
Domain: "example.com",
Audience: "https://api.example.com/",
ClientID: "123456",
},
},
},
},
})
require.Error(t, err)
err = im.Create(User{
Name: "foobaz",
Superuser: false,
Auth: UserAuth{
API: UserAuthAPI{
Auth0: UserAuthAPIAuth0{
Enable: true,
User: "auth0|123456",
Tenant: Auth0Tenant{
Domain: "datarhei-demo.eu.auth0.com",
Audience: "https://datarhei-demo.eu.auth0.com/api/v2/",
ClientID: "123456",
},
},
},
},
})
require.NoError(t, err)
identity, err := im.GetVerifierByAuth0("foobaz")
require.Error(t, err)
require.Nil(t, identity)
identity, err = im.GetVerifierByAuth0("auth0|123456")
require.NoError(t, err)
require.NotNil(t, identity)
manager, ok := im.(*identityManager)
require.True(t, ok)
require.NotNil(t, manager)
require.Equal(t, 1, len(manager.tenants))
require.Equal(t, map[string]string{"auth0|123456": "foobaz"}, manager.auth0UserIdentityMap)
require.ElementsMatch(t, []string{
"localjwt",
"auth0 domain=datarhei-demo.eu.auth0.com audience=https://datarhei-demo.eu.auth0.com/api/v2/ clientid=123456",
}, im.Validators())
err = im.Create(User{
Name: "fooboz",
Superuser: false,
Auth: UserAuth{
API: UserAuthAPI{
Auth0: UserAuthAPIAuth0{
Enable: true,
User: "auth0|123456",
Tenant: Auth0Tenant{
Domain: "datarhei-demo.eu.auth0.com",
Audience: "https://datarhei-demo.eu.auth0.com/api/v2/",
ClientID: "123456",
},
},
},
},
})
require.Error(t, err)
err = im.Create(User{
Name: "fooboz",
Superuser: false,
Auth: UserAuth{
API: UserAuthAPI{
Auth0: UserAuthAPIAuth0{
Enable: true,
User: "auth0|987654",
Tenant: Auth0Tenant{
Domain: "datarhei-demo.eu.auth0.com",
Audience: "https://datarhei-demo.eu.auth0.com/api/v2/",
ClientID: "987654",
},
},
},
},
})
require.NoError(t, err)
require.Equal(t, 1, len(manager.tenants))
require.Equal(t, map[string]string{"auth0|123456": "foobaz", "auth0|987654": "fooboz"}, manager.auth0UserIdentityMap)
require.ElementsMatch(t, []string{
"localjwt",
"auth0 domain=datarhei-demo.eu.auth0.com audience=https://datarhei-demo.eu.auth0.com/api/v2/ clientid=123456",
"auth0 domain=datarhei-demo.eu.auth0.com audience=https://datarhei-demo.eu.auth0.com/api/v2/ clientid=987654",
}, im.Validators())
im.Close()
}
func TestLoadAndSave(t *testing.T) {
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
require.NoError(t, err)
im, err := NewIdentityManager(IdentityConfig{
FS: dummyfs,
Superuser: User{Name: "foobar"},
JWTRealm: "test-realm",
JWTSecret: "abc123",
Logger: nil,
})
require.NoError(t, err)
require.NotNil(t, im)
err = im.Save()
require.NoError(t, err)
_, err = dummyfs.Stat("./users.json")
require.NoError(t, err)
data, err := dummyfs.ReadFile("./users.json")
require.NoError(t, err)
require.Equal(t, []byte("[]"), data)
err = im.Create(User{Name: "foobaz"})
require.NoError(t, err)
identity, err := im.GetVerifier("foobaz")
require.NoError(t, err)
require.NotNil(t, identity)
err = im.Save()
require.NoError(t, err)
im, err = NewIdentityManager(IdentityConfig{
FS: dummyfs,
Superuser: User{Name: "foobar"},
JWTRealm: "test-realm",
JWTSecret: "abc123",
Logger: nil,
})
require.NoError(t, err)
require.NotNil(t, im)
identity, err = im.GetVerifier("foobaz")
require.NoError(t, err)
require.NotNil(t, identity)
}
func TestUpdateUser(t *testing.T) {
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
require.NoError(t, err)
im, err := NewIdentityManager(IdentityConfig{
FS: dummyfs,
Superuser: User{Name: "foobar"},
JWTRealm: "test-realm",
JWTSecret: "abc123",
Logger: nil,
})
require.NoError(t, err)
require.NotNil(t, im)
err = im.Create(User{Name: "fooboz"})
require.NoError(t, err)
err = im.Update("unknown", User{Name: "fooboz"})
require.Error(t, err)
err = im.Update("foobar", User{Name: "foobar"})
require.Error(t, err)
err = im.Update("foobar", User{Name: "fooboz"})
require.Error(t, err)
identity, err := im.GetVerifier("foobar")
require.NoError(t, err)
require.NotNil(t, identity)
require.Equal(t, "foobar", identity.Name())
err = im.Update("foobar", User{Name: "foobaz"})
require.Error(t, err)
require.Equal(t, "foobar", identity.Name())
identity, err = im.GetVerifier("foobaz")
require.Error(t, err)
require.Nil(t, identity)
identity, err = im.GetVerifier("fooboz")
require.NoError(t, err)
require.NotNil(t, identity)
require.Equal(t, "fooboz", identity.Name())
err = im.Update("fooboz", User{Name: "foobaz"})
require.NoError(t, err)
}
func TestUpdateUserAuth0(t *testing.T) {
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
require.NoError(t, err)
im, err := NewIdentityManager(IdentityConfig{
FS: dummyfs,
Superuser: User{Name: "foobar"},
JWTRealm: "test-realm",
JWTSecret: "abc123",
Logger: nil,
})
require.NoError(t, err)
require.NotNil(t, im)
err = im.Create(User{
Name: "foobaz",
Superuser: false,
Auth: UserAuth{
API: UserAuthAPI{
Auth0: UserAuthAPIAuth0{
Enable: true,
User: "auth0|123456",
Tenant: Auth0Tenant{
Domain: "datarhei-demo.eu.auth0.com",
Audience: "https://datarhei-demo.eu.auth0.com/api/v2/",
ClientID: "123456",
},
},
},
},
})
require.NoError(t, err)
identity, err := im.GetVerifierByAuth0("auth0|123456")
require.NoError(t, err)
require.NotNil(t, identity)
identity, err = im.GetVerifier("foobaz")
require.NoError(t, err)
require.NotNil(t, identity)
user, err := im.Get("foobaz")
require.NoError(t, err)
user.Name = "fooboz"
err = im.Update("foobaz", user)
require.NoError(t, err)
identity, err = im.GetVerifierByAuth0("auth0|123456")
require.NoError(t, err)
require.NotNil(t, identity)
identity, err = im.GetVerifier("fooboz")
require.NoError(t, err)
require.NotNil(t, identity)
}
func TestRemoveUser(t *testing.T) {
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
require.NoError(t, err)
im, err := NewIdentityManager(IdentityConfig{
FS: dummyfs,
Superuser: User{Name: "foobar"},
JWTRealm: "test-realm",
JWTSecret: "abc123",
Logger: nil,
})
require.NoError(t, err)
require.NotNil(t, im)
err = im.Remove("fooboz")
require.Error(t, err)
err = im.Remove("foobar")
require.Error(t, err)
err = im.Create(User{
Name: "foobaz",
Superuser: false,
Auth: UserAuth{
API: UserAuthAPI{
Userpass: UserAuthPassword{
Enable: true,
Password: "apisecret",
},
Auth0: UserAuthAPIAuth0{},
},
Services: UserAuthServices{
Basic: UserAuthPassword{
Enable: true,
Password: "secret",
},
Token: []string{"tokensecret"},
},
},
})
require.NoError(t, err)
identity, err := im.GetVerifier("foobaz")
require.NoError(t, err)
require.NotNil(t, identity)
ok, err := identity.VerifyAPIPassword("apisecret")
require.True(t, ok)
require.NoError(t, err)
ok, err = identity.VerifyServiceBasicAuth("secret")
require.True(t, ok)
require.NoError(t, err)
ok, err = identity.VerifyServiceToken("tokensecret")
require.True(t, ok)
require.NoError(t, err)
access, refresh, err := im.CreateJWT("foobaz")
require.NoError(t, err)
ok, err = identity.VerifyJWT(access)
require.True(t, ok)
require.NoError(t, err)
ok, err = identity.VerifyJWT(refresh)
require.True(t, ok)
require.NoError(t, err)
err = im.Remove("foobaz")
require.NoError(t, err)
ok, err = identity.VerifyAPIPassword("apisecret")
require.False(t, ok)
require.Error(t, err)
ok, err = identity.VerifyServiceBasicAuth("secret")
require.False(t, ok)
require.Error(t, err)
ok, err = identity.VerifyServiceToken("tokensecret")
require.False(t, ok)
require.Error(t, err)
ok, err = identity.VerifyJWT(access)
require.False(t, ok)
require.Error(t, err)
ok, err = identity.VerifyJWT(refresh)
require.False(t, ok)
require.Error(t, err)
identity, err = im.GetVerifier("foobaz")
require.Error(t, err)
require.Nil(t, identity)
access, refresh, err = im.CreateJWT("foobaz")
require.Error(t, err)
require.Empty(t, access)
require.Empty(t, refresh)
}
func TestRemoveUserAuth0(t *testing.T) {
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
require.NoError(t, err)
im, err := NewIdentityManager(IdentityConfig{
FS: dummyfs,
Superuser: User{Name: "foobar"},
JWTRealm: "test-realm",
JWTSecret: "abc123",
Logger: nil,
})
require.NoError(t, err)
require.NotNil(t, im)
err = im.Create(User{
Name: "foobaz",
Superuser: false,
Auth: UserAuth{
API: UserAuthAPI{
Auth0: UserAuthAPIAuth0{
Enable: true,
User: "auth0|123456",
Tenant: Auth0Tenant{
Domain: "datarhei-demo.eu.auth0.com",
Audience: "https://datarhei-demo.eu.auth0.com/api/v2/",
ClientID: "123456",
},
},
},
},
})
require.NoError(t, err)
err = im.Create(User{
Name: "fooboz",
Superuser: false,
Auth: UserAuth{
API: UserAuthAPI{
Auth0: UserAuthAPIAuth0{
Enable: true,
User: "auth0|987654",
Tenant: Auth0Tenant{
Domain: "datarhei-demo.eu.auth0.com",
Audience: "https://datarhei-demo.eu.auth0.com/api/v2/",
ClientID: "987654",
},
},
},
},
})
require.NoError(t, err)
manager, ok := im.(*identityManager)
require.True(t, ok)
require.NotNil(t, manager)
require.Equal(t, 1, len(manager.tenants))
require.Equal(t, map[string]string{"auth0|123456": "foobaz", "auth0|987654": "fooboz"}, manager.auth0UserIdentityMap)
require.ElementsMatch(t, []string{
"localjwt",
"auth0 domain=datarhei-demo.eu.auth0.com audience=https://datarhei-demo.eu.auth0.com/api/v2/ clientid=123456",
"auth0 domain=datarhei-demo.eu.auth0.com audience=https://datarhei-demo.eu.auth0.com/api/v2/ clientid=987654",
}, im.Validators())
err = im.Remove("foobaz")
require.NoError(t, err)
require.Equal(t, 1, len(manager.tenants))
require.Equal(t, map[string]string{"auth0|987654": "fooboz"}, manager.auth0UserIdentityMap)
require.ElementsMatch(t, []string{
"localjwt",
"auth0 domain=datarhei-demo.eu.auth0.com audience=https://datarhei-demo.eu.auth0.com/api/v2/ clientid=987654",
}, im.Validators())
err = im.Remove("fooboz")
require.NoError(t, err)
require.Equal(t, 0, len(manager.tenants))
require.ElementsMatch(t, []string{
"localjwt",
}, im.Validators())
}
func TestAutosave(t *testing.T) {
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
require.NoError(t, err)
im, err := NewIdentityManager(IdentityConfig{
FS: dummyfs,
Superuser: User{Name: "foobar"},
JWTRealm: "test-realm",
JWTSecret: "abc123",
Logger: nil,
})
require.NoError(t, err)
require.NotNil(t, im)
err = im.Save()
require.NoError(t, err)
_, err = dummyfs.Stat("./users.json")
require.NoError(t, err)
data, err := dummyfs.ReadFile("./users.json")
require.NoError(t, err)
require.Equal(t, []byte("[]"), data)
im.Autosave(true)
err = im.Create(User{Name: "foobaz"})
require.NoError(t, err)
data, err = dummyfs.ReadFile("./users.json")
require.NoError(t, err)
require.NotEqual(t, []byte("[]"), data)
user, err := im.Get("foobaz")
require.NoError(t, err)
user.Name = "fooboz"
err = im.Update("foobaz", user)
require.NoError(t, err)
data, err = dummyfs.ReadFile("./users.json")
require.NoError(t, err)
require.NotEqual(t, []byte("[]"), data)
err = im.Remove("fooboz")
require.NoError(t, err)
data, err = dummyfs.ReadFile("./users.json")
require.NoError(t, err)
require.Equal(t, []byte("[]"), data)
}