Skip to content

Commit 34c948d

Browse files
kripanshdevtronmanish-agrawal-aivikramdevtronkartik-579
authored
feat: Resource viewer implementation for a cluster (#2811)
* API for getApiResources for a cluster * update resource api * resource delete API change * native k8s support added for APIs * gkv resource list api spec added * added gkv resource list api * resource list request dto modified * added script * namespaces api for cluster * status added for resource list * create resources API * bug fix in get api-resources API * events list api modified, for all gkv or by namespace * bug fixes * added cluster rbac for roleGroup * createResources changed to applyResources * updated cluster entity for create/update user apis * api refactored * extracted code for checking cluster entity rbac * added casbin sql script for superadmin cluster entity policies * updated spec * applyResource change to createOrUpdate * review changes and event list for all gkv fixed * containers list added in resource list api * auth for resource list api * global kind event check added for event listing * wip -fixes * wip -fixes * rbac for k8s event listing * rbac changes for k8s resource list and event list * sql file renamed and wire * namespace bug fix * added new api permission cluster list * cluster extended wire fix, resource not found check * fix * added check for timestamp for resource list * event and gvk api bug fix * resourceIf with acceptHeader for getReeourceList API * resource list api response modified * refactor resource listing and added namespace * refactoring * review comment - itr1 * merged * event listing changes revert and added updated api spec for cluster list and resource list * RBAC apply handling added * code cleanup * bug fix * reverted file change * auth on namespace list * removed unused struct * resource browser RBAC handling * code cleanup * safe checks added * not setting namespace in resource list API if gvk is global * patch fix * namespaced handling * compile fix * cluster and namespace list api handling for super admin * review change * resource listing for global kind event customize, and fix for namespace list * refactoring * aply resource bug fix * nil pointer fix * wire file * down file change * resource clubbing handling * resource name fix for groups * group roles checked for namespace and cluster list api * added one more item star if has access to all namespaces * refactoring * manager auth RBAC handling * multi resource validation handling * all namespace data reverted * RBAC in getApiResources API * RBAC fix in ApplyResource for namespace * bug fixes : 1) if cluster list is empty for non super admin user, then sending 403 * bug fixes : 1) sending 403 if empty api-resources * fix: all group with particular kind handling * Resource browser rbac (#2836) * RBAC hierarchy check added * hiererachy handlign for workload APIs * gvk fix * app group name fix * switch case correction Co-authored-by: Manish Agrawal <[email protected]> * Revert "Resource browser rbac (#2836)" (#2838) This reverts commit 094d3b8. Co-authored-by: Manish Agrawal <[email protected]> Co-authored-by: vikramdevtron <[email protected]> Co-authored-by: kartik-579 <[email protected]> Co-authored-by: Manish Agrawal <[email protected]>
1 parent e73fc15 commit 34c948d

35 files changed

+3092
-621
lines changed

api/apiToken/ApiTokenRestHandler.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,8 @@ func (impl ApiTokenRestHandlerImpl) DeleteApiToken(w http.ResponseWriter, r *htt
212212
common.WriteJsonResp(w, err, res, http.StatusOK)
213213
}
214214

215-
func (handler ApiTokenRestHandlerImpl) checkManagerAuth(token string, object string) bool {
216-
if ok := handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionUpdate, strings.ToLower(object)); !ok {
215+
func (handler ApiTokenRestHandlerImpl) checkManagerAuth(resource, token, object string) bool {
216+
if ok := handler.enforcer.Enforce(token, resource, casbin.ActionUpdate, strings.ToLower(object)); !ok {
217217
return false
218218
}
219219
return true

api/bean/UserRequest.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ type RoleFilter struct {
6060
Environment string `json:"environment"`
6161
Action string `json:"action"`
6262
AccessType string `json:"accessType"`
63+
64+
Cluster string `json:"cluster"`
65+
Namespace string `json:"namespace"`
66+
Group string `json:"group"`
67+
Kind string `json:"kind"`
68+
Resource string `json:"resource"`
6369
}
6470

6571
type Role struct {
@@ -76,6 +82,12 @@ type RoleData struct {
7682
Environment string `json:"environment"`
7783
Action string `json:"action"`
7884
AccessType string `json:"accessType"`
85+
86+
Cluster string `json:"cluster"`
87+
Namespace string `json:"namespace"`
88+
Group string `json:"group"`
89+
Kind string `json:"kind"`
90+
Resource string `json:"resource"`
7991
}
8092

8193
type SSOLoginDto struct {
@@ -95,11 +107,11 @@ const (
95107
type PolicyType int
96108

97109
const (
98-
POLICY_DIRECT PolicyType = 1
99-
POLICY_GROUP PolicyType = 1
110+
POLICY_DIRECT PolicyType = 1
111+
POLICY_GROUP PolicyType = 1
112+
SUPERADMIN = "role:super-admin___"
113+
APP_ACCESS_TYPE_HELM = "helm-app"
114+
USER_TYPE_API_TOKEN = "apiToken"
115+
CHART_GROUP_ENTITY = "chart-group"
116+
CLUSTER_ENTITIY = "cluster"
100117
)
101-
102-
const SUPERADMIN = "role:super-admin___"
103-
const APP_ACCESS_TYPE_HELM = "helm-app"
104-
105-
const USER_TYPE_API_TOKEN = "apiToken"

api/cluster/ClusterRestHandler.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ type ClusterRestHandler interface {
5151

5252
FindAllForAutoComplete(w http.ResponseWriter, r *http.Request)
5353
DeleteCluster(w http.ResponseWriter, r *http.Request)
54+
GetClusterNamespaces(w http.ResponseWriter, r *http.Request)
5455
GetAllClusterNamespaces(w http.ResponseWriter, r *http.Request)
56+
FindAllForClusterPermission(w http.ResponseWriter, r *http.Request)
5557
}
5658

5759
type ClusterRestHandlerImpl struct {
@@ -376,3 +378,68 @@ func (impl ClusterRestHandlerImpl) GetAllClusterNamespaces(w http.ResponseWriter
376378

377379
common.WriteJsonResp(w, nil, clusterNamespaces, http.StatusOK)
378380
}
381+
382+
func (impl ClusterRestHandlerImpl) GetClusterNamespaces(w http.ResponseWriter, r *http.Request) {
383+
//token := r.Header.Get("token")
384+
vars := mux.Vars(r)
385+
clusterIdString := vars["clusterId"]
386+
387+
userId, err := impl.userService.GetLoggedInUser(r)
388+
if userId == 0 || err != nil {
389+
impl.logger.Errorw("user not authorized", "error", err, "userId", userId)
390+
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
391+
return
392+
}
393+
token := r.Header.Get("token")
394+
isActionUserSuperAdmin := false
395+
if ok := impl.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*"); ok {
396+
isActionUserSuperAdmin = true
397+
}
398+
clusterId, err := strconv.Atoi(clusterIdString)
399+
if err != nil {
400+
impl.logger.Errorw("failed to extract clusterId from param", "error", err, "clusterId", clusterIdString)
401+
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
402+
return
403+
}
404+
405+
allClusterNamespaces, err := impl.clusterService.FindAllNamespacesByUserIdAndClusterId(userId, clusterId, isActionUserSuperAdmin)
406+
if err != nil {
407+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
408+
return
409+
}
410+
common.WriteJsonResp(w, nil, allClusterNamespaces, http.StatusOK)
411+
}
412+
413+
func (impl ClusterRestHandlerImpl) FindAllForClusterPermission(w http.ResponseWriter, r *http.Request) {
414+
userId, err := impl.userService.GetLoggedInUser(r)
415+
if userId == 0 || err != nil {
416+
impl.logger.Errorw("user not authorized", "error", err, "userId", userId)
417+
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
418+
return
419+
}
420+
token := r.Header.Get("token")
421+
isActionUserSuperAdmin := false
422+
if ok := impl.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*"); ok {
423+
isActionUserSuperAdmin = true
424+
}
425+
clusterList, err := impl.clusterService.FindAllForClusterByUserId(userId, isActionUserSuperAdmin)
426+
if err != nil {
427+
impl.logger.Errorw("error in deleting cluster", "err", err)
428+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
429+
return
430+
}
431+
// RBAC enforcer applying
432+
// Already applied at service layer
433+
//RBAC enforcer Ends
434+
435+
if len(clusterList) == 0 {
436+
// assumption is that if list is empty, then it can happen only in case of Unauthorized (but not sending Unauthorized for super-admin user)
437+
if isActionUserSuperAdmin {
438+
clusterList = make([]cluster.ClusterBean, 0)
439+
} else {
440+
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
441+
return
442+
}
443+
}
444+
common.WriteJsonResp(w, err, clusterList, http.StatusOK)
445+
}

api/cluster/ClusterRouter.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,19 @@ func (impl ClusterRouterImpl) InitClusterRouter(clusterRouter *mux.Router) {
5757
Methods("GET").
5858
HandlerFunc(impl.clusterRestHandler.FindAllForAutoComplete)
5959

60+
clusterRouter.Path("/namespaces/{clusterId}").
61+
Methods("GET").
62+
HandlerFunc(impl.clusterRestHandler.GetClusterNamespaces)
63+
6064
clusterRouter.Path("/namespaces").
6165
Methods("GET").
6266
HandlerFunc(impl.clusterRestHandler.GetAllClusterNamespaces)
6367

6468
clusterRouter.Path("").
6569
Methods("DELETE").
6670
HandlerFunc(impl.clusterRestHandler.DeleteCluster)
71+
72+
clusterRouter.Path("/auth-list").
73+
Methods("GET").
74+
HandlerFunc(impl.clusterRestHandler.FindAllForClusterPermission)
6775
}

api/user/UserRestHandler.go

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,21 +64,24 @@ type userNamePassword struct {
6464
}
6565

6666
type UserRestHandlerImpl struct {
67-
userService user.UserService
68-
validator *validator.Validate
69-
logger *zap.SugaredLogger
70-
enforcer casbin.Enforcer
71-
roleGroupService user.RoleGroupService
67+
userService user.UserService
68+
validator *validator.Validate
69+
logger *zap.SugaredLogger
70+
enforcer casbin.Enforcer
71+
roleGroupService user.RoleGroupService
72+
userCommonService user.UserCommonService
7273
}
7374

7475
func NewUserRestHandlerImpl(userService user.UserService, validator *validator.Validate,
75-
logger *zap.SugaredLogger, enforcer casbin.Enforcer, roleGroupService user.RoleGroupService) *UserRestHandlerImpl {
76+
logger *zap.SugaredLogger, enforcer casbin.Enforcer, roleGroupService user.RoleGroupService,
77+
userCommonService user.UserCommonService) *UserRestHandlerImpl {
7678
userAuthHandler := &UserRestHandlerImpl{
77-
userService: userService,
78-
validator: validator,
79-
logger: logger,
80-
enforcer: enforcer,
81-
roleGroupService: roleGroupService,
79+
userService: userService,
80+
validator: validator,
81+
logger: logger,
82+
enforcer: enforcer,
83+
roleGroupService: roleGroupService,
84+
userCommonService: userCommonService,
8285
}
8386
return userAuthHandler
8487
}
@@ -118,6 +121,12 @@ func (handler UserRestHandlerImpl) CreateUser(w http.ResponseWriter, r *http.Req
118121
return
119122
}
120123
}
124+
if filter.Entity == bean.CLUSTER_ENTITIY {
125+
if ok := handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth); !ok {
126+
response.WriteResponse(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized"))
127+
return
128+
}
129+
}
121130
}
122131
} else {
123132
if ok := handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, "*"); !ok {
@@ -274,6 +283,11 @@ func (handler UserRestHandlerImpl) GetById(w http.ResponseWriter, r *http.Reques
274283
authPass = false
275284
}
276285
}
286+
if filter.Entity == bean.CLUSTER_ENTITIY {
287+
if ok := handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth); !ok {
288+
authPass = false
289+
}
290+
}
277291
if authPass {
278292
filteredRoleFilter = append(filteredRoleFilter, filter)
279293
}
@@ -332,6 +346,12 @@ func (handler UserRestHandlerImpl) GetAll(w http.ResponseWriter, r *http.Request
332346
break
333347
}
334348
}
349+
if filter.Entity == bean.CLUSTER_ENTITIY {
350+
if ok := handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth); ok {
351+
isAuthorised = true
352+
break
353+
}
354+
}
335355
}
336356
}
337357
}
@@ -414,6 +434,12 @@ func (handler UserRestHandlerImpl) DeleteUser(w http.ResponseWriter, r *http.Req
414434
return
415435
}
416436
}
437+
if filter.Entity == bean.CLUSTER_ENTITIY {
438+
if ok := handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth); !ok {
439+
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
440+
return
441+
}
442+
}
417443
}
418444
} else {
419445
if ok := handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionDelete, ""); !ok {
@@ -461,6 +487,11 @@ func (handler UserRestHandlerImpl) FetchRoleGroupById(w http.ResponseWriter, r *
461487
authPass = false
462488
}
463489
}
490+
if filter.Entity == bean.CLUSTER_ENTITIY {
491+
if isValidAuth := handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth); !isValidAuth {
492+
authPass = false
493+
}
494+
}
464495
if authPass {
465496
filteredRoleFilter = append(filteredRoleFilter, filter)
466497
}
@@ -507,6 +538,12 @@ func (handler UserRestHandlerImpl) CreateRoleGroup(w http.ResponseWriter, r *htt
507538
return
508539
}
509540
}
541+
if filter.Entity == bean.CLUSTER_ENTITIY && !isActionUserSuperAdmin {
542+
if isValidAuth := handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth); !isValidAuth {
543+
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
544+
return
545+
}
546+
}
510547
}
511548
} else {
512549
if ok := handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionCreate, "*"); !ok {
@@ -616,6 +653,13 @@ func (handler UserRestHandlerImpl) FetchRoleGroups(w http.ResponseWriter, r *htt
616653
break
617654
}
618655
}
656+
if filter.Entity == bean.CLUSTER_ENTITIY {
657+
if isValidAuth := handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth); isValidAuth {
658+
isAuthorised = true
659+
break
660+
}
661+
}
662+
619663
}
620664
}
621665
}
@@ -714,6 +758,12 @@ func (handler UserRestHandlerImpl) DeleteRoleGroup(w http.ResponseWriter, r *htt
714758
return
715759
}
716760
}
761+
if filter.Entity == bean.CLUSTER_ENTITIY {
762+
if isValidAuth := handler.userCommonService.CheckRbacForClusterEntity(filter.Cluster, filter.Namespace, filter.Group, filter.Kind, filter.Resource, token, handler.CheckManagerAuth); !isValidAuth {
763+
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
764+
return
765+
}
766+
}
717767
}
718768
}
719769
//RBAC enforcer Ends
@@ -881,8 +931,8 @@ func (handler UserRestHandlerImpl) InvalidateRoleCache(w http.ResponseWriter, r
881931

882932
}
883933

884-
func (handler UserRestHandlerImpl) CheckManagerAuth(token string, object string) bool {
885-
if ok := handler.enforcer.Enforce(token, casbin.ResourceUser, casbin.ActionUpdate, strings.ToLower(object)); !ok {
934+
func (handler UserRestHandlerImpl) CheckManagerAuth(resource, token string, object string) bool {
935+
if ok := handler.enforcer.Enforce(token, resource, casbin.ActionUpdate, strings.ToLower(object)); !ok {
886936
return false
887937
}
888938
return true

0 commit comments

Comments
 (0)