diff --git a/api/http/handler/resourcecontrols/resourcecontrol_update.go b/api/http/handler/resourcecontrols/resourcecontrol_update.go index 1a5aa29d8..c2a580cb0 100644 --- a/api/http/handler/resourcecontrols/resourcecontrol_update.go +++ b/api/http/handler/resourcecontrols/resourcecontrol_update.go @@ -43,6 +43,15 @@ func (handler *Handler) resourceControlUpdate(w http.ResponseWriter, r *http.Req return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a resource control with with the specified identifier inside the database", err} } + securityContext, err := security.RetrieveRestrictedRequestContext(r) + if err != nil { + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve info from request context", err} + } + + if !security.AuthorizedResourceControlAccess(resourceControl, securityContext) { + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update the resource control", portainer.ErrResourceAccessDenied} + } + resourceControl.AdministratorsOnly = payload.AdministratorsOnly var userAccesses = make([]portainer.UserResourceAccess, 0) @@ -65,11 +74,6 @@ func (handler *Handler) resourceControlUpdate(w http.ResponseWriter, r *http.Req } resourceControl.TeamAccesses = teamAccesses - securityContext, err := security.RetrieveRestrictedRequestContext(r) - if err != nil { - return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve info from request context", err} - } - if !security.AuthorizedResourceControlUpdate(resourceControl, securityContext) { return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update the resource control", portainer.ErrResourceAccessDenied} } diff --git a/api/http/security/authorization.go b/api/http/security/authorization.go index a2efe216a..155869632 100644 --- a/api/http/security/authorization.go +++ b/api/http/security/authorization.go @@ -40,6 +40,43 @@ func AuthorizedResourceControlDeletion(resourceControl *portainer.ResourceContro return false } +// AuthorizedResourceControlAccess checks whether the user can alter an existing resource control. +func AuthorizedResourceControlAccess(resourceControl *portainer.ResourceControl, context *RestrictedRequestContext) bool { + if context.IsAdmin { + return true + } + + if resourceControl.AdministratorsOnly { + return false + } + + authorizedTeamAccess := false + for _, access := range resourceControl.TeamAccesses { + for _, membership := range context.UserMemberships { + if membership.TeamID == access.TeamID { + authorizedTeamAccess = true + break + } + } + } + if !authorizedTeamAccess { + return false + } + + authorizedUserAccess := false + for _, access := range resourceControl.UserAccesses { + if context.UserID == access.UserID { + authorizedUserAccess = true + break + } + } + if !authorizedUserAccess { + return false + } + + return true +} + // AuthorizedResourceControlUpdate ensure that the user can update a resource control object. // It reuses the creation restrictions and adds extra checks. // A non-administrator user cannot update a resource control where: @@ -56,7 +93,9 @@ func AuthorizedResourceControlUpdate(resourceControl *portainer.ResourceControl, // AuthorizedResourceControlCreation ensure that the user can create a resource control object. // A non-administrator user cannot create a resource control where: // * the AdministratorsOnly flag is set +// * he wants to create a resource control without any user/team accesses // * he wants to add more than one user in the user accesses +// * he wants tp add a user in the user accesses that is not corresponding to its id // * he wants to add a team he is not a member of func AuthorizedResourceControlCreation(resourceControl *portainer.ResourceControl, context *RestrictedRequestContext) bool { if context.IsAdmin { @@ -69,6 +108,11 @@ func AuthorizedResourceControlCreation(resourceControl *portainer.ResourceContro userAccessesCount := len(resourceControl.UserAccesses) teamAccessesCount := len(resourceControl.TeamAccesses) + + if userAccessesCount == 0 && teamAccessesCount == 0 { + return false + } + if userAccessesCount > 1 || (userAccessesCount == 1 && teamAccessesCount == 1) { return false }