mirror of
https://github.com/datarhei/core.git
synced 2025-10-07 08:51:04 +08:00
Create identities for basic auth access to mount points
This commit is contained in:
149
app/api/api.go
149
app/api/api.go
@@ -398,10 +398,6 @@ func (a *api) start() error {
|
||||
},
|
||||
},
|
||||
Services: iam.UserAuthServices{
|
||||
Basic: iam.UserAuthPassword{
|
||||
Enable: cfg.Storage.Memory.Auth.Enable,
|
||||
Password: cfg.Storage.Memory.Auth.Password,
|
||||
},
|
||||
Token: []string{
|
||||
cfg.RTMP.Token,
|
||||
cfg.SRT.Token,
|
||||
@@ -419,6 +415,126 @@ func (a *api) start() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Create policies and users in order to mimic the behaviour before IAM
|
||||
|
||||
policies := []iam.Policy{
|
||||
{
|
||||
Name: "$anon",
|
||||
Domain: "$none",
|
||||
Resource: "fs:/**",
|
||||
Actions: []string{"GET", "HEAD", "OPTIONS"},
|
||||
},
|
||||
{
|
||||
Name: "$anon",
|
||||
Domain: "$none",
|
||||
Resource: "api:/api",
|
||||
Actions: []string{"GET", "HEAD", "OPTIONS"},
|
||||
},
|
||||
{
|
||||
Name: "$anon",
|
||||
Domain: "$none",
|
||||
Resource: "api:/api/v3/widget/process/**",
|
||||
Actions: []string{"GET", "HEAD", "OPTIONS"},
|
||||
},
|
||||
}
|
||||
|
||||
users := []iam.User{}
|
||||
|
||||
if !cfg.Storage.Memory.Auth.Enable {
|
||||
policies = append(policies, iam.Policy{
|
||||
Name: "$anon",
|
||||
Domain: "$none",
|
||||
Resource: "fs:/memfs/**",
|
||||
Actions: []string{"ANY"},
|
||||
})
|
||||
} else {
|
||||
if cfg.Storage.Memory.Auth.Username != superuser.Name {
|
||||
users = append(users, iam.User{
|
||||
Name: cfg.Storage.Memory.Auth.Username,
|
||||
Auth: iam.UserAuth{
|
||||
Services: iam.UserAuthServices{
|
||||
Basic: []iam.UserAuthPassword{
|
||||
{
|
||||
Enable: cfg.Storage.Memory.Auth.Enable,
|
||||
Password: cfg.Storage.Memory.Auth.Password,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
} else {
|
||||
superuser.Auth.Services.Basic = append(superuser.Auth.Services.Basic, iam.UserAuthPassword{
|
||||
Enable: cfg.Storage.Memory.Auth.Enable,
|
||||
Password: cfg.Storage.Memory.Auth.Password,
|
||||
})
|
||||
}
|
||||
|
||||
policies = append(policies, iam.Policy{
|
||||
Name: cfg.Storage.Memory.Auth.Username,
|
||||
Domain: "$none",
|
||||
Resource: "fs:/memfs/**",
|
||||
Actions: []string{"ANY"},
|
||||
})
|
||||
}
|
||||
|
||||
for _, s := range cfg.Storage.S3 {
|
||||
if !s.Auth.Enable {
|
||||
policies = append(policies, iam.Policy{
|
||||
Name: "$anon",
|
||||
Domain: "$none",
|
||||
Resource: "fs:" + s.Mountpoint + "/**",
|
||||
Actions: []string{"ANY"},
|
||||
})
|
||||
} else {
|
||||
if s.Auth.Username != superuser.Name {
|
||||
users = append(users, iam.User{
|
||||
Name: s.Auth.Username,
|
||||
Auth: iam.UserAuth{
|
||||
Services: iam.UserAuthServices{
|
||||
Basic: []iam.UserAuthPassword{
|
||||
{
|
||||
Enable: s.Auth.Enable,
|
||||
Password: s.Auth.Password,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
} else {
|
||||
superuser.Auth.Services.Basic = append(superuser.Auth.Services.Basic, iam.UserAuthPassword{
|
||||
Enable: s.Auth.Enable,
|
||||
Password: s.Auth.Password,
|
||||
})
|
||||
}
|
||||
|
||||
policies = append(policies, iam.Policy{
|
||||
Name: s.Auth.Username,
|
||||
Domain: "$none",
|
||||
Resource: "fs:" + s.Mountpoint + "/**",
|
||||
Actions: []string{"ANY"},
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.RTMP.Enable && len(cfg.RTMP.Token) == 0 {
|
||||
policies = append(policies, iam.Policy{
|
||||
Name: "$anon",
|
||||
Domain: "$none",
|
||||
Resource: "rtmp:/**",
|
||||
Actions: []string{"ANY"},
|
||||
})
|
||||
}
|
||||
|
||||
if cfg.SRT.Enable && len(cfg.SRT.Token) == 0 {
|
||||
policies = append(policies, iam.Policy{
|
||||
Name: "$anon",
|
||||
Domain: "$none",
|
||||
Resource: "srt:**",
|
||||
Actions: []string{"ANY"},
|
||||
})
|
||||
}
|
||||
|
||||
fs, err := fs.NewRootedDiskFilesystem(fs.RootedDiskConfig{
|
||||
Root: filepath.Join(cfg.DB.Dir, "iam"),
|
||||
})
|
||||
@@ -442,24 +558,21 @@ func (a *api) start() error {
|
||||
return fmt.Errorf("iam: %w", err)
|
||||
}
|
||||
|
||||
// Create default policies for anonymous users in order to mimic the behaviour before IAM
|
||||
|
||||
iam.RemovePolicy("$anon", "$none", "", nil)
|
||||
|
||||
iam.AddPolicy("$anon", "$none", "fs:/**", []string{"GET", "HEAD", "OPTIONS"})
|
||||
iam.AddPolicy("$anon", "$none", "api:/api", []string{"GET", "HEAD", "OPTIONS"})
|
||||
iam.AddPolicy("$anon", "$none", "api:/api/v3/widget/process/**", []string{"GET", "HEAD", "OPTIONS"})
|
||||
|
||||
if !cfg.Storage.Memory.Auth.Enable {
|
||||
iam.AddPolicy("$anon", "$none", "fs:/memfs/**", []string{"ANY"})
|
||||
for _, user := range users {
|
||||
if _, err := iam.GetIdentity(user.Name); err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if cfg.RTMP.Enable && len(cfg.RTMP.Token) == 0 {
|
||||
iam.AddPolicy("$anon", "$none", "rtmp:/**", []string{"ANY"})
|
||||
err := iam.CreateIdentity(user)
|
||||
if err != nil {
|
||||
return fmt.Errorf("iam: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.SRT.Enable && len(cfg.SRT.Token) == 0 {
|
||||
iam.AddPolicy("$anon", "$none", "srt:**", []string{"ANY"})
|
||||
iam.SaveIdentities()
|
||||
|
||||
for _, policy := range policies {
|
||||
iam.AddPolicy(policy.Name, policy.Domain, policy.Resource, policy.Actions)
|
||||
}
|
||||
|
||||
a.iam = iam
|
||||
|
@@ -82,10 +82,10 @@ type Data struct {
|
||||
} `json:"disk"`
|
||||
Memory struct {
|
||||
Auth struct {
|
||||
Enable bool `json:"enable"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
} `json:"auth"`
|
||||
Enable bool `json:"enable"` // Deprecated, use IAM
|
||||
Username string `json:"username"` // Deprecated, use IAM
|
||||
Password string `json:"password"` // Deprecated, use IAM
|
||||
} `json:"auth"` // Deprecated, use IAM
|
||||
Size int64 `json:"max_size_mbytes" format:"int64"`
|
||||
Purge bool `json:"purge"`
|
||||
} `json:"memory"`
|
||||
@@ -101,13 +101,13 @@ type Data struct {
|
||||
Address string `json:"address"`
|
||||
AddressTLS string `json:"address_tls"`
|
||||
App string `json:"app"`
|
||||
Token string `json:"token"`
|
||||
Token string `json:"token"` // Deprecated, use IAM
|
||||
} `json:"rtmp"`
|
||||
SRT struct {
|
||||
Enable bool `json:"enable"`
|
||||
Address string `json:"address"`
|
||||
Passphrase string `json:"passphrase"`
|
||||
Token string `json:"token"`
|
||||
Token string `json:"token"` // Deprecated, use IAM
|
||||
Log struct {
|
||||
Enable bool `json:"enable"`
|
||||
Topics []string `json:"topics"`
|
||||
|
@@ -13,7 +13,7 @@ import (
|
||||
type S3Storage struct {
|
||||
Name string `json:"name"`
|
||||
Mountpoint string `json:"mountpoint"`
|
||||
Auth S3StorageAuth `json:"auth"`
|
||||
Auth S3StorageAuth `json:"auth"` // Deprecated, use IAM
|
||||
Endpoint string `json:"endpoint"`
|
||||
AccessKeyID string `json:"access_key_id"`
|
||||
SecretAccessKey string `json:"secret_access_key"`
|
||||
@@ -23,9 +23,9 @@ type S3Storage struct {
|
||||
}
|
||||
|
||||
type S3StorageAuth struct {
|
||||
Enable bool `json:"enable"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Enable bool `json:"enable"` // Deprecated, use IAM
|
||||
Username string `json:"username"` // Deprecated, use IAM
|
||||
Password string `json:"password"` // Deprecated, use IAM
|
||||
}
|
||||
|
||||
func (t *S3Storage) String() string {
|
||||
|
@@ -25,14 +25,17 @@ func (u *IAMUser) Marshal(user iam.User, policies []iam.Policy) {
|
||||
},
|
||||
},
|
||||
Services: IAMUserAuthServices{
|
||||
Basic: IAMUserAuthPassword{
|
||||
Enable: user.Auth.Services.Basic.Enable,
|
||||
Password: user.Auth.Services.Basic.Password,
|
||||
},
|
||||
Token: user.Auth.Services.Token,
|
||||
},
|
||||
}
|
||||
|
||||
for _, basic := range user.Auth.Services.Basic {
|
||||
u.Auth.Services.Basic = append(u.Auth.Services.Basic, IAMUserAuthPassword{
|
||||
Enable: basic.Enable,
|
||||
Password: basic.Password,
|
||||
})
|
||||
}
|
||||
|
||||
for _, p := range policies {
|
||||
u.Policies = append(u.Policies, IAMPolicy{
|
||||
Domain: p.Domain,
|
||||
@@ -63,15 +66,18 @@ func (u *IAMUser) Unmarshal() (iam.User, []iam.Policy) {
|
||||
},
|
||||
},
|
||||
Services: iam.UserAuthServices{
|
||||
Basic: iam.UserAuthPassword{
|
||||
Enable: u.Auth.Services.Basic.Enable,
|
||||
Password: u.Auth.Services.Basic.Password,
|
||||
},
|
||||
Token: u.Auth.Services.Token,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, basic := range u.Auth.Services.Basic {
|
||||
iamuser.Auth.Services.Basic = append(iamuser.Auth.Services.Basic, iam.UserAuthPassword{
|
||||
Enable: basic.Enable,
|
||||
Password: basic.Password,
|
||||
})
|
||||
}
|
||||
|
||||
iampolicies := []iam.Policy{}
|
||||
|
||||
for _, p := range u.Policies {
|
||||
@@ -103,7 +109,7 @@ type IAMUserAuthAPIAuth0 struct {
|
||||
}
|
||||
|
||||
type IAMUserAuthServices struct {
|
||||
Basic IAMUserAuthPassword `json:"basic"`
|
||||
Basic []IAMUserAuthPassword `json:"basic"`
|
||||
Token []string `json:"token"`
|
||||
}
|
||||
|
||||
|
@@ -51,12 +51,14 @@ func getIAM() (iam.IAM, error) {
|
||||
},
|
||||
},
|
||||
Services: iam.UserAuthServices{
|
||||
Basic: iam.UserAuthPassword{
|
||||
Basic: []iam.UserAuthPassword{
|
||||
{
|
||||
Enable: true,
|
||||
Password: "secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return i, nil
|
||||
|
@@ -125,7 +125,7 @@ func (a *adapter) importPolicy(model model.Model, rule []string) error {
|
||||
"domain": copiedRule[2],
|
||||
"resource": copiedRule[3],
|
||||
"actions": copiedRule[4],
|
||||
}).Log("imported policy")
|
||||
}).Log("Imported policy")
|
||||
|
||||
ok, err := model.HasPolicyEx(copiedRule[0], copiedRule[0], copiedRule[1:])
|
||||
if err != nil {
|
||||
@@ -215,7 +215,7 @@ func (a *adapter) addPolicy(ptype string, rule []string) error {
|
||||
"domain": domain,
|
||||
"resource": resource,
|
||||
"actions": actions,
|
||||
}).Log("adding policy")
|
||||
}).Log("Adding policy")
|
||||
} else if ptype == "g" {
|
||||
username = rule[0]
|
||||
role = rule[1]
|
||||
@@ -225,7 +225,7 @@ func (a *adapter) addPolicy(ptype string, rule []string) error {
|
||||
"subject": username,
|
||||
"role": role,
|
||||
"domain": domain,
|
||||
}).Log("adding role mapping")
|
||||
}).Log("Adding role mapping")
|
||||
} else {
|
||||
return fmt.Errorf("unknown ptype: %s", ptype)
|
||||
}
|
||||
@@ -415,7 +415,7 @@ func (a *adapter) removePolicy(ptype string, rule []string) error {
|
||||
"domain": domain,
|
||||
"resource": resource,
|
||||
"actions": actions,
|
||||
}).Log("removing policy")
|
||||
}).Log("Removing policy")
|
||||
} else if ptype == "g" {
|
||||
username = rule[0]
|
||||
role = rule[1]
|
||||
@@ -425,7 +425,7 @@ func (a *adapter) removePolicy(ptype string, rule []string) error {
|
||||
"subject": username,
|
||||
"role": role,
|
||||
"domain": domain,
|
||||
}).Log("adding role mapping")
|
||||
}).Log("Removing role mapping")
|
||||
} else {
|
||||
return fmt.Errorf("unknown ptype: %s", ptype)
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ type UserAuthAPIAuth0 struct {
|
||||
}
|
||||
|
||||
type UserAuthServices struct {
|
||||
Basic UserAuthPassword `json:"basic"`
|
||||
Basic []UserAuthPassword `json:"basic"`
|
||||
Token []string `json:"token"`
|
||||
}
|
||||
|
||||
@@ -58,9 +58,11 @@ func (u *User) validate() error {
|
||||
return fmt.Errorf("the name is required")
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`[^A-Za-z0-9_-]`)
|
||||
chars := `A-Za-z0-9:_-`
|
||||
|
||||
re := regexp.MustCompile(`[^` + chars + `]`)
|
||||
if re.MatchString(u.Name) {
|
||||
return fmt.Errorf("the name can only the contain [A-Za-z0-9_-]")
|
||||
return fmt.Errorf("the name can only contain [%s]", chars)
|
||||
}
|
||||
|
||||
if u.Auth.API.Userpass.Enable && len(u.Auth.API.Userpass.Password) == 0 {
|
||||
@@ -71,8 +73,10 @@ func (u *User) validate() error {
|
||||
return fmt.Errorf("a user for Auth0 login is required")
|
||||
}
|
||||
|
||||
if u.Auth.Services.Basic.Enable && len(u.Auth.Services.Basic.Password) == 0 {
|
||||
return fmt.Errorf("a password for service basic auth is required")
|
||||
for i, basic := range u.Auth.Services.Basic {
|
||||
if basic.Enable && len(basic.Password) == 0 {
|
||||
return fmt.Errorf("a password for service basic auth nr. %d is required", i)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -306,11 +310,24 @@ func (i *identity) VerifyServiceBasicAuth(password string) (bool, error) {
|
||||
return false, fmt.Errorf("invalid identity")
|
||||
}
|
||||
|
||||
if !i.user.Auth.Services.Basic.Enable {
|
||||
return false, fmt.Errorf("authentication method disabled")
|
||||
valid := false
|
||||
|
||||
for _, basic := range i.user.Auth.Services.Basic {
|
||||
if !basic.Enable {
|
||||
continue
|
||||
}
|
||||
|
||||
return i.user.Auth.Services.Basic.Password == password, nil
|
||||
if basic.Password == password {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (i *identity) GetServiceBasicAuth() string {
|
||||
@@ -321,11 +338,15 @@ func (i *identity) GetServiceBasicAuth() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
if !i.user.Auth.Services.Basic.Enable {
|
||||
return ""
|
||||
for _, basic := range i.user.Auth.Services.Basic {
|
||||
if !basic.Enable {
|
||||
continue
|
||||
}
|
||||
|
||||
return i.user.Auth.Services.Basic.Password
|
||||
return basic.Password
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (i *identity) VerifyServiceToken(token string) (bool, error) {
|
||||
@@ -528,6 +549,8 @@ func (im *identityManager) create(u User) (*identity, error) {
|
||||
|
||||
identity.valid = true
|
||||
|
||||
im.logger.Debug().WithField("name", identity.Name()).Log("Identity created")
|
||||
|
||||
return identity, nil
|
||||
}
|
||||
|
||||
@@ -573,6 +596,11 @@ func (im *identityManager) Update(name string, u User) error {
|
||||
|
||||
im.identities[identity.user.Name] = identity
|
||||
|
||||
im.logger.Debug().WithFields(log.Fields{
|
||||
"oldname": name,
|
||||
"newname": identity.Name(),
|
||||
}).Log("Identity updated")
|
||||
|
||||
if im.autosave {
|
||||
im.save(im.filePath)
|
||||
}
|
||||
@@ -765,6 +793,8 @@ func (im *identityManager) save(filePath string) error {
|
||||
|
||||
_, _, err = im.fs.WriteFileSafe(filePath, jsondata)
|
||||
|
||||
im.logger.Debug().WithField("path", filePath).Log("Identity file save")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
@@ -46,11 +46,13 @@ func TestUserAuth(t *testing.T) {
|
||||
err = user.validate()
|
||||
require.NoError(t, err)
|
||||
|
||||
user.Auth.Services.Basic.Enable = true
|
||||
user.Auth.Services.Basic = append(user.Auth.Services.Basic, UserAuthPassword{
|
||||
Enable: true,
|
||||
})
|
||||
err = user.validate()
|
||||
require.Error(t, err)
|
||||
|
||||
user.Auth.Services.Basic.Password = "secret"
|
||||
user.Auth.Services.Basic[0].Password = "secret"
|
||||
err = user.validate()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -159,8 +161,10 @@ func TestIdentityServiceBasicAuth(t *testing.T) {
|
||||
require.False(t, ok)
|
||||
require.Error(t, err)
|
||||
|
||||
identity.user.Auth.Services.Basic.Enable = true
|
||||
identity.user.Auth.Services.Basic.Password = "secret"
|
||||
identity.user.Auth.Services.Basic = append(identity.user.Auth.Services.Basic, UserAuthPassword{
|
||||
Enable: true,
|
||||
Password: "secret",
|
||||
})
|
||||
|
||||
ok, err = identity.VerifyServiceBasicAuth("secret")
|
||||
require.False(t, ok)
|
||||
@@ -172,14 +176,14 @@ func TestIdentityServiceBasicAuth(t *testing.T) {
|
||||
require.True(t, ok)
|
||||
require.NoError(t, err)
|
||||
|
||||
identity.user.Auth.Services.Basic.Enable = false
|
||||
identity.user.Auth.Services.Basic[0].Enable = false
|
||||
|
||||
ok, err = identity.VerifyServiceBasicAuth("secret")
|
||||
require.False(t, ok)
|
||||
require.Error(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
identity.user.Auth.Services.Basic.Enable = true
|
||||
identity.user.Auth.Services.Basic.Password = "terces"
|
||||
identity.user.Auth.Services.Basic[0].Enable = true
|
||||
identity.user.Auth.Services.Basic[0].Password = "terces"
|
||||
|
||||
ok, err = identity.VerifyServiceBasicAuth("secret")
|
||||
require.False(t, ok)
|
||||
@@ -610,10 +614,12 @@ func TestRemoveUser(t *testing.T) {
|
||||
Auth0: UserAuthAPIAuth0{},
|
||||
},
|
||||
Services: UserAuthServices{
|
||||
Basic: UserAuthPassword{
|
||||
Basic: []UserAuthPassword{
|
||||
{
|
||||
Enable: true,
|
||||
Password: "secret",
|
||||
},
|
||||
},
|
||||
Token: []string{"tokensecret"},
|
||||
},
|
||||
},
|
||||
|
@@ -21,10 +21,12 @@ func getIdentityManager(enableBasic bool) iam.IdentityManager {
|
||||
Auth: iam.UserAuth{
|
||||
API: iam.UserAuthAPI{},
|
||||
Services: iam.UserAuthServices{
|
||||
Basic: iam.UserAuthPassword{
|
||||
Basic: []iam.UserAuthPassword{
|
||||
{
|
||||
Enable: enableBasic,
|
||||
Password: "basicauthpassword",
|
||||
},
|
||||
},
|
||||
Token: []string{"servicetoken"},
|
||||
},
|
||||
},
|
||||
|
Reference in New Issue
Block a user