From f14213a5d4687591465a7bd453028810f5e18db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Kr=C4=8Dek?= Date: Mon, 14 Jul 2025 15:50:07 +0200 Subject: [PATCH] Add role base access control for events module --- src/app.d.ts | 13 +++++++++ src/hooks.server.ts | 28 +++++++++++++++++++ src/routes/+layout.server.ts | 13 +++++++-- src/routes/+layout.ts | 7 ++++- src/routes/+page.svelte | 1 - src/routes/private/+layout.svelte | 6 +++- .../private/errors/events/denied/+page.svelte | 1 + src/routes/private/home/+page.server.ts | 22 --------------- src/routes/private/home/+page.svelte | 13 +++------ 9 files changed, 68 insertions(+), 36 deletions(-) create mode 100644 src/routes/private/errors/events/denied/+page.svelte delete mode 100644 src/routes/private/home/+page.server.ts diff --git a/src/app.d.ts b/src/app.d.ts index 9a0cf33..4aa1e3d 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,17 +1,30 @@ import type { Session, SupabaseClient, User } from '@supabase/supabase-js' import type { Database } from './database.types.ts' // import generated types +// Define the profile type based on the database schema +type Profile = { + display_name: string | null + section_position: string | null + section: { + name: string | null + } | null +} + declare global { namespace App { // interface Error {} interface Locals { supabase: SupabaseClient safeGetSession: () => Promise<{ session: Session | null; user: User | null }> + getUserProfile: (userId: string) => Promise session: Session | null user: User | null + profile: Profile | null } interface PageData { session: Session | null + user: User | null + profile: Profile | null } // interface PageState {} // interface Platform {} diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 09e2560..ba7a5a8 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -51,6 +51,22 @@ const supabase: Handle = async ({ event, resolve }) => { return { session, user } } + /** + * Fetch user profile data including display name, section position, and section name + */ + event.locals.getUserProfile = async (userId) => { + if (!userId) return null + + const { data: profile, error } = await event.locals.supabase + .from('profiles') + .select('display_name, section_position, section:sections (name)') + .eq('id', userId) + .single() + + if (error) return null + return profile + } + return resolve(event, { filterSerializedResponseHeaders(name) { /** @@ -67,6 +83,11 @@ const authGuard: Handle = async ({ event, resolve }) => { event.locals.session = session event.locals.user = user + // Fetch the user's profile if they're authenticated + if (user) { + event.locals.profile = await event.locals.getUserProfile(user.id) + } + if (!event.locals.session && event.url.pathname.startsWith('/private')) { redirect(303, '/auth') } @@ -75,6 +96,13 @@ const authGuard: Handle = async ({ event, resolve }) => { redirect(303, '/private/home') } + // Role-based access control for events routes + if (event.url.pathname.startsWith('/private/events')) { + if (!event.locals.profile || event.locals.profile.section_position !== 'events_manager') { + redirect(303, '/private/errors/events/denied') + } + } + return resolve(event) } diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index 7543dfa..8277089 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -1,9 +1,18 @@ import type { LayoutServerLoad } from './$types' -export const load: LayoutServerLoad = async ({ locals: { safeGetSession }, cookies }) => { - const { session } = await safeGetSession() +export const load: LayoutServerLoad = async ({ locals: { safeGetSession, getUserProfile }, cookies }) => { + const { session, user } = await safeGetSession() + + // Get the user profile if the user is authenticated + let profile = null + if (user) { + profile = await getUserProfile(user.id) + } + return { session, + user, + profile, cookies: cookies.getAll(), } } \ No newline at end of file diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 46d5417..e19c00c 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -39,5 +39,10 @@ export const load: LayoutLoad = async ({ data, depends, fetch }) => { data: { user }, } = await supabase.auth.getUser() - return { session, supabase, user } + return { + session, + supabase, + user, + profile: data.profile + } } \ No newline at end of file diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index a09321c..f22a586 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,5 +1,4 @@
-
diff --git a/src/routes/private/+layout.svelte b/src/routes/private/+layout.svelte index f45a903..748cf77 100644 --- a/src/routes/private/+layout.svelte +++ b/src/routes/private/+layout.svelte @@ -3,6 +3,8 @@ import { QueryClient, QueryClientProvider } from '@tanstack/svelte-query'; import ToastContainer from '$lib/components/ToastContainer.svelte'; + let { data } = $props(); + const queryClient = new QueryClient({ defaultOptions: { queries: { @@ -22,7 +24,9 @@
diff --git a/src/routes/private/errors/events/denied/+page.svelte b/src/routes/private/errors/events/denied/+page.svelte new file mode 100644 index 0000000..884187a --- /dev/null +++ b/src/routes/private/errors/events/denied/+page.svelte @@ -0,0 +1 @@ +Access to events denied! \ No newline at end of file diff --git a/src/routes/private/home/+page.server.ts b/src/routes/private/home/+page.server.ts deleted file mode 100644 index 56a2a40..0000000 --- a/src/routes/private/home/+page.server.ts +++ /dev/null @@ -1,22 +0,0 @@ -// src/routes/my-page/+page.server.ts -import type { PageServerLoad } from './$types'; - -export const load: PageServerLoad = async ({ locals }) => { - // get the logged-in user - const { data: { user }, error: authError } = await locals.supabase.auth.getUser(); - - const { data: user_profile, error: profileError } = await locals.supabase.from('profiles').select('*, section:sections (id, name)').eq('id', user?.id).single(); - - if (authError) { - console.error('Supabase auth error:', authError); - throw new Error('Could not get user'); - } - - if (profileError) { - console.error('Supabase profile error:', profileError); - throw new Error('Could not get user profile'); - } - - return { user, user_profile }; - -}; \ No newline at end of file diff --git a/src/routes/private/home/+page.svelte b/src/routes/private/home/+page.svelte index d081370..9ac6c24 100644 --- a/src/routes/private/home/+page.svelte +++ b/src/routes/private/home/+page.svelte @@ -1,10 +1,5 @@

User Profile

@@ -16,18 +11,18 @@ {data.user?.user_metadata.display_name?.[0] ?? "U"}
- {data.user?.user_metadata.display_name} + {data.profile?.display_name}
{data.user?.email}
Section: - {data.user_profile?.section.name ?? "N/A"} + {data.profile?.section.name ?? "N/A"}
Position: - {data.user_profile?.section_position ?? "N/A"} + {data.profile?.section_position ?? "N/A"}

User guide