From 3c4660bbf3f8fb2cb93a4cff9dec5cf8e99964ae Mon Sep 17 00:00:00 2001
From: Prabhat Khera <91852476+prabhat-org@users.noreply.github.com>
Date: Mon, 25 Sep 2023 09:08:26 +1300
Subject: [PATCH] fix(permissions): non admin access to view users [EE-5825] 
 (#10352)

* fix non admin access to view users

* review comments and fix tests
---
 api/http/handler/users/user_list.go           | 26 ++++++++++++++++++-
 .../AccessControlPanelDetails.tsx             | 12 +++------
 .../users/teams/ListView/ListView.tsx         |  2 +-
 3 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/api/http/handler/users/user_list.go b/api/http/handler/users/user_list.go
index b77dc6620..5a6871991 100644
--- a/api/http/handler/users/user_list.go
+++ b/api/http/handler/users/user_list.go
@@ -31,7 +31,7 @@ func (handler *Handler) userList(w http.ResponseWriter, r *http.Request) *httper
 		return httperror.InternalServerError("Unable to retrieve info from request context", err)
 	}
 
-	if !securityContext.IsAdmin {
+	if !securityContext.IsAdmin && !securityContext.IsTeamLeader {
 		return httperror.Forbidden("Permission denied to access users list", err)
 	}
 
@@ -42,6 +42,9 @@ func (handler *Handler) userList(w http.ResponseWriter, r *http.Request) *httper
 
 	endpointID, _ := request.RetrieveNumericQueryParameter(r, "environmentId", true)
 	if endpointID == 0 {
+		if securityContext.IsAdmin {
+			sanitizeUsers(users)
+		}
 		return response.JSON(w, users)
 	}
 
@@ -60,6 +63,9 @@ func (handler *Handler) userList(w http.ResponseWriter, r *http.Request) *httper
 	for _, user := range users {
 		// the users who have the endpoint authorization
 		if _, ok := user.EndpointAuthorizations[endpoint.ID]; ok {
+			if securityContext.IsAdmin {
+				sanitizeUser(&user)
+			}
 			canAccessEndpoint = append(canAccessEndpoint, user)
 			continue
 		}
@@ -71,9 +77,27 @@ func (handler *Handler) userList(w http.ResponseWriter, r *http.Request) *httper
 		}
 
 		if security.AuthorizedEndpointAccess(endpoint, endpointGroup, user.ID, teamMemberships) {
+			if securityContext.IsAdmin {
+				sanitizeUser(&user)
+			}
 			canAccessEndpoint = append(canAccessEndpoint, user)
 		}
 	}
 
 	return response.JSON(w, canAccessEndpoint)
 }
+
+func sanitizeUser(user *portainer.User) {
+	user.Password = ""
+	user.EndpointAuthorizations = nil
+	user.ThemeSettings = portainer.UserThemeSettings{}
+	user.PortainerAuthorizations = nil
+	user.UserTheme = ""
+	user.TokenIssueAt = 0
+}
+
+func sanitizeUsers(users []portainer.User) {
+	for i := range users {
+		sanitizeUser(&users[i])
+	}
+}
diff --git a/app/react/portainer/access-control/AccessControlPanel/AccessControlPanelDetails.tsx b/app/react/portainer/access-control/AccessControlPanel/AccessControlPanelDetails.tsx
index 584ba873d..3201959cc 100644
--- a/app/react/portainer/access-control/AccessControlPanel/AccessControlPanelDetails.tsx
+++ b/app/react/portainer/access-control/AccessControlPanel/AccessControlPanelDetails.tsx
@@ -8,7 +8,6 @@ import { UserId } from '@/portainer/users/types';
 import { TeamId } from '@/react/portainer/users/teams/types';
 import { useTeams } from '@/react/portainer/users/teams/queries';
 import { useUsers } from '@/portainer/users/queries';
-import { useCurrentUser } from '@/react/hooks/useUser';
 import { pluralize } from '@/portainer/helpers/strings';
 
 import { Link } from '@@/Link';
@@ -31,8 +30,6 @@ export function AccessControlPanelDetails({
   resourceControl,
   resourceType,
 }: Props) {
-  const { isAdmin } = useCurrentUser();
-
   const inheritanceMessage = getInheritanceMessage(
     resourceType,
     resourceControl
@@ -44,10 +41,7 @@ export function AccessControlPanelDetails({
     TeamAccesses: restrictedToTeams = [],
   } = resourceControl || {};
 
-  const users = useAuthorizedUsers(
-    restrictedToUsers.map((ra) => ra.UserId),
-    isAdmin
-  );
+  const users = useAuthorizedUsers(restrictedToUsers.map((ra) => ra.UserId));
   const teams = useAuthorizedTeams(restrictedToTeams.map((ra) => ra.TeamId));
 
   const teamsLength = teams.data ? teams.data.length : 0;
@@ -62,8 +56,8 @@ export function AccessControlPanelDetails({
     )} you are not part of`;
   }
 
-  const userMessage = isAdmin
-    ? (users.data && users.data.join(', ')) || ''
+  const userMessage = users.data
+    ? users.data.join(', ')
     : `${restrictedToUsers.length} ${pluralize(
         restrictedToUsers.length,
         'user'
diff --git a/app/react/portainer/users/teams/ListView/ListView.tsx b/app/react/portainer/users/teams/ListView/ListView.tsx
index 6c45119cf..01bc181bc 100644
--- a/app/react/portainer/users/teams/ListView/ListView.tsx
+++ b/app/react/portainer/users/teams/ListView/ListView.tsx
@@ -18,7 +18,7 @@ export function ListView() {
     <>
       <PageHeader title="Teams" breadcrumbs={[{ label: 'Teams management' }]} />
 
-      {usersQuery.data && teamsQuery.data && (
+      {isAdmin && usersQuery.data && teamsQuery.data && (
         <CreateTeamForm users={usersQuery.data} teams={teamsQuery.data} />
       )}