mirror of
https://github.com/portainer/portainer.git
synced 2025-08-09 15:55:23 +02:00
fix(access): support to list users or teams with specified endpoint [EE-1704] (#7610)
This commit is contained in:
parent
f71fe87ba7
commit
53025178ef
25 changed files with 632 additions and 61 deletions
|
@ -4,12 +4,13 @@ import { ResourceControlOwnership as RCO } from '@/react/portainer/access-contro
|
|||
angular.module('portainer.app').controller('porAccessControlFormController', [
|
||||
'$q',
|
||||
'$scope',
|
||||
'$state',
|
||||
'UserService',
|
||||
'TeamService',
|
||||
'Notifications',
|
||||
'Authentication',
|
||||
'ResourceControlService',
|
||||
function ($q, $scope, UserService, TeamService, Notifications, Authentication, ResourceControlService) {
|
||||
function ($q, $scope, $state, UserService, TeamService, Notifications, Authentication, ResourceControlService) {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.RCO = RCO;
|
||||
|
@ -54,9 +55,10 @@ angular.module('portainer.app').controller('porAccessControlFormController', [
|
|||
ctrl.formData.Ownership = ctrl.RCO.ADMINISTRATORS;
|
||||
}
|
||||
|
||||
const environmentId = $state.params.endpointId;
|
||||
$q.all({
|
||||
availableTeams: TeamService.teams(),
|
||||
availableUsers: isAdmin ? UserService.users(false) : [],
|
||||
availableTeams: TeamService.teams(environmentId),
|
||||
availableUsers: isAdmin ? UserService.users(false, environmentId) : [],
|
||||
})
|
||||
.then(function success(data) {
|
||||
ctrl.availableUsers = _.orderBy(data.availableUsers, 'Username', 'asc');
|
||||
|
|
|
@ -98,5 +98,6 @@ export const componentsModule = angular
|
|||
'resourceControl',
|
||||
'resourceId',
|
||||
'resourceType',
|
||||
'environmentId',
|
||||
])
|
||||
).name;
|
||||
|
|
|
@ -8,7 +8,7 @@ angular.module('portainer.app').factory('Teams', [
|
|||
{},
|
||||
{
|
||||
create: { method: 'POST', ignoreLoadingBar: true },
|
||||
query: { method: 'GET', isArray: true },
|
||||
query: { method: 'GET', isArray: true, params: { environmentId: '@environmentId' } },
|
||||
get: { method: 'GET', params: { id: '@id' } },
|
||||
remove: { method: 'DELETE', params: { id: '@id' } },
|
||||
queryMemberships: { method: 'GET', isArray: true, params: { id: '@id', entity: 'memberships' } },
|
||||
|
|
|
@ -8,9 +8,9 @@ angular.module('portainer.app').factory('TeamService', [
|
|||
'use strict';
|
||||
var service = {};
|
||||
|
||||
service.teams = function () {
|
||||
service.teams = function (environmentId) {
|
||||
var deferred = $q.defer();
|
||||
Teams.query()
|
||||
Teams.query({ environmentId: environmentId })
|
||||
.$promise.then(function success(data) {
|
||||
var teams = data.map(function (item) {
|
||||
return new TeamViewModel(item);
|
||||
|
|
|
@ -9,8 +9,8 @@ export function UserService($q, Users, TeamService, TeamMembershipService) {
|
|||
'use strict';
|
||||
var service = {};
|
||||
|
||||
service.users = async function (includeAdministrators) {
|
||||
const users = await getUsers(includeAdministrators);
|
||||
service.users = async function (includeAdministrators, environmentId) {
|
||||
const users = await getUsers(includeAdministrators, environmentId);
|
||||
|
||||
return users.map((u) => new UserViewModel(u));
|
||||
};
|
||||
|
|
|
@ -34,16 +34,21 @@ export function useIsTeamLeader(user: User) {
|
|||
|
||||
export function useUsers<T = User[]>(
|
||||
includeAdministrator = false,
|
||||
environmentId = 0,
|
||||
enabled = true,
|
||||
select: (data: User[]) => T = (data) => data as unknown as T
|
||||
) {
|
||||
const users = useQuery(['users'], () => getUsers(includeAdministrator), {
|
||||
meta: {
|
||||
error: { title: 'Failure', message: 'Unable to load users' },
|
||||
},
|
||||
enabled,
|
||||
select,
|
||||
});
|
||||
const users = useQuery(
|
||||
['users'],
|
||||
() => getUsers(includeAdministrator, environmentId),
|
||||
{
|
||||
meta: {
|
||||
error: { title: 'Failure', message: 'Unable to load users' },
|
||||
},
|
||||
enabled,
|
||||
select,
|
||||
}
|
||||
);
|
||||
|
||||
return users;
|
||||
}
|
||||
|
|
|
@ -4,9 +4,14 @@ import { TeamMembership } from '@/react/portainer/users/teams/types';
|
|||
import { User, UserId } from './types';
|
||||
import { filterNonAdministratorUsers } from './user.helpers';
|
||||
|
||||
export async function getUsers(includeAdministrators = false) {
|
||||
export async function getUsers(
|
||||
includeAdministrators = false,
|
||||
environmentId = 0
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<User[]>(buildUrl());
|
||||
const { data } = await axios.get<User[]>(buildUrl(), {
|
||||
params: { environmentId },
|
||||
});
|
||||
|
||||
return includeAdministrators ? data : filterNonAdministratorUsers(data);
|
||||
} catch (e) {
|
||||
|
|
|
@ -256,6 +256,7 @@
|
|||
resource-id="stack.EndpointId + '_' + stack.Name"
|
||||
resource-control="stack.ResourceControl"
|
||||
resource-type="resourceType"
|
||||
environment-id="stack.EndpointId"
|
||||
on-update-success="(onUpdateResourceControlSuccess)"
|
||||
>
|
||||
</access-control-panel>
|
||||
|
|
|
@ -186,6 +186,7 @@ export function ItemView() {
|
|||
resourceId={id}
|
||||
resourceControl={container.resourceControl}
|
||||
resourceType={ResourceControlType.ContainerGroup}
|
||||
environmentId={environmentId}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -89,6 +89,7 @@ export function ItemView() {
|
|||
resourceType={ResourceControlType.Network}
|
||||
disableOwnershipChange={isSystemNetwork(networkQuery.data.Name)}
|
||||
resourceId={networkId}
|
||||
environmentId={environmentId}
|
||||
/>
|
||||
<NetworkOptionsTable options={networkQuery.data.Options} />
|
||||
<NetworkContainersTable
|
||||
|
|
|
@ -4,6 +4,7 @@ import { useUser } from '@/portainer/hooks/useUser';
|
|||
import { Icon } from '@/react/components/Icon';
|
||||
import { TeamMembership, TeamRole } from '@/react/portainer/users/teams/types';
|
||||
import { useUserMembership } from '@/portainer/users/queries';
|
||||
import { EnvironmentId } from '@/portainer/environments/types';
|
||||
|
||||
import { TableContainer, TableTitle } from '@@/datatables';
|
||||
import { Button } from '@@/buttons';
|
||||
|
@ -18,6 +19,7 @@ interface Props {
|
|||
resourceControl?: ResourceControlViewModel;
|
||||
resourceType: ResourceControlType;
|
||||
resourceId: ResourceId;
|
||||
environmentId: EnvironmentId;
|
||||
disableOwnershipChange?: boolean;
|
||||
onUpdateSuccess(): Promise<void>;
|
||||
}
|
||||
|
@ -27,6 +29,7 @@ export function AccessControlPanel({
|
|||
resourceType,
|
||||
disableOwnershipChange,
|
||||
resourceId,
|
||||
environmentId,
|
||||
onUpdateSuccess,
|
||||
}: Props) {
|
||||
const [isEditMode, toggleEditMode] = useReducer((state) => !state, false);
|
||||
|
@ -69,6 +72,7 @@ export function AccessControlPanel({
|
|||
onCancelClick={() => toggleEditMode()}
|
||||
resourceId={resourceId}
|
||||
resourceType={resourceType}
|
||||
environmentId={environmentId}
|
||||
onUpdateSuccess={handleUpdateSuccess}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -178,7 +178,7 @@ function InheritanceMessage({
|
|||
}
|
||||
|
||||
function useAuthorizedTeams(authorizedTeamIds: TeamId[]) {
|
||||
return useTeams(false, {
|
||||
return useTeams(false, 0, {
|
||||
enabled: authorizedTeamIds.length > 0,
|
||||
select: (teams) => {
|
||||
if (authorizedTeamIds.length === 0) {
|
||||
|
@ -196,7 +196,7 @@ function useAuthorizedTeams(authorizedTeamIds: TeamId[]) {
|
|||
}
|
||||
|
||||
function useAuthorizedUsers(authorizedUserIds: UserId[]) {
|
||||
return useUsers(false, authorizedUserIds.length > 0, (users) => {
|
||||
return useUsers(false, 0, authorizedUserIds.length > 0, (users) => {
|
||||
if (authorizedUserIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import { object } from 'yup';
|
|||
import { useUser } from '@/portainer/hooks/useUser';
|
||||
import { confirmAsync } from '@/portainer/services/modal.service/confirm';
|
||||
import { notifySuccess } from '@/portainer/services/notifications';
|
||||
import { EnvironmentId } from '@/portainer/environments/types';
|
||||
|
||||
import { Button } from '@@/buttons';
|
||||
import { LoadingButton } from '@@/buttons/LoadingButton';
|
||||
|
@ -27,6 +28,7 @@ interface Props {
|
|||
resourceType: ResourceControlType;
|
||||
resourceId: ResourceId;
|
||||
resourceControl?: ResourceControlViewModel;
|
||||
environmentId?: EnvironmentId;
|
||||
onCancelClick(): void;
|
||||
onUpdateSuccess(): Promise<void>;
|
||||
}
|
||||
|
@ -35,6 +37,7 @@ export function AccessControlPanelForm({
|
|||
resourceId,
|
||||
resourceType,
|
||||
resourceControl,
|
||||
environmentId,
|
||||
onCancelClick,
|
||||
onUpdateSuccess,
|
||||
}: Props) {
|
||||
|
@ -81,6 +84,7 @@ export function AccessControlPanelForm({
|
|||
values={values.accessControl}
|
||||
isPublicVisible
|
||||
errors={errors.accessControl}
|
||||
environmentId={environmentId}
|
||||
/>
|
||||
|
||||
<div className="form-group">
|
||||
|
|
|
@ -2,6 +2,7 @@ import { useCallback } from 'react';
|
|||
import { FormikErrors } from 'formik';
|
||||
|
||||
import { useUser } from '@/portainer/hooks/useUser';
|
||||
import { EnvironmentId } from '@/portainer/environments/types';
|
||||
|
||||
import { BoxSelector } from '@@/BoxSelector';
|
||||
import { FormError } from '@@/form-components/FormError';
|
||||
|
@ -19,6 +20,7 @@ interface Props {
|
|||
isPublicVisible?: boolean;
|
||||
errors?: FormikErrors<AccessControlFormData>;
|
||||
formNamespace?: string;
|
||||
environmentId?: EnvironmentId;
|
||||
}
|
||||
|
||||
export function EditDetails({
|
||||
|
@ -27,10 +29,11 @@ export function EditDetails({
|
|||
isPublicVisible = false,
|
||||
errors,
|
||||
formNamespace,
|
||||
environmentId,
|
||||
}: Props) {
|
||||
const { user, isAdmin } = useUser();
|
||||
|
||||
const { users, teams, isLoading } = useLoadState();
|
||||
const { users, teams, isLoading } = useLoadState(environmentId);
|
||||
const options = useOptions(isAdmin, teams, isPublicVisible);
|
||||
|
||||
const handleChange = useCallback(
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { useTeams } from '@/react/portainer/users/teams/queries';
|
||||
import { useUsers } from '@/portainer/users/queries';
|
||||
import { EnvironmentId } from '@/portainer/environments/types';
|
||||
|
||||
export function useLoadState() {
|
||||
const teams = useTeams();
|
||||
export function useLoadState(environmentId?: EnvironmentId) {
|
||||
const teams = useTeams(false, environmentId);
|
||||
|
||||
const users = useUsers(false);
|
||||
const users = useUsers(false, environmentId);
|
||||
|
||||
return {
|
||||
teams: teams.data,
|
||||
|
|
|
@ -12,7 +12,7 @@ export function ListView() {
|
|||
const { isAdmin } = useUser();
|
||||
|
||||
const usersQuery = useUsers(false);
|
||||
const teamsQuery = useTeams(!isAdmin, { enabled: !!usersQuery.data });
|
||||
const teamsQuery = useTeams(!isAdmin, 0, { enabled: !!usersQuery.data });
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -14,6 +14,7 @@ import { Team, TeamId, TeamMembership, TeamRole } from './types';
|
|||
|
||||
export function useTeams<T = Team[]>(
|
||||
onlyLedTeams = false,
|
||||
environmentId = 0,
|
||||
{
|
||||
enabled = true,
|
||||
select = (data) => data as unknown as T,
|
||||
|
@ -23,8 +24,8 @@ export function useTeams<T = Team[]>(
|
|||
} = {}
|
||||
) {
|
||||
const teams = useQuery(
|
||||
['teams', { onlyLedTeams }],
|
||||
() => getTeams(onlyLedTeams),
|
||||
['teams', { onlyLedTeams, environmentId }],
|
||||
() => getTeams(onlyLedTeams, environmentId),
|
||||
{
|
||||
meta: {
|
||||
error: { title: 'Failure', message: 'Unable to load teams' },
|
||||
|
|
|
@ -4,10 +4,10 @@ import { type UserId } from '@/portainer/users/types';
|
|||
import { createTeamMembership } from './team-membership.service';
|
||||
import { Team, TeamId, TeamMembership, TeamRole } from './types';
|
||||
|
||||
export async function getTeams(onlyLedTeams = false) {
|
||||
export async function getTeams(onlyLedTeams = false, environmentId = 0) {
|
||||
try {
|
||||
const { data } = await axios.get<Team[]>(buildUrl(), {
|
||||
params: { onlyLedTeams },
|
||||
params: { onlyLedTeams, environmentId },
|
||||
});
|
||||
return data;
|
||||
} catch (error) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue