mirror of
https://github.com/portainer/portainer.git
synced 2025-08-02 20:35:25 +02:00
feat(auth): add useIsEdgeAdmin hook [EE-6627] (#11101)
Some checks are pending
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
ci / build_images (map[arch:arm platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:s390x platform:linux version:]) (push) Waiting to run
ci / build_manifests (push) Blocked by required conditions
/ triage (push) Waiting to run
Lint / Run linters (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
Test / test-client (push) Waiting to run
Test / test-server (map[arch:amd64 platform:linux]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
Test / test-server (map[arch:arm64 platform:linux]) (push) Waiting to run
Some checks are pending
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
ci / build_images (map[arch:arm platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:s390x platform:linux version:]) (push) Waiting to run
ci / build_manifests (push) Blocked by required conditions
/ triage (push) Waiting to run
Lint / Run linters (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
Test / test-client (push) Waiting to run
Test / test-server (map[arch:amd64 platform:linux]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
Test / test-server (map[arch:arm64 platform:linux]) (push) Waiting to run
This commit is contained in:
parent
c08b5af85a
commit
edea9e3481
48 changed files with 389 additions and 198 deletions
|
@ -1,13 +1,10 @@
|
|||
import { useRouter } from '@uirouter/react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { EnvironmentId } from '../portainer/environments/types';
|
||||
|
||||
import { useAuthorizations } from './useUser';
|
||||
|
||||
type AuthorizationOptions = {
|
||||
authorizations: string | string[];
|
||||
forceEnvironmentId?: EnvironmentId;
|
||||
adminOnlyCE?: boolean;
|
||||
};
|
||||
|
||||
|
@ -19,24 +16,19 @@ type RedirectOptions = {
|
|||
/**
|
||||
* Redirects to the given route if the user is not authorized.
|
||||
* @param authorizations The authorizations to check.
|
||||
* @param forceEnvironmentId The environment id to use for the check.
|
||||
* @param adminOnlyCE Whether to check only for admin authorizations in CE.
|
||||
* @param adminOnlyCE Whether to allow non-admin users in CE.
|
||||
* @param to The route to redirect to.
|
||||
* @param params The params to pass to the route.
|
||||
*/
|
||||
export function useUnauthorizedRedirect(
|
||||
{
|
||||
authorizations,
|
||||
forceEnvironmentId,
|
||||
adminOnlyCE = false,
|
||||
}: AuthorizationOptions,
|
||||
{ authorizations, adminOnlyCE = false }: AuthorizationOptions,
|
||||
{ to, params }: RedirectOptions
|
||||
) {
|
||||
const router = useRouter();
|
||||
|
||||
const isAuthorized = useAuthorizations(
|
||||
authorizations,
|
||||
forceEnvironmentId,
|
||||
undefined,
|
||||
adminOnlyCE
|
||||
);
|
||||
|
||||
|
|
|
@ -7,11 +7,14 @@ import {
|
|||
PropsWithChildren,
|
||||
} from 'react';
|
||||
|
||||
import { isAdmin } from '@/portainer/users/user.helpers';
|
||||
import { isEdgeAdmin, isPureAdmin } from '@/portainer/users/user.helpers';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import { User } from '@/portainer/users/types';
|
||||
import { useLoadCurrentUser } from '@/portainer/users/queries/useLoadCurrentUser';
|
||||
|
||||
import { useEnvironment } from '../portainer/environments/queries';
|
||||
import { isBE } from '../portainer/feature-flags/feature-flags.service';
|
||||
|
||||
interface State {
|
||||
user?: User;
|
||||
}
|
||||
|
@ -39,32 +42,84 @@ export function useCurrentUser() {
|
|||
return useMemo(
|
||||
() => ({
|
||||
user,
|
||||
isAdmin: isAdmin(user),
|
||||
isPureAdmin: isPureAdmin(user),
|
||||
}),
|
||||
[user]
|
||||
);
|
||||
}
|
||||
|
||||
export function useIsPureAdmin() {
|
||||
const { isPureAdmin } = useCurrentUser();
|
||||
return isPureAdmin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the admin status of the user, (admin >= edge admin)
|
||||
* @param forceEnvironmentId to force the environment id, used where the environment id can't be loaded from the router, like sidebar
|
||||
* @returns query result with isLoading and isAdmin - isAdmin is true if the user edge admin or admin.
|
||||
*/
|
||||
export function useIsEdgeAdmin({
|
||||
forceEnvironmentId,
|
||||
noEnvScope,
|
||||
}: {
|
||||
forceEnvironmentId?: EnvironmentId;
|
||||
noEnvScope?: boolean;
|
||||
} = {}) {
|
||||
const { user } = useCurrentUser();
|
||||
const {
|
||||
params: { endpointId },
|
||||
} = useCurrentStateAndParams();
|
||||
|
||||
const envId = forceEnvironmentId || endpointId;
|
||||
const envScope = typeof noEnvScope === 'boolean' ? !noEnvScope : !!envId;
|
||||
const envQuery = useEnvironment(envScope ? envId : undefined);
|
||||
|
||||
if (!envScope) {
|
||||
return { isLoading: false, isAdmin: isEdgeAdmin(user) };
|
||||
}
|
||||
|
||||
if (envQuery.isLoading) {
|
||||
return { isLoading: true, isAdmin: false };
|
||||
}
|
||||
|
||||
return {
|
||||
isLoading: false,
|
||||
isAdmin: isEdgeAdmin(user, envQuery.data),
|
||||
};
|
||||
}
|
||||
|
||||
export function useAuthorizations(
|
||||
authorizations: string | string[],
|
||||
forceEnvironmentId?: EnvironmentId,
|
||||
adminOnlyCE = false
|
||||
) {
|
||||
const { user } = useUser();
|
||||
const { user } = useCurrentUser();
|
||||
const {
|
||||
params: { endpointId },
|
||||
} = useCurrentStateAndParams();
|
||||
const envQuery = useEnvironment(forceEnvironmentId || endpointId);
|
||||
const isAdmin = useIsEdgeAdmin({ forceEnvironmentId });
|
||||
|
||||
if (!user) {
|
||||
return false;
|
||||
return { authorized: false, isLoading: false };
|
||||
}
|
||||
|
||||
return hasAuthorizations(
|
||||
user,
|
||||
authorizations,
|
||||
forceEnvironmentId || endpointId,
|
||||
adminOnlyCE
|
||||
);
|
||||
if (envQuery.isLoading) {
|
||||
return { authorized: false, isLoading: true };
|
||||
}
|
||||
|
||||
if (isAdmin) {
|
||||
return { authorized: true, isLoading: false };
|
||||
}
|
||||
|
||||
if (!isBE && adminOnlyCE) {
|
||||
return { authorized: false, isLoading: false };
|
||||
}
|
||||
|
||||
return {
|
||||
authorized: hasAuthorizations(user, authorizations, envQuery.data?.Id),
|
||||
isLoading: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function useIsEnvironmentAdmin({
|
||||
|
@ -81,24 +136,18 @@ export function useIsEnvironmentAdmin({
|
|||
);
|
||||
}
|
||||
|
||||
export function isEnvironmentAdmin(
|
||||
user: User,
|
||||
environmentId: EnvironmentId,
|
||||
adminOnlyCE = true
|
||||
) {
|
||||
return hasAuthorizations(
|
||||
user,
|
||||
['EndpointResourcesAccess'],
|
||||
environmentId,
|
||||
adminOnlyCE
|
||||
);
|
||||
}
|
||||
|
||||
export function hasAuthorizations(
|
||||
/**
|
||||
* will return true if the user has the authorizations. assumes the user is authenticated and not an admin
|
||||
* @param user
|
||||
* @param authorizations
|
||||
* @param environmentId
|
||||
* @param adminOnlyCE
|
||||
* @returns
|
||||
*/
|
||||
function hasAuthorizations(
|
||||
user: User,
|
||||
authorizations: string | string[],
|
||||
environmentId?: EnvironmentId,
|
||||
adminOnlyCE = false
|
||||
environmentId?: EnvironmentId
|
||||
) {
|
||||
const authorizationsArray =
|
||||
typeof authorizations === 'string' ? [authorizations] : authorizations;
|
||||
|
@ -107,26 +156,13 @@ export function hasAuthorizations(
|
|||
return true;
|
||||
}
|
||||
|
||||
if (process.env.PORTAINER_EDITION === 'CE') {
|
||||
return !adminOnlyCE || isAdmin(user);
|
||||
}
|
||||
|
||||
if (!environmentId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isAdmin(user)) {
|
||||
return true;
|
||||
}
|
||||
const userEndpointAuthorizations =
|
||||
user.EndpointAuthorizations?.[environmentId] || [];
|
||||
|
||||
if (
|
||||
!user.EndpointAuthorizations ||
|
||||
!user.EndpointAuthorizations[environmentId]
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const userEndpointAuthorizations = user.EndpointAuthorizations[environmentId];
|
||||
return authorizationsArray.some(
|
||||
(authorization) => userEndpointAuthorizations[authorization]
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue