Reduce IAM API to only user and policies

This commit is contained in:
Ingo Oppermann 2023-05-17 18:19:23 +02:00
parent f03e2ca5c5
commit 6f831fd190
No known key found for this signature in database
GPG Key ID: 2AB32426E9DD229E
9 changed files with 258 additions and 150 deletions

View File

@ -119,7 +119,7 @@ type IAMAuth0Tenant struct {
}
type IAMPolicy struct {
Domain string `json:"group"`
Domain string `json:"domain"`
Resource string `json:"resource"`
Actions []string `json:"actions"`
}

View File

@ -28,6 +28,7 @@ func NewIAM(iam iam.IAM) *IAMHandler {
// @Accept json
// @Produce json
// @Param config body api.IAMUser true "User definition"
// @Param domain query string false "Domain of the acting user"
// @Success 200 {object} api.IAMUser
// @Failure 400 {object} api.Error
// @Failure 500 {object} api.Error
@ -35,10 +36,8 @@ func NewIAM(iam iam.IAM) *IAMHandler {
// @Router /api/v3/iam/user [post]
func (h *IAMHandler) AddUser(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
if !h.iam.Enforce(ctxuser, "$none", "iam:/user", "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none")
user := api.IAMUser{}
@ -48,17 +47,27 @@ func (h *IAMHandler) AddUser(c echo.Context) error {
iamuser, iampolicies := user.Unmarshal()
if !h.iam.Enforce(ctxuser, domain, "iam:"+iamuser.Name, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
for _, p := range iampolicies {
if !h.iam.Enforce(ctxuser, p.Domain, "iam:"+iamuser.Name, "write") {
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to write policy: %v", p)
}
}
if !superuser && iamuser.Superuser {
return api.Err(http.StatusForbidden, "Forbidden", "Only superusers can add superusers")
}
err := h.iam.CreateIdentity(iamuser)
if err != nil {
return api.Err(http.StatusBadRequest, "Bad request", "%s", err)
}
for _, p := range iampolicies {
if len(p.Domain) != 0 {
continue
}
h.iam.AddPolicy(p.Name, "$none", p.Resource, p.Actions)
h.iam.AddPolicy(p.Name, p.Domain, p.Resource, p.Actions)
}
err = h.iam.SaveIdentities()
@ -76,6 +85,7 @@ func (h *IAMHandler) AddUser(c echo.Context) error {
// @ID iam-3-delete-user
// @Produce json
// @Param name path string true "Username"
// @Param domain query string false "Domain of the acting user"
// @Success 200 {string} string
// @Failure 404 {object} api.Error
// @Failure 500 {object} api.Error
@ -83,17 +93,25 @@ func (h *IAMHandler) AddUser(c echo.Context) error {
// @Router /api/v3/iam/user/{name} [delete]
func (h *IAMHandler) RemoveUser(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none")
name := util.PathParam(c, "name")
if !h.iam.Enforce(ctxuser, "$none", "iam:/user", "write") {
if !h.iam.Enforce(ctxuser, domain, "iam:"+name, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
// Remove all policies of that user
h.iam.RemovePolicy(name, "", "", nil)
iamuser, err := h.iam.GetIdentity(name)
if err != nil {
return api.Err(http.StatusNotFound, "Not found", "%s", err)
}
if !superuser && iamuser.Superuser {
return api.Err(http.StatusForbidden, "Forbidden", "Only superusers can remove superusers")
}
// Remove the user
err := h.iam.DeleteIdentity(name)
err = h.iam.DeleteIdentity(name)
if err != nil {
return api.Err(http.StatusBadRequest, "Bad request", "%s", err)
}
@ -103,6 +121,9 @@ func (h *IAMHandler) RemoveUser(c echo.Context) error {
return api.Err(http.StatusInternalServerError, "Internal server error", "%s", err)
}
// Remove all policies of that user
h.iam.RemovePolicy(name, "", "", nil)
return c.JSON(http.StatusOK, "OK")
}
@ -114,6 +135,7 @@ func (h *IAMHandler) RemoveUser(c echo.Context) error {
// @Accept json
// @Produce json
// @Param name path string true "Username"
// @Param domain query string false "Domain of the acting user"
// @Param user body api.IAMUser true "User definition"
// @Success 200 {object} api.IAMUser
// @Failure 400 {object} api.Error
@ -123,15 +145,26 @@ func (h *IAMHandler) RemoveUser(c echo.Context) error {
// @Router /api/v3/iam/user/{name} [put]
func (h *IAMHandler) UpdateUser(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none")
name := util.PathParam(c, "name")
if !h.iam.Enforce(ctxuser, "$none", "iam:/user", "write") {
if !h.iam.Enforce(ctxuser, domain, "iam:"+name, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
iamuser, err := h.iam.GetIdentity(name)
if err != nil {
return api.Err(http.StatusNotFound, "Not found", "%s", err)
var iamuser iam.User
var err error
if name != "$anon" {
iamuser, err = h.iam.GetIdentity(name)
if err != nil {
return api.Err(http.StatusNotFound, "Not found", "%s", err)
}
} else {
iamuser = iam.User{
Name: "$anon",
}
}
iampolicies := h.iam.ListPolicies(name, "", "", nil)
@ -145,19 +178,31 @@ func (h *IAMHandler) UpdateUser(c echo.Context) error {
iamuser, iampolicies = user.Unmarshal()
err = h.iam.UpdateIdentity(name, iamuser)
if err != nil {
return api.Err(http.StatusBadRequest, "Bad request", "%s", err)
if !h.iam.Enforce(ctxuser, domain, "iam:"+iamuser.Name, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
h.iam.RemovePolicy(name, "$none", "", nil)
for _, p := range iampolicies {
if !h.iam.Enforce(ctxuser, p.Domain, "iam:"+iamuser.Name, "write") {
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to write policy: %v", p)
}
}
if !superuser && iamuser.Superuser {
return api.Err(http.StatusForbidden, "Forbidden", "Only superusers can modify superusers")
}
if name != "$anon" {
err = h.iam.UpdateIdentity(name, iamuser)
if err != nil {
return api.Err(http.StatusBadRequest, "Bad request", "%s", err)
}
}
h.iam.RemovePolicy(name, "", "", nil)
for _, p := range iampolicies {
if len(p.Domain) != 0 {
continue
}
h.iam.AddPolicy(p.Name, "$none", p.Resource, p.Actions)
h.iam.AddPolicy(p.Name, p.Domain, p.Resource, p.Actions)
}
err = h.iam.SaveIdentities()
@ -168,6 +213,71 @@ func (h *IAMHandler) UpdateUser(c echo.Context) error {
return c.JSON(http.StatusOK, user)
}
// UpdateUserPolicies replaces existing user policies
// @Summary Replace policies of an user
// @Description Replace policies of an user
// @Tags v16.?.?
// @ID iam-3-update-user
// @Accept json
// @Produce json
// @Param name path string true "Username"
// @Param domain query string false "Domain of the acting user"
// @Param user body []api.IAMPolicy true "Policy definitions"
// @Success 200 {array} api.IAMPolicy
// @Failure 400 {object} api.Error
// @Failure 404 {object} api.Error
// @Failure 500 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/iam/user/{name}/policy [put]
func (h *IAMHandler) UpdateUserPolicies(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none")
name := util.PathParam(c, "name")
if !h.iam.Enforce(ctxuser, domain, "iam:"+name, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
var iamuser iam.User
var err error
if name != "$anon" {
iamuser, err = h.iam.GetIdentity(name)
if err != nil {
return api.Err(http.StatusNotFound, "Not found", "%s", err)
}
} else {
iamuser = iam.User{
Name: "$anon",
}
}
policies := []api.IAMPolicy{}
if err := util.ShouldBindJSON(c, &policies); err != nil {
return api.Err(http.StatusBadRequest, "Invalid JSON", "%s", err)
}
for _, p := range policies {
if !h.iam.Enforce(ctxuser, p.Domain, "iam:"+iamuser.Name, "write") {
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to write policy: %v", p)
}
}
if !superuser && iamuser.Superuser {
return api.Err(http.StatusForbidden, "Forbidden", "Only superusers can modify superusers")
}
h.iam.RemovePolicy(name, "", "", nil)
for _, p := range policies {
h.iam.AddPolicy(iamuser.Name, p.Domain, p.Resource, p.Actions)
}
return c.JSON(http.StatusOK, policies)
}
// GetUser returns the user with the given name
// @Summary List an user by its name
// @Description List aa user by its name
@ -175,24 +285,44 @@ func (h *IAMHandler) UpdateUser(c echo.Context) error {
// @ID iam-3-get-user
// @Produce json
// @Param name path string true "Username"
// @Param domain query string false "Domain of the acting user"
// @Success 200 {object} api.IAMUser
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/iam/user/{name} [get]
func (h *IAMHandler) GetUser(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none")
name := util.PathParam(c, "name")
if !h.iam.Enforce(ctxuser, "$none", "iam:/user", "read") {
if !h.iam.Enforce(ctxuser, domain, "iam:"+name, "read") {
return api.Err(http.StatusForbidden, "Forbidden")
}
iamuser, err := h.iam.GetIdentity(name)
if err != nil {
return api.Err(http.StatusNotFound, "Not found", "%s", err)
var iamuser iam.User
var err error
if name != "$anon" {
iamuser, err = h.iam.GetIdentity(name)
if err != nil {
return api.Err(http.StatusNotFound, "Not found", "%s", err)
}
if !superuser && name != iamuser.Name {
if !h.iam.Enforce(ctxuser, domain, "iam:"+name, "write") {
iamuser = iam.User{
Name: iamuser.Name,
}
}
}
} else {
iamuser = iam.User{
Name: "$anon",
}
}
iampolicies := h.iam.ListPolicies(name, "$none", "", nil)
iampolicies := h.iam.ListPolicies(name, "", "", nil)
user := api.IAMUser{}
user.Marshal(iamuser, iampolicies)

View File

@ -77,8 +77,8 @@ func DefaultQuery(c echo.Context, name, defValue string) string {
return param
}
func DefaultContext(c echo.Context, name, defValue string) string {
value, ok := c.Get(name).(string)
func DefaultContext[T any](c echo.Context, name string, defValue T) T {
value, ok := c.Get(name).(T)
if !ok {
return defValue
}

View File

@ -12,7 +12,7 @@
// - Auth0 access token
// - Basic auth
//
// The path prefix /api/login is treated specially in order to accomodate
// The path prefix /api/login is treated specially in order to accommodate
// different ways of identification (UserPass, Auth0). All other /api paths
// only allow JWT as authentication method.
//
@ -202,11 +202,15 @@ func NewWithConfig(config Config) echo.MiddlewareFunc {
resource = "fs:" + resource
}
superuser := false
if identity != nil {
username = identity.Name()
superuser = identity.IsSuperuser()
}
c.Set("user", username)
c.Set("superuser", superuser)
if len(domain) == 0 {
domain = "$none"

View File

@ -537,18 +537,8 @@ func (s *server) setRoutesV3(v3 *echo.Group) {
v3.POST("/iam/user", s.v3handler.iam.AddUser)
v3.GET("/iam/user/:name", s.v3handler.iam.GetUser)
v3.PUT("/iam/user/:name", s.v3handler.iam.UpdateUser)
v3.PUT("/iam/user/:name/policy", s.v3handler.iam.UpdateUserPolicies)
v3.DELETE("/iam/user/:name", s.v3handler.iam.RemoveUser)
v3.POST("/iam/group", s.v3handler.iam.AddGroup)
v3.GET("/iam/group", s.v3handler.iam.ListGroups)
v3.GET("/iam/group/:group", s.v3handler.iam.GetGroup)
v3.DELETE("/iam/group/:group", s.v3handler.iam.RemoveGroup)
v3.POST("/iam/group/:group/user", s.v3handler.iam.AddGroupUser)
v3.GET("/iam/group/:group/user", s.v3handler.iam.ListGroupUsers)
v3.GET("/iam/group/:group/user/:name", s.v3handler.iam.GetGroupUser)
v3.PUT("/iam/group/:group/user/:name", s.v3handler.iam.UpdateGroupUser)
v3.DELETE("/iam/group/:group/user/:name", s.v3handler.iam.RemoveGroupUser)
}
// v3 Restreamer

View File

@ -130,7 +130,7 @@ func (am *access) ListPolicies(name, domain, resource string, actions []string)
}
func (am *access) HasDomain(name string) bool {
groups := am.adapter.getAllGroups()
groups := am.adapter.getAllDomains()
for _, g := range groups {
if g == name {
@ -142,7 +142,7 @@ func (am *access) HasDomain(name string) bool {
}
func (am *access) ListDomains() []string {
return am.adapter.getAllGroups()
return am.adapter.getAllDomains()
}
func (am *access) Enforce(name, domain, resource, action string) (bool, string) {

View File

@ -20,7 +20,7 @@ type adapter struct {
fs fs.Filesystem
filePath string
logger log.Logger
groups []Group
domains []Domain
lock sync.Mutex
}
@ -56,7 +56,7 @@ func (a *adapter) LoadPolicy(model model.Model) error {
func (a *adapter) loadPolicyFile(model model.Model) error {
if _, err := a.fs.Stat(a.filePath); os.IsNotExist(err) {
a.groups = []Group{}
a.domains = []Domain{}
return nil
}
@ -65,18 +65,18 @@ func (a *adapter) loadPolicyFile(model model.Model) error {
return err
}
groups := []Group{}
domains := []Domain{}
err = json.Unmarshal(data, &groups)
err = json.Unmarshal(data, &domains)
if err != nil {
return err
}
rule := [5]string{}
for _, group := range groups {
for _, domain := range domains {
rule[0] = "p"
rule[2] = group.Name
for name, roles := range group.Roles {
rule[2] = domain.Name
for name, roles := range domain.Roles {
rule[1] = "role:" + name
for _, role := range roles {
rule[3] = role.Resource
@ -88,7 +88,7 @@ func (a *adapter) loadPolicyFile(model model.Model) error {
}
}
for _, policy := range group.Policies {
for _, policy := range domain.Policies {
rule[1] = policy.Username
rule[3] = policy.Resource
rule[4] = formatActions(policy.Actions)
@ -99,9 +99,9 @@ func (a *adapter) loadPolicyFile(model model.Model) error {
}
rule[0] = "g"
rule[3] = group.Name
rule[3] = domain.Name
for _, ug := range group.UserRoles {
for _, ug := range domain.UserRoles {
rule[1] = ug.Username
rule[2] = "role:" + ug.Role
@ -111,7 +111,7 @@ func (a *adapter) loadPolicyFile(model model.Model) error {
}
}
a.groups = groups
a.domains = domains
return nil
}
@ -121,7 +121,7 @@ func (a *adapter) importPolicy(model model.Model, rule []string) error {
copy(copiedRule, rule)
a.logger.Debug().WithFields(log.Fields{
"username": copiedRule[1],
"subject": copiedRule[1],
"domain": copiedRule[2],
"resource": copiedRule[3],
"actions": copiedRule[4],
@ -149,7 +149,7 @@ func (a *adapter) SavePolicy(model model.Model) error {
}
func (a *adapter) savePolicyFile() error {
jsondata, err := json.MarshalIndent(a.groups, "", " ")
jsondata, err := json.MarshalIndent(a.domains, "", " ")
if err != nil {
return err
}
@ -211,7 +211,7 @@ func (a *adapter) addPolicy(ptype string, rule []string) error {
actions = formatActions(rule[3])
a.logger.Debug().WithFields(log.Fields{
"username": username,
"subject": username,
"domain": domain,
"resource": resource,
"actions": actions,
@ -222,47 +222,47 @@ func (a *adapter) addPolicy(ptype string, rule []string) error {
domain = rule[2]
a.logger.Debug().WithFields(log.Fields{
"username": username,
"role": role,
"domain": domain,
"subject": username,
"role": role,
"domain": domain,
}).Log("adding role mapping")
} else {
return fmt.Errorf("unknown ptype: %s", ptype)
}
var group *Group = nil
for i := range a.groups {
if a.groups[i].Name == domain {
group = &a.groups[i]
var dom *Domain = nil
for i := range a.domains {
if a.domains[i].Name == domain {
dom = &a.domains[i]
break
}
}
if group == nil {
g := Group{
if dom == nil {
g := Domain{
Name: domain,
Roles: map[string][]Role{},
UserRoles: []MapUserRole{},
Policies: []GroupPolicy{},
Policies: []DomainPolicy{},
}
a.groups = append(a.groups, g)
group = &a.groups[len(a.groups)-1]
a.domains = append(a.domains, g)
dom = &a.domains[len(a.domains)-1]
}
if ptype == "p" {
if strings.HasPrefix(username, "role:") {
if group.Roles == nil {
group.Roles = make(map[string][]Role)
if dom.Roles == nil {
dom.Roles = make(map[string][]Role)
}
role := strings.TrimPrefix(username, "role:")
group.Roles[role] = append(group.Roles[role], Role{
dom.Roles[role] = append(dom.Roles[role], Role{
Resource: resource,
Actions: actions,
})
} else {
group.Policies = append(group.Policies, GroupPolicy{
dom.Policies = append(dom.Policies, DomainPolicy{
Username: username,
Role: Role{
Resource: resource,
@ -271,7 +271,7 @@ func (a *adapter) addPolicy(ptype string, rule []string) error {
})
}
} else {
group.UserRoles = append(group.UserRoles, MapUserRole{
dom.UserRoles = append(dom.UserRoles, MapUserRole{
Username: username,
Role: strings.TrimPrefix(role, "role:"),
})
@ -288,7 +288,7 @@ func (a *adapter) hasPolicy(ptype string, rule []string) (bool, error) {
var actions string
if ptype == "p" {
if len(rule) != 4 {
if len(rule) < 4 {
return false, fmt.Errorf("invalid rule length. must be 'user/role, domain, resource, actions'")
}
@ -297,6 +297,10 @@ func (a *adapter) hasPolicy(ptype string, rule []string) (bool, error) {
resource = rule[2]
actions = formatActions(rule[3])
} else if ptype == "g" {
if len(rule) < 3 {
return false, fmt.Errorf("invalid rule length. must be 'user, role, domain'")
}
username = rule[0]
role = rule[1]
domain = rule[2]
@ -304,16 +308,16 @@ func (a *adapter) hasPolicy(ptype string, rule []string) (bool, error) {
return false, fmt.Errorf("unknown ptype: %s", ptype)
}
var group *Group = nil
for i := range a.groups {
if a.groups[i].Name == domain {
group = &a.groups[i]
var dom *Domain = nil
for i := range a.domains {
if a.domains[i].Name == domain {
dom = &a.domains[i]
break
}
}
if group == nil {
// if we can't find any group with that name, then the policy doesn't exist
if dom == nil {
// if we can't find any domain with that name, then the policy doesn't exist
return false, nil
}
@ -325,7 +329,7 @@ func (a *adapter) hasPolicy(ptype string, rule []string) (bool, error) {
}
if isRole {
roles, ok := group.Roles[username]
roles, ok := dom.Roles[username]
if !ok {
// unknown role, policy doesn't exist
return false, nil
@ -337,7 +341,7 @@ func (a *adapter) hasPolicy(ptype string, rule []string) (bool, error) {
}
}
} else {
for _, p := range group.Policies {
for _, p := range dom.Policies {
if p.Username == username && p.Resource == resource && formatActions(p.Actions) == actions {
return true, nil
}
@ -345,7 +349,7 @@ func (a *adapter) hasPolicy(ptype string, rule []string) (bool, error) {
}
} else {
role = strings.TrimPrefix(role, "role:")
for _, user := range group.UserRoles {
for _, user := range dom.UserRoles {
if user.Username == username && user.Role == role {
return true, nil
}
@ -407,7 +411,7 @@ func (a *adapter) removePolicy(ptype string, rule []string) error {
actions = formatActions(rule[3])
a.logger.Debug().WithFields(log.Fields{
"username": username,
"subject": username,
"domain": domain,
"resource": resource,
"actions": actions,
@ -418,18 +422,18 @@ func (a *adapter) removePolicy(ptype string, rule []string) error {
domain = rule[2]
a.logger.Debug().WithFields(log.Fields{
"username": username,
"role": role,
"domain": domain,
"subject": username,
"role": role,
"domain": domain,
}).Log("adding role mapping")
} else {
return fmt.Errorf("unknown ptype: %s", ptype)
}
var group *Group = nil
for i := range a.groups {
if a.groups[i].Name == domain {
group = &a.groups[i]
var dom *Domain = nil
for i := range a.domains {
if a.domains[i].Name == domain {
dom = &a.domains[i]
break
}
}
@ -442,7 +446,7 @@ func (a *adapter) removePolicy(ptype string, rule []string) error {
}
if isRole {
roles := group.Roles[username]
roles := dom.Roles[username]
newRoles := []Role{}
@ -454,11 +458,11 @@ func (a *adapter) removePolicy(ptype string, rule []string) error {
newRoles = append(newRoles, role)
}
group.Roles[username] = newRoles
dom.Roles[username] = newRoles
} else {
policies := []GroupPolicy{}
policies := []DomainPolicy{}
for _, p := range group.Policies {
for _, p := range dom.Policies {
if p.Username == username && p.Resource == resource && formatActions(p.Actions) == actions {
continue
}
@ -466,14 +470,14 @@ func (a *adapter) removePolicy(ptype string, rule []string) error {
policies = append(policies, p)
}
group.Policies = policies
dom.Policies = policies
}
} else {
role = strings.TrimPrefix(role, "role:")
users := []MapUserRole{}
for _, user := range group.UserRoles {
for _, user := range dom.UserRoles {
if user.Username == username && user.Role == role {
continue
}
@ -481,22 +485,22 @@ func (a *adapter) removePolicy(ptype string, rule []string) error {
users = append(users, user)
}
group.UserRoles = users
dom.UserRoles = users
}
// Remove the group if there are no rules and policies
if len(group.Roles) == 0 && len(group.UserRoles) == 0 && len(group.Policies) == 0 {
groups := []Group{}
if len(dom.Roles) == 0 && len(dom.UserRoles) == 0 && len(dom.Policies) == 0 {
groups := []Domain{}
for _, g := range a.groups {
if g.Name == group.Name {
for _, g := range a.domains {
if g.Name == dom.Name {
continue
}
groups = append(groups, g)
}
a.groups = groups
a.domains = groups
}
return nil
@ -507,25 +511,25 @@ func (a *adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int,
return fmt.Errorf("not implemented")
}
func (a *adapter) getAllGroups() []string {
func (a *adapter) getAllDomains() []string {
names := []string{}
for _, group := range a.groups {
if group.Name[0] == '$' {
for _, domain := range a.domains {
if domain.Name[0] == '$' {
continue
}
names = append(names, group.Name)
names = append(names, domain.Name)
}
return names
}
type Group struct {
type Domain struct {
Name string `json:"name"`
Roles map[string][]Role `json:"roles"`
UserRoles []MapUserRole `json:"userroles"`
Policies []GroupPolicy `json:"policies"`
Policies []DomainPolicy `json:"policies"`
}
type Role struct {
@ -538,7 +542,7 @@ type MapUserRole struct {
Role string `json:"role"`
}
type GroupPolicy struct {
type DomainPolicy struct {
Username string `json:"username"`
Role
}

View File

@ -18,18 +18,18 @@ func TestAddPolicy(t *testing.T) {
err = a.AddPolicy("p", "p", []string{"foobar", "group", "resource", "action"})
require.NoError(t, err)
require.Equal(t, 1, len(a.groups))
require.Equal(t, 1, len(a.domains))
data, err := memfs.ReadFile("/policy.json")
require.NoError(t, err)
g := []Group{}
g := []Domain{}
err = json.Unmarshal(data, &g)
require.NoError(t, err)
require.Equal(t, "group", g[0].Name)
require.Equal(t, 1, len(g[0].Policies))
require.Equal(t, GroupPolicy{
require.Equal(t, DomainPolicy{
Username: "foobar",
Role: Role{
Resource: "resource",
@ -62,24 +62,24 @@ func TestRemovePolicy(t *testing.T) {
})
require.NoError(t, err)
require.Equal(t, 1, len(a.groups))
require.Equal(t, 2, len(a.groups[0].Policies))
require.Equal(t, 1, len(a.domains))
require.Equal(t, 2, len(a.domains[0].Policies))
err = a.RemovePolicy("p", "p", []string{"foobar1", "group", "resource1", "action1"})
require.NoError(t, err)
require.Equal(t, 1, len(a.groups))
require.Equal(t, 1, len(a.groups[0].Policies))
require.Equal(t, 1, len(a.domains))
require.Equal(t, 1, len(a.domains[0].Policies))
err = a.RemovePolicy("p", "p", []string{"foobar2", "group", "resource2", "action2"})
require.NoError(t, err)
require.Equal(t, 0, len(a.groups))
require.Equal(t, 0, len(a.domains))
data, err := memfs.ReadFile("/policy.json")
require.NoError(t, err)
g := []Group{}
g := []Domain{}
err = json.Unmarshal(data, &g)
require.NoError(t, err)

View File

@ -17,22 +17,7 @@ func resourceMatch(request, domain, policy string) bool {
var match bool
var err error
if reqPrefix == "api" {
match, err = globMatch(polResource, reqResource, rune('/'))
if err != nil {
return false
}
} else if reqPrefix == "fs" {
match, err = globMatch(polResource, reqResource, rune('/'))
if err != nil {
return false
}
} else if reqPrefix == "rtmp" {
match, err = globMatch(polResource, reqResource, rune('/'))
if err != nil {
return false
}
} else if reqPrefix == "srt" {
if reqPrefix == "api" || reqPrefix == "fs" || reqPrefix == "rtmp" || reqPrefix == "srt" {
match, err = globMatch(polResource, reqResource, rune('/'))
if err != nil {
return false
@ -83,17 +68,12 @@ func actionMatchFunc(args ...interface{}) (interface{}, error) {
}
func getPrefix(s string) (string, string) {
splits := strings.SplitN(s, ":", 2)
if len(splits) == 0 {
return "", ""
prefix, resource, found := strings.Cut(s, ":")
if !found {
return "", s
}
if len(splits) == 1 {
return "", splits[0]
}
return splits[0], splits[1]
return prefix, resource
}
func globMatch(pattern, name string, separators ...rune) (bool, error) {