Files
portainer/app/react/hooks/useUser.tsx
claude code agent 28a06e80a8 refactor(ce): finish dead-BE removal — S3 backup, user-activity, LDAP, edition markers (F1-F5)
Maintainer pre-merge review follow-up (all non-blocker dead code):
F1: delete the dead S3-backup remnants (validation, query hooks, S3-only query
    key, BackupS3Model/Settings types) — kept the CE file-backup path.
F2: delete the orphaned user-activity services + their registration (kept the
    notifications component and routes).
F3: drop the unused buildOpenLDAPSettingsModel().
F4: drop the dead one-option ldap-options data (the selector was already collapsed).
F5: remove the dead data-edition attribute + its process.env typing; silence the
    intentional hasAuthorizations unused-params; drop the dead useRolesState meta.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 19:52:33 +03:00

208 lines
5.5 KiB
TypeScript

import { useCurrentStateAndParams } from '@uirouter/react';
import {
createContext,
ReactNode,
useContext,
useMemo,
PropsWithChildren,
} from 'react';
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';
interface State {
user?: User;
}
export const UserContext = createContext<State | null>(null);
UserContext.displayName = 'UserContext';
/**
* @deprecated use `useCurrentUser` instead
*/
export const useUser = useCurrentUser;
export function useCurrentUser() {
const context = useContext(UserContext);
if (context === null) {
throw new Error('should be nested under UserProvider');
}
const { user } = context;
if (typeof user === 'undefined') {
throw new Error('should be authenticated');
}
return useMemo(
() => ({
user,
isPureAdmin: isPureAdmin(user),
}),
[user]
);
}
export function useIsPureAdmin() {
const { isPureAdmin } = useCurrentUser();
return isPureAdmin;
}
/**
* Load the admin status of the user, returning true if the user is edge admin or 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),
};
}
/**
* Check if the user has some of the authorizations
*
* @param authorizations a list of authorizations to check
* @param forceEnvironmentId to force the environment id, used where the environment id can't be loaded from the router, like sidebar
* @param adminOnlyCE if true, will return false if the user is not an admin in CE
* @returns query result with isLoading and authorized - authorized is true if the user has some of the authorizations
*/
export function useAuthorizations(
authorizations: string | string[],
forceEnvironmentId?: EnvironmentId,
adminOnlyCE = false
) {
const { user } = useCurrentUser();
const {
params: { endpointId },
} = useCurrentStateAndParams();
const envQuery = useEnvironment(forceEnvironmentId || endpointId);
const isAdminQuery = useIsEdgeAdmin({ forceEnvironmentId });
if (!user) {
return { authorized: false, isLoading: false };
}
if (envQuery.isInitialLoading || isAdminQuery.isLoading) {
return { authorized: false, isLoading: true };
}
if (isAdminQuery.isAdmin) {
return { authorized: true, isLoading: false };
}
if (adminOnlyCE) {
return { authorized: false, isLoading: false };
}
return {
authorized: hasAuthorizations(user, authorizations, envQuery.data?.Id),
isLoading: false,
};
}
export function useIsEnvironmentAdmin({
forceEnvironmentId,
adminOnlyCE = true,
}: {
forceEnvironmentId?: EnvironmentId;
adminOnlyCE?: boolean;
} = {}) {
return useAuthorizations(
['EndpointResourcesAccess'],
forceEnvironmentId,
adminOnlyCE
);
}
/**
* will return true if the user has some of the authorizations. assumes the user is authenticated and not an admin
*
* @private Please use `useAuthorizations` instead. Exported only for angular's authentication service app/portainer/services/authentication.js:154
*/
/* eslint-disable @typescript-eslint/no-unused-vars -- signature kept for the AngularJS authentication.js caller; args are unused because CE has no per-endpoint authorization gating (that only existed in the Business Edition). */
export function hasAuthorizations(
user: User,
authorizations: string | string[],
environmentId?: EnvironmentId
) {
// In CE every authenticated user passes endpoint authorization checks here;
// per-endpoint authorization gating only existed in the Business Edition.
return true;
}
/* eslint-enable @typescript-eslint/no-unused-vars */
interface AuthorizedProps {
authorizations: string | string[];
environmentId?: EnvironmentId;
adminOnlyCE?: boolean;
childrenUnauthorized?: ReactNode;
}
export function Authorized({
authorizations,
environmentId,
adminOnlyCE = false,
children,
childrenUnauthorized = null,
}: PropsWithChildren<AuthorizedProps>) {
const { authorized } = useAuthorizations(
authorizations,
environmentId,
adminOnlyCE
);
return authorized ? <>{children}</> : <>{childrenUnauthorized}</>;
}
interface UserProviderProps {
children: ReactNode;
}
export function UserProvider({ children }: UserProviderProps) {
const userQuery = useLoadCurrentUser();
const providerState = useMemo(
() => ({ user: userQuery.data }),
[userQuery.data]
);
if (!providerState.user) {
return null;
}
return (
<UserContext.Provider value={providerState}>
{children}
</UserContext.Provider>
);
}