fix(security): added restrictions to see user names [EE-5825] (#10296)
* fix(security): added restrictions to see user names [EE-5825] * use pluralize methodpull/10300/head
parent
60477ae287
commit
4c16594a25
|
@ -4,6 +4,7 @@ import { createMockTeams, createMockUsers } from '@/react-tools/test-mocks';
|
|||
import { renderWithQueryClient } from '@/react-tools/test-utils';
|
||||
import { rest, server } from '@/setup-tests/server';
|
||||
import { Role } from '@/portainer/users/types';
|
||||
import { withUserProvider } from '@/react/test-utils/withUserProvider';
|
||||
|
||||
import {
|
||||
ResourceControlOwnership,
|
||||
|
@ -143,11 +144,9 @@ async function renderComponent(
|
|||
resourceType: ResourceControlType = ResourceControlType.Container,
|
||||
resourceControl?: ResourceControlViewModel
|
||||
) {
|
||||
const WithUser = withUserProvider(AccessControlPanelDetails);
|
||||
const queries = renderWithQueryClient(
|
||||
<AccessControlPanelDetails
|
||||
resourceControl={resourceControl}
|
||||
resourceType={resourceType}
|
||||
/>
|
||||
<WithUser resourceControl={resourceControl} resourceType={resourceType} />
|
||||
);
|
||||
await expect(queries.findByText('Ownership')).resolves.toBeVisible();
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ 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';
|
||||
import { Tooltip } from '@@/Tip/Tooltip';
|
||||
|
@ -29,6 +31,8 @@ export function AccessControlPanelDetails({
|
|||
resourceControl,
|
||||
resourceType,
|
||||
}: Props) {
|
||||
const { isAdmin } = useCurrentUser();
|
||||
|
||||
const inheritanceMessage = getInheritanceMessage(
|
||||
resourceType,
|
||||
resourceControl
|
||||
|
@ -40,9 +44,31 @@ export function AccessControlPanelDetails({
|
|||
TeamAccesses: restrictedToTeams = [],
|
||||
} = resourceControl || {};
|
||||
|
||||
const users = useAuthorizedUsers(restrictedToUsers.map((ra) => ra.UserId));
|
||||
const users = useAuthorizedUsers(
|
||||
restrictedToUsers.map((ra) => ra.UserId),
|
||||
isAdmin
|
||||
);
|
||||
const teams = useAuthorizedTeams(restrictedToTeams.map((ra) => ra.TeamId));
|
||||
|
||||
const teamsLength = teams.data ? teams.data.length : 0;
|
||||
const unauthoisedTeams = restrictedToTeams.length - teamsLength;
|
||||
|
||||
let teamsMessage = teams.data && teams.data.join(', ');
|
||||
if (unauthoisedTeams > 0 && teams.isFetched) {
|
||||
teamsMessage += teamsLength > 0 ? ' and' : '';
|
||||
teamsMessage += ` ${unauthoisedTeams} ${pluralize(
|
||||
unauthoisedTeams,
|
||||
'team'
|
||||
)} you are not part of`;
|
||||
}
|
||||
|
||||
const userMessage = isAdmin
|
||||
? (users.data && users.data.join(', ')) || ''
|
||||
: `${restrictedToUsers.length} ${pluralize(
|
||||
restrictedToUsers.length,
|
||||
'user'
|
||||
)}`;
|
||||
|
||||
return (
|
||||
<table className="table">
|
||||
<tbody>
|
||||
|
@ -62,17 +88,13 @@ export function AccessControlPanelDetails({
|
|||
{restrictedToUsers.length > 0 && (
|
||||
<tr data-cy="access-authorisedUsers">
|
||||
<td>Authorized users</td>
|
||||
<td aria-label="authorized-users">
|
||||
{users.data && users.data.join(', ')}
|
||||
</td>
|
||||
<td aria-label="authorized-users">{userMessage}</td>
|
||||
</tr>
|
||||
)}
|
||||
{restrictedToTeams.length > 0 && (
|
||||
<tr data-cy="access-authorisedTeams">
|
||||
<td>Authorized teams</td>
|
||||
<td aria-label="authorized-teams">
|
||||
{teams.data && teams.data.join(', ')}
|
||||
</td>
|
||||
<td aria-label="authorized-teams">{teamsMessage}</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
|
@ -197,17 +219,22 @@ function useAuthorizedTeams(authorizedTeamIds: TeamId[]) {
|
|||
});
|
||||
}
|
||||
|
||||
function useAuthorizedUsers(authorizedUserIds: UserId[]) {
|
||||
return useUsers(false, 0, authorizedUserIds.length > 0, (users) => {
|
||||
if (authorizedUserIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
function useAuthorizedUsers(authorizedUserIds: UserId[], enabled = true) {
|
||||
return useUsers(
|
||||
false,
|
||||
0,
|
||||
authorizedUserIds.length > 0 && enabled,
|
||||
(users) => {
|
||||
if (authorizedUserIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return _.compact(
|
||||
authorizedUserIds.map((id) => {
|
||||
const user = users.find((u) => u.Id === id);
|
||||
return user?.Username;
|
||||
})
|
||||
);
|
||||
});
|
||||
return _.compact(
|
||||
authorizedUserIds.map((id) => {
|
||||
const user = users.find((u) => u.Id === id);
|
||||
return user?.Username;
|
||||
})
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -32,8 +32,7 @@ export function EditDetails({
|
|||
}: Props) {
|
||||
const { user, isAdmin } = useUser();
|
||||
|
||||
const { users, teams, isLoading } = useLoadState(environmentId);
|
||||
|
||||
const { users, teams, isLoading } = useLoadState(environmentId, isAdmin);
|
||||
const handleChange = useCallback(
|
||||
(partialValues: Partial<typeof values>) => {
|
||||
onChange({ ...values, ...partialValues });
|
||||
|
@ -42,7 +41,7 @@ export function EditDetails({
|
|||
[values, onChange]
|
||||
);
|
||||
|
||||
if (isLoading || !teams || !users) {
|
||||
if (isLoading || !teams || (isAdmin && !users) || !values.authorizedUsers) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -62,7 +61,7 @@ export function EditDetails({
|
|||
{isAdmin && (
|
||||
<UsersField
|
||||
name={withNamespace('authorizedUsers')}
|
||||
users={users}
|
||||
users={users || []}
|
||||
onChange={(authorizedUsers) => handleChange({ authorizedUsers })}
|
||||
value={values.authorizedUsers}
|
||||
errors={errors?.authorizedUsers}
|
||||
|
|
|
@ -2,10 +2,10 @@ import { useTeams } from '@/react/portainer/users/teams/queries';
|
|||
import { useUsers } from '@/portainer/users/queries';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
export function useLoadState(environmentId?: EnvironmentId) {
|
||||
export function useLoadState(environmentId?: EnvironmentId, enabled = true) {
|
||||
const teams = useTeams(false, environmentId);
|
||||
|
||||
const users = useUsers(false, environmentId);
|
||||
const users = useUsers(false, environmentId, enabled);
|
||||
|
||||
return {
|
||||
teams: teams.data,
|
||||
|
|
Loading…
Reference in New Issue