From b3777b7bf8870e3237e8c77753d0910bd57dc464 Mon Sep 17 00:00:00 2001 From: Delemangi Date: Sun, 16 Jun 2024 22:30:47 +0200 Subject: [PATCH] Allow roles assigning Signed-off-by: Delemangi --- backend/app/auth/router.py | 41 +++++- backend/app/auth/schemas.py | 5 + backend/app/auth/service.py | 28 ++++ .../src/lib/components/admin/RoleRow.svelte | 7 + .../lib/components/admin/TitleUserRow.svelte | 32 +++++ .../src/lib/components/admin/UserRow.svelte | 136 ++++++++++++++++++ frontend/src/routes/admin/users/+page.svelte | 36 +++++ frontend/src/routes/user/account/+page.svelte | 8 ++ frontend/src/server/auth.ts | 27 ++++ 9 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 frontend/src/lib/components/admin/TitleUserRow.svelte create mode 100644 frontend/src/lib/components/admin/UserRow.svelte create mode 100644 frontend/src/routes/admin/users/+page.svelte diff --git a/backend/app/auth/router.py b/backend/app/auth/router.py index 0487141..7573d63 100644 --- a/backend/app/auth/router.py +++ b/backend/app/auth/router.py @@ -10,13 +10,15 @@ from .dependencies import get_current_user from .exceptions import AUTHENTICATION_2FA_EXCEPTION, CREDENTIALS_EXCEPTION from .models import User as DbUser -from .schemas import Code2FA, RoleMetadata, Token, UpdateRole, User, UserMetadata +from .schemas import Code2FA, RoleMetadata, Token, UpdateRole, UpdateUser, User, UserMetadata from .service import ( authenticate_user, create_access_token, create_user, edit_role_quotas, + edit_user_role, get_roles, + get_users, oauth2_scheme, remove_2fa_code, remove_token, @@ -163,3 +165,40 @@ async def edit_role( ) return RequestStatus(message="Role quotas updated successfully") + + +@router.get("/users", response_model=list[UserMetadata]) +async def users( + current_user: Annotated[DbUser, Depends(get_current_user)], + session: Annotated[AsyncSession, Depends(get_async_session)], +) -> list[UserMetadata]: + users = await get_users(current_user, session) + print("/users") + + return [ + UserMetadata( + username=str(user.username), + role=str(user.role.name), + files_quota=int(user.role.quota_files), + size_quota=int(user.role.quota_size), + is_2fa_enabled=(user.code_2fa is not None), + ) + for user in users + ] + + +@router.post("/users/edit") +async def edit_user( + update_input: UpdateUser, + current_user: Annotated[DbUser, Depends(get_current_user)], + session: Annotated[AsyncSession, Depends(get_async_session)], +) -> RequestStatus: + print("/users/edit") + await edit_user_role( + current_user, + update_input.username, + update_input.role_name, + session, + ) + + return RequestStatus(message="User role updated successfully") diff --git a/backend/app/auth/schemas.py b/backend/app/auth/schemas.py index 18bc5dc..382d24d 100644 --- a/backend/app/auth/schemas.py +++ b/backend/app/auth/schemas.py @@ -42,3 +42,8 @@ class UpdateRole(BaseModel): role_id: str size: int files: int + + +class UpdateUser(BaseModel): + username: str + role_name: str diff --git a/backend/app/auth/service.py b/backend/app/auth/service.py index 73eed56..0cfdbb3 100644 --- a/backend/app/auth/service.py +++ b/backend/app/auth/service.py @@ -67,6 +67,34 @@ async def edit_role_quotas( await session.commit() +async def get_users(current_user: User, session: AsyncSession) -> list[User]: + if current_user.role.name != "admin": + raise NO_PERMISSION_EXCEPTION + + users = await session.execute(select(User).options(joinedload(User.role))) + + return list(users.scalars().all()) + + +async def edit_user_role( + current_user: User, username: str, role_name: str, session: AsyncSession +) -> None: + print(username, role_name) + + if current_user.role.name != "admin": + raise NO_PERMISSION_EXCEPTION + + role_query = await session.execute(select(Role).where(Role.name == role_name)) + role = role_query.scalar_one_or_none() + + if role is None: + raise ValueError("Role not found") + + await session.execute(update(User).where(User.username == username).values(role_id=role.id)) + await session.commit() + print("done") + + async def create_user(username: str, plain_password: str, session: AsyncSession) -> User: password = pwd_context.hash(plain_password) diff --git a/frontend/src/lib/components/admin/RoleRow.svelte b/frontend/src/lib/components/admin/RoleRow.svelte index a50b739..5597189 100644 --- a/frontend/src/lib/components/admin/RoleRow.svelte +++ b/frontend/src/lib/components/admin/RoleRow.svelte @@ -15,6 +15,7 @@ } from '@svelteuidev/core'; import { isAxiosError } from 'axios'; import { Update } from 'radix-icons-svelte'; + import { clearSession } from '../../../auth/session'; import { editRole } from '../../../server/auth'; export let role: RoleMetadata; @@ -63,6 +64,12 @@ return; } + if (error.response?.status === 401) { + await clearSession(); + window.location.href = '/auth/login'; + return; + } + if (error.response?.status === 403) { alert('You do not have permission to edit this role.'); return; diff --git a/frontend/src/lib/components/admin/TitleUserRow.svelte b/frontend/src/lib/components/admin/TitleUserRow.svelte new file mode 100644 index 0000000..33a2570 --- /dev/null +++ b/frontend/src/lib/components/admin/TitleUserRow.svelte @@ -0,0 +1,32 @@ + + + + + Name + Role + Actions + + diff --git a/frontend/src/lib/components/admin/UserRow.svelte b/frontend/src/lib/components/admin/UserRow.svelte new file mode 100644 index 0000000..f858394 --- /dev/null +++ b/frontend/src/lib/components/admin/UserRow.svelte @@ -0,0 +1,136 @@ + + + + + + {user.username} + + + {user.role.toUpperCase()} + + + + (overlayShown = true)}> + + + + + + + +{#if overlayShown} + + + + Edit User + { + return { value: role.name, label: role.name.toUpperCase() }; + })} + bind:value={role} + icon={StarFilled} + /> + + + + + + + + +{/if} diff --git a/frontend/src/routes/admin/users/+page.svelte b/frontend/src/routes/admin/users/+page.svelte new file mode 100644 index 0000000..ffa294a --- /dev/null +++ b/frontend/src/routes/admin/users/+page.svelte @@ -0,0 +1,36 @@ + + +
+ Users +
+ + +{#each users as user} + +{/each} diff --git a/frontend/src/routes/user/account/+page.svelte b/frontend/src/routes/user/account/+page.svelte index 66a2ffd..e80d703 100644 --- a/frontend/src/routes/user/account/+page.svelte +++ b/frontend/src/routes/user/account/+page.svelte @@ -4,6 +4,7 @@ import { isAxiosError } from 'axios'; import { LockClosed, LockOpen2 } from 'radix-icons-svelte'; import { onMount } from 'svelte'; + import { clearSession } from '../../../auth/session'; import { getUserMetadata } from '../../../server/auth'; import { getFilesForSpecifiedUser } from '../../../server/files'; import { getPermanentToken } from '../../../server/sharex'; @@ -34,6 +35,12 @@ return; } + if (error.response?.status === 401) { + await clearSession(); + window.location.href = '/auth/login'; + return; + } + alert('An error occurred while fetching the user data.'); } }); @@ -111,6 +118,7 @@
Admin + Manage Users Manage Roles {/if} diff --git a/frontend/src/server/auth.ts b/frontend/src/server/auth.ts index d71e4d7..c6adaba 100644 --- a/frontend/src/server/auth.ts +++ b/frontend/src/server/auth.ts @@ -101,3 +101,30 @@ export const editRole = async ( return result.data; }; + +export const getUsers = async (token: string) => { + const result = await axios.get(`${BASE_URL}/auth/users`, { + headers: { + authorization: `Bearer ${token}` + } + }); + + return result.data; +}; + +export const editUser = async (token: string, username: string, role_name: string) => { + const result = await axios.post( + `${BASE_URL}/auth/users/edit`, + { + username, + role_name + }, + { + headers: { + authorization: `Bearer ${token}` + } + } + ); + + return result.data; +};