Reduce IAM API to only user and policies
This commit is contained in:
parent
f03e2ca5c5
commit
6f831fd190
@ -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"`
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
140
iam/adapter.go
140
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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user