From 6f831fd1900b65f779adc46bce5c19ab44a851db Mon Sep 17 00:00:00 2001 From: Ingo Oppermann Date: Wed, 17 May 2023 18:19:23 +0200 Subject: [PATCH] Reduce IAM API to only user and policies --- http/api/iam.go | 2 +- http/handler/api/iam.go | 192 +++++++++++++++++++++++++++++++------ http/handler/util/util.go | 4 +- http/middleware/iam/iam.go | 6 +- http/server.go | 12 +-- iam/access.go | 4 +- iam/adapter.go | 140 ++++++++++++++------------- iam/adapter_test.go | 18 ++-- iam/functions.go | 30 +----- 9 files changed, 258 insertions(+), 150 deletions(-) diff --git a/http/api/iam.go b/http/api/iam.go index 6f9ce464..be640bd2 100644 --- a/http/api/iam.go +++ b/http/api/iam.go @@ -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"` } diff --git a/http/handler/api/iam.go b/http/handler/api/iam.go index 6a7a77bc..3957ab53 100644 --- a/http/handler/api/iam.go +++ b/http/handler/api/iam.go @@ -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) diff --git a/http/handler/util/util.go b/http/handler/util/util.go index 5219aa59..d9b99b4b 100644 --- a/http/handler/util/util.go +++ b/http/handler/util/util.go @@ -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 } diff --git a/http/middleware/iam/iam.go b/http/middleware/iam/iam.go index ebb8e9f1..8a1b8d75 100644 --- a/http/middleware/iam/iam.go +++ b/http/middleware/iam/iam.go @@ -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" diff --git a/http/server.go b/http/server.go index fe552729..ccb7293f 100644 --- a/http/server.go +++ b/http/server.go @@ -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 diff --git a/iam/access.go b/iam/access.go index 73acb791..9f754504 100644 --- a/iam/access.go +++ b/iam/access.go @@ -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) { diff --git a/iam/adapter.go b/iam/adapter.go index 524925a1..74502cef 100644 --- a/iam/adapter.go +++ b/iam/adapter.go @@ -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 } diff --git a/iam/adapter_test.go b/iam/adapter_test.go index de80be84..41c613e7 100644 --- a/iam/adapter_test.go +++ b/iam/adapter_test.go @@ -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) diff --git a/iam/functions.go b/iam/functions.go index 26261904..41dce32d 100644 --- a/iam/functions.go +++ b/iam/functions.go @@ -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) {