diff --git a/api/router/router.go b/api/router/router.go index a5d29b33c3..893a0c3643 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -118,6 +118,7 @@ type MuxRouter struct { userTerminalAccessRouter terminal2.UserTerminalAccessRouter ciStatusUpdateCron cron.CiStatusUpdateCron appGroupingRouter AppGroupingRouter + rbacRoleRouter user.RbacRoleRouter } func NewMuxRouter(logger *zap.SugaredLogger, HelmRouter PipelineTriggerRouter, PipelineConfigRouter PipelineConfigRouter, @@ -146,7 +147,8 @@ func NewMuxRouter(logger *zap.SugaredLogger, HelmRouter PipelineTriggerRouter, P helmApplicationStatusUpdateHandler cron.CdApplicationStatusUpdateHandler, k8sCapacityRouter k8s.K8sCapacityRouter, webhookHelmRouter webhookHelm.WebhookHelmRouter, globalCMCSRouter GlobalCMCSRouter, userTerminalAccessRouter terminal2.UserTerminalAccessRouter, - jobRouter JobRouter, ciStatusUpdateCron cron.CiStatusUpdateCron, appGroupingRouter AppGroupingRouter) *MuxRouter { + jobRouter JobRouter, ciStatusUpdateCron cron.CiStatusUpdateCron, appGroupingRouter AppGroupingRouter, + rbacRoleRouter user.RbacRoleRouter) *MuxRouter { r := &MuxRouter{ Router: mux.NewRouter(), HelmRouter: HelmRouter, @@ -215,6 +217,7 @@ func NewMuxRouter(logger *zap.SugaredLogger, HelmRouter PipelineTriggerRouter, P ciStatusUpdateCron: ciStatusUpdateCron, JobRouter: jobRouter, appGroupingRouter: appGroupingRouter, + rbacRoleRouter: rbacRoleRouter, } return r } @@ -422,4 +425,7 @@ func (r MuxRouter) Init() { userTerminalAccessRouter := r.Router.PathPrefix("/orchestrator/user/terminal").Subrouter() r.userTerminalAccessRouter.InitTerminalAccessRouter(userTerminalAccessRouter) + + rbacRoleRouter := r.Router.PathPrefix("/orchestrator/rbac/role").Subrouter() + r.rbacRoleRouter.InitRbacRoleRouter(rbacRoleRouter) } diff --git a/api/user/RbacRoleRestHandler.go b/api/user/RbacRoleRestHandler.go new file mode 100644 index 0000000000..bca5b253d7 --- /dev/null +++ b/api/user/RbacRoleRestHandler.go @@ -0,0 +1,58 @@ +package user + +import ( + "errors" + "github.com/devtron-labs/devtron/api/restHandler/common" + "github.com/devtron-labs/devtron/pkg/user" + "github.com/devtron-labs/devtron/pkg/user/casbin" + "go.uber.org/zap" + "gopkg.in/go-playground/validator.v9" + "net/http" +) + +type RbacRoleRestHandler interface { + GetAllDefaultRoles(w http.ResponseWriter, r *http.Request) +} + +type RbacRoleRestHandlerImpl struct { + logger *zap.SugaredLogger + validator *validator.Validate + rbacRoleService user.RbacRoleService + userService user.UserService + enforcer casbin.Enforcer +} + +func NewRbacRoleHandlerImpl(logger *zap.SugaredLogger, + validator *validator.Validate, rbacRoleService user.RbacRoleService, + userService user.UserService, enforcer casbin.Enforcer) *RbacRoleRestHandlerImpl { + rbacRoleRestHandlerImpl := &RbacRoleRestHandlerImpl{ + logger: logger, + validator: validator, + rbacRoleService: rbacRoleService, + userService: userService, + enforcer: enforcer, + } + return rbacRoleRestHandlerImpl +} + +func (handler *RbacRoleRestHandlerImpl) GetAllDefaultRoles(w http.ResponseWriter, r *http.Request) { + userId, err := handler.userService.GetLoggedInUser(r) + if userId == 0 || err != nil { + common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized) + return + } + handler.logger.Debugw("request payload, GetAllDefaultRoles") + // RBAC enforcer applying + token := r.Header.Get("token") + if ok := handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionGet, "*"); !ok { + common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden) + return + } + roles, err := handler.rbacRoleService.GetAllDefaultRoles() + if err != nil { + handler.logger.Errorw("service error, GetAllDefaultRoles", "err", err) + common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) + return + } + common.WriteJsonResp(w, nil, roles, http.StatusOK) +} diff --git a/api/user/RbacRoleRouter.go b/api/user/RbacRoleRouter.go new file mode 100644 index 0000000000..5831349739 --- /dev/null +++ b/api/user/RbacRoleRouter.go @@ -0,0 +1,32 @@ +package user + +import ( + "github.com/gorilla/mux" + "go.uber.org/zap" + "gopkg.in/go-playground/validator.v9" +) + +type RbacRoleRouter interface { + InitRbacRoleRouter(rbacRoleRouter *mux.Router) +} + +type RbacRoleRouterImpl struct { + logger *zap.SugaredLogger + validator *validator.Validate + rbacRoleRestHandler RbacRoleRestHandler +} + +func NewRbacRoleRouterImpl(logger *zap.SugaredLogger, + validator *validator.Validate, rbacRoleRestHandler RbacRoleRestHandler) *RbacRoleRouterImpl { + rbacRoleRouterImpl := &RbacRoleRouterImpl{ + logger: logger, + validator: validator, + rbacRoleRestHandler: rbacRoleRestHandler, + } + return rbacRoleRouterImpl +} + +func (router RbacRoleRouterImpl) InitRbacRoleRouter(rbacRoleRouter *mux.Router) { + rbacRoleRouter.Path(""). + HandlerFunc(router.rbacRoleRestHandler.GetAllDefaultRoles).Methods("GET") +} diff --git a/api/user/wire_user.go b/api/user/wire_user.go index 9f9d4bbd51..b9bce6e75c 100644 --- a/api/user/wire_user.go +++ b/api/user/wire_user.go @@ -55,4 +55,11 @@ var UserWireSet = wire.NewSet( wire.Bind(new(repository.RbacRoleDataRepository), new(*repository.RbacRoleDataRepositoryImpl)), repository.NewRbacDataCacheFactoryImpl, wire.Bind(new(repository.RbacDataCacheFactory), new(*repository.RbacDataCacheFactoryImpl)), + + NewRbacRoleRouterImpl, + wire.Bind(new(RbacRoleRouter), new(*RbacRoleRouterImpl)), + NewRbacRoleHandlerImpl, + wire.Bind(new(RbacRoleRestHandler), new(*RbacRoleRestHandlerImpl)), + user.NewRbacRoleServiceImpl, + wire.Bind(new(user.RbacRoleService), new(*user.RbacRoleServiceImpl)), ) diff --git a/cmd/external-app/router.go b/cmd/external-app/router.go index 2ea0e9d295..ba4f7a7b31 100644 --- a/cmd/external-app/router.go +++ b/cmd/external-app/router.go @@ -57,6 +57,7 @@ type MuxRouter struct { userTerminalAccessRouter terminal.UserTerminalAccessRouter attributesRouter router.AttributesRouter appRouter router.AppRouter + rbacRoleRouter user.RbacRoleRouter } func NewMuxRouter( @@ -86,6 +87,7 @@ func NewMuxRouter( userTerminalAccessRouter terminal.UserTerminalAccessRouter, attributesRouter router.AttributesRouter, appRouter router.AppRouter, + rbacRoleRouter user.RbacRoleRouter, ) *MuxRouter { r := &MuxRouter{ Router: mux.NewRouter(), @@ -116,6 +118,7 @@ func NewMuxRouter( userTerminalAccessRouter: userTerminalAccessRouter, attributesRouter: attributesRouter, appRouter: appRouter, + rbacRoleRouter: rbacRoleRouter, } return r } @@ -157,7 +160,8 @@ func (r *MuxRouter) Init() { r.UserAuthRouter.InitUserAuthRouter(rootRouter) userRouter := baseRouter.PathPrefix("/user").Subrouter() r.userRouter.InitUserRouter(userRouter) - + rbacRoleRouter := baseRouter.PathPrefix("/rbac/role").Subrouter() + r.rbacRoleRouter.InitRbacRoleRouter(rbacRoleRouter) clusterRouter := baseRouter.PathPrefix("/cluster").Subrouter() r.clusterRouter.InitClusterRouter(clusterRouter) diff --git a/cmd/external-app/wire_gen.go b/cmd/external-app/wire_gen.go index f7635c593a..d1063c7c5b 100644 --- a/cmd/external-app/wire_gen.go +++ b/cmd/external-app/wire_gen.go @@ -323,7 +323,10 @@ func InitializeApp() (*App, error) { appCrudOperationServiceImpl := app2.NewAppCrudOperationServiceImpl(appLabelRepositoryImpl, sugaredLogger, appRepositoryImpl, userRepositoryImpl, installedAppRepositoryImpl) appRestHandlerImpl := restHandler.NewAppRestHandlerImpl(sugaredLogger, appCrudOperationServiceImpl, userServiceImpl, validate, enforcerUtilImpl, enforcerImpl, helmAppServiceImpl, enforcerUtilHelmImpl) appRouterImpl := router.NewAppRouterImpl(sugaredLogger, appRestHandlerImpl) - muxRouter := NewMuxRouter(sugaredLogger, ssoLoginRouterImpl, teamRouterImpl, userAuthRouterImpl, userRouterImpl, clusterRouterImpl, dashboardRouterImpl, helmAppRouterImpl, environmentRouterImpl, k8sApplicationRouterImpl, chartRepositoryRouterImpl, appStoreDiscoverRouterImpl, appStoreValuesRouterImpl, appStoreDeploymentRouterImpl, dashboardTelemetryRouterImpl, commonDeploymentRouterImpl, externalLinkRouterImpl, moduleRouterImpl, serverRouterImpl, apiTokenRouterImpl, k8sCapacityRouterImpl, webhookHelmRouterImpl, userAttributesRouterImpl, telemetryRouterImpl, userTerminalAccessRouterImpl, attributesRouterImpl, appRouterImpl) + rbacRoleServiceImpl := user.NewRbacRoleServiceImpl(sugaredLogger, rbacRoleDataRepositoryImpl) + rbacRoleRestHandlerImpl := user2.NewRbacRoleHandlerImpl(sugaredLogger, validate, rbacRoleServiceImpl, userServiceImpl, enforcerImpl) + rbacRoleRouterImpl := user2.NewRbacRoleRouterImpl(sugaredLogger, validate, rbacRoleRestHandlerImpl) + muxRouter := NewMuxRouter(sugaredLogger, ssoLoginRouterImpl, teamRouterImpl, userAuthRouterImpl, userRouterImpl, clusterRouterImpl, dashboardRouterImpl, helmAppRouterImpl, environmentRouterImpl, k8sApplicationRouterImpl, chartRepositoryRouterImpl, appStoreDiscoverRouterImpl, appStoreValuesRouterImpl, appStoreDeploymentRouterImpl, dashboardTelemetryRouterImpl, commonDeploymentRouterImpl, externalLinkRouterImpl, moduleRouterImpl, serverRouterImpl, apiTokenRouterImpl, k8sCapacityRouterImpl, webhookHelmRouterImpl, userAttributesRouterImpl, telemetryRouterImpl, userTerminalAccessRouterImpl, attributesRouterImpl, appRouterImpl, rbacRoleRouterImpl) mainApp := NewApp(db, sessionManager, muxRouter, telemetryEventClientImpl, posthogClient, sugaredLogger) return mainApp, nil } diff --git a/pkg/user/RbacRoleService.go b/pkg/user/RbacRoleService.go new file mode 100644 index 0000000000..dc6814ff46 --- /dev/null +++ b/pkg/user/RbacRoleService.go @@ -0,0 +1,47 @@ +package user + +import ( + "github.com/devtron-labs/devtron/pkg/user/bean" + "github.com/devtron-labs/devtron/pkg/user/repository" + "go.uber.org/zap" +) + +type RbacRoleService interface { + GetAllDefaultRoles() ([]*bean.RbacRoleDto, error) +} + +type RbacRoleServiceImpl struct { + logger *zap.SugaredLogger + rbacRoleDataRepository repository.RbacRoleDataRepository +} + +func NewRbacRoleServiceImpl(logger *zap.SugaredLogger, + rbacRoleDataRepository repository.RbacRoleDataRepository) *RbacRoleServiceImpl { + return &RbacRoleServiceImpl{ + logger: logger, + rbacRoleDataRepository: rbacRoleDataRepository, + } +} +func (impl *RbacRoleServiceImpl) GetAllDefaultRoles() ([]*bean.RbacRoleDto, error) { + //getting all roles from default data repository + defaultRoles, err := impl.rbacRoleDataRepository.GetRoleDataForAllRoles() + if err != nil { + impl.logger.Errorw("error in getting all default roles data", "err", err) + return nil, err + } + defaultRolesResp := make([]*bean.RbacRoleDto, 0, len(defaultRoles)) + for _, defaultRole := range defaultRoles { + defaultRoleResp := &bean.RbacRoleDto{ + Id: defaultRole.Id, + RoleName: defaultRole.Role, + RoleDisplayName: defaultRole.RoleDisplayName, + RoleDescription: defaultRole.RoleDescription, + RbacPolicyEntityGroupDto: &bean.RbacPolicyEntityGroupDto{ + Entity: defaultRole.Entity, + AccessType: defaultRole.AccessType, + }, + } + defaultRolesResp = append(defaultRolesResp, defaultRoleResp) + } + return defaultRolesResp, nil +} diff --git a/pkg/user/bean/bean.go b/pkg/user/bean/bean.go index b85f02d2ac..d965548b5a 100644 --- a/pkg/user/bean/bean.go +++ b/pkg/user/bean/bean.go @@ -31,3 +31,16 @@ const ( EMPTY_ROLEFILTER_ENTRY_PLACEHOLDER = "NONE" RoleNotFoundStatusPrefix = "role not fount for any given filter: " ) + +type RbacRoleDto struct { + Id int `json:"id"` // id of the default role + RoleName string `json:"roleName"` + RoleDisplayName string `json:"roleDisplayName"` + RoleDescription string `json:"roleDescription"` + *RbacPolicyEntityGroupDto +} + +type RbacPolicyEntityGroupDto struct { + Entity string `json:"entity" validate:"oneof=apps cluster chart-group"` + AccessType string `json:"accessType,omitempty"` +} diff --git a/wire_gen.go b/wire_gen.go index f2cbe9b413..838d322a25 100644 --- a/wire_gen.go +++ b/wire_gen.go @@ -701,7 +701,10 @@ func InitializeApp() (*App, error) { ciStatusUpdateCronImpl := cron.NewCiStatusUpdateCronImpl(sugaredLogger, appServiceImpl, ciWorkflowStatusUpdateConfig, ciPipelineRepositoryImpl, ciHandlerImpl) appGroupRestHandlerImpl := restHandler.NewAppGroupRestHandlerImpl(sugaredLogger, enforcerImpl, userServiceImpl, appGroupServiceImpl, validate) appGroupingRouterImpl := router.NewAppGroupingRouterImpl(pipelineConfigRestHandlerImpl, appWorkflowRestHandlerImpl, appGroupRestHandlerImpl) - muxRouter := router.NewMuxRouter(sugaredLogger, pipelineTriggerRouterImpl, pipelineConfigRouterImpl, migrateDbRouterImpl, appListingRouterImpl, environmentRouterImpl, clusterRouterImpl, webhookRouterImpl, userAuthRouterImpl, applicationRouterImpl, cdRouterImpl, projectManagementRouterImpl, gitProviderRouterImpl, gitHostRouterImpl, dockerRegRouterImpl, notificationRouterImpl, teamRouterImpl, gitWebhookHandlerImpl, workflowStatusUpdateHandlerImpl, applicationStatusHandlerImpl, ciEventHandlerImpl, pubSubClientServiceImpl, userRouterImpl, chartRefRouterImpl, configMapRouterImpl, appStoreRouterImpl, chartRepositoryRouterImpl, releaseMetricsRouterImpl, deploymentGroupRouterImpl, batchOperationRouterImpl, chartGroupRouterImpl, testSuitRouterImpl, imageScanRouterImpl, policyRouterImpl, gitOpsConfigRouterImpl, dashboardRouterImpl, attributesRouterImpl, userAttributesRouterImpl, commonRouterImpl, grafanaRouterImpl, ssoLoginRouterImpl, telemetryRouterImpl, telemetryEventClientImplExtended, bulkUpdateRouterImpl, webhookListenerRouterImpl, appRouterImpl, coreAppRouterImpl, helmAppRouterImpl, k8sApplicationRouterImpl, pProfRouterImpl, deploymentConfigRouterImpl, dashboardTelemetryRouterImpl, commonDeploymentRouterImpl, externalLinkRouterImpl, globalPluginRouterImpl, moduleRouterImpl, serverRouterImpl, apiTokenRouterImpl, cdApplicationStatusUpdateHandlerImpl, k8sCapacityRouterImpl, webhookHelmRouterImpl, globalCMCSRouterImpl, userTerminalAccessRouterImpl, jobRouterImpl, ciStatusUpdateCronImpl, appGroupingRouterImpl) + rbacRoleServiceImpl := user.NewRbacRoleServiceImpl(sugaredLogger, rbacRoleDataRepositoryImpl) + rbacRoleRestHandlerImpl := user2.NewRbacRoleHandlerImpl(sugaredLogger, validate, rbacRoleServiceImpl, userServiceImpl, enforcerImpl) + rbacRoleRouterImpl := user2.NewRbacRoleRouterImpl(sugaredLogger, validate, rbacRoleRestHandlerImpl) + muxRouter := router.NewMuxRouter(sugaredLogger, pipelineTriggerRouterImpl, pipelineConfigRouterImpl, migrateDbRouterImpl, appListingRouterImpl, environmentRouterImpl, clusterRouterImpl, webhookRouterImpl, userAuthRouterImpl, applicationRouterImpl, cdRouterImpl, projectManagementRouterImpl, gitProviderRouterImpl, gitHostRouterImpl, dockerRegRouterImpl, notificationRouterImpl, teamRouterImpl, gitWebhookHandlerImpl, workflowStatusUpdateHandlerImpl, applicationStatusHandlerImpl, ciEventHandlerImpl, pubSubClientServiceImpl, userRouterImpl, chartRefRouterImpl, configMapRouterImpl, appStoreRouterImpl, chartRepositoryRouterImpl, releaseMetricsRouterImpl, deploymentGroupRouterImpl, batchOperationRouterImpl, chartGroupRouterImpl, testSuitRouterImpl, imageScanRouterImpl, policyRouterImpl, gitOpsConfigRouterImpl, dashboardRouterImpl, attributesRouterImpl, userAttributesRouterImpl, commonRouterImpl, grafanaRouterImpl, ssoLoginRouterImpl, telemetryRouterImpl, telemetryEventClientImplExtended, bulkUpdateRouterImpl, webhookListenerRouterImpl, appRouterImpl, coreAppRouterImpl, helmAppRouterImpl, k8sApplicationRouterImpl, pProfRouterImpl, deploymentConfigRouterImpl, dashboardTelemetryRouterImpl, commonDeploymentRouterImpl, externalLinkRouterImpl, globalPluginRouterImpl, moduleRouterImpl, serverRouterImpl, apiTokenRouterImpl, cdApplicationStatusUpdateHandlerImpl, k8sCapacityRouterImpl, webhookHelmRouterImpl, globalCMCSRouterImpl, userTerminalAccessRouterImpl, jobRouterImpl, ciStatusUpdateCronImpl, appGroupingRouterImpl, rbacRoleRouterImpl) mainApp := NewApp(muxRouter, sugaredLogger, sseSSE, syncedEnforcer, db, pubSubClientServiceImpl, sessionManager, posthogClient) return mainApp, nil }