From f799aa4b9156024868a51f5ca127fa54d49ca383 Mon Sep 17 00:00:00 2001 From: kartik-579 <84493919+kartik-579@users.noreply.github.com> Date: Tue, 8 Feb 2022 16:58:01 +0530 Subject: [PATCH] feat: Terminal access for trigger roles (#1128) * wip * wip - added method for updating terminal access policy for old users * added default policy update method, fixed ci trigger bug, added api for terminal access update * wip * fixed sql query * fixed sql script * fixed sql script - default auth role * updated auth for trigger ci api * wip - ci trigger bug fix * reverted ci trigger changes * wip * wip - rbac for terminal access removed * fixed policy json error * wip - added rbac back for update terminal access and ci trigger apis * ci trigger fix + update policy api fix * wip * fixed casbin policy update input * fix sql query for getting roles * wip * wip * wip * added own KeyMatch func * updated keyMatch func * formatted code * fixed condition * review changes * updated conf file for rbac matcher func update * updated key match func * updated trigger policy for terminal object * review changes * added rbac in terminal api for charts --- api/restHandler/ArgoApplicationRestHandler.go | 28 +- .../app/BuildPipelineRestHandler.go | 51 +- api/user/UserRestHandler.go | 27 + api/user/UserRouter.go | 2 + api/user/wire_user.go | 4 + auth_model.conf | 2 +- cmd/external-app/auth_model.conf | 2 +- pkg/user/UserService.go | 10 + pkg/user/casbin/rbac.go | 58 ++ pkg/user/casbin/rbacpolicy.go | 2 + .../repository/DefaultAuthPolicyRepository.go | 75 +++ .../repository/DefaultAuthRoleRepository.go | 58 ++ pkg/user/repository/UserAuthRepository.go | 589 +++++++++++++++--- scripts/argo-assets/model.conf | 2 +- scripts/sql/33_terminal_access.down.sql | 3 + scripts/sql/33_terminal_access.up.sql | 309 +++++++++ util/rbac/EnforcerUtil.go | 15 + wire_gen.go | 4 +- 18 files changed, 1119 insertions(+), 122 deletions(-) create mode 100644 pkg/user/repository/DefaultAuthPolicyRepository.go create mode 100644 pkg/user/repository/DefaultAuthRoleRepository.go create mode 100644 scripts/sql/33_terminal_access.down.sql create mode 100644 scripts/sql/33_terminal_access.up.sql diff --git a/api/restHandler/ArgoApplicationRestHandler.go b/api/restHandler/ArgoApplicationRestHandler.go index 297036424f..537275cf27 100644 --- a/api/restHandler/ArgoApplicationRestHandler.go +++ b/api/restHandler/ArgoApplicationRestHandler.go @@ -115,6 +115,13 @@ func (impl ArgoApplicationRestHandlerImpl) GetTerminalSession(w http.ResponseWri return } request.AppId = id + //below method is for getting new object, i.e. team/env/app for new trigger policy + teamEnvRbacObject := impl.enforcerUtil.GetTeamEnvRBACNameByAppId(id, eId) + if teamEnvRbacObject == "" { + common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) + return + } + //below methods are for getting old objects for old policies (admin, manager roles) appRbacObject := impl.enforcerUtil.GetAppRBACNameByAppId(id) if appRbacObject == "" { common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) @@ -126,11 +133,24 @@ func (impl ArgoApplicationRestHandlerImpl) GetTerminalSession(w http.ResponseWri return } request.EnvironmentId = eId - if ok := impl.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionCreate, appRbacObject); !ok { - common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) - return + valid := false + + //checking if the user has access of terminal with new trigger policy, if not then will check old rbac + if ok := impl.enforcer.Enforce(token, casbin.ResourceTerminal, casbin.ActionExec, teamEnvRbacObject); !ok { + appRbacOk := impl.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionCreate, appRbacObject) + envRbacOk := impl.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionCreate, envRbacObject) + if appRbacOk && envRbacOk{ + valid = true + } + } else{ + valid = true + } + //checking rbac for charts + if ok := impl.enforcer.Enforce(token, casbin.ResourceHelmApp, casbin.ActionCreate, teamEnvRbacObject); ok { + valid = true } - if ok := impl.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionCreate, envRbacObject); !ok { + //if both the new rbac(trigger access) and old rbac fails then user is forbidden to access terminal + if !valid { common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) return } diff --git a/api/restHandler/app/BuildPipelineRestHandler.go b/api/restHandler/app/BuildPipelineRestHandler.go index c2ee8b72d8..d578bd914f 100644 --- a/api/restHandler/app/BuildPipelineRestHandler.go +++ b/api/restHandler/app/BuildPipelineRestHandler.go @@ -232,32 +232,21 @@ func (handler PipelineConfigRestHandlerImpl) TriggerCiPipeline(w http.ResponseWr handler.Logger.Infow("request payload, TriggerCiPipeline", "payload", ciTriggerRequest) //RBAC CHECK CD PIPELINE - FOR USER - pipelines, err := handler.pipelineRepository.FindAutomaticByCiPipelineId(ciTriggerRequest.PipelineId) - var authorizedPipelines []pipelineConfig.Pipeline - var unauthorizedPipelines []pipelineConfig.Pipeline - //fetching user only for getting token - triggeredBy, err := handler.userAuthService.GetById(ciTriggerRequest.TriggeredBy) - if err != nil { - handler.Logger.Errorw("service err, TriggerCiPipeline", "err", err, "payload", ciTriggerRequest) - common.WriteJsonResp(w, err, nil, http.StatusBadRequest) + pipelines, err := handler.pipelineRepository.FindByCiPipelineId(ciTriggerRequest.PipelineId) + if err!= nil{ + handler.Logger.Errorw("error in finding ccd pipelines by ciPipelineId", "err", err) + common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) return } - token := triggeredBy.AccessToken + var authorizedPipelines []pipelineConfig.Pipeline + var unauthorizedPipelines []pipelineConfig.Pipeline + token := r.Header.Get("token") for _, p := range pipelines { - pass := 0 object := handler.enforcerUtil.GetAppRBACNameByAppId(p.AppId) - if ok := handler.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionTrigger, object); !ok { - handler.Logger.Debug(fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) - } else { - pass = 1 - } + appRbacOk := handler.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionTrigger, object) object = handler.enforcerUtil.GetAppRBACByAppIdAndPipelineId(p.AppId, p.Id) - if ok := handler.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionTrigger, object); !ok { - handler.Logger.Debug(fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) - } else { - pass = 2 - } - if pass == 2 { + envRbacOk := handler.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionTrigger, object) + if appRbacOk && envRbacOk { authorizedPipelines = append(authorizedPipelines, *p) } else { unauthorizedPipelines = append(unauthorizedPipelines, *p) @@ -266,7 +255,24 @@ func (handler PipelineConfigRestHandlerImpl) TriggerCiPipeline(w http.ResponseWr resMessage := "allowed for all pipelines" response := make(map[string]string) if len(unauthorizedPipelines) > 0 { - resMessage = "not authorized for few pipelines, will not effected" + resMessage = "some pipelines not authorized" + common.WriteJsonResp(w, err, "Unauthorized User", http.StatusForbidden) + return + } + if len(authorizedPipelines) == 0{ + //user has no cd pipeline + ciPipeline, err := handler.ciPipelineRepository.FindById(ciTriggerRequest.PipelineId) + if err != nil { + handler.Logger.Errorw("err in finding ci pipeline, TriggerCiPipeline", "err", err, "ciPipelineId", ciTriggerRequest.PipelineId) + common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) + return + } + object := handler.enforcerUtil.GetAppRBACNameByAppId(ciPipeline.AppId) + if ok := handler.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionTrigger, object); !ok { + handler.Logger.Debug(fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) + common.WriteJsonResp(w, err, "Unauthorized User", http.StatusForbidden) + return + } } //RBAC CHECK CD PIPELINE - FOR USER @@ -274,6 +280,7 @@ func (handler PipelineConfigRestHandlerImpl) TriggerCiPipeline(w http.ResponseWr if err != nil { handler.Logger.Errorw("service err, TriggerCiPipeline", "err", err, "payload", ciTriggerRequest) common.WriteJsonResp(w, err, response, http.StatusInternalServerError) + return } response["apiResponse"] = strconv.Itoa(resp) response["authStatus"] = resMessage diff --git a/api/user/UserRestHandler.go b/api/user/UserRestHandler.go index e66069164d..be5d85e50a 100644 --- a/api/user/UserRestHandler.go +++ b/api/user/UserRestHandler.go @@ -51,6 +51,7 @@ type UserRestHandler interface { DeleteRoleGroup(w http.ResponseWriter, r *http.Request) CheckUserRoles(w http.ResponseWriter, r *http.Request) SyncOrchestratorToCasbin(w http.ResponseWriter, r *http.Request) + UpdateTriggerPolicyForTerminalAccess(w http.ResponseWriter, r *http.Request) } type userNamePassword struct { @@ -688,3 +689,29 @@ func (handler UserRestHandlerImpl) SyncOrchestratorToCasbin(w http.ResponseWrite } common.WriteJsonResp(w, err, flag, http.StatusOK) } + +func (handler UserRestHandlerImpl) UpdateTriggerPolicyForTerminalAccess(w http.ResponseWriter, r *http.Request) { + userId, err := handler.userService.GetLoggedInUser(r) + if userId == 0 || err != nil { + handler.logger.Errorw("unauthorized user, UpdateTriggerPolicyForTerminalAccess", "userId", userId) + common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized) + return + } + + // RBAC enforcer applying + token := r.Header.Get("token") + if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionUpdate, "*"); !ok { + handler.logger.Errorw("unauthorized user, UpdateTriggerPolicyForTerminalAccess", "userId", userId) + common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden) + return + } + //RBAC enforcer Ends + + err = handler.userService.UpdateTriggerPolicyForTerminalAccess() + if err != nil { + handler.logger.Errorw("error in updating trigger policy for terminal access", "err", err) + common.WriteJsonResp(w, err, "", http.StatusInternalServerError) + return + } + common.WriteJsonResp(w, nil, "Trigger policy updated successfully.", http.StatusOK) +} diff --git a/api/user/UserRouter.go b/api/user/UserRouter.go index 10e962b686..e95e7260fb 100644 --- a/api/user/UserRouter.go +++ b/api/user/UserRouter.go @@ -67,4 +67,6 @@ func (router UserRouterImpl) InitUserRouter(userAuthRouter *mux.Router) { HandlerFunc(router.userRestHandler.CheckUserRoles).Methods("GET") userAuthRouter.Path("/sync/orchestratortocasbin"). HandlerFunc(router.userRestHandler.SyncOrchestratorToCasbin).Methods("GET") + userAuthRouter.Path("/update/trigger/terminal"). + HandlerFunc(router.userRestHandler.UpdateTriggerPolicyForTerminalAccess).Methods("PUT") } diff --git a/api/user/wire_user.go b/api/user/wire_user.go index 0e8163768e..09c90dc679 100644 --- a/api/user/wire_user.go +++ b/api/user/wire_user.go @@ -18,6 +18,10 @@ var UserWireSet = wire.NewSet( wire.Bind(new(user.UserAuthService), new(*user.UserAuthServiceImpl)), repository.NewUserAuthRepositoryImpl, wire.Bind(new(repository.UserAuthRepository), new(*repository.UserAuthRepositoryImpl)), + repository.NewDefaultAuthPolicyRepositoryImpl, + wire.Bind(new(repository.DefaultAuthPolicyRepository), new(*repository.DefaultAuthPolicyRepositoryImpl)), + repository.NewDefaultAuthRoleRepositoryImpl, + wire.Bind(new(repository.DefaultAuthRoleRepository), new(*repository.DefaultAuthRoleRepositoryImpl)), NewUserRouterImpl, wire.Bind(new(UserRouter), new(*UserRouterImpl)), diff --git a/auth_model.conf b/auth_model.conf index 4ff2b484d2..3058041ae3 100644 --- a/auth_model.conf +++ b/auth_model.conf @@ -11,5 +11,5 @@ e = some(where (p.eft == allow)) && !some(where (p.eft == deny)) g = _, _ [matchers] -m = g(r.sub, p.sub) && keyMatch(r.res, p.res) && keyMatch(r.act, p.act) && keyMatch(r.obj, p.obj) +m = g(r.sub, p.sub) && matchKeyByPart(r.res, p.res) && matchKeyByPart(r.act, p.act) && matchKeyByPart(r.obj, p.obj) diff --git a/cmd/external-app/auth_model.conf b/cmd/external-app/auth_model.conf index 4ff2b484d2..3058041ae3 100644 --- a/cmd/external-app/auth_model.conf +++ b/cmd/external-app/auth_model.conf @@ -11,5 +11,5 @@ e = some(where (p.eft == allow)) && !some(where (p.eft == deny)) g = _, _ [matchers] -m = g(r.sub, p.sub) && keyMatch(r.res, p.res) && keyMatch(r.act, p.act) && keyMatch(r.obj, p.obj) +m = g(r.sub, p.sub) && matchKeyByPart(r.res, p.res) && matchKeyByPart(r.act, p.act) && matchKeyByPart(r.obj, p.obj) diff --git a/pkg/user/UserService.go b/pkg/user/UserService.go index 56eb7c5a58..02a0316d48 100644 --- a/pkg/user/UserService.go +++ b/pkg/user/UserService.go @@ -49,6 +49,7 @@ type UserService interface { IsSuperAdmin(userId int) (bool, error) GetByIdIncludeDeleted(id int32) (*bean.UserInfo, error) UserExists(emailId string) bool + UpdateTriggerPolicyForTerminalAccess() (err error) } type UserServiceImpl struct { @@ -959,3 +960,12 @@ func (impl UserServiceImpl) GetByIdIncludeDeleted(id int32) (*bean.UserInfo, err } return response, nil } + +func (impl UserServiceImpl) UpdateTriggerPolicyForTerminalAccess() (err error) { + err = impl.userAuthRepository.UpdateTriggerPolicyForTerminalAccess() + if err != nil { + impl.logger.Errorw("error in updating policy for terminal access to trigger role", "err", err) + return err + } + return nil +} diff --git a/pkg/user/casbin/rbac.go b/pkg/user/casbin/rbac.go index 96c8a8e869..cf1d4741b8 100644 --- a/pkg/user/casbin/rbac.go +++ b/pkg/user/casbin/rbac.go @@ -100,6 +100,8 @@ func (e *EnforcerImpl) enforce(enf *casbin.Enforcer, rvals ...interface{}) bool email = sub } rvals[0] = strings.ToLower(email) + //adding our key matching func - MatchKeyFunc, to enforcer + enf.AddFunction("matchKeyByPart", MatchKeyByPartFunc) return enf.Enforce(rvals...) } @@ -109,5 +111,61 @@ func (e *EnforcerImpl) enforceByEmail(enf *casbin.Enforcer, rvals ...interface{} if len(rvals) == 0 { return false } + //adding our key matching func - MatchKeyFunc, to enforcer + enf.AddFunction("matchKeyByPart", MatchKeyByPartFunc) return enf.Enforce(rvals...) } + +// MatchKeyByPartFunc is the wrapper of our own customised MatchKeyByPart Func +func MatchKeyByPartFunc(args ...interface{}) (interface{}, error) { + name1 := args[0].(string) + name2 := args[1].(string) + + return bool(MatchKeyByPart(name1, name2)), nil +} + +// MatchKeyByPart checks whether values in key1 matches all values of key2(values are obtained by splitting key by "/") +// For example - key1 = "a/b/c" matches key2 = "a/*/c" but not matches for key2 = "a/*/d" +func MatchKeyByPart(key1 string, key2 string) bool { + + if key2 == "*"{ + //policy must be for super-admin role or global-env action + //no need to check further + return true + } + + key1Vals := strings.Split(key1, "/") + key2Vals := strings.Split(key2, "/") + + if (len(key1Vals) != len(key2Vals)) || len(key1Vals) == 0 { + //values in keys should be more than zero and must be equal + return false + } + + for i, key2Val := range key2Vals { + key1Val := key1Vals[i] + + if key2Val == "" || key1Val == "" { + //empty values are not allowed in any key + return false + } else { + // getting index of "*" in key2, will check values of key1 accordingly + //for example - key2Val = a/bc*/d & key1Val = a/bcd/d, in this case "bc" will be checked in key1Val(upto index of "*") + j := strings.Index(key2Val, "*") + if j == -1 { + if key1Val != key2Val { + return false + } + } else if len(key1Val) > j { + if key1Val[:j] != key2Val[:j] { + return false + } + } else { + if key1Val != key2Val[:j] { + return false + } + } + } + } + return true +} diff --git a/pkg/user/casbin/rbacpolicy.go b/pkg/user/casbin/rbacpolicy.go index 4d8743a771..5384451214 100644 --- a/pkg/user/casbin/rbacpolicy.go +++ b/pkg/user/casbin/rbacpolicy.go @@ -27,6 +27,7 @@ const ( ResourceUser = "user" ResourceNotification = "notification" ResourceTemplate = "template" + ResourceTerminal = "terminal" ResourceProjects = "projects" ResourceApplications = "applications" @@ -48,4 +49,5 @@ const ( ActionSync = "sync" ActionTrigger = "trigger" ActionNotify = "notify" + ActionExec = "exec" ) diff --git a/pkg/user/repository/DefaultAuthPolicyRepository.go b/pkg/user/repository/DefaultAuthPolicyRepository.go new file mode 100644 index 0000000000..c596fe83ea --- /dev/null +++ b/pkg/user/repository/DefaultAuthPolicyRepository.go @@ -0,0 +1,75 @@ +package repository + +import ( + "github.com/devtron-labs/devtron/pkg/sql" + "github.com/go-pg/pg" + "go.uber.org/zap" +) + +type RoleType string + +const ( + MANAGER_TYPE RoleType = "manager" + ADMIN_TYPE RoleType = "admin" + TRIGGER_TYPE RoleType = "trigger" + VIEW_TYPE RoleType = "view" + ENTITY_ALL_TYPE RoleType = "entityAll" + ENTITY_VIEW_TYPE RoleType = "entityView" + ENTITY_SPECIFIC_TYPE RoleType = "entitySpecific" + ENTITY_SPECIFIC_ADMIN_TYPE RoleType = "entitySpecificAdmin" + ENTITY_SPECIFIC_VIEW_TYPE RoleType = "entitySpecificView" + ROLE_SPECIFIC_TYPE RoleType = "roleSpecific" +) + +type DefaultAuthPolicyRepository interface { + CreatePolicy(policy *DefaultAuthPolicy) (*DefaultAuthPolicy, error) + UpdatePolicyByRoleType(policy string, roleType RoleType) (*DefaultAuthPolicy, error) + GetPolicyByRoleType(roleType RoleType) (policy string, err error) +} + +type DefaultAuthPolicy struct { + TableName struct{} `sql:"default_auth_policy" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + RoleType string `sql:"role_type,notnull"` + Policy string `sql:"policy,notnull"` + sql.AuditLog +} + +type DefaultAuthPolicyRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewDefaultAuthPolicyRepositoryImpl(dbConnection *pg.DB, logger *zap.SugaredLogger) *DefaultAuthPolicyRepositoryImpl { + return &DefaultAuthPolicyRepositoryImpl{dbConnection: dbConnection, logger: logger} +} + +func (impl DefaultAuthPolicyRepositoryImpl) CreatePolicy(policy *DefaultAuthPolicy) (*DefaultAuthPolicy, error) { + err := impl.dbConnection.Insert(policy) + if err != nil { + impl.logger.Error("error in creating auth policy", "err", err) + return policy, err + } + return policy, nil +} + +func (impl DefaultAuthPolicyRepositoryImpl) UpdatePolicyByRoleType(policy string, roleType RoleType) (*DefaultAuthPolicy, error) { + var model DefaultAuthPolicy + _, err := impl.dbConnection.Model(&model).Set("policy = ?", policy). + Where("role_type = ?", roleType).Update() + if err != nil { + impl.logger.Error("error in updating auth policy", "err", err) + return &model, err + } + return &model, nil +} + +func (impl DefaultAuthPolicyRepositoryImpl) GetPolicyByRoleType(roleType RoleType) (policy string, err error) { + var model DefaultAuthPolicy + err = impl.dbConnection.Model(&model).Where("role_type = ?", roleType).Select() + if err != nil { + impl.logger.Error("error in getting policy by roleType", "err", err, "roleType", roleType) + return "", err + } + return model.Policy, nil +} diff --git a/pkg/user/repository/DefaultAuthRoleRepository.go b/pkg/user/repository/DefaultAuthRoleRepository.go new file mode 100644 index 0000000000..8d6ecf6ee3 --- /dev/null +++ b/pkg/user/repository/DefaultAuthRoleRepository.go @@ -0,0 +1,58 @@ +package repository + +import ( + "github.com/devtron-labs/devtron/pkg/sql" + "github.com/go-pg/pg" + "go.uber.org/zap" +) + +type DefaultAuthRoleRepository interface { + CreateRole(role *DefaultAuthRole) (*DefaultAuthRole, error) + UpdateRole(role *DefaultAuthRole) (*DefaultAuthRole, error) + GetRoleByRoleType(roleType RoleType) (role string, err error) +} + +type DefaultAuthRole struct { + TableName struct{} `sql:"default_auth_role" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + RoleType string `sql:"role_type,notnull"` + Role string `sql:"role,notnull"` + sql.AuditLog +} + +type DefaultAuthRoleRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewDefaultAuthRoleRepositoryImpl(dbConnection *pg.DB, logger *zap.SugaredLogger) *DefaultAuthRoleRepositoryImpl { + return &DefaultAuthRoleRepositoryImpl{dbConnection: dbConnection, logger: logger} +} + +func (impl DefaultAuthRoleRepositoryImpl) CreateRole(role *DefaultAuthRole) (*DefaultAuthRole, error) { + err := impl.dbConnection.Insert(role) + if err != nil { + impl.logger.Error("error in creating auth role", "err", err) + return role, err + } + return role, nil +} + +func (impl DefaultAuthRoleRepositoryImpl) UpdateRole(role *DefaultAuthRole) (*DefaultAuthRole, error) { + err := impl.dbConnection.Update(role) + if err != nil { + impl.logger.Error("error in updating auth role", "err", err) + return role, err + } + return role, nil +} + +func (impl DefaultAuthRoleRepositoryImpl) GetRoleByRoleType(roleType RoleType) (role string, err error) { + var model DefaultAuthRole + err = impl.dbConnection.Model(&model).Where("role_type = ?", roleType).Select() + if err != nil { + impl.logger.Error("error in getting role by roleType", "err", err, "roleType", roleType) + return "", err + } + return model.Role, nil +} diff --git a/pkg/user/repository/UserAuthRepository.go b/pkg/user/repository/UserAuthRepository.go index 5d00f08537..654dd1c592 100644 --- a/pkg/user/repository/UserAuthRepository.go +++ b/pkg/user/repository/UserAuthRepository.go @@ -22,13 +22,14 @@ package repository import ( "encoding/json" - "github.com/devtron-labs/devtron/pkg/sql" - "strings" - + "fmt" "github.com/devtron-labs/devtron/api/bean" + "github.com/devtron-labs/devtron/internal/util" + "github.com/devtron-labs/devtron/pkg/sql" "github.com/devtron-labs/devtron/pkg/user/casbin" "github.com/go-pg/pg" "go.uber.org/zap" + "strings" ) type UserAuthRepository interface { @@ -37,6 +38,7 @@ type UserAuthRepository interface { GetRolesByUserId(userId int32) ([]RoleModel, error) GetRolesByGroupId(userId int32) ([]*RoleModel, error) GetAllRole() ([]RoleModel, error) + GetRolesByActionAndAccessType(action string, accessType string) ([]RoleModel, error) GetRoleByFilter(entity string, team string, app string, env string, act string, accessType string) (RoleModel, error) CreateUserRoleMapping(userRoleModel *UserRoleModel, tx *pg.Tx) (*UserRoleModel, error) GetUserRoleMappingByUserId(userId int32) ([]*UserRoleModel, error) @@ -47,15 +49,25 @@ type UserAuthRepository interface { CreateDefaultPoliciesForGlobalEntity(entity string, entityName string, action string, tx *pg.Tx) (bool, error) CreateRoleForSuperAdminIfNotExists(tx *pg.Tx) (bool, error) SyncOrchestratorToCasbin(team string, entityName string, env string, tx *pg.Tx) (bool, error) + UpdateTriggerPolicyForTerminalAccess() error } type UserAuthRepositoryImpl struct { - dbConnection *pg.DB - Logger *zap.SugaredLogger + dbConnection *pg.DB + Logger *zap.SugaredLogger + defaultAuthPolicyRepository DefaultAuthPolicyRepository + defaultAuthRoleRepository DefaultAuthRoleRepository } -func NewUserAuthRepositoryImpl(dbConnection *pg.DB, Logger *zap.SugaredLogger) *UserAuthRepositoryImpl { - return &UserAuthRepositoryImpl{dbConnection: dbConnection, Logger: Logger} +func NewUserAuthRepositoryImpl(dbConnection *pg.DB, Logger *zap.SugaredLogger, + defaultAuthPolicyRepository DefaultAuthPolicyRepository, + defaultAuthRoleRepository DefaultAuthRoleRepository) *UserAuthRepositoryImpl { + return &UserAuthRepositoryImpl{ + dbConnection: dbConnection, + Logger: Logger, + defaultAuthPolicyRepository: defaultAuthPolicyRepository, + defaultAuthRoleRepository: defaultAuthRoleRepository, + } } type RoleModel struct { @@ -71,6 +83,17 @@ type RoleModel struct { sql.AuditLog } +type RolePolicyDetails struct { + Team string + Env string + App string + TeamObj string + EnvObj string + AppObj string + Entity string + EntityName string +} + func (impl UserAuthRepositoryImpl) CreateRole(userModel *RoleModel, tx *pg.Tx) (*RoleModel, error) { err := tx.Insert(userModel) if err != nil { @@ -131,6 +154,26 @@ func (impl UserAuthRepositoryImpl) GetAllRole() ([]RoleModel, error) { } return models, nil } + +func (impl UserAuthRepositoryImpl) GetRolesByActionAndAccessType(action string, accessType string) ([]RoleModel, error) { + var models []RoleModel + var err error + if accessType == "" { + err = impl.dbConnection.Model(&models).Where("action = ?", action). + Where("access_type is NULL"). + Select() + } else{ + err = impl.dbConnection.Model(&models).Where("action = ?", action). + Where("access_type = ?", accessType). + Select() + } + if err != nil { + impl.Logger.Error("err in getting role by action", "err", err, "action", action, "accessType", accessType) + return models, err + } + return models, nil +} + func (impl UserAuthRepositoryImpl) GetRoleByFilter(entity string, team string, app string, env string, act string, accessType string) (RoleModel, error) { var model RoleModel EMPTY := "" @@ -152,7 +195,7 @@ func (impl UserAuthRepositoryImpl) GetRoleByFilter(entity string, team string, a if len(accessType) == 0 { query = query + " and role.access_type is NULL" } else { - query += " and role.access_type='"+accessType+"'" + query += " and role.access_type='" + accessType + "'" } _, err = impl.dbConnection.Query(&model, query, entity, act) } else { @@ -161,7 +204,7 @@ func (impl UserAuthRepositoryImpl) GetRoleByFilter(entity string, team string, a if len(accessType) == 0 { query = query + " and role.access_type is NULL" } else { - query += " and role.access_type='"+accessType+"'" + query += " and role.access_type='" + accessType + "'" } _, err = impl.dbConnection.Query(&model, query, team, app, env, act) } else if len(team) > 0 && app == "" && len(env) > 0 && len(act) > 0 { @@ -169,7 +212,7 @@ func (impl UserAuthRepositoryImpl) GetRoleByFilter(entity string, team string, a if len(accessType) == 0 { query = query + " and role.access_type is NULL" } else { - query += " and role.access_type='"+accessType+"'" + query += " and role.access_type='" + accessType + "'" } _, err = impl.dbConnection.Query(&model, query, team, EMPTY, env, act) } else if len(team) > 0 && len(app) > 0 && env == "" && len(act) > 0 { @@ -178,7 +221,7 @@ func (impl UserAuthRepositoryImpl) GetRoleByFilter(entity string, team string, a if len(accessType) == 0 { query = query + " and role.access_type is NULL" } else { - query += " and role.access_type='"+accessType+"'" + query += " and role.access_type='" + accessType + "'" } _, err = impl.dbConnection.Query(&model, query, team, app, EMPTY, act) } else if len(team) > 0 && app == "" && env == "" && len(act) > 0 { @@ -187,7 +230,7 @@ func (impl UserAuthRepositoryImpl) GetRoleByFilter(entity string, team string, a if len(accessType) == 0 { query = query + " and role.access_type is NULL" } else { - query += " and role.access_type='"+accessType+"'" + query += " and role.access_type='" + accessType + "'" } _, err = impl.dbConnection.Query(&model, query, team, EMPTY, EMPTY, act) } else if team == "" && app == "" && env == "" && len(act) > 0 { @@ -196,7 +239,7 @@ func (impl UserAuthRepositoryImpl) GetRoleByFilter(entity string, team string, a if len(accessType) == 0 { query = query + " and role.access_type is NULL" } else { - query += " and role.access_type='"+accessType+"'" + query += " and role.access_type='" + accessType + "'" } _, err = impl.dbConnection.Query(&model, query, EMPTY, EMPTY, EMPTY, act) } else if team == "" && app == "" && env == "" && act == "" { @@ -246,26 +289,27 @@ func (impl UserAuthRepositoryImpl) CreateDefaultPolicies(team string, entityName return false, err } - managerPolicies := "{\r\n \"data\": [\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:manager___\",\r\n \"res\": \"applications\",\r\n \"act\": \"*\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:manager___\",\r\n \"res\": \"environment\",\r\n \"act\": \"*\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:manager___\",\r\n \"res\": \"team\",\r\n \"act\": \"*\",\r\n \"obj\": \"\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:manager___\",\r\n \"res\": \"user\",\r\n \"act\": \"*\",\r\n \"obj\": \"\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:manager___\",\r\n \"res\": \"notification\",\r\n \"act\": \"*\",\r\n \"obj\": \"\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:manager___\",\r\n \"res\": \"global-environment\",\r\n \"act\": \"*\",\r\n \"obj\": \"\"\r\n }\r\n ]\r\n}" - adminPolicies := "{\r\n \"data\": [\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:admin___\",\r\n \"res\": \"applications\",\r\n \"act\": \"*\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:admin___\",\r\n \"res\": \"environment\",\r\n \"act\": \"*\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:admin___\",\r\n \"res\": \"team\",\r\n \"act\": \"get\",\r\n \"obj\": \"\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:admin___\",\r\n \"res\": \"global-environment\",\r\n \"act\": \"get\",\r\n \"obj\": \"\"\r\n }\r\n ]\r\n}" - triggerPolicies := "{\r\n \"data\": [\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:trigger___\",\r\n \"res\": \"applications\",\r\n \"act\": \"get\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:trigger___\",\r\n \"res\": \"applications\",\r\n \"act\": \"trigger\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:trigger___\",\r\n \"res\": \"environment\",\r\n \"act\": \"trigger\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:trigger___\",\r\n \"res\": \"environment\",\r\n \"act\": \"get\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:trigger___\",\r\n \"res\": \"global-environment\",\r\n \"act\": \"get\",\r\n \"obj\": \"\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:trigger___\",\r\n \"res\": \"team\",\r\n \"act\": \"get\",\r\n \"obj\": \"\"\r\n }\r\n ]\r\n}" - viewPolicies := "{\r\n \"data\": [\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:view___\",\r\n \"res\": \"applications\",\r\n \"act\": \"get\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:view___\",\r\n \"res\": \"environment\",\r\n \"act\": \"get\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:view___\",\r\n \"res\": \"global-environment\",\r\n \"act\": \"get\",\r\n \"obj\": \"\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:view___\",\r\n \"res\": \"team\",\r\n \"act\": \"get\",\r\n \"obj\": \"\"\r\n }\r\n ]\r\n}" - - managerPolicies = strings.ReplaceAll(managerPolicies, "", team) - managerPolicies = strings.ReplaceAll(managerPolicies, "", env) - managerPolicies = strings.ReplaceAll(managerPolicies, "", entityName) - - adminPolicies = strings.ReplaceAll(adminPolicies, "", team) - adminPolicies = strings.ReplaceAll(adminPolicies, "", env) - adminPolicies = strings.ReplaceAll(adminPolicies, "", entityName) - - triggerPolicies = strings.ReplaceAll(triggerPolicies, "", team) - triggerPolicies = strings.ReplaceAll(triggerPolicies, "", env) - triggerPolicies = strings.ReplaceAll(triggerPolicies, "", entityName) - - viewPolicies = strings.ReplaceAll(viewPolicies, "", team) - viewPolicies = strings.ReplaceAll(viewPolicies, "", env) - viewPolicies = strings.ReplaceAll(viewPolicies, "", entityName) + //getting policies from db + managerPoliciesDb, err := impl.defaultAuthPolicyRepository.GetPolicyByRoleType(MANAGER_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default policy by roleType", "err", err, "roleType", MANAGER_TYPE) + return false, err + } + adminPoliciesDb, err := impl.defaultAuthPolicyRepository.GetPolicyByRoleType(ADMIN_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default policy by roleType", "err", err, "roleType", ADMIN_TYPE) + return false, err + } + triggerPoliciesDb, err := impl.defaultAuthPolicyRepository.GetPolicyByRoleType(TRIGGER_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default policy by roleType", "err", err, "roleType", TRIGGER_TYPE) + return false, err + } + viewPoliciesDb, err := impl.defaultAuthPolicyRepository.GetPolicyByRoleType(VIEW_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default policy by roleType", "err", err, "roleType", VIEW_TYPE) + return false, err + } //for START in Casbin Object teamObj := team @@ -280,21 +324,44 @@ func (impl UserAuthRepositoryImpl) CreateDefaultPolicies(team string, entityName if appObj == "" { appObj = "*" } - managerPolicies = strings.ReplaceAll(managerPolicies, "", teamObj) - managerPolicies = strings.ReplaceAll(managerPolicies, "", envObj) - managerPolicies = strings.ReplaceAll(managerPolicies, "", appObj) - adminPolicies = strings.ReplaceAll(adminPolicies, "", teamObj) - adminPolicies = strings.ReplaceAll(adminPolicies, "", envObj) - adminPolicies = strings.ReplaceAll(adminPolicies, "", appObj) + rolePolicyDetails := RolePolicyDetails{ + Team: team, + App: entityName, + Env: env, + TeamObj: teamObj, + EnvObj: envObj, + AppObj: appObj, + } - triggerPolicies = strings.ReplaceAll(triggerPolicies, "", teamObj) - triggerPolicies = strings.ReplaceAll(triggerPolicies, "", envObj) - triggerPolicies = strings.ReplaceAll(triggerPolicies, "", appObj) + //getting updated manager policies + managerPolicies, err := util.Tprintf(managerPoliciesDb, rolePolicyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated policies", "err", err, "roleType", MANAGER_TYPE) + return false, err + } + + //getting updated admin policies + adminPolicies, err := util.Tprintf(adminPoliciesDb, rolePolicyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated policies", "err", err, "roleType", ADMIN_TYPE) + return false, err + } + + //getting updated trigger policies + triggerPolicies, err := util.Tprintf(triggerPoliciesDb, rolePolicyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated policies", "err", err, "roleType", TRIGGER_TYPE) + return false, err + } + + //getting updated view policies + viewPolicies, err := util.Tprintf(viewPoliciesDb, rolePolicyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated policies", "err", err, "roleType", VIEW_TYPE) + return false, err + } - viewPolicies = strings.ReplaceAll(viewPolicies, "", teamObj) - viewPolicies = strings.ReplaceAll(viewPolicies, "", envObj) - viewPolicies = strings.ReplaceAll(viewPolicies, "", appObj) //for START in Casbin Object Ends Here var policiesManager bean.PolicyRequest @@ -335,25 +402,55 @@ func (impl UserAuthRepositoryImpl) CreateDefaultPolicies(team string, entityName casbin.AddPolicy(policiesView.Data) //Creating ROLES - roleManager := "{\r\n \"role\": \"role:manager___\",\r\n \"casbinSubjects\": [\r\n \"role:manager___\"\r\n ],\r\n \"team\": \"\",\r\n \"entityName\": \"\",\r\n \"environment\": \"\",\r\n \"action\": \"manager\",\r\n \"access_type\": \"\"\n}" - roleAdmin := "{\n \"role\": \"role:admin___\",\n \"casbinSubjects\": [\n \"role:admin___\"\n ],\n \"team\": \"\",\n \"entityName\": \"\",\n \"environment\": \"\",\n \"action\": \"admin\",\n \"access_type\": \"\"\n}" - roleTrigger := "{\n \"role\": \"role:trigger___\",\n \"casbinSubjects\": [\n \"role:trigger___\"\n ],\n \"team\": \"\",\n \"entityName\": \"\",\n \"environment\": \"\",\n \"action\": \"trigger\",\n \"access_type\": \"\"\n}" - roleView := "{\n \"role\": \"role:view___\",\n \"casbinSubjects\": [\n \"role:view___\"\n ],\n \"team\": \"\",\n \"entityName\": \"\",\n \"environment\": \"\",\n \"action\": \"view\",\n \"access_type\": \"\"\n}" - roleManager = strings.ReplaceAll(roleManager, "", team) - roleManager = strings.ReplaceAll(roleManager, "", env) - roleManager = strings.ReplaceAll(roleManager, "", entityName) + //getting roles from db + roleManagerDb, err := impl.defaultAuthRoleRepository.GetRoleByRoleType(MANAGER_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default role by roleType", "err", err, "roleType", MANAGER_TYPE) + return false, err + } + roleAdminDb, err := impl.defaultAuthRoleRepository.GetRoleByRoleType(ADMIN_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default role by roleType", "err", err, "roleType", ADMIN_TYPE) + return false, err + } + roleTriggerDb, err := impl.defaultAuthRoleRepository.GetRoleByRoleType(TRIGGER_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default role by roleType", "err", err, "roleType", TRIGGER_TYPE) + return false, err + } + roleViewDb, err := impl.defaultAuthRoleRepository.GetRoleByRoleType(VIEW_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default role by roleType", "err", err, "roleType", VIEW_TYPE) + return false, err + } - roleAdmin = strings.ReplaceAll(roleAdmin, "", team) - roleAdmin = strings.ReplaceAll(roleAdmin, "", env) - roleAdmin = strings.ReplaceAll(roleAdmin, "", entityName) + //getting updated manager role + roleManager, err := util.Tprintf(roleManagerDb, rolePolicyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated role", "err", err, "roleType", MANAGER_TYPE) + return false, err + } - roleTrigger = strings.ReplaceAll(roleTrigger, "", team) - roleTrigger = strings.ReplaceAll(roleTrigger, "", env) - roleTrigger = strings.ReplaceAll(roleTrigger, "", entityName) + //getting updated admin role + roleAdmin, err := util.Tprintf(roleAdminDb, rolePolicyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated role", "err", err, "roleType", ADMIN_TYPE) + return false, err + } - roleView = strings.ReplaceAll(roleView, "", team) - roleView = strings.ReplaceAll(roleView, "", env) - roleView = strings.ReplaceAll(roleView, "", entityName) + //getting updated trigger role + roleTrigger, err := util.Tprintf(roleTriggerDb, rolePolicyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated role", "err", err, "roleType", TRIGGER_TYPE) + return false, err + } + + //getting updated view role + roleView, err := util.Tprintf(roleViewDb, rolePolicyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated role", "err", err, "roleType", VIEW_TYPE) + return false, err + } var roleManagerData bean.RoleData err = json.Unmarshal([]byte(roleManager), &roleManagerData) @@ -544,11 +641,37 @@ func (impl UserAuthRepositoryImpl) CreateDefaultPoliciesForGlobalEntity(entity s } // Rollback tx on error. defer transaction.Rollback() - entityAllPolicy := "{\r\n \"data\": [\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:_admin\",\r\n \"res\": \"\",\r\n \"act\": \"*\",\r\n \"obj\": \"*\"\r\n }\r\n ]\r\n}" - entityViewPolicy := "{\r\n \"data\": [\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:_view\",\r\n \"res\": \"\",\r\n \"act\": \"get\",\r\n \"obj\": \"*\"\r\n }\r\n ]\r\n}" - entityAllPolicy = strings.ReplaceAll(entityAllPolicy, "", entity) - entityViewPolicy = strings.ReplaceAll(entityViewPolicy, "", entity) + //getting policies from db + entityAllPolicyDb, err := impl.defaultAuthPolicyRepository.GetPolicyByRoleType(ENTITY_ALL_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default policy by roleType", "err", err, "roleType", ENTITY_ALL_TYPE) + return false, err + } + entityViewPolicyDb, err := impl.defaultAuthPolicyRepository.GetPolicyByRoleType(ENTITY_VIEW_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default policy by roleType", "err", err, "roleType", ENTITY_VIEW_TYPE) + return false, err + } + + policyDetails := RolePolicyDetails{ + Entity: entity, + EntityName: entityName, + } + + //getting updated entityAll policies + entityAllPolicy, err := util.Tprintf(entityAllPolicyDb, policyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated policies", "err", err, "roleType", ENTITY_ALL_TYPE) + return false, err + } + + //getting updated entityView policies + entityViewPolicy, err := util.Tprintf(entityViewPolicyDb, policyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated policies", "err", err, "roleType", ENTITY_VIEW_TYPE) + return false, err + } //for START in Casbin Object Ends Here var policiesAdmin bean.PolicyRequest @@ -569,9 +692,19 @@ func (impl UserAuthRepositoryImpl) CreateDefaultPoliciesForGlobalEntity(entity s impl.Logger.Debugw("add policy request", "policies", policiesView) casbin.AddPolicy(policiesView.Data) - entitySpecificPolicy := "{\r\n \"data\": [\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:__specific\",\r\n \"res\": \"\",\r\n \"act\": \"update\",\r\n \"obj\": \"\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:__specific\",\r\n \"res\": \"\",\r\n \"act\": \"get\",\r\n \"obj\": \"\"\r\n }\r\n ]\r\n}" - entitySpecificPolicy = strings.ReplaceAll(entitySpecificPolicy, "", entity) - entitySpecificPolicy = strings.ReplaceAll(entitySpecificPolicy, "", entityName) + //getting policy from db + entitySpecificPolicyDb, err := impl.defaultAuthPolicyRepository.GetPolicyByRoleType(ENTITY_SPECIFIC_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default policy by roleType", "err", err, "roleType", ENTITY_SPECIFIC_TYPE) + return false, err + } + + //getting updated entitySpecific policies + entitySpecificPolicy, err := util.Tprintf(entitySpecificPolicyDb, policyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated policies", "err", err, "roleType", ENTITY_SPECIFIC_TYPE) + return false, err + } var policiesSpecific bean.PolicyRequest err = json.Unmarshal([]byte(entitySpecificPolicy), &policiesSpecific) @@ -584,10 +717,34 @@ func (impl UserAuthRepositoryImpl) CreateDefaultPoliciesForGlobalEntity(entity s //CASBIN ENDS //Creating ROLES - roleAdmin := "{\r\n \"role\": \"role:_admin\",\r\n \"casbinSubjects\": [\r\n \"role:_admin\"\r\n ],\r\n \"entity\": \"\",\r\n \"team\": \"\",\r\n \"application\": \"\",\r\n \"environment\": \"\",\r\n \"action\": \"admin\"\r\n}" - roleAdmin = strings.ReplaceAll(roleAdmin, "", entity) - roleView := "{\r\n \"role\": \"role:_view\",\r\n \"casbinSubjects\": [\r\n \"role:_view\"\r\n ],\r\n \"entity\": \"\",\r\n \"team\": \"\",\r\n \"application\": \"\",\r\n \"environment\": \"\",\r\n \"action\": \"view\"\r\n}" - roleView = strings.ReplaceAll(roleView, "", entity) + + //getting role from db + entitySpecificAdminDb, err := impl.defaultAuthRoleRepository.GetRoleByRoleType(ENTITY_SPECIFIC_ADMIN_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default policy by roleType", "err", err, "roleType", ENTITY_SPECIFIC_ADMIN_TYPE) + return false, err + } + + //getting updated role + roleAdmin, err := util.Tprintf(entitySpecificAdminDb, policyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated policies", "err", err, "roleType", ENTITY_SPECIFIC_ADMIN_TYPE) + return false, err + } + + //getting role from db + entitySpecificViewDb, err := impl.defaultAuthRoleRepository.GetRoleByRoleType(ENTITY_SPECIFIC_VIEW_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default policy by roleType", "err", err, "roleType", ENTITY_SPECIFIC_VIEW_TYPE) + return false, err + } + + //getting updated role + roleView, err := util.Tprintf(entitySpecificViewDb, policyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated policies", "err", err, "roleType", ENTITY_SPECIFIC_VIEW_TYPE) + return false, err + } var roleAdminData bean.RoleData err = json.Unmarshal([]byte(roleAdmin), &roleAdminData) @@ -617,9 +774,19 @@ func (impl UserAuthRepositoryImpl) CreateDefaultPoliciesForGlobalEntity(entity s } } - roleSpecific := "{\r\n \"role\": \"role:__specific\",\r\n \"casbinSubjects\": [\r\n \"role:__specific\"\r\n ],\r\n \"entity\": \"\",\r\n \"team\": \"\",\r\n \"entityName\": \"\",\r\n \"environment\": \"\",\r\n \"action\": \"update\"\r\n}" - roleSpecific = strings.ReplaceAll(roleSpecific, "", entity) - roleSpecific = strings.ReplaceAll(roleSpecific, "", entityName) + //getting role from db + roleSpecificDb, err := impl.defaultAuthRoleRepository.GetRoleByRoleType(ROLE_SPECIFIC_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default policy by roleType", "err", err, "roleType", ROLE_SPECIFIC_TYPE) + return false, err + } + + //getting updated role + roleSpecific, err := util.Tprintf(roleSpecificDb, policyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated policies", "err", err, "roleType", ROLE_SPECIFIC_TYPE) + return false, err + } var roleSpecificData bean.RoleData err = json.Unmarshal([]byte(roleSpecific), &roleSpecificData) @@ -694,16 +861,17 @@ func (impl UserAuthRepositoryImpl) createRole(roleData *bean.RoleData, tx *pg.Tx func (impl UserAuthRepositoryImpl) SyncOrchestratorToCasbin(team string, entityName string, env string, tx *pg.Tx) (bool, error) { - triggerPolicies := "{\r\n \"data\": [\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:trigger___\",\r\n \"res\": \"applications\",\r\n \"act\": \"get\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:trigger___\",\r\n \"res\": \"applications\",\r\n \"act\": \"trigger\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:trigger___\",\r\n \"res\": \"environment\",\r\n \"act\": \"trigger\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:trigger___\",\r\n \"res\": \"environment\",\r\n \"act\": \"get\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:trigger___\",\r\n \"res\": \"global-environment\",\r\n \"act\": \"get\",\r\n \"obj\": \"\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:trigger___\",\r\n \"res\": \"team\",\r\n \"act\": \"get\",\r\n \"obj\": \"\"\r\n }\r\n ]\r\n}" - viewPolicies := "{\r\n \"data\": [\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:view___\",\r\n \"res\": \"applications\",\r\n \"act\": \"get\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:view___\",\r\n \"res\": \"environment\",\r\n \"act\": \"get\",\r\n \"obj\": \"/\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:view___\",\r\n \"res\": \"global-environment\",\r\n \"act\": \"get\",\r\n \"obj\": \"\"\r\n },\r\n {\r\n \"type\": \"p\",\r\n \"sub\": \"role:view___\",\r\n \"res\": \"team\",\r\n \"act\": \"get\",\r\n \"obj\": \"\"\r\n }\r\n ]\r\n}" - - triggerPolicies = strings.ReplaceAll(triggerPolicies, "", team) - triggerPolicies = strings.ReplaceAll(triggerPolicies, "", env) - triggerPolicies = strings.ReplaceAll(triggerPolicies, "", entityName) - - viewPolicies = strings.ReplaceAll(viewPolicies, "", team) - viewPolicies = strings.ReplaceAll(viewPolicies, "", env) - viewPolicies = strings.ReplaceAll(viewPolicies, "", entityName) + //getting policies from db + triggerPoliciesDb, err := impl.defaultAuthPolicyRepository.GetPolicyByRoleType(TRIGGER_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default policy by roleType", "err", err, "roleType", TRIGGER_TYPE) + return false, err + } + viewPoliciesDb, err := impl.defaultAuthPolicyRepository.GetPolicyByRoleType(VIEW_TYPE) + if err != nil { + impl.Logger.Errorw("error in getting default policy by roleType", "err", err, "roleType", VIEW_TYPE) + return false, err + } //for START in Casbin Object teamObj := team @@ -719,17 +887,33 @@ func (impl UserAuthRepositoryImpl) SyncOrchestratorToCasbin(team string, entityN appObj = "*" } - triggerPolicies = strings.ReplaceAll(triggerPolicies, "", teamObj) - triggerPolicies = strings.ReplaceAll(triggerPolicies, "", envObj) - triggerPolicies = strings.ReplaceAll(triggerPolicies, "", appObj) + policyDetails := RolePolicyDetails{ + Team: team, + App: entityName, + Env: env, + TeamObj: teamObj, + EnvObj: envObj, + AppObj: appObj, + } + + //getting updated trigger policies + triggerPolicies, err := util.Tprintf(triggerPoliciesDb, policyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated policies", "err", err, "roleType", TRIGGER_TYPE) + return false, err + } + + //getting updated view policies + viewPolicies, err := util.Tprintf(viewPoliciesDb, policyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated policies", "err", err, "roleType", VIEW_TYPE) + return false, err + } - viewPolicies = strings.ReplaceAll(viewPolicies, "", teamObj) - viewPolicies = strings.ReplaceAll(viewPolicies, "", envObj) - viewPolicies = strings.ReplaceAll(viewPolicies, "", appObj) //for START in Casbin Object Ends Here var policiesTrigger bean.PolicyRequest - err := json.Unmarshal([]byte(triggerPolicies), &policiesTrigger) + err = json.Unmarshal([]byte(triggerPolicies), &policiesTrigger) if err != nil { impl.Logger.Errorw("decode err", "err", err) return false, err @@ -748,3 +932,224 @@ func (impl UserAuthRepositoryImpl) SyncOrchestratorToCasbin(team string, entityN return true, nil } + +func (impl UserAuthRepositoryImpl) UpdateTriggerPolicyForTerminalAccess() (err error) { + newTriggerPolicy := `{ + "data": [ + { + "type": "p", + "sub": "role:trigger_{{.Team}}_{{.Env}}_{{.App}}", + "res": "applications", + "act": "get", + "obj": "{{.TeamObj}}/{{.AppObj}}" + }, + { + "type": "p", + "sub": "role:trigger_{{.Team}}_{{.Env}}_{{.App}}", + "res": "applications", + "act": "trigger", + "obj": "{{.TeamObj}}/{{.AppObj}}" + }, + { + "type": "p", + "sub": "role:trigger_{{.Team}}_{{.Env}}_{{.App}}", + "res": "environment", + "act": "trigger", + "obj": "{{.EnvObj}}/{{.AppObj}}" + }, + { + "type": "p", + "sub": "role:trigger_{{.Team}}_{{.Env}}_{{.App}}", + "res": "environment", + "act": "get", + "obj": "{{.EnvObj}}/{{.AppObj}}" + }, + { + "type": "p", + "sub": "role:trigger_{{.Team}}_{{.Env}}_{{.App}}", + "res": "global-environment", + "act": "get", + "obj": "{{.EnvObj}}" + }, + { + "type": "p", + "sub": "role:trigger_{{.Team}}_{{.Env}}_{{.App}}", + "res": "team", + "act": "get", + "obj": "{{.TeamObj}}" + }, + { + "type": "p", + "sub": "role:trigger_{{.Team}}_{{.Env}}_{{.App}}", + "res": "terminal", + "act": "exec", + "obj": "{{.TeamObj}}/{{.EnvObj}}/{{.AppObj}}" + } + ] +}` + err = impl.UpdateDefaultPolicyByRoleType(newTriggerPolicy, TRIGGER_TYPE) + if err != nil { + impl.Logger.Errorw("error in updating default policy for trigger role", "err", err) + return err + } + return nil +} + +func (impl UserAuthRepositoryImpl) GetDefaultPolicyByRoleType(roleType RoleType) (policy string, err error) { + policy, err = impl.defaultAuthPolicyRepository.GetPolicyByRoleType(roleType) + if err != nil { + impl.Logger.Errorw("error in getting default policy by role type", "err", err, "roleType", roleType) + return "", err + } + return policy, nil +} + +func (impl UserAuthRepositoryImpl) UpdateDefaultPolicyByRoleType(newPolicy string, roleType RoleType) (err error) { + //getting all roles by role type + roles, err := impl.GetRolesByActionAndAccessType(string(roleType), "") + if err != nil { + impl.Logger.Errorw("error in getting roles for trigger action", "err", err) + return err + } + oldPolicy, err := impl.defaultAuthPolicyRepository.GetPolicyByRoleType(roleType) + if err != nil { + impl.Logger.Errorw("error in getting default policy by roleType", "err", err, "roleType", roleType) + return err + } + + //updating new policy in db + _, err = impl.defaultAuthPolicyRepository.UpdatePolicyByRoleType(newPolicy, roleType) + if err != nil { + impl.Logger.Errorw("error in updating default policy by roleType", "err", err, "roleType", roleType) + return err + } + + //getting diff between new and old policy(policies deleted/added) + addedPolicies, deletedPolicies, err := impl.GetDiffBetweenPolicies(oldPolicy, newPolicy) + if err != nil { + impl.Logger.Errorw("error in getting diff between old and new policy", "err", err) + return err + } + var addedPolicyFinal bean.PolicyRequest + var deletedPolicyFinal bean.PolicyRequest + for _, role := range roles { + teamObj := role.Team + envObj := role.Environment + appObj := role.EntityName + if teamObj == "" { + teamObj = "*" + } + if envObj == "" { + envObj = "*" + } + if appObj == "" { + appObj = "*" + } + + rolePolicyDetails := RolePolicyDetails{ + Team: role.Team, + Env: role.Environment, + App: role.EntityName, + TeamObj: teamObj, + EnvObj: envObj, + AppObj: appObj, + } + if len(addedPolicies) > 0 { + addedPolicyReq, err := impl.GetUpdatedAddedOrDeletedPolicies(addedPolicies, rolePolicyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated added policies", "err", err) + return err + } + addedPolicyFinal.Data = append(addedPolicyFinal.Data, addedPolicyReq.Data...) + } + if len(deletedPolicies) > 0 { + deletedPolicyReq, err := impl.GetUpdatedAddedOrDeletedPolicies(deletedPolicies, rolePolicyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated deleted policies", "err", err) + return err + } + deletedPolicyFinal.Data = append(deletedPolicyFinal.Data, deletedPolicyReq.Data...) + } + } + //updating all policies(for all roles) in casbin + if len(addedPolicyFinal.Data) > 0 { + casbin.AddPolicy(addedPolicyFinal.Data) + } + if len(deletedPolicyFinal.Data) > 0 { + casbin.RemovePolicy(deletedPolicyFinal.Data) + } + return nil +} + +func (impl UserAuthRepositoryImpl) GetDiffBetweenPolicies(oldPolicy string, newPolicy string) (addedPolicies []casbin.Policy, deletedPolicies []casbin.Policy, err error) { + var oldPolicyObj bean.PolicyRequest + err = json.Unmarshal([]byte(oldPolicy), &oldPolicyObj) + if err != nil { + impl.Logger.Errorw("error in un-marshaling old policy", "err", err) + return addedPolicies, deletedPolicies, err + } + + var newPolicyObj bean.PolicyRequest + err = json.Unmarshal([]byte(newPolicy), &newPolicyObj) + if err != nil { + impl.Logger.Errorw("error in un-marshaling new policy", "err", err) + return addedPolicies, deletedPolicies, err + } + + oldPolicyMap := make(map[string]bool) + for _, oldPolicyData := range oldPolicyObj.Data { + //converting all fields of data to a string + data := fmt.Sprintf("type:%s,sub:%s,res:%s,act:%s,obj:%s", oldPolicyData.Type, oldPolicyData.Sub, oldPolicyData.Res, oldPolicyData.Act, oldPolicyData.Obj) + //creating entry for data, keeping false because if present in new policy + //then will be set to true and will not be included in deletedPolicies + oldPolicyMap[data] = false + } + + for _, newPolicyData := range newPolicyObj.Data { + //converting all fields of data to a string + data := fmt.Sprintf("type:%s,sub:%s,res:%s,act:%s,obj:%s", newPolicyData.Type, newPolicyData.Sub, newPolicyData.Res, newPolicyData.Act, newPolicyData.Obj) + + if _, ok := oldPolicyMap[data]; !ok { + //data not present in old policy, to be included in addedPolicies + addedPolicies = append(addedPolicies, newPolicyData) + } else { + //data present in old policy; set old policy to true, so it does not get included in deletedPolicies + oldPolicyMap[data] = true + } + } + + //check oldPolicies for updating deletedPolicies + for _, oldPolicyData := range oldPolicyObj.Data { + data := fmt.Sprintf("type:%s,sub:%s,res:%s,act:%s,obj:%s", oldPolicyData.Type, oldPolicyData.Sub, oldPolicyData.Res, oldPolicyData.Act, oldPolicyData.Obj) + if presentInNew := oldPolicyMap[data]; !presentInNew { + //data not present in old policy, to be included in addedPolicies + deletedPolicies = append(deletedPolicies, oldPolicyData) + } + } + + return addedPolicies, deletedPolicies, nil +} + +func (impl UserAuthRepositoryImpl) GetUpdatedAddedOrDeletedPolicies(policies []casbin.Policy, rolePolicyDetails RolePolicyDetails) (bean.PolicyRequest, error) { + var policyResp bean.PolicyRequest + var policyReq bean.PolicyRequest + policyReq.Data = policies + policy, err := json.Marshal(policyReq) + if err != nil { + impl.Logger.Errorw("error in marshaling policy", "err", err) + return policyResp, err + } + //getting updated policy + updatedPolicy, err := util.Tprintf(string(policy), rolePolicyDetails) + if err != nil { + impl.Logger.Errorw("error in getting updated policy", "err", err) + return policyResp, err + } + + err = json.Unmarshal([]byte(updatedPolicy), &policyResp) + if err != nil { + impl.Logger.Errorw("error in un-marshaling policy", "err", err) + return policyResp, err + } + return policyResp, nil +} diff --git a/scripts/argo-assets/model.conf b/scripts/argo-assets/model.conf index 92664af6ff..a310a29dca 100644 --- a/scripts/argo-assets/model.conf +++ b/scripts/argo-assets/model.conf @@ -11,4 +11,4 @@ g = _, _ e = some(where (p.eft == allow)) && !some(where (p.eft == deny)) [matchers] -m = g(r.sub, p.sub) && keyMatch(r.res, p.res) && keyMatch(r.act, p.act) && keyMatch(r.obj, p.obj) +m = g(r.sub, p.sub) && matchKeyByPart(r.res, p.res) && matchKeyByPart(r.act, p.act) && matchKeyByPart(r.obj, p.obj) diff --git a/scripts/sql/33_terminal_access.down.sql b/scripts/sql/33_terminal_access.down.sql new file mode 100644 index 0000000000..bb7d7e71f4 --- /dev/null +++ b/scripts/sql/33_terminal_access.down.sql @@ -0,0 +1,3 @@ +DROP TABLE "public"."default_auth_policy" CASCADE; + +DROP TABLE "public"."default_auth_role" CASCADE; \ No newline at end of file diff --git a/scripts/sql/33_terminal_access.up.sql b/scripts/sql/33_terminal_access.up.sql new file mode 100644 index 0000000000..925b47c579 --- /dev/null +++ b/scripts/sql/33_terminal_access.up.sql @@ -0,0 +1,309 @@ +-- Sequence and defined type +CREATE SEQUENCE IF NOT EXISTS id_seq_default_auth_policy; + +-- Table Definition +CREATE TABLE "public"."default_auth_policy" ( + "id" int NOT NULL DEFAULT nextval('id_seq_default_auth_policy'::regclass), + "role_type" varchar(250) NOT NULL, + "policy" text NOT NULL, + "created_on" timestamptz, + "created_by" integer, + "updated_on" timestamptz, + "updated_by" integer, + PRIMARY KEY ("id") +); + +INSERT INTO "public"."default_auth_policy" ("id", "role_type", "policy", "created_on", "created_by", "updated_on", "updated_by") VALUES +('1', 'manager', '{ + "data": [ + { + "type": "p", + "sub": "role:manager_{{.Team}}_{{.Env}}_{{.App}}", + "res": "applications", + "act": "*", + "obj": "{{.TeamObj}}/{{.AppObj}}" + }, + { + "type": "p", + "sub": "role:manager_{{.Team}}_{{.Env}}_{{.App}}", + "res": "environment", + "act": "*", + "obj": "{{.EnvObj}}/{{.AppObj}}" + }, + { + "type": "p", + "sub": "role:manager_{{.Team}}_{{.Env}}_{{.App}}", + "res": "team", + "act": "*", + "obj": "{{.TeamObj}}" + }, + { + "type": "p", + "sub": "role:manager_{{.Team}}_{{.Env}}_{{.App}}", + "res": "user", + "act": "*", + "obj": "{{.TeamObj}}" + }, + { + "type": "p", + "sub": "role:manager_{{.Team}}_{{.Env}}_{{.App}}", + "res": "notification", + "act": "*", + "obj": "{{.TeamObj}}" + }, + { + "type": "p", + "sub": "role:manager_{{.Team}}_{{.Env}}_{{.App}}", + "res": "global-environment", + "act": "*", + "obj": "{{.EnvObj}}" + } + ] +}', 'now()', '1', 'now()', '1'), +('2', 'admin', '{ + "data": [ + { + "type": "p", + "sub": "role:admin_{{.Team}}_{{.Env}}_{{.App}}", + "res": "applications", + "act": "*", + "obj": "{{.TeamObj}}/{{.AppObj}}" + }, + { + "type": "p", + "sub": "role:admin_{{.Team}}_{{.Env}}_{{.App}}", + "res": "environment", + "act": "*", + "obj": "{{.EnvObj}}/{{.AppObj}}" + }, + { + "type": "p", + "sub": "role:admin_{{.Team}}_{{.Env}}_{{.App}}", + "res": "team", + "act": "get", + "obj": "{{.TeamObj}}" + }, + { + "type": "p", + "sub": "role:admin_{{.Team}}_{{.Env}}_{{.App}}", + "res": "global-environment", + "act": "get", + "obj": "{{.EnvObj}}" + } + ] +}', 'now()', '1', 'now()', '1'), +('3', 'trigger', '{ + "data": [ + { + "type": "p", + "sub": "role:trigger_{{.Team}}_{{.Env}}_{{.App}}", + "res": "applications", + "act": "get", + "obj": "{{.TeamObj}}/{{.AppObj}}" + }, + { + "type": "p", + "sub": "role:trigger_{{.Team}}_{{.Env}}_{{.App}}", + "res": "applications", + "act": "trigger", + "obj": "{{.TeamObj}}/{{.AppObj}}" + }, + { + "type": "p", + "sub": "role:trigger_{{.Team}}_{{.Env}}_{{.App}}", + "res": "environment", + "act": "trigger", + "obj": "{{.EnvObj}}/{{.AppObj}}" + }, + { + "type": "p", + "sub": "role:trigger_{{.Team}}_{{.Env}}_{{.App}}", + "res": "environment", + "act": "get", + "obj": "{{.EnvObj}}/{{.AppObj}}" + }, + { + "type": "p", + "sub": "role:trigger_{{.Team}}_{{.Env}}_{{.App}}", + "res": "global-environment", + "act": "get", + "obj": "{{.EnvObj}}" + }, + { + "type": "p", + "sub": "role:trigger_{{.Team}}_{{.Env}}_{{.App}}", + "res": "team", + "act": "get", + "obj": "{{.TeamObj}}" + } + ] +}', 'now()', '1', 'now()', '1'), +('4', 'view', '{ + "data": [ + { + "type": "p", + "sub": "role:view_{{.Team}}_{{.Env}}_{{.App}}", + "res": "applications", + "act": "get", + "obj": "{{.TeamObj}}/{{.AppObj}}" + }, + { + "type": "p", + "sub": "role:view_{{.Team}}_{{.Env}}_{{.App}}", + "res": "environment", + "act": "get", + "obj": "{{.EnvObj}}/{{.AppObj}}" + }, + { + "type": "p", + "sub": "role:view_{{.Team}}_{{.Env}}_{{.App}}", + "res": "global-environment", + "act": "get", + "obj": "{{.EnvObj}}" + }, + { + "type": "p", + "sub": "role:view_{{.Team}}_{{.Env}}_{{.App}}", + "res": "team", + "act": "get", + "obj": "{{.TeamObj}}" + } + ] +}', 'now()', '1', 'now()', '1'), +('5', 'entityAll', '{ + "data": [ + { + "type": "p", + "sub": "role:{{.Entity}}_admin", + "res": "{{.Entity}}", + "act": "*", + "obj": "*" + } + ] +}', 'now()', '1', 'now()', '1'), +('6', 'entityView','{ + "data": [ + { + "type": "p", + "sub": "role:{{.Entity}}_view", + "res": "{{.Entity}}", + "act": "get", + "obj": "*" + } + ] +}', 'now()', '1', 'now()', '1'), +('7', 'entitySpecific','{ + "data": [ + { + "type": "p", + "sub": "role:{{.Entity}}_{{.EntityName}}_specific", + "res": "{{.Entity}}", + "act": "update", + "obj": "{{.EntityName}}" + }, + { + "type": "p", + "sub": "role:{{.Entity}}_{{.EntityName}}_specific", + "res": "{{.Entity}}", + "act": "get", + "obj": "{{.EntityName}}" + } + ] +}', 'now()', '1', 'now()', '1'); + + + + +-- Sequence and defined type +CREATE SEQUENCE IF NOT EXISTS id_seq_default_auth_role; + +-- Table Definition +CREATE TABLE "public"."default_auth_role" ( + "id" int NOT NULL DEFAULT nextval('id_seq_default_auth_role'::regclass), + "role_type" varchar(250) NOT NULL, + "role" text NOT NULL, + "created_on" timestamptz, + "created_by" integer, + "updated_on" timestamptz, + "updated_by" integer, + PRIMARY KEY ("id") +); + +INSERT INTO "public"."default_auth_role" ("id", "role_type", "role", "created_on", "created_by", "updated_on", "updated_by") VALUES +('1', 'manager', '{ + "role": "role:manager_{{.Team}}_{{.Env}}_{{.App}}", + "casbinSubjects": [ + "role:manager_{{.Team}}_{{.Env}}_{{.App}}" + ], + "team": "{{.Team}}", + "entityName": "{{.App}}", + "environment": "{{.Env}}", + "action": "manager", + "access_type": "" +}', 'now()', '1', 'now()', '1'), +('2', 'admin', '{ + "role": "role:admin_{{.Team}}_{{.Env}}_{{.App}}", + "casbinSubjects": [ + "role:admin_{{.Team}}_{{.Env}}_{{.App}}" + ], + "team": "{{.Team}}", + "entityName": "{{.App}}", + "environment": "{{.Env}}", + "action": "admin", + "access_type": "" +}', 'now()', '1', 'now()', '1'), +('3', 'trigger', '{ + "role": "role:trigger_{{.Team}}_{{.Env}}_{{.App}}", + "casbinSubjects": [ + "role:trigger_{{.Team}}_{{.Env}}_{{.App}}" + ], + "team": "{{.Team}}", + "entityName": "{{.App}}", + "environment": "{{.Env}}", + "action": "trigger", + "access_type": "" +}', 'now()', '1', 'now()', '1'), +('4', 'view', '{ + "role": "role:view_{{.Team}}_{{.Env}}_{{.App}}", + "casbinSubjects": [ + "role:view_{{.Team}}_{{.Env}}_{{.App}}" + ], + "team": "{{.Team}}", + "entityName": "{{.App}}", + "environment": "{{.Env}}", + "action": "view", + "access_type": "" +}', 'now()', '1', 'now()', '1'), +('5', 'entitySpecificAdmin', '{ + "role": "role:{{.Entity}}_admin", + "casbinSubjects": [ + "role:{{.Entity}}_admin" + ], + "entity": "{{.Entity}}", + "team": "", + "application": "", + "environment": "", + "action": "admin" +}', 'now()', '1', 'now()', '1'), +('6', 'entitySpecificView', '{ + "role": "role:{{.Entity}}_view", + "casbinSubjects": [ + "role:{{.Entity}}_view" + ], + "entity": "{{.Entity}}", + "team": "", + "application": "", + "environment": "", + "action": "view" +}', 'now()', '1', 'now()', '1'), +('7', 'roleSpecific', '{ + "role": "role:{{.Entity}}_{{.EntityName}}_specific", + "casbinSubjects": [ + "role:{{.Entity}}_{{.EntityName}}_specific" + ], + "entity": "{{.Entity}}", + "team": "", + "entityName": "{{.EntityName}}", + "environment": "", + "action": "update" +}', 'now()', '1', 'now()', '1'); \ No newline at end of file diff --git a/util/rbac/EnforcerUtil.go b/util/rbac/EnforcerUtil.go index 8dcd993622..70fc89896a 100644 --- a/util/rbac/EnforcerUtil.go +++ b/util/rbac/EnforcerUtil.go @@ -33,6 +33,7 @@ type EnforcerUtil interface { GetAppRBACNameByAppId(appId int) string GetAppRBACByAppNameAndEnvId(appName string, envId int) string GetAppRBACByAppIdAndPipelineId(appId int, pipelineId int) string + GetTeamEnvRBACNameByAppId(appId int, envId int) string GetEnvRBACNameByAppId(appId int, envId int) string GetTeamRBACByCiPipelineId(pipelineId int) string GetEnvRBACArrayByAppId(appId int) []string @@ -149,6 +150,20 @@ func (impl EnforcerUtilImpl) GetEnvRBACNameByAppId(appId int, envId int) string return fmt.Sprintf("%s/%s", strings.ToLower(env.EnvironmentIdentifier), strings.ToLower(appName)) } +func (impl EnforcerUtilImpl) GetTeamEnvRBACNameByAppId(appId int, envId int) string { + application, err := impl.appRepo.FindAppAndProjectByAppId(appId) + if err != nil { + return fmt.Sprintf("%s/%s/%s", "", "", "") + } + var appName = application.AppName + var teamName = application.Team.Name + env, err := impl.environmentRepository.FindById(envId) + if err != nil { + return fmt.Sprintf("%s/%s/%s", strings.ToLower(teamName), "", strings.ToLower(appName)) + } + return fmt.Sprintf("%s/%s/%s", strings.ToLower(teamName), strings.ToLower(env.EnvironmentIdentifier), strings.ToLower(appName)) +} + func (impl EnforcerUtilImpl) GetTeamRBACByCiPipelineId(pipelineId int) string { ciPipeline, err := impl.ciPipelineRepository.FindById(pipelineId) if err != nil { diff --git a/wire_gen.go b/wire_gen.go index 2c65aeb1f7..ddab19d496 100644 --- a/wire_gen.go +++ b/wire_gen.go @@ -137,7 +137,9 @@ func InitializeApp() (*App, error) { if err != nil { return nil, err } - userAuthRepositoryImpl := repository2.NewUserAuthRepositoryImpl(db, sugaredLogger) + defaultAuthPolicyRepositoryImpl := repository2.NewDefaultAuthPolicyRepositoryImpl(db, sugaredLogger) + defaultAuthRoleRepositoryImpl := repository2.NewDefaultAuthRoleRepositoryImpl(db, sugaredLogger) + userAuthRepositoryImpl := repository2.NewUserAuthRepositoryImpl(db, sugaredLogger, defaultAuthPolicyRepositoryImpl, defaultAuthRoleRepositoryImpl) runtimeConfig, err := client2.GetRuntimeConfig() if err != nil { return nil, err