From 996410fc61f0f807b6b12d506ea1ac878e0f84c6 Mon Sep 17 00:00:00 2001 From: Vishal Dalwadi <51291657+VishalDalwadi@users.noreply.github.com> Date: Fri, 8 Aug 2025 22:14:42 +0530 Subject: [PATCH] NM-57: Graphs API Forbidden for Platform User (#3577) * fix(go): permissions for network graph; * fix(go): allow platform user to get network graph; * feat(go): allow read only access to host resource to network users. * feat(go): remove specific check for hosts resource. --- controllers/middleware.go | 5 +++++ logic/user_mgmt.go | 32 ++++++++++++++++++++++++++++++++ pro/controllers/metrics.go | 19 ------------------- pro/controllers/networks.go | 31 +++++++++++++++++++++++++++++++ pro/initialize.go | 1 + pro/logic/security.go | 13 +------------ pro/logic/user_mgmt.go | 11 ++++++++++- 7 files changed, 80 insertions(+), 32 deletions(-) create mode 100644 pro/controllers/networks.go diff --git a/controllers/middleware.go b/controllers/middleware.go index abc22611..2f07ab1c 100644 --- a/controllers/middleware.go +++ b/controllers/middleware.go @@ -60,6 +60,11 @@ func userMiddleWare(handler http.Handler) http.Handler { if strings.Contains(route, "networks") { r.Header.Set("TARGET_RSRC", models.NetworkRsrc.String()) } + // check 'graph' after 'networks', otherwise the + // header will be overwritten. + if strings.Contains(route, "graph") { + r.Header.Set("TARGET_RSRC", models.HostRsrc.String()) + } if strings.Contains(route, "acls") { r.Header.Set("TARGET_RSRC", models.AclRsrc.String()) } diff --git a/logic/user_mgmt.go b/logic/user_mgmt.go index d2c5282a..f518eb89 100644 --- a/logic/user_mgmt.go +++ b/logic/user_mgmt.go @@ -136,6 +136,38 @@ func ListPlatformRoles() ([]models.UserRolePermissionTemplate, error) { return userRoles, nil } +func GetAllRsrcIDForRsrc(rsrc models.RsrcType) models.RsrcID { + switch rsrc { + case models.HostRsrc: + return models.AllHostRsrcID + case models.RelayRsrc: + return models.AllRelayRsrcID + case models.RemoteAccessGwRsrc: + return models.AllRemoteAccessGwRsrcID + case models.ExtClientsRsrc: + return models.AllExtClientsRsrcID + case models.InetGwRsrc: + return models.AllInetGwRsrcID + case models.EgressGwRsrc: + return models.AllEgressGwRsrcID + case models.NetworkRsrc: + return models.AllNetworkRsrcID + case models.EnrollmentKeysRsrc: + return models.AllEnrollmentKeysRsrcID + case models.UserRsrc: + return models.AllUserRsrcID + case models.DnsRsrc: + return models.AllDnsRsrcID + case models.FailOverRsrc: + return models.AllFailOverRsrcID + case models.AclRsrc: + return models.AllAclsRsrcID + case models.TagRsrc: + return models.AllTagsRsrcID + } + return "" +} + func userRolesInit() { d, _ := json.Marshal(SuperAdminPermissionTemplate) database.Insert(SuperAdminPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME) diff --git a/pro/controllers/metrics.go b/pro/controllers/metrics.go index c15c32e0..b670d7a3 100644 --- a/pro/controllers/metrics.go +++ b/pro/controllers/metrics.go @@ -20,7 +20,6 @@ func MetricHandlers(r *mux.Router) { r.HandleFunc("/api/metrics/{network}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkNodesMetrics))).Methods(http.MethodGet) r.HandleFunc("/api/metrics", logic.SecurityCheck(true, http.HandlerFunc(getAllMetrics))).Methods(http.MethodGet) r.HandleFunc("/api/metrics-ext/{network}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkExtMetrics))).Methods(http.MethodGet) - r.HandleFunc("/api/v1/graph/{network}", logic.SecurityCheck(true, http.HandlerFunc(graph))).Methods(http.MethodGet) } // get the metrics of a given node @@ -166,21 +165,3 @@ func getAllMetrics(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(networkMetrics) } - -func graph(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - - var params = mux.Vars(r) - network := params["network"] - networkNodes, err := logic.GetNetworkNodes(network) - if err != nil { - logger.Log(1, r.Header.Get("user"), "failed to get network nodes", err.Error()) - return - } - networkNodes = logic.AddStaticNodestoList(networkNodes) - // return all the nodes in JSON/API format - apiNodes := logic.GetAllNodesAPIWithLocation(networkNodes[:]) - logic.SortApiNodes(apiNodes[:]) - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(apiNodes) -} diff --git a/pro/controllers/networks.go b/pro/controllers/networks.go new file mode 100644 index 00000000..5120705d --- /dev/null +++ b/pro/controllers/networks.go @@ -0,0 +1,31 @@ +package controllers + +import ( + "encoding/json" + "github.com/gorilla/mux" + "github.com/gravitl/netmaker/logger" + "github.com/gravitl/netmaker/logic" + "net/http" +) + +func NetworkHandlers(r *mux.Router) { + r.HandleFunc("/api/v1/networks/{network}/graph", logic.SecurityCheck(true, http.HandlerFunc(getNetworkGraph))).Methods(http.MethodGet) +} + +func getNetworkGraph(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + + var params = mux.Vars(r) + network := params["network"] + networkNodes, err := logic.GetNetworkNodes(network) + if err != nil { + logger.Log(1, r.Header.Get("user"), "failed to get network nodes", err.Error()) + return + } + networkNodes = logic.AddStaticNodestoList(networkNodes) + // return all the nodes in JSON/API format + apiNodes := logic.GetAllNodesAPIWithLocation(networkNodes[:]) + logic.SortApiNodes(apiNodes[:]) + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(apiNodes) +} diff --git a/pro/initialize.go b/pro/initialize.go index 4e3fb84a..ae6788c4 100644 --- a/pro/initialize.go +++ b/pro/initialize.go @@ -35,6 +35,7 @@ func InitPro() { proControllers.RacHandlers, proControllers.EventHandlers, proControllers.TagHandlers, + proControllers.NetworkHandlers, ) controller.ListRoles = proControllers.ListRoles logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() { diff --git a/pro/logic/security.go b/pro/logic/security.go index fbe0105d..9cad5801 100644 --- a/pro/logic/security.go +++ b/pro/logic/security.go @@ -115,13 +115,10 @@ func checkNetworkAccessPermissions(netRoleID models.UserRoleID, username, reqSco return nil } rsrcPermissionScope, ok := networkPermissionScope.NetworkLevelAccess[models.RsrcType(targetRsrc)] - if targetRsrc == models.HostRsrc.String() && !ok { - rsrcPermissionScope, ok = networkPermissionScope.NetworkLevelAccess[models.RemoteAccessGwRsrc] - } if !ok { return errors.New("access denied") } - if allRsrcsTypePermissionScope, ok := rsrcPermissionScope[models.RsrcID(fmt.Sprintf("all_%s", targetRsrc))]; ok { + if allRsrcsTypePermissionScope, ok := rsrcPermissionScope[logic.GetAllRsrcIDForRsrc(models.RsrcType(targetRsrc))]; ok { // handle extclient apis here if models.RsrcType(targetRsrc) == models.ExtClientsRsrc && allRsrcsTypePermissionScope.SelfOnly && targetRsrcID != "" { extclient, err := logic.GetExtClient(targetRsrcID, netID) @@ -138,14 +135,6 @@ func checkNetworkAccessPermissions(netRoleID models.UserRoleID, username, reqSco } } - if targetRsrc == models.HostRsrc.String() { - if allRsrcsTypePermissionScope, ok := rsrcPermissionScope[models.RsrcID(fmt.Sprintf("all_%s", models.RemoteAccessGwRsrc))]; ok { - err = checkPermissionScopeWithReqMethod(allRsrcsTypePermissionScope, reqScope) - if err == nil { - return nil - } - } - } if targetRsrcID == "" { return errors.New("target rsrc id is empty") } diff --git a/pro/logic/user_mgmt.go b/pro/logic/user_mgmt.go index 389b1bb8..6b5aa329 100644 --- a/pro/logic/user_mgmt.go +++ b/pro/logic/user_mgmt.go @@ -53,6 +53,11 @@ var NetworkUserAllPermissionTemplate = models.UserRolePermissionTemplate{ FullAccess: false, NetworkID: models.AllNetworks, NetworkLevelAccess: map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope{ + models.HostRsrc: { + models.AllHostRsrcID: models.RsrcPermissionScope{ + Read: true, + }, + }, models.RemoteAccessGwRsrc: { models.AllRemoteAccessGwRsrcID: models.RsrcPermissionScope{ Read: true, @@ -114,7 +119,6 @@ func UserRolesInit() { database.Insert(NetworkAdminAllPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME) d, _ = json.Marshal(NetworkUserAllPermissionTemplate) database.Insert(NetworkUserAllPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME) - } func UserGroupsInit() { @@ -170,6 +174,11 @@ func CreateDefaultNetworkRolesAndGroups(netID models.NetworkID) { NetworkID: netID, DenyDashboardAccess: false, NetworkLevelAccess: map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope{ + models.HostRsrc: { + models.AllHostRsrcID: models.RsrcPermissionScope{ + Read: true, + }, + }, models.RemoteAccessGwRsrc: { models.AllRemoteAccessGwRsrcID: models.RsrcPermissionScope{ Read: true,