User roles state (#5685)

* Update for useApiState

- Rename to useUserState
- Include role information

* Adds state method for checking user roles
This commit is contained in:
Oliver 2023-10-11 22:58:42 +11:00 committed by GitHub
parent 89faf8e59d
commit 149e5c3696
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 83 additions and 17 deletions

View File

@ -11,12 +11,12 @@ import { Link } from 'react-router-dom';
import { doClassicLogout } from '../../functions/auth';
import { InvenTreeStyle } from '../../globalStyle';
import { useApiState } from '../../states/ApiState';
import { useUserState } from '../../states/UserState';
import { PlaceholderPill } from '../items/Placeholder';
export function MainMenu() {
const { classes, theme } = InvenTreeStyle();
const [username] = useApiState((state) => [state.user?.name]);
const [username] = useUserState((state) => [state.user?.name]);
return (
<Menu width={260} position="bottom-end">
<Menu.Target>

View File

@ -4,14 +4,10 @@ import { IconCheck } from '@tabler/icons-react';
import axios from 'axios';
import { api } from '../App';
import {
ApiPaths,
url,
useApiState,
useServerApiState
} from '../states/ApiState';
import { ApiPaths, url, useServerApiState } from '../states/ApiState';
import { useLocalState } from '../states/LocalState';
import { useSessionState } from '../states/SessionState';
import { useUserState } from '../states/UserState';
export const doClassicLogin = async (username: string, password: string) => {
const { host } = useLocalState.getState();
@ -62,11 +58,11 @@ export const doSimpleLogin = async (email: string) => {
export const doTokenLogin = (token: string) => {
const { setToken } = useSessionState.getState();
const { fetchApiState } = useApiState.getState();
const { fetchUserState } = useUserState.getState();
const { fetchServerApiState } = useServerApiState.getState();
setToken(token);
fetchApiState();
fetchUserState();
fetchServerApiState();
};

View File

@ -7,7 +7,7 @@ import {
WidgetLayout
} from '../../components/widgets/WidgetLayout';
import { LoadingItem } from '../../functions/loading';
import { useApiState } from '../../states/ApiState';
import { useUserState } from '../../states/UserState';
const vals: LayoutItemType[] = [
{
@ -51,7 +51,7 @@ const vals: LayoutItemType[] = [
];
export default function Home() {
const [username] = useApiState((state) => [state.user?.name]);
const [username] = useUserState((state) => [state.user?.name]);
return (
<>
<Title order={1}>

View File

@ -4,13 +4,16 @@ import { api } from '../App';
import { emptyServerAPI } from '../defaults/defaults';
import { ServerAPIProps, UserProps } from './states';
interface ApiStateProps {
interface UserStateProps {
user: UserProps | undefined;
setUser: (newUser: UserProps) => void;
fetchApiState: () => void;
}
export const useApiState = create<ApiStateProps>((set, get) => ({
/**
* Global user information state, using Zustand manager
*/
export const useApiState = create<UserStateProps>((set, get) => ({
user: undefined,
setUser: (newUser: UserProps) => set({ user: newUser }),
fetchApiState: async () => {
@ -45,6 +48,7 @@ export const useServerApiState = create<ServerApiStateProps>((set, get) => ({
export enum ApiPaths {
user_me = 'api-user-me',
user_roles = 'api-user-roles',
user_token = 'api-user-token',
user_simple_login = 'api-user-simple-login',
user_reset = 'api-user-reset',
@ -64,6 +68,8 @@ export function url(path: ApiPaths, pk?: any): string {
switch (path) {
case ApiPaths.user_me:
return 'user/me/';
case ApiPaths.user_roles:
return 'user/roles/';
case ApiPaths.user_token:
return 'user/token/';
case ApiPaths.user_simple_login:

View File

@ -0,0 +1,59 @@
import { create } from 'zustand';
import { api } from '../App';
import { ApiPaths, url } from './ApiState';
import { UserProps } from './states';
interface UserStateProps {
user: UserProps | undefined;
setUser: (newUser: UserProps) => void;
fetchUserState: () => void;
}
/**
* Global user information state, using Zustand manager
*/
export const useUserState = create<UserStateProps>((set, get) => ({
user: undefined,
setUser: (newUser: UserProps) => set({ user: newUser }),
fetchUserState: async () => {
// Fetch user data
await api
.get(url(ApiPaths.user_me))
.then((response) => {
const user: UserProps = {
name: `${response.data.first_name} ${response.data.last_name}`,
email: response.data.email,
username: response.data.username
};
set({ user: user });
})
.catch((error) => {
console.error('Error fetching user data:', error);
});
// Fetch role data
await api
.get(url(ApiPaths.user_roles))
.then((response) => {
const user: UserProps = get().user as UserProps;
user.roles = response.data.roles;
user.is_staff = response.data.is_staff ?? false;
user.is_superuser = response.data.is_superuser ?? false;
set({ user: user });
})
.catch((error) => {
console.error('Error fetching user roles:', error);
});
},
checkUserRole: (role: string, permission: string) => {
// Check if the user has the specified permission for the specified role
const user: UserProps = get().user as UserProps;
if (user.is_superuser) return true;
if (user.roles === undefined) return false;
if (user.roles[role] === undefined) return false;
return user.roles[role].includes(permission);
}
}));

View File

@ -7,12 +7,17 @@ export interface HostList {
[key: string]: Host;
}
// Type interface fully defining the current user
export interface UserProps {
name: string;
email: string;
username: string;
is_staff?: boolean;
is_superuser?: boolean;
roles?: Record<string, string[]>;
}
// Type interface fully defining the current server
export interface ServerAPIProps {
server: null | string;
version: null | string;

View File

@ -7,13 +7,13 @@ import { BaseContext } from '../contexts/BaseContext';
import { defaultHostList } from '../defaults/defaultHostList';
import { url_base } from '../main';
import { routes } from '../router';
import { useApiState } from '../states/ApiState';
import { useLocalState } from '../states/LocalState';
import { useSessionState } from '../states/SessionState';
import { useUserState } from '../states/UserState';
export default function DesktopAppView() {
const [hostList] = useLocalState((state) => [state.hostList]);
const [fetchApiState] = useApiState((state) => [state.fetchApiState]);
const [fetchUserState] = useUserState((state) => [state.fetchUserState]);
// Local state initialization
if (Object.keys(hostList).length === 0) {
@ -29,7 +29,7 @@ export default function DesktopAppView() {
useEffect(() => {
if (token && !fetchedServerSession) {
setFetchedServerSession(true);
fetchApiState();
fetchUserState();
}
}, [token, fetchedServerSession]);