Skip to content

Commit

Permalink
Migrate more privileges to be permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
beverloo committed Jul 18, 2024
1 parent 9190138 commit b4b5b19
Show file tree
Hide file tree
Showing 24 changed files with 91 additions and 57 deletions.
2 changes: 1 addition & 1 deletion app/admin/AdminSidebarClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export interface AdminSidebarMenuSubMenuItem {
/**
* Whether the menu should be open by default. Defaults to false.
*/
defaultOpen?: boolean | Privilege;
defaultOpen?: boolean;

/**
* Child menu items that should be shown as part of this entry.
Expand Down
32 changes: 22 additions & 10 deletions app/admin/TopLevelLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import { type AdminSidebarMenuEntry, AdminSidebar } from './AdminSidebar';
import { Privilege } from '@lib/auth/Privileges';
import { requireAuthenticationContext } from '@lib/auth/AuthenticationContext';

import { kAnyEvent, kAnyTeam } from '@lib/auth/AccessControl';

export default async function TopLevelLayout(props: React.PropsWithChildren) {
const { access, user } = await requireAuthenticationContext();

Expand All @@ -37,7 +39,7 @@ export default async function TopLevelLayout(props: React.PropsWithChildren) {
{
icon: <FeedOutlinedIcon />,
label: 'Content',
privilege: Privilege.SystemAdministrator,
permission: 'system.content',
url: '/admin/content',
},
{
Expand All @@ -49,7 +51,13 @@ export default async function TopLevelLayout(props: React.PropsWithChildren) {
{
icon: <EventNoteIcon />,
label: 'Events',
privilege: Privilege.EventAdministrator,
permission: {
permission: 'event.visible',
options: {
event: kAnyEvent,
team: kAnyTeam,
},
},
url: '/admin/events',
},
{
Expand All @@ -63,6 +71,8 @@ export default async function TopLevelLayout(props: React.PropsWithChildren) {
{
icon: <ChatBubbleOutlineIcon />,
label: 'Communication',
// permission: system.feedback
// permission: system.internals
privilege: [
Privilege.SystemOutboxAccess,
Privilege.SystemSubscriptionManagement,
Expand All @@ -72,7 +82,7 @@ export default async function TopLevelLayout(props: React.PropsWithChildren) {
{
icon: <FeedbackOutlinedIcon />,
label: 'Feedback',
privilege: Privilege.SystemAdministrator,
permission: 'system.feedback',
url: '/admin/system/feedback',
},
{
Expand All @@ -90,7 +100,7 @@ export default async function TopLevelLayout(props: React.PropsWithChildren) {
{
icon: <WebhookIcon />,
label: 'Webhooks',
privilege: Privilege.SystemAdministrator,
permission: 'system.internals',
url: '/admin/system/webhooks',
},
],
Expand All @@ -99,11 +109,13 @@ export default async function TopLevelLayout(props: React.PropsWithChildren) {
icon: <DeviceHubIcon />,
label: 'System',
permission: [
'system.ai',
'system.displays',
'system.internals.ai',
'system.internals.scheduler',
'system.internals.settings',
{ permission: 'system.logs', operation: 'read' },
],
defaultOpen: Privilege.SystemAdministrator,
defaultOpen: access.can('system.internals'),
menu: [
{
icon: <TabletIcon />,
Expand All @@ -114,13 +126,13 @@ export default async function TopLevelLayout(props: React.PropsWithChildren) {
{
icon: <ModelTrainingIcon />,
label: 'Generative AI',
permission: 'system.ai',
permission: 'system.internals.ai',
url: '/admin/system/ai',
},
{
icon: <ApiIcon />,
label: 'Integrations',
privilege: Privilege.Administrator,
permission: 'system.internals.settings',
url: '/admin/system/integrations',
},
{
Expand All @@ -135,13 +147,13 @@ export default async function TopLevelLayout(props: React.PropsWithChildren) {
{
icon: <ManageHistoryIcon />,
label: 'Scheduler',
privilege: Privilege.SystemAdministrator,
permission: 'system.internals.scheduler',
url: '/admin/system/scheduler',
},
{
icon: <SettingsIcon />,
label: 'Settings',
privilege: Privilege.SystemAdministrator,
permission: 'system.internals.settings',
url: '/admin/system/settings',
}
]
Expand Down
7 changes: 3 additions & 4 deletions app/admin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import type { SchedulerStatus } from './dashboard/SchedulerCard';
import type { User } from '@lib/auth/User';
import { default as TopLevelLayout } from './TopLevelLayout';
import { Dashboard } from './dashboard/Dashboard';
import { Privilege, can } from '@lib/auth/Privileges';
import { RegistrationStatus } from '@lib/database/Types';
import { Temporal } from '@lib/Temporal';
import { requireAuthenticationContext } from '@lib/auth/AuthenticationContext';
Expand Down Expand Up @@ -82,15 +81,15 @@ async function fetchBirthdays(user: User) {
* give an overview of what's going on. Exact cards depend on the user's access level.
*/
export default async function AdminPage() {
const { events, user } = await requireAuthenticationContext({ check: 'admin' });
const { access, events, user } = await requireAuthenticationContext({ check: 'admin' });

// TODO: Filter for participating events in `fetchBirthdays`
const { currentBirthdays, upcomingBirthdays } = await fetchBirthdays(user);

const connectionPool = getConnectionPool();
let databaseStatus: DatabaseStatus | undefined;

if (can(user, Privilege.SystemAdministrator) && connectionPool) {
if (access.can('system.internals') && connectionPool) {
databaseStatus = {
connections: {
active: connectionPool.activeConnections(),
Expand All @@ -102,7 +101,7 @@ export default async function AdminPage() {
}

let schedulerStatus: SchedulerStatus | undefined;
if (can(user, Privilege.SystemAdministrator)) {
if (access.can('system.internals.scheduler')) {
let timeSinceLastExecutionMs: number | undefined = undefined;
if (globalScheduler.lastExecution !== undefined) {
const diffNs = process.hrtime.bigint() - globalScheduler.lastExecution;
Expand Down
2 changes: 1 addition & 1 deletion app/admin/system/ai/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { requireAuthenticationContext } from '@lib/auth/AuthenticationContext';
export default async function AiPage() {
await requireAuthenticationContext({
check: 'admin',
permission: 'system.ai',
permission: 'system.internals.ai',
});

// Settings to load for this page, shared across the different displays.
Expand Down
2 changes: 1 addition & 1 deletion app/admin/system/ai/prompt/[prompt]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { requireAuthenticationContext } from '@lib/auth/AuthenticationContext';
export default async function AiPromptExplorer(props: NextPageParams<'prompt'>) {
await requireAuthenticationContext({
check: 'admin',
permission: 'system.ai',
permission: 'system.internals.ai',
});

const personality = await readSetting('gen-ai-personality') ?? '';
Expand Down
2 changes: 1 addition & 1 deletion app/admin/system/debug/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ async function debugAction(formData: unknown) {
export default async function DebugPage() {
await requireAuthenticationContext({
check: 'admin',
privilege: Privilege.SystemAdministrator,
permission: 'system.internals',
});

const debugValues: Record<string, any> = {};
Expand Down
1 change: 0 additions & 1 deletion app/admin/system/displays/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type { Metadata } from 'next';
import type { DisplayTableEventOption, DisplayTableLocationOption } from './DisplaysTable';
import { DisplaysTable } from './DisplaysTable';
import { HelpRequestTable } from './HelpRequestTable';
import { Privilege } from '@lib/auth/Privileges';
import { Section } from '@app/admin/components/Section';
import { SectionIntroduction } from '@app/admin/components/SectionIntroduction';
import { requireAuthenticationContext } from '@lib/auth/AuthenticationContext';
Expand Down
3 changes: 1 addition & 2 deletions app/admin/system/feedback/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import type { Metadata } from 'next';

import { Privilege } from '@lib/auth/Privileges';
import { Section } from '../../components/Section';
import { SectionIntroduction } from '../../components/SectionIntroduction';
import { requireAuthenticationContext } from '@lib/auth/AuthenticationContext';
Expand All @@ -16,7 +15,7 @@ import { FeedbackDataTable } from './FeedbackDataTable';
export default async function FeedbackPage() {
await requireAuthenticationContext({
check: 'admin',
privilege: Privilege.SystemAdministrator,
permission: 'system.feedback',
});

// TODO: Consider allowing a response to be composed automagically.
Expand Down
3 changes: 1 addition & 2 deletions app/admin/system/integrations/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { AnimeCon, type AnimeConSettings } from './AnimeCon';
import { Email, type EmailSettings } from './Email';
import { Google, type GoogleSettings } from './Google';
import { StatusHeader } from './StatusHeader';
import { Privilege } from '@lib/auth/Privileges';
import { Twilio } from './Twilio';
import { VertexAI, type VertexAISettings } from './VertexAI';
import { VertexSupportedModels } from '@lib/integrations/vertexai/VertexSupportedModels';
Expand All @@ -22,7 +21,7 @@ import type { TwilioSettings } from '@lib/integrations/twilio/TwilioClient';
export default async function IntegrationsPage() {
await requireAuthenticationContext({
check: 'admin',
privilege: Privilege.Administrator,
permission: 'system.internals.settings',
});

const settings = await readSettings([
Expand Down
2 changes: 1 addition & 1 deletion app/admin/system/scheduler/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default async function TaskPage(props: NextPageParams<'id'>) {

await requireAuthenticationContext({
check: 'admin',
privilege: Privilege.SystemAdministrator,
permission: 'system.internals.scheduler',
});

const task = await db.selectFrom(tTasks)
Expand Down
2 changes: 1 addition & 1 deletion app/admin/system/scheduler/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { requireAuthenticationContext } from '@lib/auth/AuthenticationContext';
export default async function SchedulerPage() {
await requireAuthenticationContext({
check: 'admin',
privilege: Privilege.SystemAdministrator,
permission: 'system.internals.scheduler',
});

return (
Expand Down
3 changes: 1 addition & 2 deletions app/admin/system/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type { Metadata } from 'next';

import Alert from '@mui/material/Alert';

import { Privilege } from '@lib/auth/Privileges';
import { Section } from '@app/admin/components/Section';
import { SettingSection, type ConfigurableSetting } from './SettingSection';
import { readSettings, type Setting } from '@lib/Settings';
Expand All @@ -19,7 +18,7 @@ import { SettingUtilitiesSection } from './SettingUtilitiesSection';
export default async function IntegrationsPage() {
await requireAuthenticationContext({
check: 'admin',
privilege: Privilege.SystemAdministrator,
permission: 'system.internals.settings',
});

// The configuration for settings contains the individual settings, the data types, and the
Expand Down
2 changes: 1 addition & 1 deletion app/api/admin/scheduler/[[...id]]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export const { GET } = createDataTableApi(kSchedulerRowModel, kSchedulerContext,
async accessCheck(context, action, props) {
executeAccessCheck(props.authenticationContext, {
check: 'admin',
privilege: Privilege.SystemAdministrator,
permission: 'system.internals.scheduler',
});
},

Expand Down
2 changes: 1 addition & 1 deletion app/api/admin/scheduler/scheduleTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ type Response = ApiResponse<typeof kScheduleTaskDefinition>;
export async function scheduleTask(request: Request, props: ActionProps): Promise<Response> {
executeAccessCheck(props.authenticationContext, {
check: 'admin',
privilege: Privilege.SystemAdministrator,
permission: 'system.internals.scheduler',
});

if ('taskId' in request) {
Expand Down
2 changes: 1 addition & 1 deletion app/api/admin/serviceHealth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ async function runVertexAIHealthCheck(): Promise<Response> {
export async function serviceHealth(request: Request, props: ActionProps): Promise<Response> {
executeAccessCheck(props.authenticationContext, {
check: 'admin',
privilege: Privilege.SystemAdministrator,
permission: 'system.internals',
});

switch (request.service) {
Expand Down
2 changes: 1 addition & 1 deletion app/api/admin/updateSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type Response = ApiResponse<typeof kUpdateSettingsDefinition>;
export async function updateSettings(request: Request, props: ActionProps): Promise<Response> {
executeAccessCheck(props.authenticationContext, {
check: 'admin',
privilege: Privilege.SystemAdministrator,
permission: 'system.internals.settings',
});

const settings: { [k: string]: boolean | number | string } = { /* will be composed */ };
Expand Down
2 changes: 1 addition & 1 deletion app/api/admin/vertexAi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ type Response = ApiResponse<typeof kVertexAiDefinition>;
export async function vertexAi(request: Request, props: ActionProps): Promise<Response> {
executeAccessCheck(props.authenticationContext, {
check: 'admin',
privilege: Privilege.SystemAdministrator,
permission: 'system.internals.settings',
});

const { settings } = request;
Expand Down
6 changes: 3 additions & 3 deletions app/api/ai/generatePrompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export async function generatePrompt(request: Request, props: ActionProps): Prom
case 'approve-volunteer':
executeAccessCheck(props.authenticationContext, {
check: 'admin',
permission: or('system.ai', {
permission: or('system.internals.ai', {
permission: 'event.applications',
operation: 'update',
options: {
Expand Down Expand Up @@ -190,7 +190,7 @@ export async function generatePrompt(request: Request, props: ActionProps): Prom
case 'reject-volunteer':
executeAccessCheck(props.authenticationContext, {
check: 'admin',
permission: or('system.ai', {
permission: or('system.internals.ai', {
permission: 'event.applications',
operation: 'update',
options: {
Expand All @@ -209,7 +209,7 @@ export async function generatePrompt(request: Request, props: ActionProps): Prom

// Install personality and prompt overrides when provided. This feature is only accessible
// through the Generative AI Explorer pages, and relies on a special permission.
if (request.overrides && props.access.can('system.ai'))
if (request.overrides && props.access.can('system.internals.ai'))
generator.setOverrides(request.overrides.personality, request.overrides.prompt);

const { context, prompt, subject } = await generator.build(request.language);
Expand Down
2 changes: 1 addition & 1 deletion app/api/ai/updateSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type Response = ApiResponse<typeof kUpdateAiSettingsDefinition>;
export async function updateSettings(request: Request, props: ActionProps): Promise<Response> {
executeAccessCheck(props.authenticationContext, {
check: 'admin',
permission: 'system.ai',
permission: 'system.internals.ai',
});

if (request.personality) {
Expand Down
3 changes: 1 addition & 2 deletions app/api/event/schedule/submitFeedback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import type { ActionProps } from '../../Action';
import type { ApiDefinition, ApiRequest, ApiResponse } from '../../Types';
import { Log, LogType } from '@lib/Log';
import { LogSeverity } from '@lib/database/Types';
import { Privilege, can } from '@lib/auth/Privileges';
import db, { tFeedback } from '@lib/database';

/**
Expand Down Expand Up @@ -66,7 +65,7 @@ export async function submitFeedback(request: Request, props: ActionProps): Prom
let userId: number | undefined = props.user.userId;
let feedbackName: string | undefined;

if (can(props.user, Privilege.Feedback) && !!request.overrides) {
if (props.access.can('system.feedback') && !!request.overrides) {
if (!!request.overrides.userId || !!request.overrides.name) {
userId = request.overrides.userId ?? undefined;
feedbackName = request.overrides.name ?? undefined;
Expand Down
3 changes: 1 addition & 2 deletions app/feedback/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { notFound } from 'next/navigation';

import { ExportLayout } from '@app/exports/[slug]/ExportLayout';
import { FeedbackForm } from './FeedbackForm';
import { Privilege } from '@lib/auth/Privileges';
import { Temporal } from '@lib/Temporal';
import { requireAuthenticationContext } from '@lib/auth/AuthenticationContext';

Expand All @@ -18,7 +17,7 @@ import { getStaticContent } from '@lib/Content';
* soliciting feedback from volunteers. The page will automatically determine the right event.
*/
export default async function FeedbackPage() {
await requireAuthenticationContext({ privilege: Privilege.Feedback });
await requireAuthenticationContext({ permission: 'system.feedback' });

const currentTime = Temporal.Now.zonedDateTimeISO('utc');
const eventSelectionTime = currentTime.subtract({ days: 30 });
Expand Down
Loading

0 comments on commit b4b5b19

Please sign in to comment.