mirror of
https://github.com/datarhei/core.git
synced 2025-10-06 00:17:07 +08:00
Create identity and access packages for IAM
This commit is contained in:
@@ -27,6 +27,8 @@ import (
|
|||||||
httpfs "github.com/datarhei/core/v16/http/fs"
|
httpfs "github.com/datarhei/core/v16/http/fs"
|
||||||
"github.com/datarhei/core/v16/http/router"
|
"github.com/datarhei/core/v16/http/router"
|
||||||
"github.com/datarhei/core/v16/iam"
|
"github.com/datarhei/core/v16/iam"
|
||||||
|
iamaccess "github.com/datarhei/core/v16/iam/access"
|
||||||
|
iamidentity "github.com/datarhei/core/v16/iam/identity"
|
||||||
"github.com/datarhei/core/v16/io/fs"
|
"github.com/datarhei/core/v16/io/fs"
|
||||||
"github.com/datarhei/core/v16/log"
|
"github.com/datarhei/core/v16/log"
|
||||||
"github.com/datarhei/core/v16/math/rand"
|
"github.com/datarhei/core/v16/math/rand"
|
||||||
@@ -390,14 +392,14 @@ func (a *api) start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
superuser := iam.User{
|
superuser := iamidentity.User{
|
||||||
Name: cfg.API.Auth.Username,
|
Name: cfg.API.Auth.Username,
|
||||||
Superuser: true,
|
Superuser: true,
|
||||||
Auth: iam.UserAuth{
|
Auth: iamidentity.UserAuth{
|
||||||
API: iam.UserAuthAPI{
|
API: iamidentity.UserAuthAPI{
|
||||||
Auth0: iam.UserAuthAPIAuth0{},
|
Auth0: iamidentity.UserAuthAPIAuth0{},
|
||||||
},
|
},
|
||||||
Services: iam.UserAuthServices{
|
Services: iamidentity.UserAuthServices{
|
||||||
Token: []string{
|
Token: []string{
|
||||||
cfg.RTMP.Token,
|
cfg.RTMP.Token,
|
||||||
cfg.SRT.Token,
|
cfg.SRT.Token,
|
||||||
@@ -412,7 +414,7 @@ func (a *api) start() error {
|
|||||||
|
|
||||||
if cfg.API.Auth.Auth0.Enable {
|
if cfg.API.Auth.Auth0.Enable {
|
||||||
superuser.Auth.API.Auth0.User = cfg.API.Auth.Auth0.Tenants[0].Users[0]
|
superuser.Auth.API.Auth0.User = cfg.API.Auth.Auth0.Tenants[0].Users[0]
|
||||||
superuser.Auth.API.Auth0.Tenant = iam.Auth0Tenant{
|
superuser.Auth.API.Auth0.Tenant = iamidentity.Auth0Tenant{
|
||||||
Domain: cfg.API.Auth.Auth0.Tenants[0].Domain,
|
Domain: cfg.API.Auth.Auth0.Tenants[0].Domain,
|
||||||
Audience: cfg.API.Auth.Auth0.Tenants[0].Audience,
|
Audience: cfg.API.Auth.Auth0.Tenants[0].Audience,
|
||||||
ClientID: cfg.API.Auth.Auth0.Tenants[0].ClientID,
|
ClientID: cfg.API.Auth.Auth0.Tenants[0].ClientID,
|
||||||
@@ -431,8 +433,19 @@ func (a *api) start() error {
|
|||||||
secret = cfg.API.Auth.Username + cfg.API.Auth.Password + cfg.API.Auth.JWT.Secret
|
secret = cfg.API.Auth.Username + cfg.API.Auth.Password + cfg.API.Auth.JWT.Secret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
policyAdapter, err := iamaccess.NewJSONAdapter(fs, "./policy.json", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
identityAdapter, err := iamidentity.NewJSONAdapter(fs, "./users.json", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
manager, err := iam.NewIAM(iam.Config{
|
manager, err := iam.NewIAM(iam.Config{
|
||||||
FS: fs,
|
PolicyAdapter: policyAdapter,
|
||||||
|
IdentityAdapter: identityAdapter,
|
||||||
Superuser: superuser,
|
Superuser: superuser,
|
||||||
JWTRealm: "datarhei-core",
|
JWTRealm: "datarhei-core",
|
||||||
JWTSecret: secret,
|
JWTSecret: secret,
|
||||||
@@ -445,7 +458,7 @@ func (a *api) start() error {
|
|||||||
// Check if there are already file created by IAM. If not, create policies
|
// Check if there are already file created by IAM. If not, create policies
|
||||||
// and users based on the config in order to mimic the behaviour before IAM.
|
// and users based on the config in order to mimic the behaviour before IAM.
|
||||||
if len(fs.List("/", "/*.json")) == 0 {
|
if len(fs.List("/", "/*.json")) == 0 {
|
||||||
policies := []iam.Policy{
|
policies := []iamaccess.Policy{
|
||||||
{
|
{
|
||||||
Name: "$anon",
|
Name: "$anon",
|
||||||
Domain: "$none",
|
Domain: "$none",
|
||||||
@@ -466,19 +479,19 @@ func (a *api) start() error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
users := map[string]iam.User{}
|
users := map[string]iamidentity.User{}
|
||||||
|
|
||||||
if cfg.Storage.Memory.Auth.Enable && cfg.Storage.Memory.Auth.Username != superuser.Name {
|
if cfg.Storage.Memory.Auth.Enable && cfg.Storage.Memory.Auth.Username != superuser.Name {
|
||||||
users[cfg.Storage.Memory.Auth.Username] = iam.User{
|
users[cfg.Storage.Memory.Auth.Username] = iamidentity.User{
|
||||||
Name: cfg.Storage.Memory.Auth.Username,
|
Name: cfg.Storage.Memory.Auth.Username,
|
||||||
Auth: iam.UserAuth{
|
Auth: iamidentity.UserAuth{
|
||||||
Services: iam.UserAuthServices{
|
Services: iamidentity.UserAuthServices{
|
||||||
Basic: []string{cfg.Storage.Memory.Auth.Password},
|
Basic: []string{cfg.Storage.Memory.Auth.Password},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
policies = append(policies, iam.Policy{
|
policies = append(policies, iamaccess.Policy{
|
||||||
Name: cfg.Storage.Memory.Auth.Username,
|
Name: cfg.Storage.Memory.Auth.Username,
|
||||||
Domain: "$none",
|
Domain: "$none",
|
||||||
Resource: "fs:/memfs/**",
|
Resource: "fs:/memfs/**",
|
||||||
@@ -490,10 +503,10 @@ func (a *api) start() error {
|
|||||||
if s.Auth.Enable && s.Auth.Username != superuser.Name {
|
if s.Auth.Enable && s.Auth.Username != superuser.Name {
|
||||||
user, ok := users[s.Auth.Username]
|
user, ok := users[s.Auth.Username]
|
||||||
if !ok {
|
if !ok {
|
||||||
users[s.Auth.Username] = iam.User{
|
users[s.Auth.Username] = iamidentity.User{
|
||||||
Name: s.Auth.Username,
|
Name: s.Auth.Username,
|
||||||
Auth: iam.UserAuth{
|
Auth: iamidentity.UserAuth{
|
||||||
Services: iam.UserAuthServices{
|
Services: iamidentity.UserAuthServices{
|
||||||
Basic: []string{s.Auth.Password},
|
Basic: []string{s.Auth.Password},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -503,7 +516,7 @@ func (a *api) start() error {
|
|||||||
users[s.Auth.Username] = user
|
users[s.Auth.Username] = user
|
||||||
}
|
}
|
||||||
|
|
||||||
policies = append(policies, iam.Policy{
|
policies = append(policies, iamaccess.Policy{
|
||||||
Name: s.Auth.Username,
|
Name: s.Auth.Username,
|
||||||
Domain: "$none",
|
Domain: "$none",
|
||||||
Resource: "fs:" + s.Mountpoint + "/**",
|
Resource: "fs:" + s.Mountpoint + "/**",
|
||||||
@@ -513,7 +526,7 @@ func (a *api) start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cfg.RTMP.Enable && len(cfg.RTMP.Token) == 0 {
|
if cfg.RTMP.Enable && len(cfg.RTMP.Token) == 0 {
|
||||||
policies = append(policies, iam.Policy{
|
policies = append(policies, iamaccess.Policy{
|
||||||
Name: "$anon",
|
Name: "$anon",
|
||||||
Domain: "$none",
|
Domain: "$none",
|
||||||
Resource: "rtmp:/**",
|
Resource: "rtmp:/**",
|
||||||
@@ -522,7 +535,7 @@ func (a *api) start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cfg.SRT.Enable && len(cfg.SRT.Token) == 0 {
|
if cfg.SRT.Enable && len(cfg.SRT.Token) == 0 {
|
||||||
policies = append(policies, iam.Policy{
|
policies = append(policies, iamaccess.Policy{
|
||||||
Name: "$anon",
|
Name: "$anon",
|
||||||
Domain: "$none",
|
Domain: "$none",
|
||||||
Resource: "srt:**",
|
Resource: "srt:**",
|
||||||
@@ -737,7 +750,7 @@ func (a *api) start() error {
|
|||||||
}
|
}
|
||||||
template += "/{name}"
|
template += "/{name}"
|
||||||
|
|
||||||
var identity iam.IdentityVerifier = nil
|
var identity iamidentity.Verifier = nil
|
||||||
|
|
||||||
if len(config.Owner) == 0 {
|
if len(config.Owner) == 0 {
|
||||||
identity = a.iam.GetDefaultVerifier()
|
identity = a.iam.GetDefaultVerifier()
|
||||||
@@ -763,7 +776,7 @@ func (a *api) start() error {
|
|||||||
template += ",mode:publish"
|
template += ",mode:publish"
|
||||||
}
|
}
|
||||||
|
|
||||||
var identity iam.IdentityVerifier = nil
|
var identity iamidentity.Verifier = nil
|
||||||
|
|
||||||
if len(config.Owner) == 0 {
|
if len(config.Owner) == 0 {
|
||||||
identity = a.iam.GetDefaultVerifier()
|
identity = a.iam.GetDefaultVerifier()
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import "github.com/datarhei/core/v16/iam"
|
import (
|
||||||
|
"github.com/datarhei/core/v16/iam/access"
|
||||||
|
"github.com/datarhei/core/v16/iam/identity"
|
||||||
|
)
|
||||||
|
|
||||||
type IAMUser struct {
|
type IAMUser struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -9,7 +12,7 @@ type IAMUser struct {
|
|||||||
Policies []IAMPolicy `json:"policies"`
|
Policies []IAMPolicy `json:"policies"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *IAMUser) Marshal(user iam.User, policies []iam.Policy) {
|
func (u *IAMUser) Marshal(user identity.User, policies []access.Policy) {
|
||||||
u.Name = user.Name
|
u.Name = user.Name
|
||||||
u.Superuser = user.Superuser
|
u.Superuser = user.Superuser
|
||||||
u.Auth = IAMUserAuth{
|
u.Auth = IAMUserAuth{
|
||||||
@@ -39,33 +42,33 @@ func (u *IAMUser) Marshal(user iam.User, policies []iam.Policy) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *IAMUser) Unmarshal() (iam.User, []iam.Policy) {
|
func (u *IAMUser) Unmarshal() (identity.User, []access.Policy) {
|
||||||
iamuser := iam.User{
|
iamuser := identity.User{
|
||||||
Name: u.Name,
|
Name: u.Name,
|
||||||
Superuser: u.Superuser,
|
Superuser: u.Superuser,
|
||||||
Auth: iam.UserAuth{
|
Auth: identity.UserAuth{
|
||||||
API: iam.UserAuthAPI{
|
API: identity.UserAuthAPI{
|
||||||
Password: u.Auth.API.Password,
|
Password: u.Auth.API.Password,
|
||||||
Auth0: iam.UserAuthAPIAuth0{
|
Auth0: identity.UserAuthAPIAuth0{
|
||||||
User: u.Auth.API.Auth0.User,
|
User: u.Auth.API.Auth0.User,
|
||||||
Tenant: iam.Auth0Tenant{
|
Tenant: identity.Auth0Tenant{
|
||||||
Domain: u.Auth.API.Auth0.Tenant.Domain,
|
Domain: u.Auth.API.Auth0.Tenant.Domain,
|
||||||
Audience: u.Auth.API.Auth0.Tenant.Audience,
|
Audience: u.Auth.API.Auth0.Tenant.Audience,
|
||||||
ClientID: u.Auth.API.Auth0.Tenant.ClientID,
|
ClientID: u.Auth.API.Auth0.Tenant.ClientID,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Services: iam.UserAuthServices{
|
Services: identity.UserAuthServices{
|
||||||
Basic: u.Auth.Services.Basic,
|
Basic: u.Auth.Services.Basic,
|
||||||
Token: u.Auth.Services.Token,
|
Token: u.Auth.Services.Token,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
iampolicies := []iam.Policy{}
|
iampolicies := []access.Policy{}
|
||||||
|
|
||||||
for _, p := range u.Policies {
|
for _, p := range u.Policies {
|
||||||
iampolicies = append(iampolicies, iam.Policy{
|
iampolicies = append(iampolicies, access.Policy{
|
||||||
Name: u.Name,
|
Name: u.Name,
|
||||||
Domain: p.Domain,
|
Domain: p.Domain,
|
||||||
Resource: p.Resource,
|
Resource: p.Resource,
|
||||||
|
@@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/datarhei/core/v16/http/api"
|
"github.com/datarhei/core/v16/http/api"
|
||||||
"github.com/datarhei/core/v16/http/handler/util"
|
"github.com/datarhei/core/v16/http/handler/util"
|
||||||
"github.com/datarhei/core/v16/iam"
|
"github.com/datarhei/core/v16/iam"
|
||||||
|
"github.com/datarhei/core/v16/iam/identity"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
@@ -70,11 +71,6 @@ func (h *IAMHandler) AddUser(c echo.Context) error {
|
|||||||
h.iam.AddPolicy(p.Name, p.Domain, p.Resource, p.Actions)
|
h.iam.AddPolicy(p.Name, p.Domain, p.Resource, p.Actions)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = h.iam.SaveIdentities()
|
|
||||||
if err != nil {
|
|
||||||
return api.Err(http.StatusInternalServerError, "Internal server error", "%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, user)
|
return c.JSON(http.StatusOK, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,11 +112,6 @@ func (h *IAMHandler) RemoveUser(c echo.Context) error {
|
|||||||
return api.Err(http.StatusBadRequest, "Bad request", "%s", err)
|
return api.Err(http.StatusBadRequest, "Bad request", "%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = h.iam.SaveIdentities()
|
|
||||||
if err != nil {
|
|
||||||
return api.Err(http.StatusInternalServerError, "Internal server error", "%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove all policies of that user
|
// Remove all policies of that user
|
||||||
h.iam.RemovePolicy(name, "", "", nil)
|
h.iam.RemovePolicy(name, "", "", nil)
|
||||||
|
|
||||||
@@ -153,7 +144,7 @@ func (h *IAMHandler) UpdateUser(c echo.Context) error {
|
|||||||
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to modify this user")
|
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to modify this user")
|
||||||
}
|
}
|
||||||
|
|
||||||
var iamuser iam.User
|
var iamuser identity.User
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if name != "$anon" {
|
if name != "$anon" {
|
||||||
@@ -162,7 +153,7 @@ func (h *IAMHandler) UpdateUser(c echo.Context) error {
|
|||||||
return api.Err(http.StatusNotFound, "Not found", "%s", err)
|
return api.Err(http.StatusNotFound, "Not found", "%s", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
iamuser = iam.User{
|
iamuser = identity.User{
|
||||||
Name: "$anon",
|
Name: "$anon",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,11 +196,6 @@ func (h *IAMHandler) UpdateUser(c echo.Context) error {
|
|||||||
h.iam.AddPolicy(p.Name, p.Domain, p.Resource, p.Actions)
|
h.iam.AddPolicy(p.Name, p.Domain, p.Resource, p.Actions)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = h.iam.SaveIdentities()
|
|
||||||
if err != nil {
|
|
||||||
return api.Err(http.StatusInternalServerError, "Internal server error", "%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, user)
|
return c.JSON(http.StatusOK, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,7 +225,7 @@ func (h *IAMHandler) UpdateUserPolicies(c echo.Context) error {
|
|||||||
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to modify this user")
|
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to modify this user")
|
||||||
}
|
}
|
||||||
|
|
||||||
var iamuser iam.User
|
var iamuser identity.User
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if name != "$anon" {
|
if name != "$anon" {
|
||||||
@@ -248,7 +234,7 @@ func (h *IAMHandler) UpdateUserPolicies(c echo.Context) error {
|
|||||||
return api.Err(http.StatusNotFound, "Not found", "%s", err)
|
return api.Err(http.StatusNotFound, "Not found", "%s", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
iamuser = iam.User{
|
iamuser = identity.User{
|
||||||
Name: "$anon",
|
Name: "$anon",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -300,7 +286,7 @@ func (h *IAMHandler) GetUser(c echo.Context) error {
|
|||||||
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to access this user")
|
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to access this user")
|
||||||
}
|
}
|
||||||
|
|
||||||
var iamuser iam.User
|
var iamuser identity.User
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if name != "$anon" {
|
if name != "$anon" {
|
||||||
@@ -311,13 +297,13 @@ func (h *IAMHandler) GetUser(c echo.Context) error {
|
|||||||
|
|
||||||
if !superuser && name != iamuser.Name {
|
if !superuser && name != iamuser.Name {
|
||||||
if !h.iam.Enforce(ctxuser, domain, "iam:"+name, "write") {
|
if !h.iam.Enforce(ctxuser, domain, "iam:"+name, "write") {
|
||||||
iamuser = iam.User{
|
iamuser = identity.User{
|
||||||
Name: iamuser.Name,
|
Name: iamuser.Name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
iamuser = iam.User{
|
iamuser = identity.User{
|
||||||
Name: "$anon",
|
Name: "$anon",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,8 @@ import (
|
|||||||
"github.com/datarhei/core/v16/http/api"
|
"github.com/datarhei/core/v16/http/api"
|
||||||
"github.com/datarhei/core/v16/http/mock"
|
"github.com/datarhei/core/v16/http/mock"
|
||||||
"github.com/datarhei/core/v16/iam"
|
"github.com/datarhei/core/v16/iam"
|
||||||
|
"github.com/datarhei/core/v16/iam/access"
|
||||||
|
"github.com/datarhei/core/v16/iam/identity"
|
||||||
"github.com/datarhei/core/v16/io/fs"
|
"github.com/datarhei/core/v16/io/fs"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
@@ -33,9 +35,20 @@ func getDummyRestreamHandler() (*RestreamHandler, error) {
|
|||||||
return nil, fmt.Errorf("failed to create memory filesystem: %w", err)
|
return nil, fmt.Errorf("failed to create memory filesystem: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
policyAdapter, err := access.NewJSONAdapter(memfs, "./policy.json", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
identityAdapter, err := identity.NewJSONAdapter(memfs, "./users.json", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
iam, err := iam.NewIAM(iam.Config{
|
iam, err := iam.NewIAM(iam.Config{
|
||||||
FS: memfs,
|
PolicyAdapter: policyAdapter,
|
||||||
Superuser: iam.User{
|
IdentityAdapter: identityAdapter,
|
||||||
|
Superuser: identity.User{
|
||||||
Name: "foobar",
|
Name: "foobar",
|
||||||
},
|
},
|
||||||
JWTRealm: "",
|
JWTRealm: "",
|
||||||
|
@@ -44,6 +44,7 @@ import (
|
|||||||
"github.com/datarhei/core/v16/http/api"
|
"github.com/datarhei/core/v16/http/api"
|
||||||
"github.com/datarhei/core/v16/http/handler/util"
|
"github.com/datarhei/core/v16/http/handler/util"
|
||||||
"github.com/datarhei/core/v16/iam"
|
"github.com/datarhei/core/v16/iam"
|
||||||
|
iamidentity "github.com/datarhei/core/v16/iam/identity"
|
||||||
"github.com/datarhei/core/v16/log"
|
"github.com/datarhei/core/v16/log"
|
||||||
|
|
||||||
jwtgo "github.com/golang-jwt/jwt/v4"
|
jwtgo "github.com/golang-jwt/jwt/v4"
|
||||||
@@ -117,7 +118,7 @@ func NewWithConfig(config Config) echo.MiddlewareFunc {
|
|||||||
isAPISuperuser = true
|
isAPISuperuser = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var identity iam.IdentityVerifier = nil
|
var identity iamidentity.Verifier = nil
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
username := "$anon"
|
username := "$anon"
|
||||||
@@ -231,7 +232,7 @@ var ErrAuthRequired = errors.New("unauthorized")
|
|||||||
var ErrUnauthorized = errors.New("unauthorized")
|
var ErrUnauthorized = errors.New("unauthorized")
|
||||||
var ErrBadRequest = errors.New("bad request")
|
var ErrBadRequest = errors.New("bad request")
|
||||||
|
|
||||||
func (m *iammiddleware) findIdentityFromBasicAuth(c echo.Context) (iam.IdentityVerifier, error) {
|
func (m *iammiddleware) findIdentityFromBasicAuth(c echo.Context) (iamidentity.Verifier, error) {
|
||||||
basic := "basic"
|
basic := "basic"
|
||||||
auth := c.Request().Header.Get(echo.HeaderAuthorization)
|
auth := c.Request().Header.Get(echo.HeaderAuthorization)
|
||||||
l := len(basic)
|
l := len(basic)
|
||||||
@@ -290,7 +291,7 @@ func (m *iammiddleware) findIdentityFromBasicAuth(c echo.Context) (iam.IdentityV
|
|||||||
return identity, nil
|
return identity, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *iammiddleware) findIdentityFromJWT(c echo.Context) (iam.IdentityVerifier, error) {
|
func (m *iammiddleware) findIdentityFromJWT(c echo.Context) (iamidentity.Verifier, error) {
|
||||||
// Look for an Auth header
|
// Look for an Auth header
|
||||||
values := c.Request().Header.Values("Authorization")
|
values := c.Request().Header.Values("Authorization")
|
||||||
prefix := "Bearer "
|
prefix := "Bearer "
|
||||||
@@ -356,7 +357,7 @@ func (m *iammiddleware) findIdentityFromJWT(c echo.Context) (iam.IdentityVerifie
|
|||||||
return identity, nil
|
return identity, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *iammiddleware) findIdentityFromUserpass(c echo.Context) (iam.IdentityVerifier, error) {
|
func (m *iammiddleware) findIdentityFromUserpass(c echo.Context) (iamidentity.Verifier, error) {
|
||||||
var login api.Login
|
var login api.Login
|
||||||
|
|
||||||
if err := util.ShouldBindJSON(c, &login); err != nil {
|
if err := util.ShouldBindJSON(c, &login); err != nil {
|
||||||
@@ -383,7 +384,7 @@ func (m *iammiddleware) findIdentityFromUserpass(c echo.Context) (iam.IdentityVe
|
|||||||
return identity, nil
|
return identity, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *iammiddleware) findIdentityFromAuth0(c echo.Context) (iam.IdentityVerifier, error) {
|
func (m *iammiddleware) findIdentityFromAuth0(c echo.Context) (iamidentity.Verifier, error) {
|
||||||
// Look for an Auth header
|
// Look for an Auth header
|
||||||
values := c.Request().Header.Values("Authorization")
|
values := c.Request().Header.Values("Authorization")
|
||||||
prefix := "Bearer "
|
prefix := "Bearer "
|
||||||
|
@@ -14,6 +14,8 @@ import (
|
|||||||
apihandler "github.com/datarhei/core/v16/http/handler/api"
|
apihandler "github.com/datarhei/core/v16/http/handler/api"
|
||||||
"github.com/datarhei/core/v16/http/validator"
|
"github.com/datarhei/core/v16/http/validator"
|
||||||
"github.com/datarhei/core/v16/iam"
|
"github.com/datarhei/core/v16/iam"
|
||||||
|
iamaccess "github.com/datarhei/core/v16/iam/access"
|
||||||
|
iamidentity "github.com/datarhei/core/v16/iam/identity"
|
||||||
"github.com/datarhei/core/v16/io/fs"
|
"github.com/datarhei/core/v16/io/fs"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
@@ -28,9 +30,20 @@ func getIAM() (iam.IAM, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
policyAdapter, err := iamaccess.NewJSONAdapter(dummyfs, "./policy.json", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
identityAdapter, err := iamidentity.NewJSONAdapter(dummyfs, "./users.json", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
i, err := iam.NewIAM(iam.Config{
|
i, err := iam.NewIAM(iam.Config{
|
||||||
FS: dummyfs,
|
PolicyAdapter: policyAdapter,
|
||||||
Superuser: iam.User{
|
IdentityAdapter: identityAdapter,
|
||||||
|
Superuser: iamidentity.User{
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
},
|
},
|
||||||
JWTRealm: "datarhei-core",
|
JWTRealm: "datarhei-core",
|
||||||
@@ -41,13 +54,13 @@ func getIAM() (iam.IAM, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
i.CreateIdentity(iam.User{
|
i.CreateIdentity(iamidentity.User{
|
||||||
Name: "foobar",
|
Name: "foobar",
|
||||||
Auth: iam.UserAuth{
|
Auth: iamidentity.UserAuth{
|
||||||
API: iam.UserAuthAPI{
|
API: iamidentity.UserAuthAPI{
|
||||||
Password: "secret",
|
Password: "secret",
|
||||||
},
|
},
|
||||||
Services: iam.UserAuthServices{
|
Services: iamidentity.UserAuthServices{
|
||||||
Basic: []string{"secret"},
|
Basic: []string{"secret"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@@ -17,6 +17,8 @@ import (
|
|||||||
"github.com/datarhei/core/v16/http/errorhandler"
|
"github.com/datarhei/core/v16/http/errorhandler"
|
||||||
"github.com/datarhei/core/v16/http/validator"
|
"github.com/datarhei/core/v16/http/validator"
|
||||||
"github.com/datarhei/core/v16/iam"
|
"github.com/datarhei/core/v16/iam"
|
||||||
|
iamaccess "github.com/datarhei/core/v16/iam/access"
|
||||||
|
iamidentity "github.com/datarhei/core/v16/iam/identity"
|
||||||
"github.com/datarhei/core/v16/internal/testhelper"
|
"github.com/datarhei/core/v16/internal/testhelper"
|
||||||
"github.com/datarhei/core/v16/io/fs"
|
"github.com/datarhei/core/v16/io/fs"
|
||||||
"github.com/datarhei/core/v16/restream"
|
"github.com/datarhei/core/v16/restream"
|
||||||
@@ -53,9 +55,20 @@ func DummyRestreamer(pathPrefix string) (restream.Restreamer, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
policyAdapter, err := iamaccess.NewJSONAdapter(memfs, "./policy.json", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
identityAdapter, err := iamidentity.NewJSONAdapter(memfs, "./users.json", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
iam, err := iam.NewIAM(iam.Config{
|
iam, err := iam.NewIAM(iam.Config{
|
||||||
FS: memfs,
|
PolicyAdapter: policyAdapter,
|
||||||
Superuser: iam.User{
|
IdentityAdapter: identityAdapter,
|
||||||
|
Superuser: iamidentity.User{
|
||||||
Name: "foobar",
|
Name: "foobar",
|
||||||
},
|
},
|
||||||
JWTRealm: "",
|
JWTRealm: "",
|
||||||
|
@@ -1,10 +1,8 @@
|
|||||||
package iam
|
package access
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/io/fs"
|
|
||||||
"github.com/datarhei/core/v16/log"
|
"github.com/datarhei/core/v16/log"
|
||||||
|
|
||||||
"github.com/casbin/casbin/v2"
|
"github.com/casbin/casbin/v2"
|
||||||
@@ -18,45 +16,42 @@ type Policy struct {
|
|||||||
Actions []string
|
Actions []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessEnforcer interface {
|
type Enforcer interface {
|
||||||
Enforce(name, domain, resource, action string) (bool, string)
|
Enforce(name, domain, resource, action string) (bool, string)
|
||||||
|
|
||||||
HasDomain(name string) bool
|
HasDomain(name string) bool
|
||||||
ListDomains() []string
|
ListDomains() []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessManager interface {
|
type Manager interface {
|
||||||
AccessEnforcer
|
Enforcer
|
||||||
|
|
||||||
HasPolicy(name, domain, resource string, actions []string) bool
|
HasPolicy(name, domain, resource string, actions []string) bool
|
||||||
AddPolicy(name, domain, resource string, actions []string) bool
|
AddPolicy(name, domain, resource string, actions []string) bool
|
||||||
RemovePolicy(name, domain, resource string, actions []string) bool
|
RemovePolicy(name, domain, resource string, actions []string) bool
|
||||||
ListPolicies(name, domain, resource string, actions []string) []Policy
|
ListPolicies(name, domain, resource string, actions []string) []Policy
|
||||||
|
ReloadPolicies() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type access struct {
|
type access struct {
|
||||||
fs fs.Filesystem
|
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
|
|
||||||
adapter *adapter
|
adapter Adapter
|
||||||
|
model model.Model
|
||||||
enforcer *casbin.Enforcer
|
enforcer *casbin.Enforcer
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessConfig struct {
|
type Config struct {
|
||||||
FS fs.Filesystem
|
Adapter Adapter
|
||||||
Logger log.Logger
|
Logger log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAccessManager(config AccessConfig) (AccessManager, error) {
|
func New(config Config) (Manager, error) {
|
||||||
am := &access{
|
am := &access{
|
||||||
fs: config.FS,
|
adapter: config.Adapter,
|
||||||
logger: config.Logger,
|
logger: config.Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
if am.fs == nil {
|
|
||||||
return nil, fmt.Errorf("a filesystem has to be provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
if am.logger == nil {
|
if am.logger == nil {
|
||||||
am.logger = log.New("")
|
am.logger = log.New("")
|
||||||
}
|
}
|
||||||
@@ -68,12 +63,7 @@ func NewAccessManager(config AccessConfig) (AccessManager, error) {
|
|||||||
m.AddDef("e", "e", "some(where (p.eft == allow))")
|
m.AddDef("e", "e", "some(where (p.eft == allow))")
|
||||||
m.AddDef("m", "m", `g(r.sub, p.sub, r.dom) && r.dom == p.dom && ResourceMatch(r.obj, r.dom, p.obj) && ActionMatch(r.act, p.act) || r.sub == "$superuser"`)
|
m.AddDef("m", "m", `g(r.sub, p.sub, r.dom) && r.dom == p.dom && ResourceMatch(r.obj, r.dom, p.obj) && ActionMatch(r.act, p.act) || r.sub == "$superuser"`)
|
||||||
|
|
||||||
a, err := newAdapter(am.fs, "./policy.json", am.logger)
|
e, err := casbin.NewEnforcer(m, am.adapter)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
e, err := casbin.NewEnforcer(m, a)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -82,7 +72,7 @@ func NewAccessManager(config AccessConfig) (AccessManager, error) {
|
|||||||
e.AddFunction("ActionMatch", actionMatchFunc)
|
e.AddFunction("ActionMatch", actionMatchFunc)
|
||||||
|
|
||||||
am.enforcer = e
|
am.enforcer = e
|
||||||
am.adapter = a
|
am.model = m
|
||||||
|
|
||||||
return am, nil
|
return am, nil
|
||||||
}
|
}
|
||||||
@@ -129,20 +119,18 @@ func (am *access) ListPolicies(name, domain, resource string, actions []string)
|
|||||||
return policies
|
return policies
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (am *access) ReloadPolicies() error {
|
||||||
|
am.model.ClearPolicy()
|
||||||
|
|
||||||
|
return am.adapter.LoadPolicy(am.model)
|
||||||
|
}
|
||||||
|
|
||||||
func (am *access) HasDomain(name string) bool {
|
func (am *access) HasDomain(name string) bool {
|
||||||
groups := am.adapter.getAllDomains()
|
return am.adapter.HasDomain(name)
|
||||||
|
|
||||||
for _, g := range groups {
|
|
||||||
if g == name {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *access) ListDomains() []string {
|
func (am *access) ListDomains() []string {
|
||||||
return am.adapter.getAllDomains()
|
return am.adapter.AllDomains()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *access) Enforce(name, domain, resource, action string) (bool, string) {
|
func (am *access) Enforce(name, domain, resource, action string) (bool, string) {
|
@@ -1,4 +1,4 @@
|
|||||||
package iam
|
package access
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@@ -7,12 +7,21 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccessManager(t *testing.T) {
|
func createAdapter() (Adapter, error) {
|
||||||
memfs, err := fs.NewMemFilesystemFromDir("./fixtures", fs.MemConfig{})
|
memfs, err := fs.NewMemFilesystemFromDir("./fixtures", fs.MemConfig{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewJSONAdapter(memfs, "./policy.json", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccessManager(t *testing.T) {
|
||||||
|
adapter, err := createAdapter()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
am, err := NewAccessManager(AccessConfig{
|
am, err := New(Config{
|
||||||
FS: memfs,
|
Adapter: adapter,
|
||||||
Logger: nil,
|
Logger: nil,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
@@ -1,4 +1,4 @@
|
|||||||
package iam
|
package access
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/datarhei/core/v16/log"
|
"github.com/datarhei/core/v16/log"
|
||||||
|
|
||||||
"github.com/casbin/casbin/v2/model"
|
"github.com/casbin/casbin/v2/model"
|
||||||
|
"github.com/casbin/casbin/v2/persist"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Adapter is the file adapter for Casbin.
|
// Adapter is the file adapter for Casbin.
|
||||||
@@ -24,7 +25,14 @@ type adapter struct {
|
|||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAdapter(fs fs.Filesystem, filePath string, logger log.Logger) (*adapter, error) {
|
type Adapter interface {
|
||||||
|
persist.BatchAdapter
|
||||||
|
|
||||||
|
AllDomains() []string
|
||||||
|
HasDomain(string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewJSONAdapter(fs fs.Filesystem, filePath string, logger log.Logger) (Adapter, error) {
|
||||||
a := &adapter{
|
a := &adapter{
|
||||||
fs: fs,
|
fs: fs,
|
||||||
filePath: filePath,
|
filePath: filePath,
|
||||||
@@ -72,6 +80,8 @@ func (a *adapter) loadPolicyFile(model model.Model) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model.ClearPolicy()
|
||||||
|
|
||||||
rule := [5]string{}
|
rule := [5]string{}
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
rule[0] = "p"
|
rule[0] = "p"
|
||||||
@@ -511,7 +521,10 @@ func (a *adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int,
|
|||||||
return fmt.Errorf("not implemented")
|
return fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *adapter) getAllDomains() []string {
|
func (a *adapter) AllDomains() []string {
|
||||||
|
a.lock.Lock()
|
||||||
|
defer a.lock.Unlock()
|
||||||
|
|
||||||
names := []string{}
|
names := []string{}
|
||||||
|
|
||||||
for _, domain := range a.domains {
|
for _, domain := range a.domains {
|
||||||
@@ -525,6 +538,23 @@ func (a *adapter) getAllDomains() []string {
|
|||||||
return names
|
return names
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *adapter) HasDomain(name string) bool {
|
||||||
|
a.lock.Lock()
|
||||||
|
defer a.lock.Unlock()
|
||||||
|
|
||||||
|
for _, domain := range a.domains {
|
||||||
|
if domain.Name[0] == '$' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if domain.Name == name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type Domain struct {
|
type Domain struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Roles map[string][]Role `json:"roles"`
|
Roles map[string][]Role `json:"roles"`
|
@@ -1,4 +1,4 @@
|
|||||||
package iam
|
package access
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -12,9 +12,12 @@ func TestAddPolicy(t *testing.T) {
|
|||||||
memfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
memfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
a, err := newAdapter(memfs, "/policy.json", nil)
|
ai, err := NewJSONAdapter(memfs, "/policy.json", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
a, ok := ai.(*adapter)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
err = a.AddPolicy("p", "p", []string{"foobar", "group", "resource", "action"})
|
err = a.AddPolicy("p", "p", []string{"foobar", "group", "resource", "action"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -53,9 +56,12 @@ func TestRemovePolicy(t *testing.T) {
|
|||||||
memfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
memfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
a, err := newAdapter(memfs, "/policy.json", nil)
|
ai, err := NewJSONAdapter(memfs, "/policy.json", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
a, ok := ai.(*adapter)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
err = a.AddPolicies("p", "p", [][]string{
|
err = a.AddPolicies("p", "p", [][]string{
|
||||||
{"foobar1", "group", "resource1", "action1"},
|
{"foobar1", "group", "resource1", "action1"},
|
||||||
{"foobar2", "group", "resource2", "action2"},
|
{"foobar2", "group", "resource2", "action2"},
|
@@ -1,4 +1,4 @@
|
|||||||
package iam
|
package access
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
64
iam/iam.go
64
iam/iam.go
@@ -1,7 +1,8 @@
|
|||||||
package iam
|
package iam
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/datarhei/core/v16/io/fs"
|
"github.com/datarhei/core/v16/iam/access"
|
||||||
|
"github.com/datarhei/core/v16/iam/identity"
|
||||||
"github.com/datarhei/core/v16/log"
|
"github.com/datarhei/core/v16/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,21 +19,21 @@ type IAM interface {
|
|||||||
HasPolicy(name, domain, resource string, actions []string) bool
|
HasPolicy(name, domain, resource string, actions []string) bool
|
||||||
AddPolicy(name, domain, resource string, actions []string) bool
|
AddPolicy(name, domain, resource string, actions []string) bool
|
||||||
RemovePolicy(name, domain, resource string, actions []string) bool
|
RemovePolicy(name, domain, resource string, actions []string) bool
|
||||||
|
ListPolicies(name, domain, resource string, actions []string) []access.Policy
|
||||||
ListPolicies(name, domain, resource string, actions []string) []Policy
|
ReloadPolicies() error
|
||||||
|
|
||||||
Validators() []string
|
Validators() []string
|
||||||
|
|
||||||
CreateIdentity(u User) error
|
CreateIdentity(u identity.User) error
|
||||||
GetIdentity(name string) (User, error)
|
GetIdentity(name string) (identity.User, error)
|
||||||
UpdateIdentity(name string, u User) error
|
UpdateIdentity(name string, u identity.User) error
|
||||||
DeleteIdentity(name string) error
|
DeleteIdentity(name string) error
|
||||||
ListIdentities() []User
|
ListIdentities() []identity.User
|
||||||
SaveIdentities() error
|
ReloadIndentities() error
|
||||||
|
|
||||||
GetVerifier(name string) (IdentityVerifier, error)
|
GetVerifier(name string) (identity.Verifier, error)
|
||||||
GetVerfierFromAuth0(name string) (IdentityVerifier, error)
|
GetVerfierFromAuth0(name string) (identity.Verifier, error)
|
||||||
GetDefaultVerifier() IdentityVerifier
|
GetDefaultVerifier() identity.Verifier
|
||||||
|
|
||||||
CreateJWT(name string) (string, string, error)
|
CreateJWT(name string) (string, string, error)
|
||||||
|
|
||||||
@@ -40,23 +41,24 @@ type IAM interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type iam struct {
|
type iam struct {
|
||||||
im IdentityManager
|
im identity.Manager
|
||||||
am AccessManager
|
am access.Manager
|
||||||
|
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
FS fs.Filesystem
|
PolicyAdapter access.Adapter
|
||||||
Superuser User
|
IdentityAdapter identity.Adapter
|
||||||
|
Superuser identity.User
|
||||||
JWTRealm string
|
JWTRealm string
|
||||||
JWTSecret string
|
JWTSecret string
|
||||||
Logger log.Logger
|
Logger log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIAM(config Config) (IAM, error) {
|
func NewIAM(config Config) (IAM, error) {
|
||||||
im, err := NewIdentityManager(IdentityConfig{
|
im, err := identity.New(identity.Config{
|
||||||
FS: config.FS,
|
Adapter: config.IdentityAdapter,
|
||||||
Superuser: config.Superuser,
|
Superuser: config.Superuser,
|
||||||
JWTRealm: config.JWTRealm,
|
JWTRealm: config.JWTRealm,
|
||||||
JWTSecret: config.JWTSecret,
|
JWTSecret: config.JWTSecret,
|
||||||
@@ -66,8 +68,8 @@ func NewIAM(config Config) (IAM, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
am, err := NewAccessManager(AccessConfig{
|
am, err := access.New(access.Config{
|
||||||
FS: config.FS,
|
Adapter: config.PolicyAdapter,
|
||||||
Logger: config.Logger,
|
Logger: config.Logger,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -138,15 +140,15 @@ func (i *iam) Enforce(name, domain, resource, action string) bool {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *iam) CreateIdentity(u User) error {
|
func (i *iam) CreateIdentity(u identity.User) error {
|
||||||
return i.im.Create(u)
|
return i.im.Create(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *iam) GetIdentity(name string) (User, error) {
|
func (i *iam) GetIdentity(name string) (identity.User, error) {
|
||||||
return i.im.Get(name)
|
return i.im.Get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *iam) UpdateIdentity(name string, u User) error {
|
func (i *iam) UpdateIdentity(name string, u identity.User) error {
|
||||||
return i.im.Update(name, u)
|
return i.im.Update(name, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,23 +156,23 @@ func (i *iam) DeleteIdentity(name string) error {
|
|||||||
return i.im.Delete(name)
|
return i.im.Delete(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *iam) ListIdentities() []User {
|
func (i *iam) ListIdentities() []identity.User {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *iam) SaveIdentities() error {
|
func (i *iam) ReloadIndentities() error {
|
||||||
return i.im.Save()
|
return i.im.Reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *iam) GetVerifier(name string) (IdentityVerifier, error) {
|
func (i *iam) GetVerifier(name string) (identity.Verifier, error) {
|
||||||
return i.im.GetVerifier(name)
|
return i.im.GetVerifier(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *iam) GetVerfierFromAuth0(name string) (IdentityVerifier, error) {
|
func (i *iam) GetVerfierFromAuth0(name string) (identity.Verifier, error) {
|
||||||
return i.im.GetVerifierFromAuth0(name)
|
return i.im.GetVerifierFromAuth0(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *iam) GetDefaultVerifier() IdentityVerifier {
|
func (i *iam) GetDefaultVerifier() identity.Verifier {
|
||||||
v, _ := i.im.GetDefaultVerifier()
|
v, _ := i.im.GetDefaultVerifier()
|
||||||
|
|
||||||
return v
|
return v
|
||||||
@@ -220,6 +222,10 @@ func (i *iam) RemovePolicy(name, domain, resource string, actions []string) bool
|
|||||||
return i.am.RemovePolicy(name, domain, resource, actions)
|
return i.am.RemovePolicy(name, domain, resource, actions)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *iam) ListPolicies(name, domain, resource string, actions []string) []Policy {
|
func (i *iam) ListPolicies(name, domain, resource string, actions []string) []access.Policy {
|
||||||
return i.am.ListPolicies(name, domain, resource, actions)
|
return i.am.ListPolicies(name, domain, resource, actions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *iam) ReloadPolicies() error {
|
||||||
|
return i.am.ReloadPolicies()
|
||||||
|
}
|
||||||
|
89
iam/identity/adapter.go
Normal file
89
iam/identity/adapter.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package identity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/datarhei/core/v16/io/fs"
|
||||||
|
"github.com/datarhei/core/v16/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Adapter interface {
|
||||||
|
LoadIdentities() ([]User, error)
|
||||||
|
SaveIdentities(user []User) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type fileAdapter struct {
|
||||||
|
fs fs.Filesystem
|
||||||
|
filePath string
|
||||||
|
logger log.Logger
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewJSONAdapter(fs fs.Filesystem, filePath string, logger log.Logger) (Adapter, error) {
|
||||||
|
a := &fileAdapter{
|
||||||
|
fs: fs,
|
||||||
|
filePath: filePath,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.fs == nil {
|
||||||
|
return nil, fmt.Errorf("a filesystem must be provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.filePath == "" {
|
||||||
|
return nil, fmt.Errorf("invalid file path, file path cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.logger == nil {
|
||||||
|
a.logger = log.New("")
|
||||||
|
}
|
||||||
|
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *fileAdapter) LoadIdentities() ([]User, error) {
|
||||||
|
a.lock.Lock()
|
||||||
|
defer a.lock.Unlock()
|
||||||
|
|
||||||
|
if _, err := a.fs.Stat(a.filePath); os.IsNotExist(err) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := a.fs.ReadFile(a.filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
users := []User{}
|
||||||
|
|
||||||
|
err = json.Unmarshal(data, &users)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
a.logger.Debug().WithField("path", a.filePath).Log("Identity file loaded")
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *fileAdapter) SaveIdentities(user []User) error {
|
||||||
|
a.lock.Lock()
|
||||||
|
defer a.lock.Unlock()
|
||||||
|
|
||||||
|
jsondata, err := json.MarshalIndent(user, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = a.fs.WriteFileSafe(a.filePath, jsondata)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
a.logger.Debug().WithField("path", a.filePath).Log("Identity file save")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@@ -1,15 +1,12 @@
|
|||||||
package iam
|
package identity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/iam/jwks"
|
"github.com/datarhei/core/v16/iam/jwks"
|
||||||
"github.com/datarhei/core/v16/io/fs"
|
|
||||||
"github.com/datarhei/core/v16/log"
|
"github.com/datarhei/core/v16/log"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
@@ -79,7 +76,7 @@ func (u *User) clone() User {
|
|||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
type IdentityVerifier interface {
|
type Verifier interface {
|
||||||
Name() string
|
Name() string
|
||||||
|
|
||||||
VerifyJWT(jwt string) (bool, error)
|
VerifyJWT(jwt string) (bool, error)
|
||||||
@@ -369,21 +366,23 @@ func (i *identity) IsSuperuser() bool {
|
|||||||
return i.user.Superuser
|
return i.user.Superuser
|
||||||
}
|
}
|
||||||
|
|
||||||
type IdentityManager interface {
|
type Manager interface {
|
||||||
Create(identity User) error
|
Create(identity User) error
|
||||||
Update(name string, identity User) error
|
Update(name string, identity User) error
|
||||||
Delete(name string) error
|
Delete(name string) error
|
||||||
|
|
||||||
Get(name string) (User, error)
|
Get(name string) (User, error)
|
||||||
GetVerifier(name string) (IdentityVerifier, error)
|
GetVerifier(name string) (Verifier, error)
|
||||||
GetVerifierFromAuth0(name string) (IdentityVerifier, error)
|
GetVerifierFromAuth0(name string) (Verifier, error)
|
||||||
GetDefaultVerifier() (IdentityVerifier, error)
|
GetDefaultVerifier() (Verifier, error)
|
||||||
|
|
||||||
|
Reload() error // Reload users from adapter
|
||||||
|
Save() error // Save users to adapter
|
||||||
|
List() []User // List all users
|
||||||
|
|
||||||
Validators() []string
|
Validators() []string
|
||||||
CreateJWT(name string) (string, string, error)
|
CreateJWT(name string) (string, string, error)
|
||||||
|
|
||||||
Save() error
|
|
||||||
Autosave(bool)
|
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,8 +394,7 @@ type identityManager struct {
|
|||||||
|
|
||||||
auth0UserIdentityMap map[string]string
|
auth0UserIdentityMap map[string]string
|
||||||
|
|
||||||
fs fs.Filesystem
|
adapter Adapter
|
||||||
filePath string
|
|
||||||
autosave bool
|
autosave bool
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
|
|
||||||
@@ -406,21 +404,20 @@ type identityManager struct {
|
|||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type IdentityConfig struct {
|
type Config struct {
|
||||||
FS fs.Filesystem
|
Adapter Adapter
|
||||||
Superuser User
|
Superuser User
|
||||||
JWTRealm string
|
JWTRealm string
|
||||||
JWTSecret string
|
JWTSecret string
|
||||||
Logger log.Logger
|
Logger log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIdentityManager(config IdentityConfig) (IdentityManager, error) {
|
func New(config Config) (Manager, error) {
|
||||||
im := &identityManager{
|
im := &identityManager{
|
||||||
identities: map[string]*identity{},
|
identities: map[string]*identity{},
|
||||||
tenants: map[string]*auth0Tenant{},
|
tenants: map[string]*auth0Tenant{},
|
||||||
auth0UserIdentityMap: map[string]string{},
|
auth0UserIdentityMap: map[string]string{},
|
||||||
fs: config.FS,
|
adapter: config.Adapter,
|
||||||
filePath: "./users.json",
|
|
||||||
jwtRealm: config.JWTRealm,
|
jwtRealm: config.JWTRealm,
|
||||||
jwtSecret: []byte(config.JWTSecret),
|
jwtSecret: []byte(config.JWTSecret),
|
||||||
logger: config.Logger,
|
logger: config.Logger,
|
||||||
@@ -430,13 +427,8 @@ func NewIdentityManager(config IdentityConfig) (IdentityManager, error) {
|
|||||||
im.logger = log.New("")
|
im.logger = log.New("")
|
||||||
}
|
}
|
||||||
|
|
||||||
if im.fs == nil {
|
if im.adapter == nil {
|
||||||
return nil, fmt.Errorf("no filesystem provided")
|
return nil, fmt.Errorf("no adapter provided")
|
||||||
}
|
|
||||||
|
|
||||||
err := im.load(im.filePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Superuser.Superuser = true
|
config.Superuser.Superuser = true
|
||||||
@@ -446,7 +438,13 @@ func NewIdentityManager(config IdentityConfig) (IdentityManager, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
im.root = identity
|
im.root = identity
|
||||||
im.autosave = true
|
|
||||||
|
err = im.Reload()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
im.Save()
|
||||||
|
|
||||||
return im, nil
|
return im, nil
|
||||||
}
|
}
|
||||||
@@ -455,7 +453,7 @@ func (im *identityManager) Close() {
|
|||||||
im.lock.Lock()
|
im.lock.Lock()
|
||||||
defer im.lock.Unlock()
|
defer im.lock.Unlock()
|
||||||
|
|
||||||
im.fs = 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.root = nil
|
im.root = nil
|
||||||
@@ -467,6 +465,51 @@ func (im *identityManager) Close() {
|
|||||||
im.tenants = map[string]*auth0Tenant{}
|
im.tenants = map[string]*auth0Tenant{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (im *identityManager) Reload() error {
|
||||||
|
users, err := im.adapter.LoadIdentities()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("load users from adapter: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
im.lock.Lock()
|
||||||
|
defer im.lock.Unlock()
|
||||||
|
|
||||||
|
im.autosave = false
|
||||||
|
defer func() {
|
||||||
|
im.autosave = true
|
||||||
|
}()
|
||||||
|
|
||||||
|
names := []string{}
|
||||||
|
|
||||||
|
for name := range im.identities {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
im.delete(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, u := range users {
|
||||||
|
if im.root != nil && u.Name == im.root.user.Name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := im.identities[u.Name]
|
||||||
|
if ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
identity, err := im.create(u)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
im.identities[identity.user.Name] = identity
|
||||||
|
}
|
||||||
|
|
||||||
|
return 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
|
||||||
@@ -492,7 +535,7 @@ func (im *identityManager) Create(u User) error {
|
|||||||
im.identities[identity.user.Name] = identity
|
im.identities[identity.user.Name] = identity
|
||||||
|
|
||||||
if im.autosave {
|
if im.autosave {
|
||||||
im.save(im.filePath)
|
im.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -580,7 +623,7 @@ func (im *identityManager) Update(name string, u User) error {
|
|||||||
}).Log("Identity updated")
|
}).Log("Identity updated")
|
||||||
|
|
||||||
if im.autosave {
|
if im.autosave {
|
||||||
im.save(im.filePath)
|
im.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -590,7 +633,12 @@ func (im *identityManager) Delete(name string) error {
|
|||||||
im.lock.Lock()
|
im.lock.Lock()
|
||||||
defer im.lock.Unlock()
|
defer im.lock.Unlock()
|
||||||
|
|
||||||
return im.delete(name)
|
err := im.delete(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im *identityManager) delete(name string) error {
|
func (im *identityManager) delete(name string) error {
|
||||||
@@ -611,7 +659,7 @@ func (im *identityManager) delete(name string) error {
|
|||||||
|
|
||||||
if len(identity.user.Auth.API.Auth0.User) == 0 {
|
if len(identity.user.Auth.API.Auth0.User) == 0 {
|
||||||
if im.autosave {
|
if im.autosave {
|
||||||
im.save(im.filePath)
|
im.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -633,7 +681,7 @@ func (im *identityManager) delete(name string) error {
|
|||||||
delete(im.tenants, identity.user.Auth.API.Auth0.Tenant.key())
|
delete(im.tenants, identity.user.Auth.API.Auth0.Tenant.key())
|
||||||
|
|
||||||
if im.autosave {
|
if im.autosave {
|
||||||
im.save(im.filePath)
|
im.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -657,7 +705,9 @@ func (im *identityManager) delete(name string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if im.autosave {
|
if im.autosave {
|
||||||
im.save(im.filePath)
|
if err := im.save(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -696,14 +746,14 @@ func (im *identityManager) Get(name string) (User, error) {
|
|||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im *identityManager) GetVerifier(name string) (IdentityVerifier, error) {
|
func (im *identityManager) GetVerifier(name string) (Verifier, error) {
|
||||||
im.lock.RLock()
|
im.lock.RLock()
|
||||||
defer im.lock.RUnlock()
|
defer im.lock.RUnlock()
|
||||||
|
|
||||||
return im.getIdentity(name)
|
return im.getIdentity(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im *identityManager) GetVerifierFromAuth0(name string) (IdentityVerifier, error) {
|
func (im *identityManager) GetVerifierFromAuth0(name string) (Verifier, error) {
|
||||||
im.lock.RLock()
|
im.lock.RLock()
|
||||||
defer im.lock.RUnlock()
|
defer im.lock.RUnlock()
|
||||||
|
|
||||||
@@ -715,65 +765,38 @@ func (im *identityManager) GetVerifierFromAuth0(name string) (IdentityVerifier,
|
|||||||
return im.getIdentity(name)
|
return im.getIdentity(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im *identityManager) GetDefaultVerifier() (IdentityVerifier, error) {
|
func (im *identityManager) GetDefaultVerifier() (Verifier, error) {
|
||||||
return im.root, nil
|
return im.root, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im *identityManager) load(filePath string) error {
|
func (im *identityManager) List() []User {
|
||||||
if _, err := im.fs.Stat(filePath); os.IsNotExist(err) {
|
im.lock.RLock()
|
||||||
return nil
|
defer im.lock.RUnlock()
|
||||||
}
|
|
||||||
|
|
||||||
data, err := im.fs.ReadFile(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
users := []User{}
|
users := []User{}
|
||||||
|
|
||||||
err = json.Unmarshal(data, &users)
|
for _, identity := range im.identities {
|
||||||
if err != nil {
|
users = append(users, identity.user.clone())
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range users {
|
return users
|
||||||
err = im.Create(u)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im *identityManager) Save() error {
|
func (im *identityManager) Save() error {
|
||||||
im.lock.RLock()
|
im.lock.RLock()
|
||||||
defer im.lock.RUnlock()
|
defer im.lock.RUnlock()
|
||||||
|
|
||||||
return im.save(im.filePath)
|
return im.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im *identityManager) save(filePath string) error {
|
func (im *identityManager) save() error {
|
||||||
if filePath == "" {
|
|
||||||
return fmt.Errorf("invalid file path, file path cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
users := []User{}
|
users := []User{}
|
||||||
|
|
||||||
for _, u := range im.identities {
|
for _, u := range im.identities {
|
||||||
users = append(users, u.user)
|
users = append(users, u.user)
|
||||||
}
|
}
|
||||||
|
|
||||||
jsondata, err := json.MarshalIndent(users, "", " ")
|
return im.adapter.SaveIdentities(users)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, err = im.fs.WriteFileSafe(filePath, jsondata)
|
|
||||||
|
|
||||||
im.logger.Debug().WithField("path", filePath).Log("Identity file save")
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im *identityManager) Autosave(auto bool) {
|
func (im *identityManager) Autosave(auto bool) {
|
@@ -1,4 +1,4 @@
|
|||||||
package iam
|
package identity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@@ -7,6 +7,15 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func createAdapter() (Adapter, error) {
|
||||||
|
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewJSONAdapter(dummyfs, "./users.json", nil)
|
||||||
|
}
|
||||||
|
|
||||||
func TestUserName(t *testing.T) {
|
func TestUserName(t *testing.T) {
|
||||||
user := User{}
|
user := User{}
|
||||||
|
|
||||||
@@ -39,11 +48,11 @@ func TestIdentity(t *testing.T) {
|
|||||||
identity.user.Superuser = true
|
identity.user.Superuser = true
|
||||||
require.True(t, identity.IsSuperuser())
|
require.True(t, identity.IsSuperuser())
|
||||||
|
|
||||||
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
adapter, err := createAdapter()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
im, err := NewIdentityManager(IdentityConfig{
|
im, err := New(Config{
|
||||||
FS: dummyfs,
|
Adapter: adapter,
|
||||||
Superuser: User{Name: "foobar"},
|
Superuser: User{Name: "foobar"},
|
||||||
JWTRealm: "test-realm",
|
JWTRealm: "test-realm",
|
||||||
JWTSecret: "abc123",
|
JWTSecret: "abc123",
|
||||||
@@ -58,11 +67,11 @@ func TestIdentity(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultIdentity(t *testing.T) {
|
func TestDefaultIdentity(t *testing.T) {
|
||||||
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
adapter, err := createAdapter()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
im, err := NewIdentityManager(IdentityConfig{
|
im, err := New(Config{
|
||||||
FS: dummyfs,
|
Adapter: adapter,
|
||||||
Superuser: User{Name: "foobar"},
|
Superuser: User{Name: "foobar"},
|
||||||
JWTRealm: "test-realm",
|
JWTRealm: "test-realm",
|
||||||
JWTSecret: "abc123",
|
JWTSecret: "abc123",
|
||||||
@@ -186,11 +195,11 @@ func TestIdentityServiceTokenAuth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestJWT(t *testing.T) {
|
func TestJWT(t *testing.T) {
|
||||||
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
adapter, err := createAdapter()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
im, err := NewIdentityManager(IdentityConfig{
|
im, err := New(Config{
|
||||||
FS: dummyfs,
|
Adapter: adapter,
|
||||||
Superuser: User{Name: "foobar"},
|
Superuser: User{Name: "foobar"},
|
||||||
JWTRealm: "test-realm",
|
JWTRealm: "test-realm",
|
||||||
JWTSecret: "abc123",
|
JWTSecret: "abc123",
|
||||||
@@ -239,11 +248,11 @@ func TestJWT(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateUser(t *testing.T) {
|
func TestCreateUser(t *testing.T) {
|
||||||
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
adapter, err := createAdapter()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
im, err := NewIdentityManager(IdentityConfig{
|
im, err := New(Config{
|
||||||
FS: dummyfs,
|
Adapter: adapter,
|
||||||
Superuser: User{Name: "foobar"},
|
Superuser: User{Name: "foobar"},
|
||||||
JWTRealm: "test-realm",
|
JWTRealm: "test-realm",
|
||||||
JWTSecret: "abc123",
|
JWTSecret: "abc123",
|
||||||
@@ -263,11 +272,11 @@ func TestCreateUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateUserAuth0(t *testing.T) {
|
func TestCreateUserAuth0(t *testing.T) {
|
||||||
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
adapter, err := createAdapter()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
im, err := NewIdentityManager(IdentityConfig{
|
im, err := New(Config{
|
||||||
FS: dummyfs,
|
Adapter: adapter,
|
||||||
Superuser: User{Name: "foobar"},
|
Superuser: User{Name: "foobar"},
|
||||||
JWTRealm: "test-realm",
|
JWTRealm: "test-realm",
|
||||||
JWTSecret: "abc123",
|
JWTSecret: "abc123",
|
||||||
@@ -383,11 +392,13 @@ func TestCreateUserAuth0(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadAndSave(t *testing.T) {
|
func TestLoadAndSave(t *testing.T) {
|
||||||
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
adptr, err := createAdapter()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
im, err := NewIdentityManager(IdentityConfig{
|
dummyfs := adptr.(*fileAdapter).fs
|
||||||
FS: dummyfs,
|
|
||||||
|
im, err := New(Config{
|
||||||
|
Adapter: adptr,
|
||||||
Superuser: User{Name: "foobar"},
|
Superuser: User{Name: "foobar"},
|
||||||
JWTRealm: "test-realm",
|
JWTRealm: "test-realm",
|
||||||
JWTSecret: "abc123",
|
JWTSecret: "abc123",
|
||||||
@@ -416,8 +427,8 @@ func TestLoadAndSave(t *testing.T) {
|
|||||||
err = im.Save()
|
err = im.Save()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
im, err = NewIdentityManager(IdentityConfig{
|
im, err = New(Config{
|
||||||
FS: dummyfs,
|
Adapter: adptr,
|
||||||
Superuser: User{Name: "foobar"},
|
Superuser: User{Name: "foobar"},
|
||||||
JWTRealm: "test-realm",
|
JWTRealm: "test-realm",
|
||||||
JWTSecret: "abc123",
|
JWTSecret: "abc123",
|
||||||
@@ -432,11 +443,11 @@ func TestLoadAndSave(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateUser(t *testing.T) {
|
func TestUpdateUser(t *testing.T) {
|
||||||
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
adapter, err := createAdapter()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
im, err := NewIdentityManager(IdentityConfig{
|
im, err := New(Config{
|
||||||
FS: dummyfs,
|
Adapter: adapter,
|
||||||
Superuser: User{Name: "foobar"},
|
Superuser: User{Name: "foobar"},
|
||||||
JWTRealm: "test-realm",
|
JWTRealm: "test-realm",
|
||||||
JWTSecret: "abc123",
|
JWTSecret: "abc123",
|
||||||
@@ -480,11 +491,11 @@ func TestUpdateUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateUserAuth0(t *testing.T) {
|
func TestUpdateUserAuth0(t *testing.T) {
|
||||||
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
adapter, err := createAdapter()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
im, err := NewIdentityManager(IdentityConfig{
|
im, err := New(Config{
|
||||||
FS: dummyfs,
|
Adapter: adapter,
|
||||||
Superuser: User{Name: "foobar"},
|
Superuser: User{Name: "foobar"},
|
||||||
JWTRealm: "test-realm",
|
JWTRealm: "test-realm",
|
||||||
JWTSecret: "abc123",
|
JWTSecret: "abc123",
|
||||||
@@ -537,11 +548,11 @@ func TestUpdateUserAuth0(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRemoveUser(t *testing.T) {
|
func TestRemoveUser(t *testing.T) {
|
||||||
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
adapter, err := createAdapter()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
im, err := NewIdentityManager(IdentityConfig{
|
im, err := New(Config{
|
||||||
FS: dummyfs,
|
Adapter: adapter,
|
||||||
Superuser: User{Name: "foobar"},
|
Superuser: User{Name: "foobar"},
|
||||||
JWTRealm: "test-realm",
|
JWTRealm: "test-realm",
|
||||||
JWTSecret: "abc123",
|
JWTSecret: "abc123",
|
||||||
@@ -633,11 +644,11 @@ func TestRemoveUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRemoveUserAuth0(t *testing.T) {
|
func TestRemoveUserAuth0(t *testing.T) {
|
||||||
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
adapter, err := createAdapter()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
im, err := NewIdentityManager(IdentityConfig{
|
im, err := New(Config{
|
||||||
FS: dummyfs,
|
Adapter: adapter,
|
||||||
Superuser: User{Name: "foobar"},
|
Superuser: User{Name: "foobar"},
|
||||||
JWTRealm: "test-realm",
|
JWTRealm: "test-realm",
|
||||||
JWTSecret: "abc123",
|
JWTSecret: "abc123",
|
||||||
@@ -716,11 +727,13 @@ func TestRemoveUserAuth0(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAutosave(t *testing.T) {
|
func TestAutosave(t *testing.T) {
|
||||||
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
adptr, err := createAdapter()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
im, err := NewIdentityManager(IdentityConfig{
|
dummyfs := adptr.(*fileAdapter).fs
|
||||||
FS: dummyfs,
|
|
||||||
|
im, err := New(Config{
|
||||||
|
Adapter: adptr,
|
||||||
Superuser: User{Name: "foobar"},
|
Superuser: User{Name: "foobar"},
|
||||||
JWTRealm: "test-realm",
|
JWTRealm: "test-realm",
|
||||||
JWTSecret: "abc123",
|
JWTSecret: "abc123",
|
||||||
@@ -739,8 +752,6 @@ func TestAutosave(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, []byte("[]"), data)
|
require.Equal(t, []byte("[]"), data)
|
||||||
|
|
||||||
im.Autosave(true)
|
|
||||||
|
|
||||||
err = im.Create(User{Name: "foobaz"})
|
err = im.Create(User{Name: "foobaz"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
@@ -7,6 +7,8 @@ import (
|
|||||||
|
|
||||||
"github.com/datarhei/core/v16/ffmpeg"
|
"github.com/datarhei/core/v16/ffmpeg"
|
||||||
"github.com/datarhei/core/v16/iam"
|
"github.com/datarhei/core/v16/iam"
|
||||||
|
iamaccess "github.com/datarhei/core/v16/iam/access"
|
||||||
|
iamidentity "github.com/datarhei/core/v16/iam/identity"
|
||||||
"github.com/datarhei/core/v16/internal/testhelper"
|
"github.com/datarhei/core/v16/internal/testhelper"
|
||||||
"github.com/datarhei/core/v16/io/fs"
|
"github.com/datarhei/core/v16/io/fs"
|
||||||
"github.com/datarhei/core/v16/net"
|
"github.com/datarhei/core/v16/net"
|
||||||
@@ -39,9 +41,20 @@ func getDummyRestreamer(portrange net.Portranger, validatorIn, validatorOut ffmp
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
policyAdapter, err := iamaccess.NewJSONAdapter(dummyfs, "./policy.json", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
identityAdapter, err := iamidentity.NewJSONAdapter(dummyfs, "./users.json", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
iam, err := iam.NewIAM(iam.Config{
|
iam, err := iam.NewIAM(iam.Config{
|
||||||
FS: dummyfs,
|
PolicyAdapter: policyAdapter,
|
||||||
Superuser: iam.User{
|
IdentityAdapter: identityAdapter,
|
||||||
|
Superuser: iamidentity.User{
|
||||||
Name: "foobar",
|
Name: "foobar",
|
||||||
},
|
},
|
||||||
JWTRealm: "",
|
JWTRealm: "",
|
||||||
|
@@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/iam"
|
iamidentity "github.com/datarhei/core/v16/iam/identity"
|
||||||
"github.com/datarhei/core/v16/rtmp"
|
"github.com/datarhei/core/v16/rtmp"
|
||||||
srturl "github.com/datarhei/core/v16/srt/url"
|
srturl "github.com/datarhei/core/v16/srt/url"
|
||||||
)
|
)
|
||||||
@@ -25,7 +25,7 @@ type Config struct {
|
|||||||
|
|
||||||
// to a new identity, i.e. adjusting the credentials to the given identity.
|
// to a new identity, i.e. adjusting the credentials to the given identity.
|
||||||
type Rewriter interface {
|
type Rewriter interface {
|
||||||
RewriteAddress(address string, identity iam.IdentityVerifier, mode Access) string
|
RewriteAddress(address string, identity iamidentity.Verifier, mode Access) string
|
||||||
}
|
}
|
||||||
|
|
||||||
type rewrite struct {
|
type rewrite struct {
|
||||||
@@ -44,7 +44,7 @@ func New(config Config) (Rewriter, error) {
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *rewrite) RewriteAddress(address string, identity iam.IdentityVerifier, mode Access) string {
|
func (g *rewrite) RewriteAddress(address string, identity iamidentity.Verifier, mode Access) string {
|
||||||
u, err := url.Parse(address)
|
u, err := url.Parse(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return address
|
return address
|
||||||
@@ -104,7 +104,7 @@ func (g *rewrite) isLocal(u *url.URL) bool {
|
|||||||
return host == base.Host
|
return host == base.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *rewrite) httpURL(u *url.URL, mode Access, identity iam.IdentityVerifier) string {
|
func (g *rewrite) httpURL(u *url.URL, mode Access, identity iamidentity.Verifier) string {
|
||||||
password := identity.GetServiceBasicAuth()
|
password := identity.GetServiceBasicAuth()
|
||||||
|
|
||||||
if len(password) == 0 {
|
if len(password) == 0 {
|
||||||
@@ -116,7 +116,7 @@ func (g *rewrite) httpURL(u *url.URL, mode Access, identity iam.IdentityVerifier
|
|||||||
return u.String()
|
return u.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *rewrite) rtmpURL(u *url.URL, mode Access, identity iam.IdentityVerifier) string {
|
func (g *rewrite) rtmpURL(u *url.URL, mode Access, identity iamidentity.Verifier) string {
|
||||||
token := identity.GetServiceToken()
|
token := identity.GetServiceToken()
|
||||||
|
|
||||||
// Remove the existing token from the path
|
// Remove the existing token from the path
|
||||||
@@ -131,7 +131,7 @@ func (g *rewrite) rtmpURL(u *url.URL, mode Access, identity iam.IdentityVerifier
|
|||||||
return u.String()
|
return u.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *rewrite) srtURL(u *url.URL, mode Access, identity iam.IdentityVerifier) string {
|
func (g *rewrite) srtURL(u *url.URL, mode Access, identity iamidentity.Verifier) string {
|
||||||
token := identity.GetServiceToken()
|
token := identity.GetServiceToken()
|
||||||
|
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
|
@@ -4,21 +4,26 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/iam"
|
iamidentity "github.com/datarhei/core/v16/iam/identity"
|
||||||
"github.com/datarhei/core/v16/io/fs"
|
"github.com/datarhei/core/v16/io/fs"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getIdentityManager(enableBasic bool) iam.IdentityManager {
|
func getIdentityManager(enableBasic bool) (iamidentity.Manager, error) {
|
||||||
dummyfs, _ := fs.NewMemFilesystem(fs.MemConfig{})
|
dummyfs, _ := fs.NewMemFilesystem(fs.MemConfig{})
|
||||||
|
|
||||||
superuser := iam.User{
|
adapter, err := iamidentity.NewJSONAdapter(dummyfs, "./users.json", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
superuser := iamidentity.User{
|
||||||
Name: "foobar",
|
Name: "foobar",
|
||||||
Superuser: false,
|
Superuser: false,
|
||||||
Auth: iam.UserAuth{
|
Auth: iamidentity.UserAuth{
|
||||||
API: iam.UserAuthAPI{},
|
API: iamidentity.UserAuthAPI{},
|
||||||
Services: iam.UserAuthServices{
|
Services: iamidentity.UserAuthServices{
|
||||||
Token: []string{"servicetoken"},
|
Token: []string{"servicetoken"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -28,19 +33,20 @@ func getIdentityManager(enableBasic bool) iam.IdentityManager {
|
|||||||
superuser.Auth.Services.Basic = []string{"basicauthpassword"}
|
superuser.Auth.Services.Basic = []string{"basicauthpassword"}
|
||||||
}
|
}
|
||||||
|
|
||||||
im, _ := iam.NewIdentityManager(iam.IdentityConfig{
|
im, err := iamidentity.New(iamidentity.Config{
|
||||||
FS: dummyfs,
|
Adapter: adapter,
|
||||||
Superuser: superuser,
|
Superuser: superuser,
|
||||||
JWTRealm: "",
|
JWTRealm: "",
|
||||||
JWTSecret: "",
|
JWTSecret: "",
|
||||||
Logger: nil,
|
Logger: nil,
|
||||||
})
|
})
|
||||||
|
|
||||||
return im
|
return im, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRewriteHTTP(t *testing.T) {
|
func TestRewriteHTTP(t *testing.T) {
|
||||||
im := getIdentityManager(false)
|
im, err := getIdentityManager(false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
rewrite, err := New(Config{
|
rewrite, err := New(Config{
|
||||||
HTTPBase: "http://localhost:8080/",
|
HTTPBase: "http://localhost:8080/",
|
||||||
@@ -70,7 +76,8 @@ func TestRewriteHTTP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRewriteHTTPPassword(t *testing.T) {
|
func TestRewriteHTTPPassword(t *testing.T) {
|
||||||
im := getIdentityManager(true)
|
im, err := getIdentityManager(true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
rewrite, err := New(Config{
|
rewrite, err := New(Config{
|
||||||
HTTPBase: "http://localhost:8080/",
|
HTTPBase: "http://localhost:8080/",
|
||||||
@@ -100,7 +107,8 @@ func TestRewriteHTTPPassword(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRewriteRTMP(t *testing.T) {
|
func TestRewriteRTMP(t *testing.T) {
|
||||||
im := getIdentityManager(false)
|
im, err := getIdentityManager(false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
rewrite, err := New(Config{
|
rewrite, err := New(Config{
|
||||||
RTMPBase: "rtmp://localhost:1935/live",
|
RTMPBase: "rtmp://localhost:1935/live",
|
||||||
@@ -128,7 +136,8 @@ func TestRewriteRTMP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRewriteSRT(t *testing.T) {
|
func TestRewriteSRT(t *testing.T) {
|
||||||
im := getIdentityManager(false)
|
im, err := getIdentityManager(false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
rewrite, err := New(Config{
|
rewrite, err := New(Config{
|
||||||
SRTBase: "srt://localhost:6000/",
|
SRTBase: "srt://localhost:6000/",
|
||||||
|
@@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/datarhei/core/v16/cluster/proxy"
|
"github.com/datarhei/core/v16/cluster/proxy"
|
||||||
"github.com/datarhei/core/v16/iam"
|
"github.com/datarhei/core/v16/iam"
|
||||||
|
iamidentity "github.com/datarhei/core/v16/iam/identity"
|
||||||
"github.com/datarhei/core/v16/log"
|
"github.com/datarhei/core/v16/log"
|
||||||
"github.com/datarhei/core/v16/session"
|
"github.com/datarhei/core/v16/session"
|
||||||
|
|
||||||
@@ -467,7 +468,7 @@ func (s *server) findIdentityFromStreamKey(key string) (string, error) {
|
|||||||
return "$anon", nil
|
return "$anon", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var identity iam.IdentityVerifier
|
var identity iamidentity.Verifier
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
var token string
|
var token string
|
||||||
|
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/datarhei/core/v16/cluster/proxy"
|
"github.com/datarhei/core/v16/cluster/proxy"
|
||||||
"github.com/datarhei/core/v16/iam"
|
"github.com/datarhei/core/v16/iam"
|
||||||
|
iamidentity "github.com/datarhei/core/v16/iam/identity"
|
||||||
"github.com/datarhei/core/v16/log"
|
"github.com/datarhei/core/v16/log"
|
||||||
"github.com/datarhei/core/v16/session"
|
"github.com/datarhei/core/v16/session"
|
||||||
"github.com/datarhei/core/v16/srt/url"
|
"github.com/datarhei/core/v16/srt/url"
|
||||||
@@ -477,7 +478,7 @@ func (s *server) findIdentityFromToken(key string) (string, error) {
|
|||||||
return "$anon", nil
|
return "$anon", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var identity iam.IdentityVerifier
|
var identity iamidentity.Verifier
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
var token string
|
var token string
|
||||||
|
Reference in New Issue
Block a user