mirror of
https://github.com/datarhei/core.git
synced 2025-11-02 12:14:12 +08:00
WIP: identity management and testing
This commit is contained in:
267
iam/identity.go
267
iam/identity.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user