diff --git a/app/routes/users/components/manage-banner.tsx b/app/routes/users/components/manage-banner.tsx index 8905e2a..726e95e 100644 --- a/app/routes/users/components/manage-banner.tsx +++ b/app/routes/users/components/manage-banner.tsx @@ -4,11 +4,12 @@ import Link from '~/components/Link'; import type { HeadplaneConfig } from '~/server/config/schema'; import CreateUser from '../dialogs/create-user'; -interface Props { +interface ManageBannerProps { oidc?: NonNullable; + isDisabled?: boolean; } -export default function ManageBanner({ oidc }: Props) { +export default function ManageBanner({ oidc, isDisabled }: ManageBannerProps) { return (
@@ -60,7 +61,7 @@ export default function ManageBanner({ oidc }: Props) { : 'You can add, remove, and rename users here.'}

- +
diff --git a/app/routes/users/dialogs/create-user.tsx b/app/routes/users/dialogs/create-user.tsx index dd9819e..dc85ae8 100644 --- a/app/routes/users/dialogs/create-user.tsx +++ b/app/routes/users/dialogs/create-user.tsx @@ -1,11 +1,15 @@ import Dialog from '~/components/Dialog'; import Input from '~/components/Input'; +interface CreateUserProps { + isDisabled?: boolean; +} + // TODO: Support image upload for user avatars -export default function CreateUser() { +export default function CreateUser({ isDisabled }: CreateUserProps) { return ( - Add a new user + Add a new user Add a new user diff --git a/app/routes/users/overview.tsx b/app/routes/users/overview.tsx index 630d468..f930f77 100644 --- a/app/routes/users/overview.tsx +++ b/app/routes/users/overview.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'; import type { ActionFunctionArgs, LoaderFunctionArgs } from 'react-router'; import { useLoaderData, useSubmit } from 'react-router'; import type { LoadContext } from '~/server'; +import { Capabilities } from '~/server/web/roles'; import { Machine, User } from '~/types'; import cn from '~/utils/cn'; import ManageBanner from './components/manage-banner'; @@ -17,6 +18,19 @@ export async function loader({ context, }: LoaderFunctionArgs) { const session = await context.sessions.auth(request); + const check = await context.sessions.check(request, Capabilities.read_users); + if (!check) { + // Not authorized to view this page + throw new Error( + 'You do not have permission to view this page. Please contact your administrator.', + ); + } + + const writablePermission = await context.sessions.check( + request, + Capabilities.write_users, + ); + const [machines, apiUsers] = await Promise.all([ context.client.get<{ nodes: Machine[] }>( 'v1/node', @@ -63,6 +77,7 @@ export async function loader({ } return { + writable: writablePermission, // whether the user can write to the API oidc: context.config.oidc, roles, magic, @@ -93,7 +108,7 @@ export default function Page() { Manage the users in your network and their permissions. Tip: You can drag machines between users to change ownership.

- + diff --git a/app/routes/users/user-actions.ts b/app/routes/users/user-actions.ts index 89d3320..d54eafd 100644 --- a/app/routes/users/user-actions.ts +++ b/app/routes/users/user-actions.ts @@ -9,8 +9,12 @@ export async function userAction({ context, }: ActionFunctionArgs) { const session = await context.sessions.auth(request); - const apiKey = session.get('api_key')!; + const check = await context.sessions.check(request, Capabilities.write_users); + if (!check) { + return data({ success: false }, 403); + } + const apiKey = session.get('api_key')!; const formData = await request.formData(); const action = formData.get('action_id')?.toString(); if (!action) { @@ -84,20 +88,6 @@ async function reassignUser( context: LoadContext, session: Session, ) { - const executor = session.get('user'); - if (!executor?.subject) { - return data({ success: false }, 400); - } - - const check = await context.sessions.checkSubject( - executor.subject, - Capabilities.write_users, - ); - - if (!check) { - return data({ success: false }, 403); - } - const userId = formData.get('user_id')?.toString(); const newRole = formData.get('new_role')?.toString(); if (!userId || !newRole) {