diff --git a/pkg/services/navtree/models.go b/pkg/services/navtree/models.go index 3c7078f44c0..1b05263e151 100644 --- a/pkg/services/navtree/models.go +++ b/pkg/services/navtree/models.go @@ -86,6 +86,7 @@ func (root *NavTreeRoot) AddSection(node *NavLink) { root.Children = append(root.Children, node) } +// RemoveSection removes a section from the root node. Does not recurse into children. func (root *NavTreeRoot) RemoveSection(node *NavLink) { var result []*NavLink @@ -98,6 +99,26 @@ func (root *NavTreeRoot) RemoveSection(node *NavLink) { root.Children = result } +// RemoveSectionByID removes a section by ID from the root node and all its children +func (root *NavTreeRoot) RemoveSectionByID(id string) bool { + var result []*NavLink + + for i, child := range root.Children { + if child.Id == id { + // Remove the node by slicing it out + result = append(root.Children[:i], root.Children[i+1:]...) + root.Children = result + return true + } else if len(child.Children) > 0 { + if removed := RemoveById(child, id); removed { + return true + } + } + } + + return false +} + func (root *NavTreeRoot) FindById(id string) *NavLink { return FindById(root.Children, id) } @@ -227,3 +248,22 @@ func FindByURL(nodes []*NavLink, url string) *NavLink { return nil } + +func RemoveById(node *NavLink, id string) bool { + var result []*NavLink + + for i, child := range node.Children { + if child.Id == id { + // Remove the node by slicing it out + result = append(node.Children[:i], node.Children[i+1:]...) + node.Children = result + return true + } else if len(child.Children) > 0 { + if removed := RemoveById(child, id); removed { + return true + } + } + } + + return false +} diff --git a/pkg/services/navtree/navtreeimpl/admin.go b/pkg/services/navtree/navtreeimpl/admin.go index 80b45148c11..7b16a0bb1ad 100644 --- a/pkg/services/navtree/navtreeimpl/admin.go +++ b/pkg/services/navtree/navtreeimpl/admin.go @@ -163,9 +163,8 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink Children: accessNodeLinks, } - if len(usersNode.Children) > 0 { - configNodes = append(configNodes, usersNode) - } + // Always append admin access as it's injected by grafana-auth-app. + configNodes = append(configNodes, usersNode) if authConfigUIAvailable && hasAccess(ssoutils.EvalAuthenticationSettings(s.cfg)) || (hasAccess(ssoutils.OauthSettingsEvaluator(s.cfg)) && s.features.IsEnabled(ctx, featuremgmt.FlagSsoSettingsApi)) { diff --git a/pkg/services/navtree/navtreeimpl/navtree.go b/pkg/services/navtree/navtreeimpl/navtree.go index 47572206831..6abf4c2f62f 100644 --- a/pkg/services/navtree/navtreeimpl/navtree.go +++ b/pkg/services/navtree/navtreeimpl/navtree.go @@ -163,6 +163,11 @@ func (s *ServiceImpl) GetNavTree(c *contextmodel.ReqContext, prefs *pref.Prefere return nil, err } + // remove user access if empty. Happens if grafana-auth-app is not injected + if sec := treeRoot.FindById(navtree.NavIDCfgAccess); sec != nil && (sec.Children == nil || len(sec.Children) == 0) { + treeRoot.RemoveSectionByID(navtree.NavIDCfgAccess) + } + if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagPinNavItems) { bookmarks := s.buildBookmarksNavLinks(prefs, treeRoot)