diff --git a/api/auth/user/UserRestHandler.go b/api/auth/user/UserRestHandler.go index 037de4166b9..c4f892a332b 100644 --- a/api/auth/user/UserRestHandler.go +++ b/api/auth/user/UserRestHandler.go @@ -19,9 +19,10 @@ package user import ( "encoding/json" "errors" - "fmt" util2 "github.com/devtron-labs/devtron/api/auth/user/util" "github.com/devtron-labs/devtron/pkg/auth/user/helper" + "github.com/devtron-labs/devtron/pkg/auth/user/repository" + "github.com/go-pg/pg" "github.com/gorilla/schema" "net/http" "strconv" @@ -34,7 +35,6 @@ import ( user2 "github.com/devtron-labs/devtron/pkg/auth/user" bean2 "github.com/devtron-labs/devtron/pkg/auth/user/bean" "github.com/devtron-labs/devtron/util/response" - "github.com/go-pg/pg" "github.com/gorilla/mux" "go.uber.org/zap" "gopkg.in/go-playground/validator.v9" @@ -129,68 +129,20 @@ func (handler UserRestHandlerImpl) CreateUser(w http.ResponseWriter, r *http.Req // RBAC enforcer applying token := r.Header.Get("token") - isActionUserSuperAdmin := false - if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*"); ok { - isActionUserSuperAdmin = true + isAuthorised, err := handler.checkRBACForUserCreate(token, userInfo.SuperAdmin, userInfo.RoleFilters, userInfo.UserRoleGroup) + if err != nil { + common.WriteJsonResp(w, err, "", http.StatusInternalServerError) + return } - if userInfo.RoleFilters != nil && len(userInfo.RoleFilters) > 0 { - for _, filter := range userInfo.RoleFilters { - if filter.AccessType == bean.APP_ACCESS_TYPE_HELM && !isActionUserSuperAdmin { - response.WriteResponse(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized")) - return - } - if len(filter.Team) > 0 { - if ok := handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, filter.Team); !ok { - response.WriteResponse(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized")) - return - } - } - if filter.Entity == bean.CLUSTER_ENTITIY { - if ok := handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth); !ok { - response.WriteResponse(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized")) - return - } - } - } - } else { - if ok := handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, "*"); !ok { - response.WriteResponse(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized")) - return - } + if !isAuthorised { + response.WriteResponse(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized")) + return } - // auth check inside groups - if len(userInfo.UserRoleGroup) > 0 { - groupRoles, err := handler.roleGroupService.FetchRolesForUserRoleGroups(userInfo.UserRoleGroup) - if err != nil && err != pg.ErrNoRows { - handler.logger.Errorw("service err, UpdateUser", "err", err, "payload", userInfo) - common.WriteJsonResp(w, err, "", http.StatusInternalServerError) - return - } - - if len(groupRoles) > 0 { - for _, groupRole := range groupRoles { - if groupRole.AccessType == bean.APP_ACCESS_TYPE_HELM && !isActionUserSuperAdmin { - response.WriteResponse(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized")) - return - } - if len(groupRole.Team) > 0 { - if ok := handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, groupRole.Team); !ok { - response.WriteResponse(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized")) - return - } - } - } - } else { - if ok := handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, "*"); !ok { - response.WriteResponse(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized")) - return - } - } - } //RBAC enforcer Ends - - res, restrictedGroups, err := handler.userService.CreateUser(&userInfo, token, handler.CheckManagerAuth) + //In create req, we also check if any email exists already. If yes, then in that case we go on and merge existing roles and groups with the ones in request + //but rbac is only checked on create request roles and groups as existing roles and groups are assumed to be checked when created/updated before + res, err := handler.userService.CreateUser(&userInfo) if err != nil { handler.logger.Errorw("service err, CreateUser", "err", err, "payload", userInfo) if _, ok := err.(*util.ApiError); ok { @@ -201,23 +153,8 @@ func (handler UserRestHandlerImpl) CreateUser(w http.ResponseWriter, r *http.Req } return } - - if len(restrictedGroups) == 0 { - common.WriteJsonResp(w, err, res, http.StatusOK) - } else { - errorMessageForGroupsWithoutSuperAdmin, errorMessageForGroupsWithSuperAdmin := helper.CreateErrorMessageForUserRoleGroups(restrictedGroups) - - if len(restrictedGroups) != len(userInfo.UserRoleGroup) { - // warning - message := fmt.Errorf("User permissions added partially. %s%s", errorMessageForGroupsWithoutSuperAdmin, errorMessageForGroupsWithSuperAdmin) - common.WriteJsonResp(w, message, nil, http.StatusExpectationFailed) - - } else { - //error - message := fmt.Errorf("Permission could not be added. %s%s", errorMessageForGroupsWithoutSuperAdmin, errorMessageForGroupsWithSuperAdmin) - common.WriteJsonResp(w, message, nil, http.StatusBadRequest) - } - } + common.WriteJsonResp(w, err, res, http.StatusOK) + return } func (handler UserRestHandlerImpl) UpdateUser(w http.ResponseWriter, r *http.Request) { @@ -255,31 +192,29 @@ func (handler UserRestHandlerImpl) UpdateUser(w http.ResponseWriter, r *http.Req return } - res, rolesChanged, groupsModified, restrictedGroups, err := handler.userService.UpdateUser(&userInfo, token, handler.CheckManagerAuth) - + res, err := handler.userService.UpdateUser(&userInfo, token, handler.checkRBACForUserUpdate) if err != nil { handler.logger.Errorw("service err, UpdateUser", "err", err, "payload", userInfo) common.WriteJsonResp(w, err, "", http.StatusInternalServerError) return } - - if len(restrictedGroups) == 0 { - common.WriteJsonResp(w, err, res, http.StatusOK) - } else { - errorMessageForGroupsWithoutSuperAdmin, errorMessageForGroupsWithSuperAdmin := helper.CreateErrorMessageForUserRoleGroups(restrictedGroups) - - if rolesChanged || groupsModified { - // warning - message := fmt.Errorf("User permissions updated partially. %s%s", errorMessageForGroupsWithoutSuperAdmin, errorMessageForGroupsWithSuperAdmin) - common.WriteJsonResp(w, message, nil, http.StatusExpectationFailed) - - } else { - //error - message := fmt.Errorf("Permission could not be added/removed. %s%s", errorMessageForGroupsWithoutSuperAdmin, errorMessageForGroupsWithSuperAdmin) - common.WriteJsonResp(w, message, nil, http.StatusBadRequest) - } - } - + common.WriteJsonResp(w, err, res, http.StatusOK) + //if len(restrictedGroups) == 0 { + // common.WriteJsonResp(w, err, res, http.StatusOK) + //} else { + // errorMessageForGroupsWithoutSuperAdmin, errorMessageForGroupsWithSuperAdmin := helper.CreateErrorMessageForUserRoleGroups(restrictedGroups) + // + // if rolesChanged || groupsModified { + // // warning + // message := fmt.Errorf("User permissions updated partially. %s%s", errorMessageForGroupsWithoutSuperAdmin, errorMessageForGroupsWithSuperAdmin) + // common.WriteJsonResp(w, message, nil, http.StatusExpectationFailed) + // + // } else { + // //error + // message := fmt.Errorf("Permission could not be added/removed. %s%s", errorMessageForGroupsWithoutSuperAdmin, errorMessageForGroupsWithSuperAdmin) + // common.WriteJsonResp(w, message, nil, http.StatusBadRequest) + // } + //} } func (handler UserRestHandlerImpl) GetById(w http.ResponseWriter, r *http.Request) { @@ -410,6 +345,7 @@ func (handler UserRestHandlerImpl) GetAllV2(w http.ResponseWriter, r *http.Reque common.WriteJsonResp(w, err, res, http.StatusOK) } + func (handler UserRestHandlerImpl) GetAll(w http.ResponseWriter, r *http.Request) { userId, err := handler.userService.GetLoggedInUser(r) if userId == 0 || err != nil { @@ -693,41 +629,16 @@ func (handler UserRestHandlerImpl) CreateRoleGroup(w http.ResponseWriter, r *htt // RBAC enforcer applying token := r.Header.Get("token") - isActionUserSuperAdmin := false - if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*"); ok { - isActionUserSuperAdmin = true + isAuthorised, err := handler.checkRBACForUserCreate(token, request.SuperAdmin, request.RoleFilters, nil) + if err != nil { + common.WriteJsonResp(w, err, "", http.StatusInternalServerError) + return } - - if request.SuperAdmin && !isActionUserSuperAdmin { - common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden) + if !isAuthorised { + response.WriteResponse(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized")) return } - if request.RoleFilters != nil && len(request.RoleFilters) > 0 { - for _, filter := range request.RoleFilters { - if filter.AccessType == bean.APP_ACCESS_TYPE_HELM && !isActionUserSuperAdmin { - common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden) - return - } - if len(filter.Team) > 0 { - if ok := handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, filter.Team); !ok { - common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden) - return - } - } - if filter.Entity == bean.CLUSTER_ENTITIY && !isActionUserSuperAdmin { - if isValidAuth := handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth); !isValidAuth { - common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden) - return - } - } - } - } else { - if ok := handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, "*"); !ok { - common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden) - return - } - } //RBAC enforcer Ends err = handler.validator.Struct(request) if err != nil { @@ -787,7 +698,7 @@ func (handler UserRestHandlerImpl) UpdateRoleGroup(w http.ResponseWriter, r *htt return } - res, err := handler.roleGroupService.UpdateRoleGroup(&request, token, handler.CheckManagerAuth) + res, err := handler.roleGroupService.UpdateRoleGroup(&request, token, handler.checkRBACForRoleGroupUpdate) if err != nil { handler.logger.Errorw("service err, UpdateRoleGroup", "err", err, "payload", request) common.WriteJsonResp(w, err, "", http.StatusInternalServerError) @@ -996,31 +907,15 @@ func (handler UserRestHandlerImpl) DeleteRoleGroup(w http.ResponseWriter, r *htt common.WriteJsonResp(w, err, "", http.StatusInternalServerError) return } - token := r.Header.Get("token") - isActionUserSuperAdmin := false - if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*"); ok { - isActionUserSuperAdmin = true + isAuthorised, err := handler.checkRBACForRoleGroupDelete(token, userGroup.RoleFilters) + if err != nil { + common.WriteJsonResp(w, err, "", http.StatusInternalServerError) + return } - if userGroup.RoleFilters != nil && len(userGroup.RoleFilters) > 0 { - for _, filter := range userGroup.RoleFilters { - if filter.AccessType == bean.APP_ACCESS_TYPE_HELM && !isActionUserSuperAdmin { - common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden) - return - } - if len(filter.Team) > 0 { - if ok := handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionDelete, filter.Team); !ok { - common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden) - return - } - } - if filter.Entity == bean.CLUSTER_ENTITIY { - if isValidAuth := handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth); !isValidAuth { - common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden) - return - } - } - } + if !isAuthorised { + response.WriteResponse(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized")) + return } //RBAC enforcer Ends @@ -1217,3 +1112,236 @@ func (handler UserRestHandlerImpl) CheckManagerAuth(resource, token string, obje return true } + +func (handler UserRestHandlerImpl) checkRBACForUserCreate(token string, requestSuperAdmin bool, roleFilters []bean.RoleFilter, + roleGroups []bean.UserRoleGroup) (isAuthorised bool, err error) { + isActionUserSuperAdmin := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*") + if requestSuperAdmin && !isActionUserSuperAdmin { + return false, nil + } + isAuthorised = isActionUserSuperAdmin + if !isAuthorised { + if roleFilters != nil && len(roleFilters) > 0 { //auth check inside roleFilters + for _, filter := range roleFilters { + switch { + case filter.AccessType == bean.APP_ACCESS_TYPE_HELM || filter.Entity == bean2.EntityJobs: + isAuthorised = isActionUserSuperAdmin + case len(filter.Team) > 0: + isAuthorised = handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, filter.Team) + case filter.Entity == bean.CLUSTER_ENTITIY: + isAuthorised = handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth) + case filter.Entity == bean.CHART_GROUP_ENTITY && len(roleFilters) == 1: //if only chartGroup entity is present in request then access will be judged through super-admin access + isAuthorised = isActionUserSuperAdmin + case filter.Entity == bean.CHART_GROUP_ENTITY && len(roleFilters) > 1: //if entities apart from chartGroup entity are present, not checking chartGroup access + isAuthorised = true + default: + isAuthorised = false + } + if !isAuthorised { + break + } + } + } + if len(roleGroups) > 0 { // auth check inside groups + groupRoles, err := handler.roleGroupService.FetchRolesForUserRoleGroups(roleGroups) + if err != nil && err != pg.ErrNoRows { + handler.logger.Errorw("service err, UpdateUser", "err", err, "payload", roleGroups) + return false, err + } + if len(groupRoles) > 0 { + for _, groupRole := range groupRoles { + switch { + case groupRole.Action == bean.ACTION_SUPERADMIN: + isAuthorised = isActionUserSuperAdmin + case groupRole.AccessType == bean.APP_ACCESS_TYPE_HELM || groupRole.Entity == bean2.EntityJobs: + isAuthorised = isActionUserSuperAdmin + case len(groupRole.Team) > 0: + isAuthorised = handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, groupRole.Team) + case groupRole.Entity == bean.CLUSTER_ENTITIY: + isAuthorised = handler.userCommonService.CheckRbacForClusterEntity(groupRole.Cluster, groupRole.Namespace, groupRole.Group, groupRole.Kind, groupRole.Resource, token, handler.CheckManagerAuth) + case groupRole.Entity == bean.CHART_GROUP_ENTITY && len(groupRoles) == 1: //if only chartGroup entity is present in request then access will be judged through super-admin access + isAuthorised = isActionUserSuperAdmin + case groupRole.Entity == bean.CHART_GROUP_ENTITY && len(groupRoles) > 1: //if entities apart from chartGroup entity are present, not checking chartGroup access + isAuthorised = true + default: + isAuthorised = false + } + if !isAuthorised { + break + } + } + } else { + isAuthorised = false + } + } + } + return isAuthorised, nil +} + +func (handler UserRestHandlerImpl) checkRBACForUserUpdate(token string, userInfo *bean.UserInfo, isUserAlreadySuperAdmin bool, eliminatedRoleFilters, + eliminatedGroupRoles []*repository.RoleModel) (isAuthorised bool, err error) { + isActionUserSuperAdmin := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*") + requestSuperAdmin := userInfo.SuperAdmin + if (requestSuperAdmin || isUserAlreadySuperAdmin) && !isActionUserSuperAdmin { + //if user is going to be provided with super-admin access or already a super-admin then the action user should be a super-admin + return false, nil + } + roleFilters := userInfo.RoleFilters + roleGroups := userInfo.UserRoleGroup + isAuthorised = isActionUserSuperAdmin + eliminatedRolesToBeChecked := append(eliminatedRoleFilters, eliminatedGroupRoles...) + if !isAuthorised { + if roleFilters != nil && len(roleFilters) > 0 { //auth check inside roleFilters + for _, filter := range roleFilters { + switch { + case filter.AccessType == bean.APP_ACCESS_TYPE_HELM || filter.Entity == bean2.EntityJobs: + isAuthorised = isActionUserSuperAdmin + case len(filter.Team) > 0: + isAuthorised = handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, filter.Team) + case filter.Entity == bean.CLUSTER_ENTITIY: + isAuthorised = handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth) + case filter.Entity == bean.CHART_GROUP_ENTITY: + isAuthorised = true + default: + isAuthorised = false + } + if !isAuthorised { + break + } + } + } + if eliminatedRolesToBeChecked != nil && len(eliminatedRolesToBeChecked) > 0 { + for _, filter := range eliminatedRolesToBeChecked { + switch { + case filter.AccessType == bean.APP_ACCESS_TYPE_HELM || filter.Entity == bean2.EntityJobs: + isAuthorised = isActionUserSuperAdmin + case len(filter.Team) > 0: + isAuthorised = handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, filter.Team) + case filter.Entity == bean.CLUSTER_ENTITIY: + isAuthorised = handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth) + case filter.Entity == bean.CHART_GROUP_ENTITY: + isAuthorised = true + default: + isAuthorised = false + } + if !isAuthorised { + break + } + } + } + if len(roleGroups) > 0 { // auth check inside groups + groupRoles, err := handler.roleGroupService.FetchRolesForUserRoleGroups(roleGroups) + if err != nil && err != pg.ErrNoRows { + handler.logger.Errorw("service err, UpdateUser", "err", err, "payload", roleGroups) + return false, err + } + if len(groupRoles) > 0 { + for _, groupRole := range groupRoles { + switch { + case groupRole.Action == bean.ACTION_SUPERADMIN: + isAuthorised = isActionUserSuperAdmin + case groupRole.AccessType == bean.APP_ACCESS_TYPE_HELM || groupRole.Entity == bean2.EntityJobs: + isAuthorised = isActionUserSuperAdmin + case len(groupRole.Team) > 0: + isAuthorised = handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, groupRole.Team) + case groupRole.Entity == bean.CLUSTER_ENTITIY: + isAuthorised = handler.userCommonService.CheckRbacForClusterEntity(groupRole.Cluster, groupRole.Namespace, groupRole.Group, groupRole.Kind, groupRole.Resource, token, handler.CheckManagerAuth) + case groupRole.Entity == bean.CHART_GROUP_ENTITY: + isAuthorised = true + default: + isAuthorised = false + } + if !isAuthorised { + break + } + } + } else { + isAuthorised = false + } + } + } + return isAuthorised, nil +} + +func (handler UserRestHandlerImpl) checkRBACForRoleGroupUpdate(token string, groupInfo *bean.RoleGroup, + eliminatedRoleFilters []*repository.RoleModel) (isAuthorised bool, err error) { + isActionUserSuperAdmin := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*") + requestSuperAdmin := groupInfo.SuperAdmin + if requestSuperAdmin && !isActionUserSuperAdmin { + //if user is going to be provided with super-admin access or already a super-admin then the action user should be a super-admin + return false, nil + } + isAuthorised = isActionUserSuperAdmin + if !isAuthorised { + if groupInfo.RoleFilters != nil && len(groupInfo.RoleFilters) > 0 { //auth check inside roleFilters + for _, filter := range groupInfo.RoleFilters { + switch { + case filter.Action == bean.ACTION_SUPERADMIN: + isAuthorised = isActionUserSuperAdmin + case filter.AccessType == bean.APP_ACCESS_TYPE_HELM || filter.Entity == bean2.EntityJobs: + isAuthorised = isActionUserSuperAdmin + case len(filter.Team) > 0: + isAuthorised = handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, filter.Team) + case filter.Entity == bean.CLUSTER_ENTITIY: + isAuthorised = handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth) + case filter.Entity == bean.CHART_GROUP_ENTITY: + isAuthorised = true + default: + isAuthorised = false + } + if !isAuthorised { + break + } + } + } + if len(eliminatedRoleFilters) > 0 { + for _, filter := range eliminatedRoleFilters { + switch { + case filter.AccessType == bean.APP_ACCESS_TYPE_HELM || filter.Entity == bean2.EntityJobs: + isAuthorised = isActionUserSuperAdmin + case len(filter.Team) > 0: + isAuthorised = handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, filter.Team) + case filter.Entity == bean.CLUSTER_ENTITIY: + isAuthorised = handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth) + case filter.Entity == bean.CHART_GROUP_ENTITY: + isAuthorised = true + default: + isAuthorised = false + } + if !isAuthorised { + break + } + } + } + } + return isAuthorised, nil +} + +func (handler UserRestHandlerImpl) checkRBACForRoleGroupDelete(token string, groupRoles []bean.RoleFilter) (isAuthorised bool, err error) { + isActionUserSuperAdmin := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*") + isAuthorised = isActionUserSuperAdmin + if !isAuthorised { + if groupRoles != nil && len(groupRoles) > 0 { //auth check inside roleFilters + for _, filter := range groupRoles { + switch { + case filter.Action == bean.ACTION_SUPERADMIN: + isAuthorised = isActionUserSuperAdmin + case filter.AccessType == bean.APP_ACCESS_TYPE_HELM || filter.Entity == bean2.EntityJobs: + isAuthorised = isActionUserSuperAdmin + case len(filter.Team) > 0: + isAuthorised = handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, filter.Team) + case filter.Entity == bean.CLUSTER_ENTITIY: + isAuthorised = handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth) + case filter.Entity == bean.CHART_GROUP_ENTITY: + isAuthorised = true + default: + isAuthorised = false + } + if !isAuthorised { + break + } + } + } + } + return isAuthorised, nil +} diff --git a/api/bean/UserRequest.go b/api/bean/UserRequest.go index 392c864a6ad..2ba15a68ca1 100644 --- a/api/bean/UserRequest.go +++ b/api/bean/UserRequest.go @@ -118,6 +118,7 @@ const ( USER_TYPE_API_TOKEN = "apiToken" CHART_GROUP_ENTITY = "chart-group" CLUSTER_ENTITIY = "cluster" + ACTION_SUPERADMIN = "super-admin" ) type UserListingResponse struct { diff --git a/cmd/external-app/wire_gen.go b/cmd/external-app/wire_gen.go index ab3bca55cdc..bab4ef2b470 100644 --- a/cmd/external-app/wire_gen.go +++ b/cmd/external-app/wire_gen.go @@ -1,6 +1,6 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run github.com/google/wire/cmd/wire +//go:generate go run -mod=mod github.com/google/wire/cmd/wire //go:build !wireinject // +build !wireinject diff --git a/pkg/apiToken/ApiTokenService.go b/pkg/apiToken/ApiTokenService.go index 91f49c32c1e..a6f54d7ce04 100644 --- a/pkg/apiToken/ApiTokenService.go +++ b/pkg/apiToken/ApiTokenService.go @@ -205,7 +205,7 @@ func (impl ApiTokenServiceImpl) CreateApiToken(request *openapi.CreateApiTokenRe EmailId: email, UserType: bean.USER_TYPE_API_TOKEN, } - createUserResponse, _, err := impl.userService.CreateUser(&createUserRequest, token, managerAuth) + createUserResponse, err := impl.userService.CreateUser(&createUserRequest) if err != nil { impl.logger.Errorw("error while creating user for api-token", "email", email, "error", err) return nil, err diff --git a/pkg/auth/user/RoleGroupService.go b/pkg/auth/user/RoleGroupService.go index a04493dee36..696eb208371 100644 --- a/pkg/auth/user/RoleGroupService.go +++ b/pkg/auth/user/RoleGroupService.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" "github.com/devtron-labs/devtron/pkg/auth/user/repository/helper" + "net/http" "strings" "time" @@ -37,7 +38,8 @@ import ( type RoleGroupService interface { CreateRoleGroup(request *bean.RoleGroup) (*bean.RoleGroup, error) - UpdateRoleGroup(request *bean.RoleGroup, token string, managerAuth func(resource, token string, object string) bool) (*bean.RoleGroup, error) + UpdateRoleGroup(request *bean.RoleGroup, token string, checkRBACForGroupUpdate func(token string, groupInfo *bean.RoleGroup, + eliminatedRoleFilters []*repository.RoleModel) (isAuthorised bool, err error)) (*bean.RoleGroup, error) FetchDetailedRoleGroups(req *bean.ListingRequest) ([]*bean.RoleGroup, error) FetchRoleGroupsById(id int32) (*bean.RoleGroup, error) FetchRoleGroups() ([]*bean.RoleGroup, error) @@ -136,21 +138,21 @@ func (impl RoleGroupServiceImpl) CreateRoleGroup(request *bean.RoleGroup) (*bean for index, roleFilter := range request.RoleFilters { entity := roleFilter.Entity if entity == bean.CLUSTER_ENTITIY { - policiesToBeAdded, err := impl.CreateOrUpdateRoleGroupForClusterEntity(roleFilter, request.UserId, model, nil, "", nil, tx, mapping[index]) + policiesToBeAdded, err := impl.CreateOrUpdateRoleGroupForClusterEntity(roleFilter, request.UserId, model, nil, tx, mapping[index]) policies = append(policies, policiesToBeAdded...) if err != nil { // making it non-blocking as it is being done for multiple Role filters and does not want this to be blocking. impl.logger.Errorw("error in creating updating role group for cluster entity", "err", err, "roleFilter", roleFilter) } } else if entity == bean2.EntityJobs { - policiesToBeAdded, err := impl.CreateOrUpdateRoleGroupForJobsEntity(roleFilter, request.UserId, model, nil, "", nil, tx, mapping[index]) + policiesToBeAdded, err := impl.CreateOrUpdateRoleGroupForJobsEntity(roleFilter, request.UserId, model, nil, tx, mapping[index]) policies = append(policies, policiesToBeAdded...) if err != nil { // making it non-blocking as it is being done for multiple Role filters and does not want this to be blocking. impl.logger.Errorw("error in creating updating role group for jobs entity", "err", err, "roleFilter", roleFilter) } } else { - policiesToBeAdded, err := impl.CreateOrUpdateRoleGroupForOtherEntity(roleFilter, request, model, nil, "", nil, tx, mapping[index]) + policiesToBeAdded, err := impl.CreateOrUpdateRoleGroupForOtherEntity(roleFilter, request, model, nil, tx, mapping[index]) policies = append(policies, policiesToBeAdded...) if err != nil { // making it non-blocking as it is being done for multiple Role filters and does not want this to be blocking. @@ -199,7 +201,7 @@ func (impl RoleGroupServiceImpl) CreateRoleGroup(request *bean.RoleGroup) (*bean return request, nil } -func (impl RoleGroupServiceImpl) CreateOrUpdateRoleGroupForClusterEntity(roleFilter bean.RoleFilter, userId int32, model *repository.RoleGroup, existingRoles map[int]*repository.RoleGroupRoleMapping, token string, managerAuth func(resource string, token string, object string) bool, tx *pg.Tx, capacity int) ([]casbin2.Policy, error) { +func (impl RoleGroupServiceImpl) CreateOrUpdateRoleGroupForClusterEntity(roleFilter bean.RoleFilter, userId int32, model *repository.RoleGroup, existingRoles map[int]*repository.RoleGroupRoleMapping, tx *pg.Tx, capacity int) ([]casbin2.Policy, error) { //var policiesToBeAdded []casbin2.Policy namespaces := strings.Split(roleFilter.Namespace, ",") groups := strings.Split(roleFilter.Group, ",") @@ -213,12 +215,6 @@ func (impl RoleGroupServiceImpl) CreateOrUpdateRoleGroupForClusterEntity(roleFil for _, group := range groups { for _, kind := range kinds { for _, resource := range resources { - if managerAuth != nil { - isValidAuth := impl.userCommonService.CheckRbacForClusterEntity(roleFilter.Cluster, namespace, group, kind, resource, token, managerAuth) - if !isValidAuth { - continue - } - } roleModel, err := impl.userAuthRepository.GetRoleByFilterForAllTypes(entity, "", "", "", "", accessType, roleFilter.Cluster, namespace, group, kind, resource, actionType, false, "") if err != nil { impl.logger.Errorw("error in getting new role model by filter") @@ -263,7 +259,7 @@ func (impl RoleGroupServiceImpl) CreateOrUpdateRoleGroupForClusterEntity(roleFil return policiesToBeAdded, nil } -func (impl RoleGroupServiceImpl) CreateOrUpdateRoleGroupForOtherEntity(roleFilter bean.RoleFilter, request *bean.RoleGroup, model *repository.RoleGroup, existingRoles map[int]*repository.RoleGroupRoleMapping, token string, managerAuth func(resource string, token string, object string) bool, tx *pg.Tx, capacity int) ([]casbin2.Policy, error) { +func (impl RoleGroupServiceImpl) CreateOrUpdateRoleGroupForOtherEntity(roleFilter bean.RoleFilter, request *bean.RoleGroup, model *repository.RoleGroup, existingRoles map[int]*repository.RoleGroupRoleMapping, tx *pg.Tx, capacity int) ([]casbin2.Policy, error) { actionType := roleFilter.Action accessType := roleFilter.AccessType entity := roleFilter.Entity @@ -319,7 +315,7 @@ func (impl RoleGroupServiceImpl) CreateOrUpdateRoleGroupForOtherEntity(roleFilte return policiesToBeAdded, nil } -func (impl RoleGroupServiceImpl) CreateOrUpdateRoleGroupForJobsEntity(roleFilter bean.RoleFilter, userId int32, model *repository.RoleGroup, existingRoles map[int]*repository.RoleGroupRoleMapping, token string, managerAuth func(resource string, token string, object string) bool, tx *pg.Tx, capacity int) ([]casbin2.Policy, error) { +func (impl RoleGroupServiceImpl) CreateOrUpdateRoleGroupForJobsEntity(roleFilter bean.RoleFilter, userId int32, model *repository.RoleGroup, existingRoles map[int]*repository.RoleGroupRoleMapping, tx *pg.Tx, capacity int) ([]casbin2.Policy, error) { actionType := roleFilter.Action accessType := roleFilter.AccessType entity := roleFilter.Entity @@ -372,7 +368,8 @@ func (impl RoleGroupServiceImpl) CreateOrUpdateRoleGroupForJobsEntity(roleFilter return policiesToBeAdded, nil } -func (impl RoleGroupServiceImpl) UpdateRoleGroup(request *bean.RoleGroup, token string, managerAuth func(resource, token string, object string) bool) (*bean.RoleGroup, error) { +func (impl RoleGroupServiceImpl) UpdateRoleGroup(request *bean.RoleGroup, token string, checkRBACForGroupUpdate func(token string, groupInfo *bean.RoleGroup, + eliminatedRoleFilters []*repository.RoleModel) (isAuthorised bool, err error)) (*bean.RoleGroup, error) { dbConnection := impl.roleGroupRepository.GetConnection() tx, err := dbConnection.Begin() if err != nil { @@ -404,6 +401,8 @@ func (impl RoleGroupServiceImpl) UpdateRoleGroup(request *bean.RoleGroup, token var eliminatedPolicies []casbin2.Policy capacity, mapping := impl.userCommonService.GetCapacityForRoleFilter(request.RoleFilters) var policies = make([]casbin2.Policy, 0, capacity) + var eliminatedRoleModels []*repository.RoleModel + var items []casbin2.Policy if request.SuperAdmin == false { roleGroupMappingModels, err := impl.roleGroupRepository.GetRoleGroupRoleMappingByRoleGroupId(roleGroup.Id) if err != nil { @@ -417,7 +416,7 @@ func (impl RoleGroupServiceImpl) UpdateRoleGroup(request *bean.RoleGroup, token // DELETE PROCESS STARTS - items, err := impl.userCommonService.RemoveRolesAndReturnEliminatedPoliciesForGroups(request, existingRoles, eliminatedRoles, tx, token, managerAuth) + items, eliminatedRoleModels, err = impl.userCommonService.RemoveRolesAndReturnEliminatedPoliciesForGroups(request, existingRoles, eliminatedRoles, tx) if err != nil { return nil, err } @@ -427,24 +426,16 @@ func (impl RoleGroupServiceImpl) UpdateRoleGroup(request *bean.RoleGroup, token //Adding New Policies for index, roleFilter := range request.RoleFilters { if roleFilter.Entity == bean.CLUSTER_ENTITIY { - policiesToBeAdded, err := impl.CreateOrUpdateRoleGroupForClusterEntity(roleFilter, request.UserId, roleGroup, existingRoles, token, managerAuth, tx, mapping[index]) + policiesToBeAdded, err := impl.CreateOrUpdateRoleGroupForClusterEntity(roleFilter, request.UserId, roleGroup, existingRoles, tx, mapping[index]) policies = append(policies, policiesToBeAdded...) if err != nil { impl.logger.Errorw("error in creating updating role group for cluster entity", "err", err, "roleFilter", roleFilter) } } else { - if len(roleFilter.Team) > 0 { - // check auth only for apps permission, skip for chart group - rbacObject := fmt.Sprintf("%s", roleFilter.Team) - isValidAuth := managerAuth(casbin2.ResourceUser, token, rbacObject) - if !isValidAuth { - continue - } - } switch roleFilter.Entity { case bean2.EntityJobs: { - policiesToBeAdded, err := impl.CreateOrUpdateRoleGroupForJobsEntity(roleFilter, request.UserId, roleGroup, existingRoles, token, managerAuth, tx, mapping[index]) + policiesToBeAdded, err := impl.CreateOrUpdateRoleGroupForJobsEntity(roleFilter, request.UserId, roleGroup, existingRoles, tx, mapping[index]) policies = append(policies, policiesToBeAdded...) if err != nil { impl.logger.Errorw("error in creating updating role group for jobs entity", "err", err, "roleFilter", roleFilter) @@ -452,7 +443,7 @@ func (impl RoleGroupServiceImpl) UpdateRoleGroup(request *bean.RoleGroup, token } default: { - policiesToBeAdded, err := impl.CreateOrUpdateRoleGroupForOtherEntity(roleFilter, request, roleGroup, existingRoles, token, managerAuth, tx, mapping[index]) + policiesToBeAdded, err := impl.CreateOrUpdateRoleGroupForOtherEntity(roleFilter, request, roleGroup, existingRoles, tx, mapping[index]) policies = append(policies, policiesToBeAdded...) if err != nil { impl.logger.Errorw("error in creating updating role group for other entity", "err", err, "roleFilter", roleFilter) @@ -486,6 +477,22 @@ func (impl RoleGroupServiceImpl) UpdateRoleGroup(request *bean.RoleGroup, token policies = append(policies, casbin2.Policy{Type: "g", Sub: casbin2.Subject(roleGroup.CasbinName), Obj: casbin2.Object(roleModel.Role)}) } } + + if checkRBACForGroupUpdate != nil { + isAuthorised, err := checkRBACForGroupUpdate(token, request, eliminatedRoleModels) + if err != nil { + impl.logger.Errorw("error in checking RBAC for role group update", "err", err, "request", request) + return nil, err + } else if !isAuthorised { + impl.logger.Errorw("rbac check failed for role group update", "request", request) + return nil, &util.ApiError{ + Code: "403", + HttpStatusCode: http.StatusForbidden, + UserMessage: "unauthorized", + } + } + } + //deleting policies from casbin impl.logger.Debugw("eliminated policies", "eliminatedPolicies", eliminatedPolicies) if len(eliminatedPolicies) > 0 { diff --git a/pkg/auth/user/UserCommonService.go b/pkg/auth/user/UserCommonService.go index c0ed2c3371f..6ce69cf3007 100644 --- a/pkg/auth/user/UserCommonService.go +++ b/pkg/auth/user/UserCommonService.go @@ -36,8 +36,8 @@ import ( type UserCommonService interface { CreateDefaultPoliciesForAllTypes(team, entityName, env, entity, cluster, namespace, group, kind, resource, actionType, accessType, workflow string, userId int32) (bool, error, []casbin.Policy) - RemoveRolesAndReturnEliminatedPolicies(userInfo *bean.UserInfo, existingRoleIds map[int]repository.UserRoleModel, eliminatedRoleIds map[int]*repository.UserRoleModel, tx *pg.Tx, token string, managerAuth func(resource, token, object string) bool) ([]casbin.Policy, error) - RemoveRolesAndReturnEliminatedPoliciesForGroups(request *bean.RoleGroup, existingRoles map[int]*repository.RoleGroupRoleMapping, eliminatedRoles map[int]*repository.RoleGroupRoleMapping, tx *pg.Tx, token string, managerAuth func(resource string, token string, object string) bool) ([]casbin.Policy, error) + RemoveRolesAndReturnEliminatedPolicies(userInfo *bean.UserInfo, existingRoleIds map[int]repository.UserRoleModel, eliminatedRoleIds map[int]*repository.UserRoleModel, tx *pg.Tx) ([]casbin.Policy, []*repository.RoleModel, error) + RemoveRolesAndReturnEliminatedPoliciesForGroups(request *bean.RoleGroup, existingRoles map[int]*repository.RoleGroupRoleMapping, eliminatedRoles map[int]*repository.RoleGroupRoleMapping, tx *pg.Tx) ([]casbin.Policy, []*repository.RoleModel, error) CheckRbacForClusterEntity(cluster, namespace, group, kind, resource, token string, managerAuth func(resource, token, object string) bool) bool GetCapacityForRoleFilter(roleFilters []bean.RoleFilter) (int, map[int]int) BuildRoleFilterKeyForCluster(roleFilterMap map[string]*bean.RoleFilter, role repository.RoleModel, key string) @@ -238,7 +238,7 @@ func getResolvedPValMapValue(rawValue string) string { func (impl UserCommonServiceImpl) RemoveRolesAndReturnEliminatedPolicies(userInfo *bean.UserInfo, existingRoleIds map[int]repository.UserRoleModel, eliminatedRoleIds map[int]*repository.UserRoleModel, - tx *pg.Tx, token string, managerAuth func(resource, token, object string) bool) ([]casbin.Policy, error) { + tx *pg.Tx) ([]casbin.Policy, []*repository.RoleModel, error) { var eliminatedPolicies []casbin.Policy // DELETE Removed Items for _, roleFilter := range userInfo.RoleFilters { @@ -253,14 +253,10 @@ func (impl UserCommonServiceImpl) RemoveRolesAndReturnEliminatedPolicies(userInf for _, group := range groups { for _, kind := range kinds { for _, resource := range resources { - isValidAuth := impl.CheckRbacForClusterEntity(roleFilter.Cluster, namespace, group, kind, resource, token, managerAuth) - if !isValidAuth { - continue - } roleModel, err := impl.userAuthRepository.GetRoleByFilterForAllTypes(roleFilter.Entity, "", "", "", "", accessType, roleFilter.Cluster, namespace, group, kind, resource, actionType, false, "") if err != nil { impl.logger.Errorw("Error in fetching roles by filter", "roleFilter", roleFilter) - return nil, err + return nil, nil, err } if roleModel.Id == 0 { impl.logger.Warnw("no role found for given filter", "filter", roleFilter) @@ -274,13 +270,6 @@ func (impl UserCommonServiceImpl) RemoveRolesAndReturnEliminatedPolicies(userInf } } } else if roleFilter.Entity == bean2.EntityJobs { - if len(roleFilter.Team) > 0 { // check auth only for apps permission, skip for chart group - rbacObject := fmt.Sprintf("%s", roleFilter.Team) - isValidAuth := managerAuth(casbin.ResourceUser, token, rbacObject) - if !isValidAuth { - continue - } - } entityNames := strings.Split(roleFilter.EntityName, ",") environments := strings.Split(roleFilter.Environment, ",") workflows := strings.Split(roleFilter.Workflow, ",") @@ -292,7 +281,7 @@ func (impl UserCommonServiceImpl) RemoveRolesAndReturnEliminatedPolicies(userInf roleModel, err := impl.userAuthRepository.GetRoleByFilterForAllTypes(roleFilter.Entity, roleFilter.Team, entityName, environment, actionType, accessType, "", "", "", "", "", actionType, false, workflow) if err != nil { impl.logger.Errorw("Error in fetching roles by filter", "user", userInfo) - return nil, err + return nil, nil, err } if roleModel.Id == 0 { impl.logger.Debugw("no role found for given filter", "filter", roleFilter) @@ -306,13 +295,6 @@ func (impl UserCommonServiceImpl) RemoveRolesAndReturnEliminatedPolicies(userInf } } } else { - if len(roleFilter.Team) > 0 { // check auth only for apps permission, skip for chart group - rbacObject := fmt.Sprintf("%s", roleFilter.Team) - isValidAuth := managerAuth(casbin.ResourceUser, token, rbacObject) - if !isValidAuth { - continue - } - } entityNames := strings.Split(roleFilter.EntityName, ",") environments := strings.Split(roleFilter.Environment, ",") actionType := roleFilter.Action @@ -322,11 +304,11 @@ func (impl UserCommonServiceImpl) RemoveRolesAndReturnEliminatedPolicies(userInf roleModel, err := impl.userAuthRepository.GetRoleByFilterForAllTypes(roleFilter.Entity, roleFilter.Team, entityName, environment, actionType, accessType, "", "", "", "", "", actionType, false, "") if err != nil { impl.logger.Errorw("Error in fetching roles by filter", "user", userInfo) - return nil, err + return nil, nil, err } oldRoleModel, err := impl.userAuthRepository.GetRoleByFilterForAllTypes(roleFilter.Entity, roleFilter.Team, entityName, environment, actionType, accessType, "", "", "", "", "", actionType, true, "") if err != nil { - return nil, err + return nil, nil, err } if roleModel.Id == 0 { impl.logger.Debugw("no role found for given filter", "filter", roleFilter) @@ -349,37 +331,25 @@ func (impl UserCommonServiceImpl) RemoveRolesAndReturnEliminatedPolicies(userInf // delete remaining Ids from casbin role mapping table in orchestrator and casbin policy db // which are existing but not provided in this request - + eliminatedRoles := make([]*repository.RoleModel, 0, len(eliminatedRoleIds)) for _, userRoleModel := range eliminatedRoleIds { role, err := impl.userAuthRepository.GetRoleById(userRoleModel.RoleId) if err != nil { - return nil, err - } - if len(role.Team) > 0 { - rbacObject := fmt.Sprintf("%s", role.Team) - isValidAuth := managerAuth(casbin.ResourceUser, token, rbacObject) - if !isValidAuth { - continue - } - } - if role.Entity == bean.CLUSTER_ENTITIY { - isValidAuth := impl.CheckRbacForClusterEntity(role.Cluster, role.Namespace, role.Group, role.Kind, role.Resource, token, managerAuth) - if !isValidAuth { - continue - } + return nil, nil, err } + eliminatedRoles = append(eliminatedRoles, role) _, err = impl.userAuthRepository.DeleteUserRoleMapping(userRoleModel, tx) if err != nil { impl.logger.Errorw("Error in delete user role mapping", "user", userInfo) - return nil, err + return nil, nil, err } eliminatedPolicies = append(eliminatedPolicies, casbin.Policy{Type: "g", Sub: casbin.Subject(userInfo.EmailId), Obj: casbin.Object(role.Role)}) } // DELETE ENDS - return eliminatedPolicies, nil + return eliminatedPolicies, eliminatedRoles, nil } -func (impl UserCommonServiceImpl) RemoveRolesAndReturnEliminatedPoliciesForGroups(request *bean.RoleGroup, existingRoles map[int]*repository.RoleGroupRoleMapping, eliminatedRoles map[int]*repository.RoleGroupRoleMapping, tx *pg.Tx, token string, managerAuth func(resource string, token string, object string) bool) ([]casbin.Policy, error) { +func (impl UserCommonServiceImpl) RemoveRolesAndReturnEliminatedPoliciesForGroups(request *bean.RoleGroup, existingRoles map[int]*repository.RoleGroupRoleMapping, eliminatedRoles map[int]*repository.RoleGroupRoleMapping, tx *pg.Tx) ([]casbin.Policy, []*repository.RoleModel, error) { // Filter out removed items in current request //var policies []casbin.Policy for _, roleFilter := range request.RoleFilters { @@ -395,19 +365,15 @@ func (impl UserCommonServiceImpl) RemoveRolesAndReturnEliminatedPoliciesForGroup for _, group := range groups { for _, kind := range kinds { for _, resource := range resources { - isValidAuth := impl.CheckRbacForClusterEntity(roleFilter.Cluster, namespace, group, kind, resource, token, managerAuth) - if !isValidAuth { - continue - } roleModel, err := impl.userAuthRepository.GetRoleByFilterForAllTypes(entity, "", "", "", "", accessType, roleFilter.Cluster, namespace, group, kind, resource, actionType, false, "") if err != nil { impl.logger.Errorw("Error in fetching roles by filter", "user", request) - return nil, err + return nil, nil, err } oldRoleModel, err := impl.userAuthRepository.GetRoleByFilterForAllTypes(entity, "", "", "", "", accessType, roleFilter.Cluster, namespace, group, kind, resource, actionType, true, "") if err != nil { impl.logger.Errorw("Error in fetching roles by filter", "user", request) - return nil, err + return nil, nil, err } if roleModel.Id == 0 && oldRoleModel.Id == 0 { impl.logger.Warnw("no role found for given filter", "filter", roleFilter) @@ -425,13 +391,6 @@ func (impl UserCommonServiceImpl) RemoveRolesAndReturnEliminatedPoliciesForGroup } } } else if entity == bean2.EntityJobs { - if len(roleFilter.Team) > 0 { // check auth only for apps permission, skip for chart group - rbacObject := fmt.Sprintf("%s", roleFilter.Team) - isValidAuth := managerAuth(casbin.ResourceUser, token, rbacObject) - if !isValidAuth { - continue - } - } entityNames := strings.Split(roleFilter.EntityName, ",") environments := strings.Split(roleFilter.Environment, ",") workflows := strings.Split(roleFilter.Workflow, ",") @@ -443,7 +402,7 @@ func (impl UserCommonServiceImpl) RemoveRolesAndReturnEliminatedPoliciesForGroup roleModel, err := impl.userAuthRepository.GetRoleByFilterForAllTypes(roleFilter.Entity, roleFilter.Team, entityName, environment, actionType, accessType, "", "", "", "", "", "", false, workflow) if err != nil { impl.logger.Errorw("Error in fetching roles by filter", "user", request) - return nil, err + return nil, nil, err } if roleModel.Id == 0 { impl.logger.Warnw("no role found for given filter", "filter", roleFilter) @@ -457,13 +416,6 @@ func (impl UserCommonServiceImpl) RemoveRolesAndReturnEliminatedPoliciesForGroup } } } else { - if len(roleFilter.Team) > 0 { // check auth only for apps permission, skip for chart group - rbacObject := fmt.Sprintf("%s", roleFilter.Team) - isValidAuth := managerAuth(casbin.ResourceUser, token, rbacObject) - if !isValidAuth { - continue - } - } entityNames := strings.Split(roleFilter.EntityName, ",") environments := strings.Split(roleFilter.Environment, ",") accessType := roleFilter.AccessType @@ -473,12 +425,12 @@ func (impl UserCommonServiceImpl) RemoveRolesAndReturnEliminatedPoliciesForGroup roleModel, err := impl.userAuthRepository.GetRoleByFilterForAllTypes(roleFilter.Entity, roleFilter.Team, entityName, environment, actionType, accessType, "", "", "", "", "", "", false, "") if err != nil { impl.logger.Errorw("Error in fetching roles by filter", "user", request) - return nil, err + return nil, nil, err } oldRoleModel, err := impl.userAuthRepository.GetRoleByFilterForAllTypes(roleFilter.Entity, roleFilter.Team, entityName, environment, actionType, accessType, "", "", "", "", "", "", true, "") if err != nil { impl.logger.Errorw("Error in fetching roles by filter by old values", "user", request) - return nil, err + return nil, nil, err } if roleModel.Id == 0 && oldRoleModel.Id == 0 { impl.logger.Warnw("no role found for given filter", "filter", roleFilter) @@ -502,35 +454,24 @@ func (impl UserCommonServiceImpl) RemoveRolesAndReturnEliminatedPoliciesForGroup //delete remaining Ids from casbin role mapping table in orchestrator and casbin policy db // which are existing but not provided in this request var eliminatedPolicies []casbin.Policy + eliminatedRoleModels := make([]*repository.RoleModel, 0, len(eliminatedRoles)) for _, model := range eliminatedRoles { role, err := impl.userAuthRepository.GetRoleById(model.RoleId) if err != nil { - return nil, err - } - if len(role.Team) > 0 { - rbacObject := fmt.Sprintf("%s", role.Team) - isValidAuth := managerAuth(casbin.ResourceUser, token, rbacObject) - if !isValidAuth { - continue - } - } - if role.Entity == bean.CLUSTER_ENTITIY { - isValidAuth := impl.CheckRbacForClusterEntity(role.Cluster, role.Namespace, role.Group, role.Kind, role.Resource, token, managerAuth) - if !isValidAuth { - continue - } + return nil, nil, err } + eliminatedRoleModels = append(eliminatedRoleModels, role) _, err = impl.roleGroupRepository.DeleteRoleGroupRoleMapping(model, tx) if err != nil { - return nil, err + return nil, nil, err } policyGroup, err := impl.roleGroupRepository.GetRoleGroupById(model.RoleGroupId) if err != nil { - return nil, err + return nil, nil, err } eliminatedPolicies = append(eliminatedPolicies, casbin.Policy{Type: "g", Sub: casbin.Subject(policyGroup.CasbinName), Obj: casbin.Object(role.Role)}) } - return eliminatedPolicies, nil + return eliminatedPolicies, eliminatedRoleModels, nil } func containsArr(s []string, e string) bool { diff --git a/pkg/auth/user/UserService.go b/pkg/auth/user/UserService.go index 3e17f76e442..e065c61bf33 100644 --- a/pkg/auth/user/UserService.go +++ b/pkg/auth/user/UserService.go @@ -51,9 +51,10 @@ const ( ) type UserService interface { - CreateUser(userInfo *bean.UserInfo, token string, managerAuth func(resource, token string, object string) bool) ([]*bean.UserInfo, []bean.RestrictedGroup, error) + CreateUser(userInfo *bean.UserInfo) ([]*bean.UserInfo, error) SelfRegisterUserIfNotExists(userInfo *bean.UserInfo) ([]*bean.UserInfo, error) - UpdateUser(userInfo *bean.UserInfo, token string, managerAuth func(resource, token string, object string) bool) (*bean.UserInfo, bool, bool, []bean.RestrictedGroup, error) + UpdateUser(userInfo *bean.UserInfo, token string, checkRBACForUserUpdate func(token string, userInfo *bean.UserInfo, isUserAlreadySuperAdmin bool, + eliminatedRoleFilters, eliminatedGroupRoles []*repository.RoleModel) (isAuthorised bool, err error)) (*bean.UserInfo, error) GetById(id int32) (*bean.UserInfo, error) GetAll() ([]bean.UserInfo, error) GetAllWithFilters(request *bean.ListingRequest) (*bean.UserListingResponse, error) @@ -276,34 +277,33 @@ func (impl *UserServiceImpl) saveUser(userInfo *bean.UserInfo, emailId string) ( return userInfo, nil } -func (impl *UserServiceImpl) CreateUser(userInfo *bean.UserInfo, token string, managerAuth func(resource, token string, object string) bool) ([]*bean.UserInfo, []bean.RestrictedGroup, error) { +func (impl *UserServiceImpl) CreateUser(userInfo *bean.UserInfo) ([]*bean.UserInfo, error) { var pass []string var userResponse []*bean.UserInfo - var restrictedGroups []bean.RestrictedGroup emailIds := strings.Split(userInfo.EmailId, ",") for _, emailId := range emailIds { dbUser, err := impl.userRepository.FetchActiveOrDeletedUserByEmail(emailId) if err != nil && err != pg.ErrNoRows { impl.logger.Errorw("error while fetching user from db", "error", err) - return nil, nil, err + return nil, err } //if found, update it with new roles if dbUser != nil && dbUser.Id > 0 { - userInfo, err = impl.updateUserIfExists(userInfo, dbUser, emailId, token, managerAuth) + userInfo, err = impl.updateUserIfExists(userInfo, dbUser, emailId) if err != nil { impl.logger.Errorw("error while create user if exists in db", "error", err) - return nil, nil, err + return nil, err } } // if not found, create new user if err == pg.ErrNoRows { - userInfo, restrictedGroups, err = impl.createUserIfNotExists(userInfo, emailId, token, managerAuth) + userInfo, err = impl.createUserIfNotExists(userInfo, emailId) if err != nil { impl.logger.Errorw("error while create user if not exists in db", "error", err) - return nil, nil, err + return nil, err } } @@ -313,11 +313,10 @@ func (impl *UserServiceImpl) CreateUser(userInfo *bean.UserInfo, token string, m userResponse = append(userResponse, &bean.UserInfo{Id: userInfo.Id, EmailId: emailId, Groups: userInfo.Groups, RoleFilters: userInfo.RoleFilters, SuperAdmin: userInfo.SuperAdmin, UserRoleGroup: userInfo.UserRoleGroup}) } - return userResponse, restrictedGroups, nil + return userResponse, nil } -func (impl *UserServiceImpl) updateUserIfExists(userInfo *bean.UserInfo, dbUser *repository.UserModel, emailId string, - token string, managerAuth func(resource, token, object string) bool) (*bean.UserInfo, error) { +func (impl *UserServiceImpl) updateUserIfExists(userInfo *bean.UserInfo, dbUser *repository.UserModel, emailId string) (*bean.UserInfo, error) { updateUserInfo, err := impl.GetById(dbUser.Id) if err != nil && err != pg.ErrNoRows { impl.logger.Errorw("error while fetching user from db", "error", err) @@ -332,8 +331,8 @@ func (impl *UserServiceImpl) updateUserIfExists(userInfo *bean.UserInfo, dbUser updateUserInfo.Groups = impl.mergeGroups(updateUserInfo.Groups, userInfo.Groups) updateUserInfo.UserRoleGroup = impl.mergeUserRoleGroup(updateUserInfo.UserRoleGroup, userInfo.UserRoleGroup) updateUserInfo.UserId = userInfo.UserId - updateUserInfo.EmailId = emailId // override case sensitivity - updateUserInfo, _, _, _, err = impl.UpdateUser(updateUserInfo, token, managerAuth) + updateUserInfo.EmailId = emailId // override case sensitivity + updateUserInfo, err = impl.UpdateUser(updateUserInfo, "", nil) //rbac already checked in create request handled if err != nil { impl.logger.Errorw("error while update user", "error", err) return nil, err @@ -341,12 +340,12 @@ func (impl *UserServiceImpl) updateUserIfExists(userInfo *bean.UserInfo, dbUser return userInfo, nil } -func (impl *UserServiceImpl) createUserIfNotExists(userInfo *bean.UserInfo, emailId string, token string, managerAuth func(resource string, token string, object string) bool) (*bean.UserInfo, []bean.RestrictedGroup, error) { +func (impl *UserServiceImpl) createUserIfNotExists(userInfo *bean.UserInfo, emailId string) (*bean.UserInfo, error) { // if not found, create new user dbConnection := impl.userRepository.GetConnection() tx, err := dbConnection.Begin() if err != nil { - return nil, nil, err + return nil, err } // Rollback tx on error. defer tx.Rollback() @@ -354,7 +353,7 @@ func (impl *UserServiceImpl) createUserIfNotExists(userInfo *bean.UserInfo, emai _, err = impl.validateUserRequest(userInfo) if err != nil { err = &util.ApiError{HttpStatusCode: http.StatusBadRequest, UserMessage: "Invalid request, please provide role filters"} - return nil, nil, err + return nil, err } //create new user in our db on d basis of info got from google api or hex. assign a basic role @@ -376,67 +375,54 @@ func (impl *UserServiceImpl) createUserIfNotExists(userInfo *bean.UserInfo, emai InternalMessage: "failed to create new user in db", UserMessage: fmt.Sprintf("requested by %d", userInfo.UserId), } - return nil, nil, err + return nil, err } userInfo.Id = model.Id //loading policy for safety casbin2.LoadPolicy() - var restrictedGroups []bean.RestrictedGroup - //Starts Role and Mapping capacity, mapping := impl.userCommonService.GetCapacityForRoleFilter(userInfo.RoleFilters) //var policies []casbin2.Policy var policies = make([]casbin2.Policy, 0, capacity) if userInfo.SuperAdmin == false { - isActionPerformingUserSuperAdmin, err := impl.IsSuperAdmin(int(userInfo.UserId)) - if err != nil { - return nil, nil, err - } for index, roleFilter := range userInfo.RoleFilters { impl.logger.Infow("Creating Or updating User Roles for RoleFilter ") entity := roleFilter.Entity - policiesToBeAdded, _, err := impl.CreateOrUpdateUserRolesForAllTypes(roleFilter, userInfo.UserId, model, nil, token, managerAuth, tx, entity, mapping[index]) + policiesToBeAdded, _, err := impl.CreateOrUpdateUserRolesForAllTypes(roleFilter, userInfo.UserId, model, nil, tx, entity, mapping[index]) if err != nil { impl.logger.Errorw("error in creating user roles for Alltypes", "err", err) - return nil, nil, err + return nil, err } policies = append(policies, policiesToBeAdded...) - } // START GROUP POLICY for _, item := range userInfo.UserRoleGroup { userGroup, err := impl.roleGroupRepository.GetRoleGroupByName(item.RoleGroup.Name) if err != nil { - return nil, nil, err - } - hasAccessToGroup, hasSuperAdminPermission := impl.checkGroupAuth(userGroup.CasbinName, token, managerAuth, isActionPerformingUserSuperAdmin) - if hasAccessToGroup { - policies = append(policies, casbin2.Policy{Type: "g", Sub: casbin2.Subject(userInfo.EmailId), Obj: casbin2.Object(userGroup.CasbinName)}) - } else { - restrictedGroup := adapter.CreateRestrictedGroup(item.RoleGroup.Name, hasSuperAdminPermission) - restrictedGroups = append(restrictedGroups, restrictedGroup) + return nil, err } + policies = append(policies, casbin2.Policy{Type: "g", Sub: casbin2.Subject(userInfo.EmailId), Obj: casbin2.Object(userGroup.CasbinName)}) + // below is old code where we used to re check group access, but not needed now as we have moved group rbac to restHandler + + //hasAccessToGroup, hasSuperAdminPermission := impl.checkGroupAuth(userGroup.CasbinName, token, managerAuth, isActionPerformingUserSuperAdmin) + //if hasAccessToGroup { + //policies = append(policies, casbin2.Policy{Type: "g", Sub: casbin2.Subject(userInfo.EmailId), Obj: casbin2.Object(userGroup.CasbinName)}) + //} else { + // restrictedGroup := adapter.CreateRestrictedGroup(item.RoleGroup.Name, hasSuperAdminPermission) + // restrictedGroups = append(restrictedGroups, restrictedGroup) + //} } // END GROUP POLICY } else if userInfo.SuperAdmin == true { - - isSuperAdmin, err := impl.IsSuperAdmin(int(userInfo.UserId)) - if err != nil { - return nil, nil, err - } - if isSuperAdmin == false { - err = &util.ApiError{HttpStatusCode: http.StatusForbidden, UserMessage: "Invalid request, not allow to update super admin type user"} - return nil, nil, err - } flag, err := impl.userAuthRepository.CreateRoleForSuperAdminIfNotExists(tx, userInfo.UserId) if err != nil || flag == false { - return nil, nil, err + return nil, err } roleModel, err := impl.userAuthRepository.GetRoleByFilterForAllTypes("", "", "", "", bean2.SUPER_ADMIN, "", "", "", "", "", "", "", false, "") if err != nil { - return nil, nil, err + return nil, err } if roleModel.Id > 0 { userRoleModel := &repository.UserRoleModel{UserId: model.Id, RoleId: roleModel.Id, AuditLog: sql.AuditLog{ @@ -447,11 +433,10 @@ func (impl *UserServiceImpl) createUserIfNotExists(userInfo *bean.UserInfo, emai }} userRoleModel, err = impl.userAuthRepository.CreateUserRoleMapping(userRoleModel, tx) if err != nil { - return nil, nil, err + return nil, err } policies = append(policies, casbin2.Policy{Type: "g", Sub: casbin2.Subject(model.EmailId), Obj: casbin2.Object(roleModel.Role)}) } - } impl.logger.Infow("Checking the length of policies to be added and Adding in casbin ") if len(policies) > 0 { @@ -462,30 +447,30 @@ func (impl *UserServiceImpl) createUserIfNotExists(userInfo *bean.UserInfo, emai //Ends err = tx.Commit() if err != nil { - return nil, nil, err + return nil, err } //loading policy for syncing orchestrator to casbin with newly added policies casbin2.LoadPolicy() - return userInfo, restrictedGroups, nil + return userInfo, nil } -func (impl *UserServiceImpl) CreateOrUpdateUserRolesForAllTypes(roleFilter bean.RoleFilter, userId int32, model *repository.UserModel, existingRoles map[int]repository.UserRoleModel, token string, managerAuth func(resource string, token string, object string) bool, tx *pg.Tx, entity string, capacity int) ([]casbin2.Policy, bool, error) { +func (impl *UserServiceImpl) CreateOrUpdateUserRolesForAllTypes(roleFilter bean.RoleFilter, userId int32, model *repository.UserModel, existingRoles map[int]repository.UserRoleModel, tx *pg.Tx, entity string, capacity int) ([]casbin2.Policy, bool, error) { //var policiesToBeAdded []casbin2.Policy var policiesToBeAdded = make([]casbin2.Policy, 0, capacity) var err error rolesChanged := false if entity == bean2.CLUSTER { - policiesToBeAdded, rolesChanged, err = impl.createOrUpdateUserRolesForClusterEntity(roleFilter, userId, model, existingRoles, token, managerAuth, tx, entity, capacity) + policiesToBeAdded, rolesChanged, err = impl.createOrUpdateUserRolesForClusterEntity(roleFilter, userId, model, existingRoles, tx, entity, capacity) if err != nil { return nil, false, err } } else if entity == bean2.EntityJobs { - policiesToBeAdded, rolesChanged, err = impl.createOrUpdateUserRolesForJobsEntity(roleFilter, userId, model, existingRoles, token, managerAuth, tx, entity, capacity) + policiesToBeAdded, rolesChanged, err = impl.createOrUpdateUserRolesForJobsEntity(roleFilter, userId, model, existingRoles, tx, entity, capacity) if err != nil { return nil, false, err } } else { - policiesToBeAdded, rolesChanged, err = impl.createOrUpdateUserRolesForOtherEntity(roleFilter, userId, model, existingRoles, token, managerAuth, tx, entity, capacity) + policiesToBeAdded, rolesChanged, err = impl.createOrUpdateUserRolesForOtherEntity(roleFilter, userId, model, existingRoles, tx, entity, capacity) if err != nil { return nil, false, err } @@ -493,7 +478,7 @@ func (impl *UserServiceImpl) CreateOrUpdateUserRolesForAllTypes(roleFilter bean. return policiesToBeAdded, rolesChanged, nil } -func (impl *UserServiceImpl) createOrUpdateUserRolesForClusterEntity(roleFilter bean.RoleFilter, userId int32, model *repository.UserModel, existingRoles map[int]repository.UserRoleModel, token string, managerAuth func(resource string, token string, object string) bool, tx *pg.Tx, entity string, capacity int) ([]casbin2.Policy, bool, error) { +func (impl *UserServiceImpl) createOrUpdateUserRolesForClusterEntity(roleFilter bean.RoleFilter, userId int32, model *repository.UserModel, existingRoles map[int]repository.UserRoleModel, tx *pg.Tx, entity string, capacity int) ([]casbin2.Policy, bool, error) { //var policiesToBeAdded []casbin2.Policy rolesChanged := false @@ -510,12 +495,6 @@ func (impl *UserServiceImpl) createOrUpdateUserRolesForClusterEntity(roleFilter for _, group := range groups { for _, kind := range kinds { for _, resource := range resources { - if managerAuth != nil { - isValidAuth := impl.userCommonService.CheckRbacForClusterEntity(roleFilter.Cluster, namespace, group, kind, resource, token, managerAuth) - if !isValidAuth { - continue - } - } impl.logger.Infow("Getting Role by filter for cluster") roleModel, err := impl.userAuthRepository.GetRoleByFilterForAllTypes(entity, "", "", "", "", accessType, roleFilter.Cluster, namespace, group, kind, resource, actionType, false, "") if err != nil { @@ -646,12 +625,13 @@ func (impl UserServiceImpl) mergeUserRoleGroup(oldUserRoleGroups []bean.UserRole return finalUserRoleGroups } -func (impl *UserServiceImpl) UpdateUser(userInfo *bean.UserInfo, token string, managerAuth func(resource, token string, object string) bool) (*bean.UserInfo, bool, bool, []bean.RestrictedGroup, error) { +func (impl *UserServiceImpl) UpdateUser(userInfo *bean.UserInfo, token string, checkRBACForUserUpdate func(token string, userInfo *bean.UserInfo, + isUserAlreadySuperAdmin bool, eliminatedRoleFilters, eliminatedGroupRoles []*repository.RoleModel) (isAuthorised bool, err error)) (*bean.UserInfo, error) { //checking if request for same user is being processed isLocked := impl.getUserReqLockStateById(userInfo.Id) if isLocked { impl.logger.Errorw("received concurrent request for user update, UpdateUser", "userId", userInfo.Id) - return nil, false, false, nil, &util.ApiError{ + return nil, &util.ApiError{ Code: "409", HttpStatusCode: http.StatusConflict, UserMessage: ConcurrentRequestLockError, @@ -661,7 +641,7 @@ func (impl *UserServiceImpl) UpdateUser(userInfo *bean.UserInfo, token string, m err := impl.lockUnlockUserReqState(userInfo.Id, true) if err != nil { impl.logger.Errorw("error in locking, lockUnlockUserReqState", "userId", userInfo.Id) - return nil, false, false, nil, err + return nil, err } defer func() { err = impl.lockUnlockUserReqState(userInfo.Id, false) @@ -673,30 +653,12 @@ func (impl *UserServiceImpl) UpdateUser(userInfo *bean.UserInfo, token string, m //validating if action user is not admin and trying to update user who has super admin polices, return 403 isUserSuperAdmin, err := impl.IsSuperAdmin(int(userInfo.Id)) if err != nil { - return nil, false, false, nil, err - } - isActionPerformingUserSuperAdmin, err := impl.IsSuperAdmin(int(userInfo.UserId)) - if err != nil { - return nil, false, false, nil, err - } - //if request comes to make user as a super admin or user already a super admin (who'is going to be updated), action performing user should have super admin access - if userInfo.SuperAdmin || isUserSuperAdmin { - if !isActionPerformingUserSuperAdmin { - err = &util.ApiError{HttpStatusCode: http.StatusForbidden, UserMessage: "Invalid request, not allow to update super admin type user"} - impl.logger.Errorw("Invalid request, not allow to update super admin type user", "error", err) - return nil, false, false, nil, err - } - } - if userInfo.SuperAdmin && isUserSuperAdmin { - err = &util.ApiError{HttpStatusCode: http.StatusBadRequest, UserMessage: "User Already A Super Admin"} - impl.logger.Errorw("user already a superAdmin", "error", err) - return nil, false, false, nil, err + return nil, err } - dbConnection := impl.userRepository.GetConnection() tx, err := dbConnection.Begin() if err != nil { - return nil, false, false, nil, err + return nil, err } // Rollback tx on error. defer tx.Rollback() @@ -704,22 +666,20 @@ func (impl *UserServiceImpl) UpdateUser(userInfo *bean.UserInfo, token string, m model, err := impl.userRepository.GetByIdIncludeDeleted(userInfo.Id) if err != nil { impl.logger.Errorw("error while fetching user from db", "error", err) - return nil, false, false, nil, err + return nil, err } var eliminatedPolicies []casbin2.Policy capacity, mapping := impl.userCommonService.GetCapacityForRoleFilter(userInfo.RoleFilters) var addedPolicies = make([]casbin2.Policy, 0, capacity) - restrictedGroups := []bean.RestrictedGroup{} - rolesChanged := false - groupsModified := false //loading policy for safety casbin2.LoadPolicy() + var eliminatedRoles, eliminatedGroupRoles []*repository.RoleModel if userInfo.SuperAdmin == false { //Starts Role and Mapping userRoleModels, err := impl.userAuthRepository.GetUserRoleMappingByUserId(model.Id) if err != nil { - return nil, false, false, nil, err + return nil, err } existingRoleIds := make(map[int]repository.UserRoleModel) eliminatedRoleIds := make(map[int]*repository.UserRoleModel) @@ -732,40 +692,34 @@ func (impl *UserServiceImpl) UpdateUser(userInfo *bean.UserInfo, token string, m _, err = impl.validateUserRequest(userInfo) if err != nil { err = &util.ApiError{HttpStatusCode: http.StatusBadRequest, UserMessage: "Invalid request, please provide role filters"} - return nil, false, false, nil, err + return nil, err } // DELETE Removed Items - items, err := impl.userCommonService.RemoveRolesAndReturnEliminatedPolicies(userInfo, existingRoleIds, eliminatedRoleIds, tx, token, managerAuth) + var items []casbin2.Policy + items, eliminatedRoles, err = impl.userCommonService.RemoveRolesAndReturnEliminatedPolicies(userInfo, existingRoleIds, eliminatedRoleIds, tx) if err != nil { - return nil, false, false, nil, err + return nil, err } eliminatedPolicies = append(eliminatedPolicies, items...) - if len(eliminatedPolicies) > 0 { - rolesChanged = true - } //Adding New Policies for index, roleFilter := range userInfo.RoleFilters { entity := roleFilter.Entity - - policiesToBeAdded, rolesChangedFromRoleUpdate, err := impl.CreateOrUpdateUserRolesForAllTypes(roleFilter, userInfo.UserId, model, existingRoleIds, token, managerAuth, tx, entity, mapping[index]) + policiesToBeAdded, _, err := impl.CreateOrUpdateUserRolesForAllTypes(roleFilter, userInfo.UserId, model, existingRoleIds, tx, entity, mapping[index]) if err != nil { impl.logger.Errorw("error in creating user roles for All Types", "err", err) - return nil, false, false, nil, err + return nil, err } addedPolicies = append(addedPolicies, policiesToBeAdded...) - rolesChanged = rolesChangedFromRoleUpdate - } //ROLE GROUP SETUP newGroupMap := make(map[string]string) oldGroupMap := make(map[string]string) userCasbinRoles, err := impl.CheckUserRoles(userInfo.Id) - if err != nil { - return nil, false, false, nil, err + return nil, err } for _, oldItem := range userCasbinRoles { oldGroupMap[oldItem] = oldItem @@ -774,62 +728,85 @@ func (impl *UserServiceImpl) UpdateUser(userInfo *bean.UserInfo, token string, m for _, item := range userInfo.UserRoleGroup { userGroup, err := impl.roleGroupRepository.GetRoleGroupByName(item.RoleGroup.Name) if err != nil { - return nil, false, false, nil, err + return nil, err } newGroupMap[userGroup.CasbinName] = userGroup.CasbinName if _, ok := oldGroupMap[userGroup.CasbinName]; !ok { - //check permission for new group which is going to add - hasAccessToGroup, hasSuperAdminPermission := impl.checkGroupAuth(userGroup.CasbinName, token, managerAuth, isActionPerformingUserSuperAdmin) - if hasAccessToGroup { - groupsModified = true - addedPolicies = append(addedPolicies, casbin2.Policy{Type: "g", Sub: casbin2.Subject(userInfo.EmailId), Obj: casbin2.Object(userGroup.CasbinName)}) - } else { - restrictedGroup := adapter.CreateRestrictedGroup(item.RoleGroup.Name, hasSuperAdminPermission) - restrictedGroups = append(restrictedGroups, restrictedGroup) - } + addedPolicies = append(addedPolicies, casbin2.Policy{Type: "g", Sub: casbin2.Subject(userInfo.EmailId), Obj: casbin2.Object(userGroup.CasbinName)}) + // //check permission for new group which is going to add + //hasAccessToGroup, hasSuperAdminPermission := impl.checkGroupAuth(userGroup.CasbinName, token, managerAuth, isActionPerformingUserSuperAdmin) + //if hasAccessToGroup { + // groupsModified = true + // addedPolicies = append(addedPolicies, casbin2.Policy{Type: "g", Sub: casbin2.Subject(userInfo.EmailId), Obj: casbin2.Object(userGroup.CasbinName)}) + //} else { + // restrictedGroup := adapter.CreateRestrictedGroup(item.RoleGroup.Name, hasSuperAdminPermission) + // restrictedGroups = append(restrictedGroups, restrictedGroup) + //} } } - + eliminatedGroupCasbinNames := make([]string, 0, len(newGroupMap)) for _, item := range userCasbinRoles { if _, ok := newGroupMap[item]; !ok { if item != bean.SUPERADMIN { //check permission for group which is going to eliminate if strings.HasPrefix(item, "group:") { - hasAccessToGroup, hasSuperAdminPermission := impl.checkGroupAuth(item, token, managerAuth, isActionPerformingUserSuperAdmin) - if hasAccessToGroup { - if strings.HasPrefix(item, "group:") { - groupsModified = true - } - eliminatedPolicies = append(eliminatedPolicies, casbin2.Policy{Type: "g", Sub: casbin2.Subject(userInfo.EmailId), Obj: casbin2.Object(item)}) - } else { - restrictedGroup := adapter.CreateRestrictedGroup(item, hasSuperAdminPermission) - restrictedGroups = append(restrictedGroups, restrictedGroup) - } + eliminatedPolicies = append(eliminatedPolicies, casbin2.Policy{Type: "g", Sub: casbin2.Subject(userInfo.EmailId), Obj: casbin2.Object(item)}) + eliminatedGroupCasbinNames = append(eliminatedGroupCasbinNames, item) + //hasAccessToGroup, hasSuperAdminPermission := impl.checkGroupAuth(item, token, managerAuth, isActionPerformingUserSuperAdmin) + //if hasAccessToGroup { + // if strings.HasPrefix(item, "group:") { + // groupsModified = true + // } + // eliminatedPolicies = append(eliminatedPolicies, casbin2.Policy{Type: "g", Sub: casbin2.Subject(userInfo.EmailId), Obj: casbin2.Object(item)}) + //} else { + // restrictedGroup := adapter.CreateRestrictedGroup(item, hasSuperAdminPermission) + // restrictedGroups = append(restrictedGroups, restrictedGroup) + //} } } } + } // END GROUP POLICY + if len(eliminatedGroupCasbinNames) > 0 { + eliminatedGroupRoles, err = impl.roleGroupRepository.GetRolesByGroupCasbinNames(eliminatedGroupCasbinNames) + if err != nil { + impl.logger.Errorw("error, GetRolesByGroupCasbinNames", "err", err, "eliminatedGroupCasbinNames", eliminatedGroupCasbinNames) + return nil, err + } } - // END GROUP POLICY - } else if userInfo.SuperAdmin == true { flag, err := impl.userAuthRepository.CreateRoleForSuperAdminIfNotExists(tx, userInfo.UserId) if err != nil || flag == false { - return nil, false, false, nil, err + return nil, err } roleModel, err := impl.userAuthRepository.GetRoleByFilterForAllTypes("", "", "", "", bean2.SUPER_ADMIN, "", "", "", "", "", "", "", false, "") if err != nil { - return nil, false, false, nil, err + return nil, err } if roleModel.Id > 0 { userRoleModel := &repository.UserRoleModel{UserId: model.Id, RoleId: roleModel.Id} userRoleModel, err = impl.userAuthRepository.CreateUserRoleMapping(userRoleModel, tx) if err != nil { - return nil, false, false, nil, err + return nil, err } addedPolicies = append(addedPolicies, casbin2.Policy{Type: "g", Sub: casbin2.Subject(model.EmailId), Obj: casbin2.Object(roleModel.Role)}) } } + if checkRBACForUserUpdate != nil { + isAuthorised, err := checkRBACForUserUpdate(token, userInfo, isUserSuperAdmin, eliminatedRoles, eliminatedGroupRoles) + if err != nil { + impl.logger.Errorw("error in checking RBAC for user update", "err", err, "userInfo", userInfo) + return nil, err + } else if !isAuthorised { + impl.logger.Errorw("rbac check failed for user update", "userInfo", userInfo) + return nil, &util.ApiError{ + Code: "403", + HttpStatusCode: http.StatusForbidden, + UserMessage: "unauthorized", + } + } + } + //updating in casbin if len(eliminatedPolicies) > 0 { pRes := casbin2.RemovePolicy(eliminatedPolicies) @@ -848,15 +825,15 @@ func (impl *UserServiceImpl) UpdateUser(userInfo *bean.UserInfo, token string, m model, err = impl.userRepository.UpdateUser(model, tx) if err != nil { impl.logger.Errorw("error while fetching user from db", "error", err) - return nil, false, false, nil, err + return nil, err } err = tx.Commit() if err != nil { - return nil, false, false, nil, err + return nil, err } //loading policy for syncing orchestrator to casbin with newly added policies casbin2.LoadPolicy() - return userInfo, rolesChanged, groupsModified, restrictedGroups, nil + return userInfo, nil } func (impl *UserServiceImpl) GetById(id int32) (*bean.UserInfo, error) { @@ -1572,19 +1549,18 @@ func (impl *UserServiceImpl) CheckUserRoles(id int32) ([]string, error) { } if len(groups) > 0 { // getting unique, handling for duplicate roles - grps, err := impl.getUniquesRolesByGroupCasbinNames(groups) + roleFromGroups, err := impl.getUniquesRolesByGroupCasbinNames(groups) if err != nil { impl.logger.Errorw("error in getUniquesRolesByGroupCasbinNames", "err", err) return nil, err } - groups = append(groups, grps...) + groups = append(groups, roleFromGroups...) } return groups, nil } func (impl UserServiceImpl) getUniquesRolesByGroupCasbinNames(groupCasbinNames []string) ([]string, error) { - var groups []string rolesModels, err := impl.roleGroupRepository.GetRolesByGroupCasbinNames(groupCasbinNames) if err != nil && err != pg.ErrNoRows { impl.logger.Errorw("error in getting roles by group names", "err", err) @@ -1598,10 +1574,7 @@ func (impl UserServiceImpl) getUniquesRolesByGroupCasbinNames(groupCasbinNames [ for role, _ := range uniqueRolesFromGroupMap { rolesFromGroup = append(rolesFromGroup, role) } - if len(rolesFromGroup) > 0 { - groups = append(groups, rolesFromGroup...) - } - return groups, nil + return rolesFromGroup, nil } func (impl *UserServiceImpl) SyncOrchestratorToCasbin() (bool, error) { @@ -1773,7 +1746,7 @@ func (impl *UserServiceImpl) GetRoleFiltersByUserRoleGroups(userRoleGroups []bea return roleFilters, nil } -func (impl *UserServiceImpl) createOrUpdateUserRolesForOtherEntity(roleFilter bean.RoleFilter, userId int32, model *repository.UserModel, existingRoles map[int]repository.UserRoleModel, token string, managerAuth func(resource string, token string, object string) bool, tx *pg.Tx, entity string, capacity int) ([]casbin2.Policy, bool, error) { +func (impl *UserServiceImpl) createOrUpdateUserRolesForOtherEntity(roleFilter bean.RoleFilter, userId int32, model *repository.UserModel, existingRoles map[int]repository.UserRoleModel, tx *pg.Tx, entity string, capacity int) ([]casbin2.Policy, bool, error) { rolesChanged := false var policiesToBeAdded = make([]casbin2.Policy, 0, capacity) actionType := roleFilter.Action @@ -1782,14 +1755,6 @@ func (impl *UserServiceImpl) createOrUpdateUserRolesForOtherEntity(roleFilter be environments := strings.Split(roleFilter.Environment, ",") for _, environment := range environments { for _, entityName := range entityNames { - if managerAuth != nil && entity != bean.CHART_GROUP_ENTITY { - // check auth only for apps permission, skip for chart group - rbacObject := fmt.Sprintf("%s", roleFilter.Team) - isValidAuth := managerAuth(casbin2.ResourceUser, token, rbacObject) - if !isValidAuth { - continue - } - } roleModel, err := impl.userAuthRepository.GetRoleByFilterForAllTypes(entity, roleFilter.Team, entityName, environment, actionType, accessType, "", "", "", "", "", actionType, false, "") if err != nil { impl.logger.Errorw("error in getting role by all type", "err", err, "roleFilter", roleFilter) @@ -1835,8 +1800,7 @@ func (impl *UserServiceImpl) createOrUpdateUserRolesForOtherEntity(roleFilter be return policiesToBeAdded, rolesChanged, nil } -func (impl *UserServiceImpl) createOrUpdateUserRolesForJobsEntity(roleFilter bean.RoleFilter, userId int32, model *repository.UserModel, existingRoles map[int]repository.UserRoleModel, token string, managerAuth func(resource string, token string, object string) bool, tx *pg.Tx, entity string, capacity int) ([]casbin2.Policy, bool, error) { - +func (impl *UserServiceImpl) createOrUpdateUserRolesForJobsEntity(roleFilter bean.RoleFilter, userId int32, model *repository.UserModel, existingRoles map[int]repository.UserRoleModel, tx *pg.Tx, entity string, capacity int) ([]casbin2.Policy, bool, error) { rolesChanged := false actionType := roleFilter.Action accessType := roleFilter.AccessType @@ -1847,14 +1811,6 @@ func (impl *UserServiceImpl) createOrUpdateUserRolesForJobsEntity(roleFilter bea for _, environment := range environments { for _, entityName := range entityNames { for _, workflow := range workflows { - if managerAuth != nil { - // check auth only for apps permission, skip for chart group - rbacObject := fmt.Sprintf("%s", roleFilter.Team) - isValidAuth := managerAuth(casbin2.ResourceUser, token, rbacObject) - if !isValidAuth { - continue - } - } roleModel, err := impl.userAuthRepository.GetRoleByFilterForAllTypes(entity, roleFilter.Team, entityName, environment, actionType, accessType, "", "", "", "", "", actionType, false, workflow) if err != nil { impl.logger.Errorw("error in getting role by all type", "err", err, "roleFilter", roleFilter)