diff --git a/app/features/authentication/routes/login.tsx b/app/features/authentication/routes/login.tsx index eca4cd8..52edab4 100644 --- a/app/features/authentication/routes/login.tsx +++ b/app/features/authentication/routes/login.tsx @@ -23,7 +23,7 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - // Vérifier que le sous-domaine correspond à une assemblée existante + // Verify that the subdomain matches an existing congregation await resolveCongregationFromRequest(request) const shouldStartSetup = await needSetupProcess() @@ -33,7 +33,7 @@ export async function loader({ request }: Route.LoaderArgs) { const session = await getSession(request.headers.get('Cookie')) if (session.has('userId') === true) { - // En mode multi-tenant, vérifier que la session correspond à l'assemblée du sous-domaine + // In multi-tenant mode, verify the session matches the subdomain's congregation const urlCongregation = await resolveCongregationFromRequest(request) if (urlCongregation) { const uid = Number(session.get('userId')) diff --git a/app/features/authentication/routes/password-invalidation.tsx b/app/features/authentication/routes/password-invalidation.tsx index b4fd32b..0f02a67 100644 --- a/app/features/authentication/routes/password-invalidation.tsx +++ b/app/features/authentication/routes/password-invalidation.tsx @@ -2,9 +2,9 @@ import ResetPasswordRequired from 'emails/reset-password-required' import { redirect } from 'react-router' import { createPasswordResetToken } from '~/features/authentication/server/invalidate-user-password.server' import { sendResetUserPasswordEmail } from '~/features/authentication/server/send-reset-user-password-email.server' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { resolveCongregation } from '~/shared/libs/congregation.server' import { unscopedDb as db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' @@ -19,8 +19,8 @@ export function loader() { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManageUser = await verifyRole(request, Role.SettingsUserManager) + const { session, can } = await authenticateAndAuthorize(request, [Role.SettingsUserManager]) + const canManageUser = can(Role.SettingsUserManager) if (!canManageUser) throw redirect('/') diff --git a/app/features/authentication/routes/user/_layout.tsx b/app/features/authentication/routes/user/_layout.tsx index f9cb434..277d899 100644 --- a/app/features/authentication/routes/user/_layout.tsx +++ b/app/features/authentication/routes/user/_layout.tsx @@ -1,14 +1,14 @@ import { Outlet } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import type { Route } from './+types/_layout' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' export const meta: Route.MetaFunction = () => { return [{ title: 'Mon compte - Unitae' }] } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) + await authenticateAndAuthorize(request) return {} } diff --git a/app/features/authentication/routes/user/profile.tsx b/app/features/authentication/routes/user/profile.tsx index 64c828f..c375667 100644 --- a/app/features/authentication/routes/user/profile.tsx +++ b/app/features/authentication/routes/user/profile.tsx @@ -1,6 +1,6 @@ import { Form, redirect } from 'react-router' import { changeUserPassword } from '~/features/authentication/server/change-user-password.server' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import logger from '~/shared/libs/logger.server' import { Alert, AlertDescription } from '~/shared/ui/alert' import { Button } from '~/shared/ui/button' @@ -10,14 +10,14 @@ import { Label } from '~/shared/ui/label' import { PageHeader } from '~/shared/ui/PageHeader' import type { Route } from './+types/profile' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' export const meta: Route.MetaFunction = () => { return [{ title: 'Mon profil - Unitae' }] } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser, session, congregation } = await verifySession(request) - + const { currentUser, session, congregation } = await authenticateAndAuthorize(request) logger.info(`Loading profile data. User ID: ${currentUser.id}.`) return { @@ -98,7 +98,7 @@ export default function ProfilePage({ loaderData }: Route.ComponentProps) { } export async function action({ request }: Route.ActionArgs) { - const { session, currentUser } = await verifySession(request) + const { session, currentUser } = await authenticateAndAuthorize(request) const formData = await request.formData() const password = formData.get('password') const newPassword = formData.get('new_password') diff --git a/app/features/authentication/server/session.server.ts b/app/features/authentication/server/session.server.ts index ca55448..7130d9e 100644 --- a/app/features/authentication/server/session.server.ts +++ b/app/features/authentication/server/session.server.ts @@ -62,7 +62,7 @@ export async function verifySession(request: Request) { }) } - // En mode multi-tenant, vérifier que le sous-domaine correspond à l'assemblée de l'utilisateur + // In multi-tenant mode, verify that the subdomain matches the user's congregation const urlCongregation = await resolveCongregationFromRequest(request) if (urlCongregation && urlCongregation.id !== user.congregationId) { throw redirect('/login', { diff --git a/app/features/authorization/server/verify-role.server.ts b/app/features/authorization/server/verify-role.server.ts index dfecd95..ce334b4 100644 --- a/app/features/authorization/server/verify-role.server.ts +++ b/app/features/authorization/server/verify-role.server.ts @@ -31,7 +31,7 @@ export async function verifyRole(request: Request, roleKey: Role) { }) if (adminRole != null) { - // Restaurer le contexte ALS — les requêtes unscopedDb via l'adaptateur pg le cassent + // Restore ALS context — unscopedDb queries via the pg adapter break it congregationContext.enterWith({ congregationId }) return true } diff --git a/app/features/board/routes/_layout.tsx b/app/features/board/routes/_layout.tsx index af6d9d4..af13c54 100644 --- a/app/features/board/routes/_layout.tsx +++ b/app/features/board/routes/_layout.tsx @@ -1,7 +1,7 @@ import { data, Outlet } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import type { Route } from './+types/_layout' export const meta: Route.MetaFunction = () => { @@ -9,13 +9,13 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { session } = await verifySession(request) - const canUploadDocument = await verifyRole(request, Role.BoardUploader) - const canViewTerritories = await verifyRole(request, Role.TerritoriesViewer) - const canManageSettings = await verifyRole(request, Role.SettingsUserManager) - const canViewPublishers = await verifyRole(request, Role.PublisherViewer) - const canManageBoard = await verifyRole(request, Role.BoardValidator) - const canViewProspection = await verifyRole(request, Role.ProspectionViewer) + const { session, can } = await authenticateAndAuthorize(request, [Role.BoardUploader, Role.TerritoriesViewer, Role.SettingsUserManager, Role.PublisherViewer, Role.BoardValidator, Role.ProspectionViewer]) + const canUploadDocument = can(Role.BoardUploader) + const canViewTerritories = can(Role.TerritoriesViewer) + const canManageSettings = can(Role.SettingsUserManager) + const canViewPublishers = can(Role.PublisherViewer) + const canManageBoard = can(Role.BoardValidator) + const canViewProspection = can(Role.ProspectionViewer) const messages = { success: session.get('success'), error: session.get('error') } return data( diff --git a/app/features/board/routes/documents/delete.tsx b/app/features/board/routes/documents/delete.tsx index 3f81059..1fcbc25 100644 --- a/app/features/board/routes/documents/delete.tsx +++ b/app/features/board/routes/documents/delete.tsx @@ -1,7 +1,7 @@ import { Form, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { deleteFile } from '~/features/board/server/document' import { db } from '~/shared/libs/db.server' import logger from '~/shared/libs/logger.server' @@ -12,8 +12,8 @@ import { Card, CardContent } from '~/shared/ui/card' import type { Route } from './+types/delete' export async function loader({ request, params }: Route.LoaderArgs) { - await verifySession(request) - const canUploadDocument = await verifyRole(request, Role.BoardUploader) + const { can } = await authenticateAndAuthorize(request, [Role.BoardUploader]) + const canUploadDocument = can(Role.BoardUploader) if (!canUploadDocument) { throw redirect('/') @@ -48,8 +48,8 @@ export default function DeleteDocumentPage({ loaderData }: Route.ComponentProps) } export async function action({ request, params }: Route.ActionArgs) { - const { session, currentUser } = await verifySession(request) - const canUploadDocument = await verifyRole(request, Role.BoardUploader) + const { session, currentUser, can } = await authenticateAndAuthorize(request, [Role.BoardUploader]) + const canUploadDocument = can(Role.BoardUploader) if (!canUploadDocument) { throw redirect('/') diff --git a/app/features/board/routes/documents/edit.tsx b/app/features/board/routes/documents/edit.tsx index 4bccff2..acf88ce 100644 --- a/app/features/board/routes/documents/edit.tsx +++ b/app/features/board/routes/documents/edit.tsx @@ -1,8 +1,8 @@ import { Trash2 } from 'lucide-react' import { Form, Link, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' import { Button } from '~/shared/ui/button' @@ -18,9 +18,9 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request, params }: Route.LoaderArgs) { - await verifySession(request) - const canUploadDocument = await verifyRole(request, Role.BoardUploader) - const canManageBoard = await verifyRole(request, Role.BoardValidator) + const { can } = await authenticateAndAuthorize(request, [Role.BoardUploader, Role.BoardValidator]) + const canUploadDocument = can(Role.BoardUploader) + const canManageBoard = can(Role.BoardValidator) if (!canUploadDocument) { throw redirect('/') @@ -156,8 +156,8 @@ export default function EditDocumentPage({ loaderData }: Route.ComponentProps) { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManageBoard = await verifyRole(request, Role.BoardValidator) + const { session, can } = await authenticateAndAuthorize(request, [Role.BoardValidator]) + const canManageBoard = can(Role.BoardValidator) const form = await request.formData() const title = String(form.get('title')) diff --git a/app/features/board/routes/documents/list.tsx b/app/features/board/routes/documents/list.tsx index 96f97d5..c85865e 100644 --- a/app/features/board/routes/documents/list.tsx +++ b/app/features/board/routes/documents/list.tsx @@ -1,8 +1,7 @@ import { ChevronDown, ChevronUp, Eye, FileText, Pencil, Trash2 } from 'lucide-react' import { Form, Link, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { DocumentVisibility } from '~/features/board/ui/DocumentVisibility' import { db } from '~/shared/libs/db.server' import logger from '~/shared/libs/logger.server' @@ -19,8 +18,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canUploadDocument = await verifyRole(request, Role.BoardUploader) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.BoardUploader]) + const canUploadDocument = can(Role.BoardUploader) if (!canUploadDocument) { logger.warn(`Tried to load board documents. User ID: ${currentUser.id}. Does NOT have rights to upload document.`) diff --git a/app/features/board/routes/documents/move-down.tsx b/app/features/board/routes/documents/move-down.tsx index 94885d5..2a0702f 100644 --- a/app/features/board/routes/documents/move-down.tsx +++ b/app/features/board/routes/documents/move-down.tsx @@ -1,7 +1,7 @@ import { redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' @@ -12,8 +12,8 @@ export function loader(_args: Route.LoaderArgs) { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManageBoard = await verifyRole(request, Role.BoardValidator) + const { session, can } = await authenticateAndAuthorize(request, [Role.BoardValidator]) + const canManageBoard = can(Role.BoardValidator) if (!canManageBoard) { throw redirect('/') diff --git a/app/features/board/routes/documents/move-up.tsx b/app/features/board/routes/documents/move-up.tsx index 04ba061..3a06380 100644 --- a/app/features/board/routes/documents/move-up.tsx +++ b/app/features/board/routes/documents/move-up.tsx @@ -1,7 +1,7 @@ import { redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' @@ -12,8 +12,8 @@ export function loader(_args: Route.LoaderArgs) { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManageBoard = await verifyRole(request, Role.BoardValidator) + const { session, can } = await authenticateAndAuthorize(request, [Role.BoardValidator]) + const canManageBoard = can(Role.BoardValidator) if (!canManageBoard) { throw redirect('/') diff --git a/app/features/board/routes/documents/new.tsx b/app/features/board/routes/documents/new.tsx index 1cf631e..4ccdeec 100644 --- a/app/features/board/routes/documents/new.tsx +++ b/app/features/board/routes/documents/new.tsx @@ -1,8 +1,8 @@ import { type FileUpload, parseFormData } from '@mjackson/form-data-parser' import { Form, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { saveFile } from '~/features/board/server/document' import { sendNewDocumentNotificationEmail } from '~/features/board/server/notifications' import { db } from '~/shared/libs/db.server' @@ -21,9 +21,9 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canUploadDocument = await verifyRole(request, Role.BoardUploader) - const canManageBoard = await verifyRole(request, Role.BoardValidator) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.BoardUploader, Role.BoardValidator]) + const canUploadDocument = can(Role.BoardUploader) + const canManageBoard = can(Role.BoardValidator) if (!canUploadDocument) { logger.warn( @@ -132,10 +132,9 @@ export default function NewDocumentPage({ loaderData }: Route.ComponentProps) { } export async function action({ request }: Route.ActionArgs) { - const { currentUser, session, congregation } = await verifySession(request) - - const canUploadDocument = await verifyRole(request, Role.BoardUploader) - const canManageBoard = await verifyRole(request, Role.BoardValidator) + const { currentUser, session, congregation, can } = await authenticateAndAuthorize(request, [Role.BoardUploader, Role.BoardValidator]) + const canUploadDocument = can(Role.BoardUploader) + const canManageBoard = can(Role.BoardValidator) if (!canUploadDocument) { logger.warn( diff --git a/app/features/board/routes/documents/pdf-loader.tsx b/app/features/board/routes/documents/pdf-loader.tsx index d6bb3c6..b5feb9d 100644 --- a/app/features/board/routes/documents/pdf-loader.tsx +++ b/app/features/board/routes/documents/pdf-loader.tsx @@ -1,18 +1,18 @@ import { redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { getFileStream } from '~/features/board/server/document' import { db } from '~/shared/libs/db.server' import logger from '~/shared/libs/logger.server' import { requireParamId } from '~/shared/libs/params.server' import type { Route } from './+types/pdf-loader' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' export const meta: Route.MetaFunction = () => { return [{ title: `Tableau d'affichage - Unitae` }] } export async function loader({ params, request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) + const { currentUser } = await authenticateAndAuthorize(request) logger.info(`Loading document ID: ${params.documentId}. User ID: ${currentUser.id}.`, { currentUser }) const document = await db.boardDocument.update({ diff --git a/app/features/board/routes/index.tsx b/app/features/board/routes/index.tsx index 7f96213..6ef9184 100644 --- a/app/features/board/routes/index.tsx +++ b/app/features/board/routes/index.tsx @@ -1,7 +1,6 @@ import { FileText } from 'lucide-react' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { DocumentCard } from '~/features/board/ui/DocumentCard' import { db } from '~/shared/libs/db.server' import logger from '~/shared/libs/logger.server' @@ -14,9 +13,9 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canUploadDocument = await verifyRole(request, Role.BoardUploader) - const canManageBoard = await verifyRole(request, Role.BoardValidator) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.BoardUploader, Role.BoardValidator]) + const canUploadDocument = can(Role.BoardUploader) + const canManageBoard = can(Role.BoardValidator) const folders = await db.boardSection.findMany({ where: {}, diff --git a/app/features/board/routes/sections/delete.tsx b/app/features/board/routes/sections/delete.tsx index fed80c0..df89b02 100644 --- a/app/features/board/routes/sections/delete.tsx +++ b/app/features/board/routes/sections/delete.tsx @@ -1,7 +1,7 @@ import { Form, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' import { Button } from '~/shared/ui/button' @@ -10,8 +10,8 @@ import { Card, CardContent } from '~/shared/ui/card' import type { Route } from './+types/delete' export async function loader({ request, params }: Route.LoaderArgs) { - await verifySession(request) - const canManageBoard = await verifyRole(request, Role.BoardValidator) + const { can } = await authenticateAndAuthorize(request, [Role.BoardValidator]) + const canManageBoard = can(Role.BoardValidator) if (!canManageBoard) { throw redirect('/') @@ -46,8 +46,8 @@ export default function DeleteSectionPage({ loaderData }: Route.ComponentProps) } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManageBoard = await verifyRole(request, Role.BoardValidator) + const { session, can } = await authenticateAndAuthorize(request, [Role.BoardValidator]) + const canManageBoard = can(Role.BoardValidator) if (!canManageBoard) { throw redirect('/') diff --git a/app/features/board/routes/sections/edit.tsx b/app/features/board/routes/sections/edit.tsx index 495d941..8e18e5d 100644 --- a/app/features/board/routes/sections/edit.tsx +++ b/app/features/board/routes/sections/edit.tsx @@ -1,8 +1,8 @@ import { Trash2 } from 'lucide-react' import { Form, Link, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' import { Button } from '~/shared/ui/button' @@ -18,8 +18,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request, params }: Route.LoaderArgs) { - await verifySession(request) - const canManageBoard = await verifyRole(request, Role.BoardValidator) + const { can } = await authenticateAndAuthorize(request, [Role.BoardValidator]) + const canManageBoard = can(Role.BoardValidator) if (!canManageBoard) { throw redirect('/') @@ -77,7 +77,7 @@ export default function EditSectionPage({ loaderData }: Route.ComponentProps) { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) + const { session } = await authenticateAndAuthorize(request) const form = await request.formData() const name = String(form.get('name')) diff --git a/app/features/board/routes/sections/list.tsx b/app/features/board/routes/sections/list.tsx index aca397e..71c2a4a 100644 --- a/app/features/board/routes/sections/list.tsx +++ b/app/features/board/routes/sections/list.tsx @@ -1,8 +1,7 @@ import { ChevronDown, ChevronUp, FolderOpen, Pencil, Trash2 } from 'lucide-react' import { Form, Link, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import logger from '~/shared/libs/logger.server' import { Button } from '~/shared/ui/button' @@ -18,8 +17,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canManageBoard = await verifyRole(request, Role.BoardValidator) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.BoardValidator]) + const canManageBoard = can(Role.BoardValidator) if (!canManageBoard) { logger.warn(`Tried to load board sections. User ID: ${currentUser.id}. Does NOT have rights to manage board.`) diff --git a/app/features/board/routes/sections/move-down.tsx b/app/features/board/routes/sections/move-down.tsx index ade23fb..c0bb343 100644 --- a/app/features/board/routes/sections/move-down.tsx +++ b/app/features/board/routes/sections/move-down.tsx @@ -1,7 +1,7 @@ import { redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' @@ -12,8 +12,8 @@ export function loader(_args: Route.LoaderArgs) { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManageBoard = await verifyRole(request, Role.BoardValidator) + const { session, can } = await authenticateAndAuthorize(request, [Role.BoardValidator]) + const canManageBoard = can(Role.BoardValidator) if (!canManageBoard) { throw redirect('/') diff --git a/app/features/board/routes/sections/move-up.tsx b/app/features/board/routes/sections/move-up.tsx index bf9e6cd..e03f337 100644 --- a/app/features/board/routes/sections/move-up.tsx +++ b/app/features/board/routes/sections/move-up.tsx @@ -1,7 +1,7 @@ import { redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' @@ -12,8 +12,8 @@ export function loader(_args: Route.LoaderArgs) { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManageBoard = await verifyRole(request, Role.BoardValidator) + const { session, can } = await authenticateAndAuthorize(request, [Role.BoardValidator]) + const canManageBoard = can(Role.BoardValidator) if (!canManageBoard) { throw redirect('/') diff --git a/app/features/board/routes/sections/new.tsx b/app/features/board/routes/sections/new.tsx index 106ff71..a2207bf 100644 --- a/app/features/board/routes/sections/new.tsx +++ b/app/features/board/routes/sections/new.tsx @@ -1,7 +1,7 @@ import { Form, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { Button } from '~/shared/ui/button' import { Card, CardContent } from '~/shared/ui/card' @@ -16,8 +16,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canManageBoard = await verifyRole(request, Role.BoardValidator) + const { can } = await authenticateAndAuthorize(request, [Role.BoardValidator]) + const canManageBoard = can(Role.BoardValidator) if (!canManageBoard) { throw redirect('/') @@ -49,7 +49,7 @@ export default function NewSectionPage() { } export async function action({ request }: Route.ActionArgs) { - const { session, congregation } = await verifySession(request) + const { session, congregation } = await authenticateAndAuthorize(request) const form = await request.formData() const name = String(form.get('name')) diff --git a/app/features/events/routes/days-off/delete.tsx b/app/features/events/routes/days-off/delete.tsx index cbb4b7d..49508b2 100644 --- a/app/features/events/routes/days-off/delete.tsx +++ b/app/features/events/routes/days-off/delete.tsx @@ -1,6 +1,6 @@ import { Form, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { EventKind } from '~/features/events/model/event-kind.type' import { db } from '~/shared/libs/db.server' import logger from '~/shared/libs/logger.server' @@ -9,10 +9,10 @@ import { Button } from '~/shared/ui/button' import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '~/shared/ui/card' import type { Route } from './+types/delete' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' export async function loader({ request, params }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - + const { currentUser } = await authenticateAndAuthorize(request) logger.info(`Trying to remove days off. User ID: ${currentUser.id}. Event: ${params.eventId}`) const event = await db.event.findUnique({ @@ -55,8 +55,7 @@ export default function DeleteDayOff({ loaderData }: Route.ComponentProps) { } export async function action({ request, params }: Route.ActionArgs) { - const { session, currentUser } = await verifySession(request) - + const { session, currentUser } = await authenticateAndAuthorize(request) const event = await db.event.findUnique({ where: { id: requireParamId(params.eventId, '/me/days-off') }, include: { createdBy: true }, diff --git a/app/features/events/routes/days-off/list.tsx b/app/features/events/routes/days-off/list.tsx index a0edad2..fcf2031 100644 --- a/app/features/events/routes/days-off/list.tsx +++ b/app/features/events/routes/days-off/list.tsx @@ -1,6 +1,5 @@ import { CalendarOff, X } from 'lucide-react' import { Link } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { getNextDaysOffs } from '~/features/events/server/days-off.server' import logger from '~/shared/libs/logger.server' import { Button } from '~/shared/ui/button' @@ -9,13 +8,14 @@ import { EmptyState } from '~/shared/ui/EmptyState' import { PageHeader } from '~/shared/ui/PageHeader' import type { Route } from './+types/list' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' export const meta: Route.MetaFunction = () => { return [{ title: 'Mes absences - Unitae' }] } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser, session } = await verifySession(request) + const { currentUser, session } = await authenticateAndAuthorize(request) const events = await getNextDaysOffs(currentUser.id) logger.info(`Loading personal Days Off list. User ID: ${currentUser.id}`) diff --git a/app/features/events/routes/days-off/new.tsx b/app/features/events/routes/days-off/new.tsx index 5df2a74..ebe95f2 100644 --- a/app/features/events/routes/days-off/new.tsx +++ b/app/features/events/routes/days-off/new.tsx @@ -1,6 +1,6 @@ import { useState } from 'react' import { Form, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { createDayOff } from '~/features/events/server/days-off.server' import logger from '~/shared/libs/logger.server' import { Button } from '~/shared/ui/button' @@ -10,14 +10,14 @@ import { Label } from '~/shared/ui/label' import { PageHeader } from '~/shared/ui/PageHeader' import type { Route } from './+types/new' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' export const meta: Route.MetaFunction = () => { return [{ title: 'Ajouter une absence - Unitae' }] } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser, session } = await verifySession(request) - + const { currentUser, session } = await authenticateAndAuthorize(request) logger.info(`Loading personal Days Off form. User ID: ${currentUser.id}.`) return { @@ -75,7 +75,7 @@ export default function DaysOffPage() { } export async function action({ request }: Route.ActionArgs) { - const { currentUser, session, congregation } = await verifySession(request) + const { currentUser, session, congregation } = await authenticateAndAuthorize(request) const formData = await request.formData() const startDate = new Date(String(formData.get('start_date'))) const endDate = new Date(String(formData.get('end_date'))) diff --git a/app/features/events/routes/programs/days-off.tsx b/app/features/events/routes/programs/days-off.tsx index 3d8185c..6fbf762 100644 --- a/app/features/events/routes/programs/days-off.tsx +++ b/app/features/events/routes/programs/days-off.tsx @@ -1,8 +1,7 @@ import { CalendarOff } from 'lucide-react' import { redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { EventKind } from '~/features/events/model/event-kind.type' import { computeFilters } from '~/features/events/server/event-filters.server' import EventFilters from '~/features/events/ui/EventFilters' @@ -21,9 +20,9 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canViewPrograms = await verifyRole(request, Role.ProgramViewer) - const canManagePrograms = await verifyRole(request, Role.ProgramManager) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.ProgramViewer, Role.ProgramManager]) + const canViewPrograms = can(Role.ProgramViewer) + const canManagePrograms = can(Role.ProgramManager) if (!canViewPrograms) { logger.warn(`Try to load programs. User ID: ${currentUser.id}. Does NOT have rights to access programs.`) diff --git a/app/features/events/routes/programs/list.tsx b/app/features/events/routes/programs/list.tsx index 8959681..d4132dc 100644 --- a/app/features/events/routes/programs/list.tsx +++ b/app/features/events/routes/programs/list.tsx @@ -1,8 +1,7 @@ import { CalendarOff } from 'lucide-react' import { Link, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { computeFilters } from '~/features/events/server/event-filters.server' import EventFilters from '~/features/events/ui/EventFilters' import { db } from '~/shared/libs/db.server' @@ -20,9 +19,9 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canViewPrograms = await verifyRole(request, Role.ProgramViewer) - const canManagePrograms = await verifyRole(request, Role.ProgramManager) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.ProgramViewer, Role.ProgramManager]) + const canViewPrograms = can(Role.ProgramViewer) + const canManagePrograms = can(Role.ProgramManager) if (!canViewPrograms) { logger.warn(`Try to load programs. User ID: ${currentUser.id}. Does NOT have rights to access programs.`) diff --git a/app/features/publishers/routes/_layout.tsx b/app/features/publishers/routes/_layout.tsx index 237c61b..c4516c0 100644 --- a/app/features/publishers/routes/_layout.tsx +++ b/app/features/publishers/routes/_layout.tsx @@ -1,7 +1,7 @@ import { data, Outlet, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import type { Route } from './+types/_layout' @@ -10,12 +10,12 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { session } = await verifySession(request) - const canViewTerritories = await verifyRole(request, Role.TerritoriesViewer) - const canManageSettings = await verifyRole(request, Role.SettingsUserManager) - const canViewPublishers = await verifyRole(request, Role.PublisherViewer) - const canViewPrograms = await verifyRole(request, Role.ProgramViewer) - const canViewProspection = await verifyRole(request, Role.ProspectionViewer) + const { session, can } = await authenticateAndAuthorize(request, [Role.TerritoriesViewer, Role.SettingsUserManager, Role.PublisherViewer, Role.ProgramViewer, Role.ProspectionViewer]) + const canViewTerritories = can(Role.TerritoriesViewer) + const canManageSettings = can(Role.SettingsUserManager) + const canViewPublishers = can(Role.PublisherViewer) + const canViewPrograms = can(Role.ProgramViewer) + const canViewProspection = can(Role.ProspectionViewer) if (!canViewPublishers && !canViewPrograms) { throw redirect('/') diff --git a/app/features/publishers/routes/activity/delete.tsx b/app/features/publishers/routes/activity/delete.tsx index 99c07bf..9ad54b0 100644 --- a/app/features/publishers/routes/activity/delete.tsx +++ b/app/features/publishers/routes/activity/delete.tsx @@ -1,8 +1,8 @@ import { Form, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' import { Button } from '~/shared/ui/button' @@ -11,8 +11,8 @@ import { Card, CardContent, CardFooter } from '~/shared/ui/card' import type { Route } from './+types/delete' export async function loader({ request, params }: Route.LoaderArgs) { - await verifySession(request) - const canManageActivity = await verifyRole(request, Role.ActivityManager) + const { can } = await authenticateAndAuthorize(request, [Role.ActivityManager]) + const canManageActivity = can(Role.ActivityManager) if (!canManageActivity) { throw redirect('/') @@ -64,8 +64,8 @@ export default function DeleteActivity({ loaderData }: Route.ComponentProps) { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManageActivity = await verifyRole(request, Role.ActivityManager) + const { session, can } = await authenticateAndAuthorize(request, [Role.ActivityManager]) + const canManageActivity = can(Role.ActivityManager) if (!canManageActivity) { throw redirect('/') diff --git a/app/features/publishers/routes/activity/edit.tsx b/app/features/publishers/routes/activity/edit.tsx index a5c8818..ea71f03 100644 --- a/app/features/publishers/routes/activity/edit.tsx +++ b/app/features/publishers/routes/activity/edit.tsx @@ -1,9 +1,9 @@ import { Trash2 } from 'lucide-react' import { useState } from 'react' import { Form, Link, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' import { PublisherType } from '~/shared/types/publisher-type' @@ -20,8 +20,9 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request, params }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.PublisherManager]) + const canManagePublisher = can(Role.PublisherManager) + const canManageMyGroupActivity = currentUser.responsibleFor?.id === currentUser.publisherGroupId || currentUser.deputyFor?.id === currentUser.publisherGroupId @@ -169,8 +170,9 @@ export default function EditActivity({ loaderData }: Route.ComponentProps) { export async function action({ request, params }: Route.ActionArgs) { const previousPage = request.headers.get('referer') - const { currentUser, session } = await verifySession(request) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const { currentUser, session, can } = await authenticateAndAuthorize(request, [Role.PublisherManager]) + const canManagePublisher = can(Role.PublisherManager) + const canManageMyGroupActivity = currentUser.responsibleFor?.id === currentUser.publisherGroupId || currentUser.deputyFor?.id === currentUser.publisherGroupId diff --git a/app/features/publishers/routes/activity/excel-export.tsx b/app/features/publishers/routes/activity/excel-export.tsx index 938524a..5b52a12 100644 --- a/app/features/publishers/routes/activity/excel-export.tsx +++ b/app/features/publishers/routes/activity/excel-export.tsx @@ -1,8 +1,7 @@ import { redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { generatePublishersYearlyActivityXlsx } from '~/features/publishers/server/generate-publishers-yearly-activity-xlsx.server' import logger from '~/shared/libs/logger.server' @@ -13,8 +12,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ params, request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canViewActivities = await verifyRole(request, Role.ActivityViewer) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.ActivityViewer]) + const canViewActivities = can(Role.ActivityViewer) if (!canViewActivities) { logger.warn( diff --git a/app/features/publishers/routes/activity/new.tsx b/app/features/publishers/routes/activity/new.tsx index 930ffb7..ba52a71 100644 --- a/app/features/publishers/routes/activity/new.tsx +++ b/app/features/publishers/routes/activity/new.tsx @@ -1,9 +1,9 @@ import { useState } from 'react' import { Form, redirect, useSearchParams } from 'react-router' import { sanitizeUser } from '~/features/authentication/server/sanitize-user.server' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getPublishers } from '~/features/publishers/server/publishers' import { db } from '~/shared/libs/db.server' import { PublisherType } from '~/shared/types/publisher-type' @@ -19,8 +19,9 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.PublisherManager]) + const canManagePublisher = can(Role.PublisherManager) + const canManageMyGroupActivity = currentUser.responsibleFor?.id === currentUser.publisherGroupId || currentUser.deputyFor?.id === currentUser.publisherGroupId @@ -273,8 +274,9 @@ export default function NewActivity({ loaderData }: Route.ComponentProps) { } export async function action({ request }: Route.ActionArgs) { - const { currentUser, session, congregation } = await verifySession(request) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const { currentUser, session, congregation, can } = await authenticateAndAuthorize(request, [Role.PublisherManager]) + const canManagePublisher = can(Role.PublisherManager) + const canManageMyGroupActivity = currentUser.responsibleFor?.id === currentUser.publisherGroupId || currentUser.deputyFor?.id === currentUser.publisherGroupId diff --git a/app/features/publishers/routes/activity/pdf-export.tsx b/app/features/publishers/routes/activity/pdf-export.tsx index 5dd4edb..762ad7e 100644 --- a/app/features/publishers/routes/activity/pdf-export.tsx +++ b/app/features/publishers/routes/activity/pdf-export.tsx @@ -1,8 +1,7 @@ import { redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { renderActivityPdfZip } from '~/features/publishers/server/render-activity-pdf-zip.server' import logger from '~/shared/libs/logger.server' @@ -13,8 +12,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ params, request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canViewActivities = await verifyRole(request, Role.ActivityViewer) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.ActivityViewer]) + const canViewActivities = can(Role.ActivityViewer) if (!canViewActivities) { logger.warn( diff --git a/app/features/publishers/routes/activity/publisher-list.tsx b/app/features/publishers/routes/activity/publisher-list.tsx index 883c561..673fede 100644 --- a/app/features/publishers/routes/activity/publisher-list.tsx +++ b/app/features/publishers/routes/activity/publisher-list.tsx @@ -1,9 +1,8 @@ import { ChevronLeft, ChevronRight, Download, Pencil, Plus, Users } from 'lucide-react' import { Link, redirect, useSearchParams } from 'react-router' import { sanitizeUser } from '~/features/authentication/server/sanitize-user.server' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getPublisherStats } from '~/features/publishers/server/get-publisher-stats.server' import { getPublisherWithActivities } from '~/features/publishers/server/get-publisher-with-activities.server' import PublisherActivityStats from '~/features/publishers/ui/PublisherActivityStats' @@ -22,9 +21,9 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canViewActivities = await verifyRole(request, Role.ActivityViewer) - const canManageActivities = await verifyRole(request, Role.ActivityManager) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.ActivityViewer, Role.ActivityManager]) + const canViewActivities = can(Role.ActivityViewer) + const canManageActivities = can(Role.ActivityManager) if (!canViewActivities) { logger.warn( diff --git a/app/features/publishers/routes/publishers/delete-group.tsx b/app/features/publishers/routes/publishers/delete-group.tsx index 68ab241..ba51bdb 100644 --- a/app/features/publishers/routes/publishers/delete-group.tsx +++ b/app/features/publishers/routes/publishers/delete-group.tsx @@ -1,8 +1,8 @@ import { Form, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' import { Button } from '~/shared/ui/button' @@ -11,8 +11,8 @@ import { Card, CardContent, CardFooter } from '~/shared/ui/card' import type { Route } from './+types/delete-group' export async function loader({ request, params }: Route.LoaderArgs) { - await verifySession(request) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const { can } = await authenticateAndAuthorize(request, [Role.PublisherManager]) + const canManagePublisher = can(Role.PublisherManager) if (!canManagePublisher) { throw redirect('/') @@ -55,12 +55,13 @@ export default function DeleteGroup({ loaderData }: Route.ComponentProps) { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const { session, can } = await authenticateAndAuthorize(request, [Role.PublisherManager]) + const canManagePublisher = can(Role.PublisherManager) if (!canManagePublisher) { throw redirect('/') } + const group = await db.publisherGroup.delete({ where: { id: requireParamId(params.groupId, '/congregation/publisher-groups') }, }) diff --git a/app/features/publishers/routes/publishers/edit-group.tsx b/app/features/publishers/routes/publishers/edit-group.tsx index 190c7f4..a3fb074 100644 --- a/app/features/publishers/routes/publishers/edit-group.tsx +++ b/app/features/publishers/routes/publishers/edit-group.tsx @@ -1,8 +1,8 @@ import { Trash2 } from 'lucide-react' import { Form, Link, redirect } from 'react-router' -import { commitSession, getSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession, getSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' import { Button } from '~/shared/ui/button' @@ -14,8 +14,8 @@ import { PageHeader } from '~/shared/ui/PageHeader' import type { Route } from './+types/edit-group' export async function loader({ request, params }: Route.LoaderArgs) { - await verifySession(request) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const { can } = await authenticateAndAuthorize(request, [Role.PublisherManager]) + const canManagePublisher = can(Role.PublisherManager) if (!canManagePublisher) { throw redirect('/') @@ -133,9 +133,9 @@ export default function EditGroup({ loaderData }: Route.ComponentProps) { } export async function action({ request, params }: Route.ActionArgs) { - await verifySession(request) + const { can } = await authenticateAndAuthorize(request, [Role.PublisherManager]) const previousPage = request.headers.get('referer') - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const canManagePublisher = can(Role.PublisherManager) if (!canManagePublisher) { throw redirect(previousPage ?? '/') diff --git a/app/features/publishers/routes/publishers/edit-publisher.tsx b/app/features/publishers/routes/publishers/edit-publisher.tsx index 9120228..7bffdf8 100644 --- a/app/features/publishers/routes/publishers/edit-publisher.tsx +++ b/app/features/publishers/routes/publishers/edit-publisher.tsx @@ -1,8 +1,8 @@ import { Archive, IdCard } from 'lucide-react' import { Form, redirect } from 'react-router' -import { commitSession, getSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession, getSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import PublisherFieldServiceForm from '~/features/publishers/ui/PublisherFieldServiceForm' import PublisherNominationForm from '~/features/publishers/ui/PublisherNominationForm' import PublisherPersonalInformationForm from '~/features/publishers/ui/PublisherPersonalInformationForm' @@ -19,8 +19,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request, params }: Route.LoaderArgs) { - await verifySession(request) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const { can } = await authenticateAndAuthorize(request, [Role.PublisherManager]) + const canManagePublisher = can(Role.PublisherManager) if (!canManagePublisher) { throw redirect('/') @@ -91,7 +91,7 @@ export default function EditPublisher({ loaderData }: Route.ComponentProps) { } export async function action({ request, params }: Route.ActionArgs) { - await verifySession(request) + await authenticateAndAuthorize(request) const form = await request.formData() const firstname = form.get('firstname') const lastname = form.get('lastname') diff --git a/app/features/publishers/routes/publishers/group-list.tsx b/app/features/publishers/routes/publishers/group-list.tsx index deb0cfd..3fb9b28 100644 --- a/app/features/publishers/routes/publishers/group-list.tsx +++ b/app/features/publishers/routes/publishers/group-list.tsx @@ -1,8 +1,7 @@ import { Eye, Pencil, UsersRound } from 'lucide-react' import { Link, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import logger from '~/shared/libs/logger.server' import { Button } from '~/shared/ui/button' @@ -18,9 +17,9 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canViewPublishers = await verifyRole(request, Role.PublisherViewer) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.PublisherViewer, Role.PublisherManager]) + const canViewPublishers = can(Role.PublisherViewer) + const canManagePublisher = can(Role.PublisherManager) if (!canViewPublishers) { logger.warn( diff --git a/app/features/publishers/routes/publishers/group.tsx b/app/features/publishers/routes/publishers/group.tsx index ce1b669..5822697 100644 --- a/app/features/publishers/routes/publishers/group.tsx +++ b/app/features/publishers/routes/publishers/group.tsx @@ -1,8 +1,8 @@ import { BarChart3, Eye, Mail, Pencil, Plus } from 'lucide-react' import { Link, redirect } from 'react-router' -import { commitSession, getSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession, getSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getGroup } from '~/features/publishers/server/groups' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' @@ -15,10 +15,10 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '~ import type { Route } from './+types/group' export async function loader({ request, params }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canViewPublishers = await verifyRole(request, Role.PublisherViewer) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) - const canManageActivity = await verifyRole(request, Role.ActivityManager) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.PublisherViewer, Role.PublisherManager, Role.ActivityManager]) + const canViewPublishers = can(Role.PublisherViewer) + const canManagePublisher = can(Role.PublisherManager) + const canManageActivity = can(Role.ActivityManager) if (!canViewPublishers) { throw redirect('/') @@ -229,9 +229,9 @@ export default function ViewGroup({ loaderData }: Route.ComponentProps) { } export async function action({ request, params }: Route.ActionArgs) { - await verifySession(request) + const { can } = await authenticateAndAuthorize(request, [Role.PublisherManager]) const previousPage = request.headers.get('referer') - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const canManagePublisher = can(Role.PublisherManager) if (!canManagePublisher) { throw redirect(previousPage ?? '/') diff --git a/app/features/publishers/routes/publishers/new-group.tsx b/app/features/publishers/routes/publishers/new-group.tsx index 0a8baff..bfefe5f 100644 --- a/app/features/publishers/routes/publishers/new-group.tsx +++ b/app/features/publishers/routes/publishers/new-group.tsx @@ -1,8 +1,8 @@ import { Form, redirect } from 'react-router' -import { commitSession, getSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession, getSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { Button } from '~/shared/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '~/shared/ui/card' @@ -13,8 +13,8 @@ import { PageHeader } from '~/shared/ui/PageHeader' import type { Route } from './+types/new-group' export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const { can } = await authenticateAndAuthorize(request, [Role.PublisherManager]) + const canManagePublisher = can(Role.PublisherManager) if (!canManagePublisher) { throw redirect('/') @@ -108,9 +108,9 @@ export default function NewGroup({ loaderData }: Route.ComponentProps) { } export async function action({ request }: Route.ActionArgs) { - const { congregation } = await verifySession(request) + const { congregation, can } = await authenticateAndAuthorize(request, [Role.PublisherManager]) const previousPage = request.headers.get('referer') - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const canManagePublisher = can(Role.PublisherManager) if (!canManagePublisher) { throw redirect(previousPage ?? '/') diff --git a/app/features/publishers/routes/publishers/new-publisher.tsx b/app/features/publishers/routes/publishers/new-publisher.tsx index 9becc2f..cac17a3 100644 --- a/app/features/publishers/routes/publishers/new-publisher.tsx +++ b/app/features/publishers/routes/publishers/new-publisher.tsx @@ -1,7 +1,7 @@ import { Form, redirect } from 'react-router' -import { commitSession, getSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession, getSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import PublisherFieldServiceForm from '~/features/publishers/ui/PublisherFieldServiceForm' import PublisherNominationForm from '~/features/publishers/ui/PublisherNominationForm' import PublisherPersonalInformationForm from '~/features/publishers/ui/PublisherPersonalInformationForm' @@ -17,8 +17,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const { can } = await authenticateAndAuthorize(request, [Role.PublisherManager]) + const canManagePublisher = can(Role.PublisherManager) if (!canManagePublisher) { throw redirect('/') @@ -50,7 +50,7 @@ export default function NewPublisher({ loaderData }: Route.ComponentProps) { } export async function action({ request }: Route.ActionArgs) { - const { congregation } = await verifySession(request) + const { congregation } = await authenticateAndAuthorize(request) const form = await request.formData() const firstname = String(form.get('firstname')) const lastname = String(form.get('lastname')) diff --git a/app/features/publishers/routes/publishers/publisher-list.tsx b/app/features/publishers/routes/publishers/publisher-list.tsx index da13a08..0de1341 100644 --- a/app/features/publishers/routes/publishers/publisher-list.tsx +++ b/app/features/publishers/routes/publishers/publisher-list.tsx @@ -1,8 +1,7 @@ import { BarChart3, Eye, Mail, Pencil, Users } from 'lucide-react' import { Link, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getPublishersWithGroup } from '~/features/publishers/server/publishers' import logger from '~/shared/libs/logger.server' import { Button } from '~/shared/ui/button' @@ -18,10 +17,10 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canViewPublishers = await verifyRole(request, Role.PublisherViewer) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) - const canViewActivities = await verifyRole(request, Role.ActivityViewer) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.PublisherViewer, Role.PublisherManager, Role.ActivityViewer]) + const canViewPublishers = can(Role.PublisherViewer) + const canManagePublisher = can(Role.PublisherManager) + const canViewActivities = can(Role.ActivityViewer) if (!canViewPublishers) { logger.warn( diff --git a/app/features/publishers/routes/publishers/publisher.tsx b/app/features/publishers/routes/publishers/publisher.tsx index e0ed824..f159fd6 100644 --- a/app/features/publishers/routes/publishers/publisher.tsx +++ b/app/features/publishers/routes/publishers/publisher.tsx @@ -1,9 +1,8 @@ import { Archive, Download, IdCard, Pencil } from 'lucide-react' import { Form, Link, redirect } from 'react-router' import { sanitizeUser } from '~/features/authentication/server/sanitize-user.server' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { PublisherActivityDownloadLink } from '~/features/publishers/ui/PublisherActivityDownloadLink' import { db } from '~/shared/libs/db.server' import logger from '~/shared/libs/logger.server' @@ -20,10 +19,10 @@ export const meta: Route.MetaFunction = ({ data }) => { } export async function loader({ request, params }: Route.LoaderArgs) { - const { currentUser, session } = await verifySession(request) - const canViewPublisher = await verifyRole(request, Role.PublisherViewer) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) - const canManageActivity = await verifyRole(request, Role.ActivityManager) + const { currentUser, session, can } = await authenticateAndAuthorize(request, [Role.PublisherViewer, Role.PublisherManager, Role.ActivityManager]) + const canViewPublisher = can(Role.PublisherViewer) + const canManagePublisher = can(Role.PublisherManager) + const canManageActivity = can(Role.ActivityManager) if (!canViewPublisher) { logger.warn(`Tried to load publisher file. User ID: ${currentUser.id}. Does NOT have rights to view publishers.`) diff --git a/app/features/settings/routes/_layout.tsx b/app/features/settings/routes/_layout.tsx index cc3d1a2..89ad0b0 100644 --- a/app/features/settings/routes/_layout.tsx +++ b/app/features/settings/routes/_layout.tsx @@ -1,7 +1,6 @@ import { Outlet, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import type { Route } from './+types/_layout' @@ -10,13 +9,13 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { session } = await verifySession(request) - const canViewTerritories = await verifyRole(request, Role.TerritoriesViewer) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) - const canManageUsers = await verifyRole(request, Role.SettingsUserManager) - const canViewPublishers = await verifyRole(request, Role.PublisherViewer) - const canManageSettings = await verifyRole(request, Role.Admin) - const canViewProspection = await verifyRole(request, Role.ProspectionViewer) + const { session, can } = await authenticateAndAuthorize(request, [Role.TerritoriesViewer, Role.TerritoriesManager, Role.SettingsUserManager, Role.PublisherViewer, Role.Admin, Role.ProspectionViewer]) + const canViewTerritories = can(Role.TerritoriesViewer) + const canManageTerritories = can(Role.TerritoriesManager) + const canManageUsers = can(Role.SettingsUserManager) + const canViewPublishers = can(Role.PublisherViewer) + const canManageSettings = can(Role.Admin) + const canViewProspection = can(Role.ProspectionViewer) if (!canManageUsers && !canManageSettings) { throw redirect('/') diff --git a/app/features/settings/routes/congregation/event-kind-list.tsx b/app/features/settings/routes/congregation/event-kind-list.tsx index fb19b16..f183fab 100644 --- a/app/features/settings/routes/congregation/event-kind-list.tsx +++ b/app/features/settings/routes/congregation/event-kind-list.tsx @@ -1,7 +1,6 @@ import { redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getAllEventType } from '~/features/events/server/event-kind.server' import { Badge } from '~/shared/ui/badge' @@ -14,8 +13,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canManageSettings = await verifyRole(request, Role.Admin) + const { can } = await authenticateAndAuthorize(request, [Role.Admin]) + const canManageSettings = can(Role.Admin) if (!canManageSettings) { throw redirect('/') diff --git a/app/features/settings/routes/congregation/settings.tsx b/app/features/settings/routes/congregation/settings.tsx index be4c027..fe5ba81 100644 --- a/app/features/settings/routes/congregation/settings.tsx +++ b/app/features/settings/routes/congregation/settings.tsx @@ -1,8 +1,7 @@ import { ArrowRight } from 'lucide-react' import { Form, Link, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getBoolSetting, setSetting } from '~/features/settings/server/settings' import { db, unscopedDb } from '~/shared/libs/db.server' import { CongregationSettingKey } from '~/shared/types/congregation-setting-key' @@ -20,8 +19,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { congregation } = await verifySession(request) - const canManageSettings = await verifyRole(request, Role.Admin) + const { congregation, can } = await authenticateAndAuthorize(request, [Role.Admin]) + const canManageSettings = can(Role.Admin) if (!canManageSettings) { throw redirect('/') @@ -107,8 +106,8 @@ export default function BuildingSettingsPage({ loaderData }: Route.ComponentProp } export async function action({ request }: Route.ActionArgs) { - const { congregation } = await verifySession(request) - const canManageSettings = await verifyRole(request, Role.Admin) + const { congregation, can } = await authenticateAndAuthorize(request, [Role.Admin]) + const canManageSettings = can(Role.Admin) if (!canManageSettings) { throw redirect('/') diff --git a/app/features/settings/routes/territories/settings.tsx b/app/features/settings/routes/territories/settings.tsx index ddeae55..9bfb7f0 100644 --- a/app/features/settings/routes/territories/settings.tsx +++ b/app/features/settings/routes/territories/settings.tsx @@ -1,7 +1,6 @@ import { Form, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getBoolSetting, getSetting, setSetting } from '~/features/settings/server/settings' import { getTerritoryPolygon } from '~/features/territories/server/get-territory-polygon.server' import { @@ -27,8 +26,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') @@ -135,8 +134,8 @@ export default function BuildingSettingsPage({ loaderData }: Route.ComponentProp } export async function action({ request }: Route.ActionArgs) { - const { congregation } = await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { congregation, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') diff --git a/app/features/settings/routes/users/edit-user.tsx b/app/features/settings/routes/users/edit-user.tsx index 6f312b4..fc4931a 100644 --- a/app/features/settings/routes/users/edit-user.tsx +++ b/app/features/settings/routes/users/edit-user.tsx @@ -1,8 +1,8 @@ import { IdCard, UserPlus } from 'lucide-react' import { data, Form, Link, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' import { Alert, AlertDescription } from '~/shared/ui/alert' @@ -21,9 +21,9 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request, params }: Route.LoaderArgs) { - const { session } = await verifySession(request) - const canManageUser = await verifyRole(request, Role.SettingsUserManager) - const isAdmin = await verifyRole(request, Role.Admin) + const { session, can } = await authenticateAndAuthorize(request, [Role.SettingsUserManager, Role.Admin]) + const canManageUser = can(Role.SettingsUserManager) + const isAdmin = can(Role.Admin) if (!canManageUser) { throw redirect('/') @@ -210,8 +210,8 @@ export default function SettingsLayout({ loaderData }: Route.ComponentProps) { } export async function action({ request, params }: Route.ActionArgs) { - const { congregation } = await verifySession(request) - const canManageUser = await verifyRole(request, Role.SettingsUserManager) + const { congregation, can } = await authenticateAndAuthorize(request, [Role.SettingsUserManager]) + const canManageUser = can(Role.SettingsUserManager) if (!canManageUser) { throw redirect('/') diff --git a/app/features/settings/routes/users/make-publisher.tsx b/app/features/settings/routes/users/make-publisher.tsx index ac7b83e..529a8d0 100644 --- a/app/features/settings/routes/users/make-publisher.tsx +++ b/app/features/settings/routes/users/make-publisher.tsx @@ -1,16 +1,15 @@ import { redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' import type { Route } from './+types/make-publisher' export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const { session, can } = await authenticateAndAuthorize(request, [Role.PublisherManager]) + const canManagePublisher = can(Role.PublisherManager) if (!canManagePublisher) { throw redirect('/') diff --git a/app/features/settings/routes/users/new-user.tsx b/app/features/settings/routes/users/new-user.tsx index 6718261..d9b4927 100644 --- a/app/features/settings/routes/users/new-user.tsx +++ b/app/features/settings/routes/users/new-user.tsx @@ -1,9 +1,8 @@ import { Form, redirect } from 'react-router' import { createPasswordResetToken } from '~/features/authentication/server/invalidate-user-password.server' import { sendResetUserPasswordEmail } from '~/features/authentication/server/send-reset-user-password-email.server' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { LimitService } from '~/shared/libs/limits.server' import { Button } from '~/shared/ui/button' @@ -18,8 +17,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canManageUser = await verifyRole(request, Role.SettingsUserManager) + const { can } = await authenticateAndAuthorize(request, [Role.SettingsUserManager]) + const canManageUser = can(Role.SettingsUserManager) if (!canManageUser) { throw redirect('/') @@ -61,7 +60,7 @@ export default function SettingsLayout({ loaderData }: Route.ComponentProps) { } export async function action({ request }: Route.ActionArgs) { - const { congregation } = await verifySession(request) + const { congregation } = await authenticateAndAuthorize(request) const form = await request.formData() const firstname = String(form.get('firstname')) const lastname = String(form.get('lastname')) diff --git a/app/features/settings/routes/users/unmake-publisher.tsx b/app/features/settings/routes/users/unmake-publisher.tsx index e87254a..1dcb8d9 100644 --- a/app/features/settings/routes/users/unmake-publisher.tsx +++ b/app/features/settings/routes/users/unmake-publisher.tsx @@ -1,16 +1,15 @@ import { redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' import type { Route } from './+types/unmake-publisher' export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) + const { session, can } = await authenticateAndAuthorize(request, [Role.PublisherManager]) + const canManagePublisher = can(Role.PublisherManager) if (!canManagePublisher) { throw redirect('/') diff --git a/app/features/settings/routes/users/user-list.tsx b/app/features/settings/routes/users/user-list.tsx index 559e6cc..09c590e 100644 --- a/app/features/settings/routes/users/user-list.tsx +++ b/app/features/settings/routes/users/user-list.tsx @@ -1,9 +1,8 @@ import { BadgeCheck, BadgeMinus, IdCard, Pencil, UserPlus } from 'lucide-react' import { Form, Link, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import logger from '~/shared/libs/logger.server' import { Badge } from '~/shared/ui/badge' @@ -19,10 +18,10 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canManageUser = await verifyRole(request, Role.SettingsUserManager) - const canViewPublishers = await verifyRole(request, Role.PublisherViewer) - const canManagePublishers = await verifyRole(request, Role.PublisherManager) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.SettingsUserManager, Role.PublisherViewer, Role.PublisherManager]) + const canManageUser = can(Role.SettingsUserManager) + const canViewPublishers = can(Role.PublisherViewer) + const canManagePublishers = can(Role.PublisherManager) if (!canManageUser) { logger.warn(`Tried to load users. User ID: ${currentUser.id}. Does NOT have rights to manage users.`) diff --git a/app/features/territories/routes/_layout.tsx b/app/features/territories/routes/_layout.tsx index 47dc614..ee96fc0 100644 --- a/app/features/territories/routes/_layout.tsx +++ b/app/features/territories/routes/_layout.tsx @@ -1,7 +1,6 @@ import { Outlet, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import type { Route } from './+types/_layout' @@ -10,12 +9,12 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canViewTerritories = await verifyRole(request, Role.TerritoriesViewer) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) - const canManageSettings = await verifyRole(request, Role.SettingsUserManager) - const canViewPublishers = await verifyRole(request, Role.PublisherViewer) - const canViewProspection = await verifyRole(request, Role.ProspectionViewer) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesViewer, Role.TerritoriesManager, Role.SettingsUserManager, Role.PublisherViewer, Role.ProspectionViewer]) + const canViewTerritories = can(Role.TerritoriesViewer) + const canManageTerritories = can(Role.TerritoriesManager) + const canManageSettings = can(Role.SettingsUserManager) + const canViewPublishers = can(Role.PublisherViewer) + const canViewProspection = can(Role.ProspectionViewer) if (!canViewTerritories && !canViewProspection) { throw redirect('/') diff --git a/app/features/territories/routes/attributions/delete.tsx b/app/features/territories/routes/attributions/delete.tsx index b00723c..c437f05 100644 --- a/app/features/territories/routes/attributions/delete.tsx +++ b/app/features/territories/routes/attributions/delete.tsx @@ -1,8 +1,8 @@ import { Form, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' import { Button } from '~/shared/ui/button' @@ -11,8 +11,8 @@ import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '~/shared/u import type { Route } from './+types/delete' export async function loader({ request, params }: Route.LoaderArgs) { - await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') @@ -58,8 +58,8 @@ export default function DeleteGroup({ loaderData }: Route.ComponentProps) { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { session, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') diff --git a/app/features/territories/routes/attributions/edit.tsx b/app/features/territories/routes/attributions/edit.tsx index 7ae1580..e5579eb 100644 --- a/app/features/territories/routes/attributions/edit.tsx +++ b/app/features/territories/routes/attributions/edit.tsx @@ -2,9 +2,8 @@ import { ArrowDownToLine, X } from 'lucide-react' import { useState } from 'react' import { Form, redirect } from 'react-router' import type { Prisma } from '~/database/generated/client' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getPublishers } from '~/features/publishers/server/publishers' import { getBoolSetting } from '~/features/settings/server/settings' import { TerritoryAttributionKind } from '~/features/territories/model/territory-attribution-kind.type' @@ -26,8 +25,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request, params }: Route.LoaderArgs) { - await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') @@ -187,8 +186,8 @@ export default function EditAttributionPage({ loaderData }: Route.ComponentProps } export async function action({ request, params }: Route.ActionArgs) { - await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') diff --git a/app/features/territories/routes/attributions/excel-export.tsx b/app/features/territories/routes/attributions/excel-export.tsx index 54bcf7e..55865fb 100644 --- a/app/features/territories/routes/attributions/excel-export.tsx +++ b/app/features/territories/routes/attributions/excel-export.tsx @@ -1,7 +1,6 @@ import { redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { generateS13ExportExcel } from '~/features/territories/server/s13-export.server' import { getTerritoriesExportData } from '~/features/territories/server/territories-export-data.server' import logger from '~/shared/libs/logger.server' @@ -13,8 +12,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ params, request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canViewTerritories = await verifyRole(request, Role.TerritoriesViewer) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.TerritoriesViewer]) + const canViewTerritories = can(Role.TerritoriesViewer) if (!canViewTerritories) { logger.warn( diff --git a/app/features/territories/routes/attributions/list.tsx b/app/features/territories/routes/attributions/list.tsx index b7abf65..fa4ce76 100644 --- a/app/features/territories/routes/attributions/list.tsx +++ b/app/features/territories/routes/attributions/list.tsx @@ -1,8 +1,8 @@ import { CalendarCheck, Pencil, X } from 'lucide-react' import { data, Link, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getGroups } from '~/features/publishers/server/groups' import { getBoolSetting } from '~/features/settings/server/settings' import { TerritoryAttributionKind } from '~/features/territories/model/territory-attribution-kind.type' @@ -28,12 +28,12 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser, session } = await verifySession(request) - const canViewTerritories = await verifyRole(request, Role.TerritoriesViewer) - const canManagePublisher = await verifyRole(request, Role.PublisherManager) - const canViewPublisher = await verifyRole(request, Role.PublisherViewer) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) - const canViewProspection = await verifyRole(request, Role.ProspectionViewer) + const { currentUser, session, can } = await authenticateAndAuthorize(request, [Role.TerritoriesViewer, Role.PublisherManager, Role.PublisherViewer, Role.TerritoriesManager, Role.ProspectionViewer]) + const canViewTerritories = can(Role.TerritoriesViewer) + const canManagePublisher = can(Role.PublisherManager) + const canViewPublisher = can(Role.PublisherViewer) + const canManageTerritories = can(Role.TerritoriesManager) + const canViewProspection = can(Role.ProspectionViewer) if (!canViewTerritories) { if (canViewProspection) { diff --git a/app/features/territories/routes/attributions/new.tsx b/app/features/territories/routes/attributions/new.tsx index 7fee6a3..d7d9ee0 100644 --- a/app/features/territories/routes/attributions/new.tsx +++ b/app/features/territories/routes/attributions/new.tsx @@ -1,7 +1,6 @@ import { Form, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getBoolSetting } from '~/features/settings/server/settings' import { TerritoryAttributionKind } from '~/features/territories/model/territory-attribution-kind.type' import { aggregateEntrance } from '~/features/territories/server/buildings' @@ -21,8 +20,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') @@ -124,8 +123,8 @@ export default function CreateAttributionPage({ loaderData }: Route.ComponentPro } export async function action({ request }: Route.ActionArgs) { - const { congregation } = await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { congregation, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') diff --git a/app/features/territories/routes/attributions/pdf-export.tsx b/app/features/territories/routes/attributions/pdf-export.tsx index fad382e..939bdd7 100644 --- a/app/features/territories/routes/attributions/pdf-export.tsx +++ b/app/features/territories/routes/attributions/pdf-export.tsx @@ -1,8 +1,7 @@ import { pdf } from '@react-pdf/renderer' import { redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getTerritoriesExportData } from '~/features/territories/server/territories-export-data.server' import { TerritoryAttributionDocument } from '~/features/territories/ui/TerritoryAttributionDocument' import logger from '~/shared/libs/logger.server' @@ -14,8 +13,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ params, request }: Route.LoaderArgs) { - const { currentUser } = await verifySession(request) - const canViewTerritories = await verifyRole(request, Role.TerritoriesViewer) + const { currentUser, can } = await authenticateAndAuthorize(request, [Role.TerritoriesViewer]) + const canViewTerritories = can(Role.TerritoriesViewer) if (!canViewTerritories) { logger.warn( diff --git a/app/features/territories/routes/attributions/territories.tsx b/app/features/territories/routes/attributions/territories.tsx index 48abb3c..882c17e 100644 --- a/app/features/territories/routes/attributions/territories.tsx +++ b/app/features/territories/routes/attributions/territories.tsx @@ -1,8 +1,8 @@ import { ExternalLink, Send } from 'lucide-react' import { data, Link, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { TerritoryKind } from '~/features/territories/model/territory-kind.type' import { getZips } from '~/features/territories/server/buildings' import { findAvailableTerritoriesPaginated } from '~/features/territories/server/territories' @@ -23,8 +23,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { currentUser, session } = await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { currentUser, session, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { logger.warn( diff --git a/app/features/territories/routes/prospection/_layout.tsx b/app/features/territories/routes/prospection/_layout.tsx index e090f22..b9bc60d 100644 --- a/app/features/territories/routes/prospection/_layout.tsx +++ b/app/features/territories/routes/prospection/_layout.tsx @@ -1,8 +1,8 @@ import { Map as MapIcon, RefreshCw } from 'lucide-react' import { data, Form, Link, NavLink, Outlet, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getSetting } from '~/features/settings/server/settings' import { TerritoryAccess } from '~/features/territories/model/territory-access.type' import { getZips } from '~/features/territories/server/buildings' @@ -20,10 +20,10 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { session } = await verifySession(request) - const canViewProspection = await verifyRole(request, Role.ProspectionViewer) - const canManageProspection = await verifyRole(request, Role.ProspectionManager) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { session, can } = await authenticateAndAuthorize(request, [Role.ProspectionViewer, Role.ProspectionManager, Role.TerritoriesManager]) + const canViewProspection = can(Role.ProspectionViewer) + const canManageProspection = can(Role.ProspectionManager) + const canManageTerritories = can(Role.TerritoriesManager) if (!canViewProspection) { throw redirect('/') diff --git a/app/features/territories/routes/prospection/active-building-list.tsx b/app/features/territories/routes/prospection/active-building-list.tsx index 7bff4b3..e1b053c 100644 --- a/app/features/territories/routes/prospection/active-building-list.tsx +++ b/app/features/territories/routes/prospection/active-building-list.tsx @@ -1,8 +1,7 @@ import { Eye, Search } from 'lucide-react' import { Link, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { findBuildingsPaginated, getProspectionStaleDate } from '~/features/territories/server/buildings' import { BuildingStatus } from '~/features/territories/ui/BuildingStatus' import { Button } from '~/shared/ui/button' @@ -16,10 +15,10 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canViewProspection = await verifyRole(request, Role.ProspectionViewer) - const canManageProspection = await verifyRole(request, Role.ProspectionManager) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { can } = await authenticateAndAuthorize(request, [Role.ProspectionViewer, Role.ProspectionManager, Role.TerritoriesManager]) + const canViewProspection = can(Role.ProspectionViewer) + const canManageProspection = can(Role.ProspectionManager) + const canManageTerritories = can(Role.TerritoriesManager) if (!canViewProspection) { throw redirect('/') diff --git a/app/features/territories/routes/prospection/building-list.tsx b/app/features/territories/routes/prospection/building-list.tsx index 06852f8..a41e2f8 100644 --- a/app/features/territories/routes/prospection/building-list.tsx +++ b/app/features/territories/routes/prospection/building-list.tsx @@ -1,8 +1,7 @@ import { Eye, Search } from 'lucide-react' import { Link, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { computeFilters } from '~/features/territories/server/building-filters' import { findBuildingsPaginated, getProspectionStaleDate } from '~/features/territories/server/buildings' import { BuildingStatus } from '~/features/territories/ui/BuildingStatus' @@ -17,10 +16,10 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canViewProspection = await verifyRole(request, Role.ProspectionViewer) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) - const canManageProspection = await verifyRole(request, Role.ProspectionManager) + const { can } = await authenticateAndAuthorize(request, [Role.ProspectionViewer, Role.TerritoriesManager, Role.ProspectionManager]) + const canViewProspection = can(Role.ProspectionViewer) + const canManageTerritories = can(Role.TerritoriesManager) + const canManageProspection = can(Role.ProspectionManager) if (!canViewProspection) { throw redirect('/') diff --git a/app/features/territories/routes/prospection/building.tsx b/app/features/territories/routes/prospection/building.tsx index f205690..3bc9ebc 100644 --- a/app/features/territories/routes/prospection/building.tsx +++ b/app/features/territories/routes/prospection/building.tsx @@ -1,8 +1,8 @@ import { Pencil, Search } from 'lucide-react' import { Link, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getBuildingDetails } from '~/features/territories/server/get-building-details.server' import { setBuildingNotes } from '~/features/territories/server/set-building-notes.server' import ArchiveBuildingToggleButton from '~/features/territories/ui/ArchiveBuildingToggleButton' @@ -22,11 +22,11 @@ export const meta: Route.MetaFunction = ({ data }) => { } export async function loader({ request, params }: Route.LoaderArgs) { - const { currentUser, session } = await verifySession(request) - const canViewProspection = await verifyRole(request, Role.ProspectionViewer) - const canManageProspection = await verifyRole(request, Role.ProspectionManager) - const canViewTerritories = await verifyRole(request, Role.TerritoriesViewer) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { currentUser, session, can } = await authenticateAndAuthorize(request, [Role.ProspectionViewer, Role.ProspectionManager, Role.TerritoriesViewer, Role.TerritoriesManager]) + const canViewProspection = can(Role.ProspectionViewer) + const canManageProspection = can(Role.ProspectionManager) + const canViewTerritories = can(Role.TerritoriesViewer) + const canManageTerritories = can(Role.TerritoriesManager) if (!canViewProspection) { logger.warn( @@ -123,9 +123,9 @@ export default function BuildingPage({ loaderData }: Route.ComponentProps) { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) + const { session, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') } diff --git a/app/features/territories/routes/prospection/delete-building.tsx b/app/features/territories/routes/prospection/delete-building.tsx index 70bf573..9d3eab1 100644 --- a/app/features/territories/routes/prospection/delete-building.tsx +++ b/app/features/territories/routes/prospection/delete-building.tsx @@ -1,8 +1,8 @@ import { Form, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' import { Button } from '~/shared/ui/button' @@ -11,8 +11,8 @@ import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '~/shared/u import type { Route } from './+types/delete-building' export async function loader({ request, params }: Route.LoaderArgs) { - await verifySession(request) - const canManageProspection = await verifyRole(request, Role.ProspectionManager) + const { can } = await authenticateAndAuthorize(request, [Role.ProspectionManager]) + const canManageProspection = can(Role.ProspectionManager) if (!canManageProspection) { throw redirect('/') @@ -57,8 +57,8 @@ export default function DeleteBuilding({ loaderData }: Route.ComponentProps) { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { session, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') diff --git a/app/features/territories/routes/prospection/disable-building.tsx b/app/features/territories/routes/prospection/disable-building.tsx index 1f44168..ad97a39 100644 --- a/app/features/territories/routes/prospection/disable-building.tsx +++ b/app/features/territories/routes/prospection/disable-building.tsx @@ -1,8 +1,8 @@ import { redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' @@ -13,8 +13,8 @@ export function loader(_args: Route.LoaderArgs) { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { session, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') diff --git a/app/features/territories/routes/prospection/edit-building-prospection.tsx b/app/features/territories/routes/prospection/edit-building-prospection.tsx index 3c3733f..95b69fb 100644 --- a/app/features/territories/routes/prospection/edit-building-prospection.tsx +++ b/app/features/territories/routes/prospection/edit-building-prospection.tsx @@ -1,9 +1,9 @@ import { Pencil } from 'lucide-react' import { useState } from 'react' import { data, Form, Link, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getBuildingDetails } from '~/features/territories/server/get-building-details.server' import { getBuildings } from '~/features/territories/server/get-buildings.server' import { serializeSharedEntranceFromBuilding } from '~/features/territories/server/serialize-shared-entrance-from-building.server' @@ -29,9 +29,9 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request, params }: Route.LoaderArgs) { - const { session } = await verifySession(request) - const canManageProspection = await verifyRole(request, Role.ProspectionManager) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { session, can } = await authenticateAndAuthorize(request, [Role.ProspectionManager, Role.TerritoriesManager]) + const canManageProspection = can(Role.ProspectionManager) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageProspection) { throw redirect('/') @@ -124,9 +124,9 @@ export default function EditBuildingPage({ loaderData }: Route.ComponentProps) { } export async function action({ request, params }: Route.ActionArgs) { - const { session, congregation } = await verifySession(request) - const canManageProspection = await verifyRole(request, Role.ProspectionManager) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { session, congregation, can } = await authenticateAndAuthorize(request, [Role.ProspectionManager, Role.TerritoriesManager]) + const canManageProspection = can(Role.ProspectionManager) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageProspection) { throw redirect('/') diff --git a/app/features/territories/routes/prospection/edit-building.tsx b/app/features/territories/routes/prospection/edit-building.tsx index 2a43371..9b80dc2 100644 --- a/app/features/territories/routes/prospection/edit-building.tsx +++ b/app/features/territories/routes/prospection/edit-building.tsx @@ -1,8 +1,8 @@ import { Trash2 } from 'lucide-react' import { data, Form, Link, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { editBuilding } from '~/features/territories/server/edit-building.server' import { getBuildingDetails } from '~/features/territories/server/get-building-details.server' import logger from '~/shared/libs/logger.server' @@ -25,9 +25,9 @@ export const meta: Route.MetaFunction = ({ data }) => { } export async function loader({ request, params }: Route.LoaderArgs) { - const { session } = await verifySession(request) + const { session, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') } @@ -121,9 +121,9 @@ export default function EditBuildingPage({ loaderData }: Route.ComponentProps) { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) + const { session, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') } diff --git a/app/features/territories/routes/prospection/enable-building.tsx b/app/features/territories/routes/prospection/enable-building.tsx index d346afe..7c81c75 100644 --- a/app/features/territories/routes/prospection/enable-building.tsx +++ b/app/features/territories/routes/prospection/enable-building.tsx @@ -1,8 +1,8 @@ import { redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' @@ -13,8 +13,8 @@ export function loader(_args: Route.LoaderArgs) { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { session, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') diff --git a/app/features/territories/routes/prospection/missing-building-list.tsx b/app/features/territories/routes/prospection/missing-building-list.tsx index e615b6f..02c8f8c 100644 --- a/app/features/territories/routes/prospection/missing-building-list.tsx +++ b/app/features/territories/routes/prospection/missing-building-list.tsx @@ -1,8 +1,7 @@ import { Eye, Search } from 'lucide-react' import { Link, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { findBuildingsPaginated, getProspectionStaleDate } from '~/features/territories/server/buildings' import { BuildingStatus } from '~/features/territories/ui/BuildingStatus' import { Button } from '~/shared/ui/button' @@ -17,10 +16,10 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canViewProspection = await verifyRole(request, Role.ProspectionViewer) - const canManageProspection = await verifyRole(request, Role.ProspectionManager) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { can } = await authenticateAndAuthorize(request, [Role.ProspectionViewer, Role.ProspectionManager, Role.TerritoriesManager]) + const canViewProspection = can(Role.ProspectionViewer) + const canManageProspection = can(Role.ProspectionManager) + const canManageTerritories = can(Role.TerritoriesManager) if (!canViewProspection) { throw redirect('/') diff --git a/app/features/territories/routes/prospection/need-check-building-list.tsx b/app/features/territories/routes/prospection/need-check-building-list.tsx index 4efb788..067fccb 100644 --- a/app/features/territories/routes/prospection/need-check-building-list.tsx +++ b/app/features/territories/routes/prospection/need-check-building-list.tsx @@ -1,9 +1,8 @@ import { Eye, Search } from 'lucide-react' import { Link, redirect } from 'react-router' import type { Prisma } from '~/database/generated/client' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getSetting } from '~/features/settings/server/settings' import { TerritoryAccess } from '~/features/territories/model/territory-access.type' import { findBuildingsWithEntrancePaginated } from '~/features/territories/server/buildings' @@ -21,10 +20,10 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canViewProspection = await verifyRole(request, Role.ProspectionViewer) - const canManageProspection = await verifyRole(request, Role.ProspectionManager) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { can } = await authenticateAndAuthorize(request, [Role.ProspectionViewer, Role.ProspectionManager, Role.TerritoriesManager]) + const canViewProspection = can(Role.ProspectionViewer) + const canManageProspection = can(Role.ProspectionManager) + const canManageTerritories = can(Role.TerritoriesManager) if (!canViewProspection) { throw redirect('/') diff --git a/app/features/territories/routes/prospection/new-building-list.tsx b/app/features/territories/routes/prospection/new-building-list.tsx index 2aa3c53..1953457 100644 --- a/app/features/territories/routes/prospection/new-building-list.tsx +++ b/app/features/territories/routes/prospection/new-building-list.tsx @@ -1,8 +1,7 @@ import { Eye, Search } from 'lucide-react' import { Link, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { findBuildingsPaginated, getProspectionStaleDate } from '~/features/territories/server/buildings' import { BuildingStatus } from '~/features/territories/ui/BuildingStatus' import { Button } from '~/shared/ui/button' @@ -17,10 +16,10 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canViewProspection = await verifyRole(request, Role.ProspectionViewer) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) - const canManageProspection = await verifyRole(request, Role.ProspectionManager) + const { can } = await authenticateAndAuthorize(request, [Role.ProspectionViewer, Role.TerritoriesManager, Role.ProspectionManager]) + const canViewProspection = can(Role.ProspectionViewer) + const canManageTerritories = can(Role.TerritoriesManager) + const canManageProspection = can(Role.ProspectionManager) if (!canViewProspection) { throw redirect('/') diff --git a/app/features/territories/routes/prospection/new-building.tsx b/app/features/territories/routes/prospection/new-building.tsx index b18748a..ebe6ae7 100644 --- a/app/features/territories/routes/prospection/new-building.tsx +++ b/app/features/territories/routes/prospection/new-building.tsx @@ -1,7 +1,7 @@ import { Form, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { createBuilding } from '~/features/territories/server/create-building.server' import { Button } from '~/shared/ui/button' import { Card, CardContent } from '~/shared/ui/card' @@ -16,8 +16,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') @@ -67,9 +67,9 @@ export default function CreateBuildingPage() { } export async function action({ request }: Route.ActionArgs) { - const { session, congregation } = await verifySession(request) + const { session, congregation, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') } diff --git a/app/features/territories/routes/prospection/sync-buildings.tsx b/app/features/territories/routes/prospection/sync-buildings.tsx index c4a81b9..222469b 100644 --- a/app/features/territories/routes/prospection/sync-buildings.tsx +++ b/app/features/territories/routes/prospection/sync-buildings.tsx @@ -1,10 +1,10 @@ import { redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { syncQueue } from '~/features/territories/server/sync-queue.server' -import { congregationContext, db } from '~/shared/libs/db.server' +import { db } from '~/shared/libs/db.server' import type { Route } from './+types/sync-buildings' @@ -17,8 +17,8 @@ export function loader(_args: Route.LoaderArgs) { } export async function action({ request }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { session, currentUser, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') @@ -34,12 +34,10 @@ export async function action({ request }: Route.ActionArgs) { throw redirect('/') } - const ctx = congregationContext.getStore() - await syncQueue.add('sync', { userName: user.firstname ?? undefined, userEmail: user.email, - congregationId: ctx?.congregationId ?? user.congregationId, + congregationId: currentUser.congregationId, }) session.flash( diff --git a/app/features/territories/routes/split-tool/_layout.tsx b/app/features/territories/routes/split-tool/_layout.tsx index 6d3da7f..9439b9c 100644 --- a/app/features/territories/routes/split-tool/_layout.tsx +++ b/app/features/territories/routes/split-tool/_layout.tsx @@ -1,7 +1,7 @@ import { data, NavLink, Outlet, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getBoolSetting } from '~/features/settings/server/settings' import { TerritoryKind } from '~/features/territories/model/territory-kind.type' import { getZips } from '~/features/territories/server/buildings' @@ -18,9 +18,9 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { session } = await verifySession(request) + const { session, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') } diff --git a/app/features/territories/routes/split-tool/commerces.tsx b/app/features/territories/routes/split-tool/commerces.tsx index 6f3f94f..9baf2b4 100644 --- a/app/features/territories/routes/split-tool/commerces.tsx +++ b/app/features/territories/routes/split-tool/commerces.tsx @@ -1,9 +1,8 @@ import { useEffect, useState } from 'react' import { Form, redirect } from 'react-router' import type { Prisma } from '~/database/generated/client' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getOptionalEnv } from '~/shared/libs/env.server' import { TerritoryKind } from '~/features/territories/model/territory-kind.type' import { computeFilters } from '~/features/territories/server/building-filters' @@ -20,8 +19,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canViewTerritories = await verifyRole(request, Role.TerritoriesViewer) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesViewer]) + const canViewTerritories = can(Role.TerritoriesViewer) if (!canViewTerritories) { throw redirect('/') diff --git a/app/features/territories/routes/split-tool/create.tsx b/app/features/territories/routes/split-tool/create.tsx index 2a2be25..69a12fa 100644 --- a/app/features/territories/routes/split-tool/create.tsx +++ b/app/features/territories/routes/split-tool/create.tsx @@ -1,8 +1,8 @@ import { redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { TerritoryKind } from '~/features/territories/model/territory-kind.type' import { db } from '~/shared/libs/db.server' import { LimitService } from '~/shared/libs/limits.server' @@ -14,8 +14,8 @@ export function loader(_args: Route.LoaderArgs) { } export async function action({ request }: Route.ActionArgs) { - const { session, congregation } = await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { session, congregation, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') diff --git a/app/features/territories/routes/split-tool/hotels.tsx b/app/features/territories/routes/split-tool/hotels.tsx index 1bab6f4..4b30edc 100644 --- a/app/features/territories/routes/split-tool/hotels.tsx +++ b/app/features/territories/routes/split-tool/hotels.tsx @@ -1,9 +1,8 @@ import { useEffect, useState } from 'react' import { Form, redirect } from 'react-router' import type { Prisma } from '~/database/generated/client' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getOptionalEnv } from '~/shared/libs/env.server' import { TerritoryKind } from '~/features/territories/model/territory-kind.type' import { computeFilters } from '~/features/territories/server/building-filters' @@ -20,8 +19,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canViewTerritories = await verifyRole(request, Role.TerritoriesViewer) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesViewer]) + const canViewTerritories = can(Role.TerritoriesViewer) if (!canViewTerritories) { throw redirect('/') diff --git a/app/features/territories/routes/split-tool/list.tsx b/app/features/territories/routes/split-tool/list.tsx index 778b185..371f4ec 100644 --- a/app/features/territories/routes/split-tool/list.tsx +++ b/app/features/territories/routes/split-tool/list.tsx @@ -1,9 +1,8 @@ import { useEffect, useState } from 'react' import { Form, redirect } from 'react-router' import type { Prisma } from '~/database/generated/client' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getBoolSetting } from '~/features/settings/server/settings' import { getOptionalEnv } from '~/shared/libs/env.server' import { TerritoryKind } from '~/features/territories/model/territory-kind.type' @@ -22,8 +21,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canViewTerritories = await verifyRole(request, Role.TerritoriesViewer) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesViewer]) + const canViewTerritories = can(Role.TerritoriesViewer) if (!canViewTerritories) { throw redirect('/') diff --git a/app/features/territories/routes/split-tool/phones.tsx b/app/features/territories/routes/split-tool/phones.tsx index 31fa229..e5e1ebe 100644 --- a/app/features/territories/routes/split-tool/phones.tsx +++ b/app/features/territories/routes/split-tool/phones.tsx @@ -1,9 +1,8 @@ import { useEffect, useState } from 'react' import { Form, redirect } from 'react-router' import type { Prisma } from '~/database/generated/client' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getBoolSetting } from '~/features/settings/server/settings' import { getOptionalEnv } from '~/shared/libs/env.server' import { TerritoryKind } from '~/features/territories/model/territory-kind.type' @@ -22,8 +21,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canViewTerritories = await verifyRole(request, Role.TerritoriesViewer) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesViewer]) + const canViewTerritories = can(Role.TerritoriesViewer) if (!canViewTerritories) { throw redirect('/') diff --git a/app/features/territories/routes/split-tool/university.tsx b/app/features/territories/routes/split-tool/university.tsx index 287e92e..4285835 100644 --- a/app/features/territories/routes/split-tool/university.tsx +++ b/app/features/territories/routes/split-tool/university.tsx @@ -1,9 +1,8 @@ import { useEffect, useState } from 'react' import { Form, redirect } from 'react-router' import type { Prisma } from '~/database/generated/client' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getOptionalEnv } from '~/shared/libs/env.server' import { TerritoryKind } from '~/features/territories/model/territory-kind.type' import { computeFilters } from '~/features/territories/server/building-filters' @@ -20,8 +19,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canViewTerritories = await verifyRole(request, Role.TerritoriesViewer) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesViewer]) + const canViewTerritories = can(Role.TerritoriesViewer) if (!canViewTerritories) { throw redirect('/') diff --git a/app/features/territories/routes/stats/index.tsx b/app/features/territories/routes/stats/index.tsx index 2b32b9b..754c5e6 100644 --- a/app/features/territories/routes/stats/index.tsx +++ b/app/features/territories/routes/stats/index.tsx @@ -1,9 +1,8 @@ import { Info } from 'lucide-react' import { redirect } from 'react-router' import { Cell, Pie, PieChart } from 'recharts' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getGroups } from '~/features/publishers/server/groups' import { TerritoryAttributionKind } from '~/features/territories/model/territory-attribution-kind.type' import { countActiveWorkingTerritories } from '~/features/territories/server/active-working-territories.server' @@ -47,8 +46,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') diff --git a/app/features/territories/routes/territory/delete.tsx b/app/features/territories/routes/territory/delete.tsx index 46da83f..be3bc23 100644 --- a/app/features/territories/routes/territory/delete.tsx +++ b/app/features/territories/routes/territory/delete.tsx @@ -1,7 +1,7 @@ import { Form, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { db } from '~/shared/libs/db.server' import { requireParamId } from '~/shared/libs/params.server' import { Button } from '~/shared/ui/button' @@ -10,8 +10,8 @@ import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '~/shared/u import type { Route } from './+types/delete' export async function loader({ request, params }: Route.LoaderArgs) { - await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') @@ -53,8 +53,8 @@ export default function DeleteTerritory({ loaderData }: Route.ComponentProps) { } export async function action({ request, params }: Route.ActionArgs) { - const { session } = await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { session, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') diff --git a/app/features/territories/routes/territory/edit.tsx b/app/features/territories/routes/territory/edit.tsx index b20008a..8a0d2d5 100644 --- a/app/features/territories/routes/territory/edit.tsx +++ b/app/features/territories/routes/territory/edit.tsx @@ -1,9 +1,8 @@ import { Download, ExternalLink, Trash2, X } from 'lucide-react' import { useState } from 'react' import { Form, Link, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getBoolSetting } from '~/features/settings/server/settings' import { getOptionalEnv } from '~/shared/libs/env.server' import type { TerritoryAttributionKind } from '~/features/territories/model/territory-attribution-kind.type' @@ -32,8 +31,8 @@ export const meta: Route.MetaFunction = ({ data }) => { } export async function loader({ request, params }: Route.LoaderArgs) { - await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') @@ -314,8 +313,8 @@ export default function EditTerritoryPage({ loaderData }: Route.ComponentProps) } export async function action({ request, params }: Route.ActionArgs) { - await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') diff --git a/app/features/territories/routes/territory/list.tsx b/app/features/territories/routes/territory/list.tsx index 095ed45..f0e3eb2 100644 --- a/app/features/territories/routes/territory/list.tsx +++ b/app/features/territories/routes/territory/list.tsx @@ -1,8 +1,8 @@ import { Map as MapIcon, Pencil, Trash2 } from 'lucide-react' import { data, Link, redirect } from 'react-router' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getBoolSetting } from '~/features/settings/server/settings' import { getOptionalEnv } from '~/shared/libs/env.server' import type { TerritoryAttributionKind } from '~/features/territories/model/territory-attribution-kind.type' @@ -28,14 +28,14 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - const { session } = await verifySession(request) - const canViewTerritories = await verifyRole(request, Role.TerritoriesViewer) + const { session, can } = await authenticateAndAuthorize(request, [Role.TerritoriesViewer, Role.TerritoriesManager]) + const canViewTerritories = can(Role.TerritoriesViewer) if (!canViewTerritories) { throw redirect('/') } - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const canManageTerritories = can(Role.TerritoriesManager) const phoneTypeActive = await getBoolSetting(TerritorySettingKey.TerritoryTypePhoneActive) const apiKey = getOptionalEnv('GOOGLE_MAPS_API_KEY') diff --git a/app/features/territories/routes/territory/new.tsx b/app/features/territories/routes/territory/new.tsx index d5180c5..0077679 100644 --- a/app/features/territories/routes/territory/new.tsx +++ b/app/features/territories/routes/territory/new.tsx @@ -1,9 +1,8 @@ import { ExternalLink, Trash2 } from 'lucide-react' import { useState } from 'react' import { Form, Link, redirect } from 'react-router' -import { verifySession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { getBoolSetting } from '~/features/settings/server/settings' import { getOptionalEnv } from '~/shared/libs/env.server' import { TerritoryKind } from '~/features/territories/model/territory-kind.type' @@ -26,8 +25,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { - await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') @@ -156,8 +155,8 @@ export default function NewTerritoryPage({ loaderData }: Route.ComponentProps) { } export async function action({ request }: Route.ActionArgs) { - const { congregation } = await verifySession(request) - const canManageTerritories = await verifyRole(request, Role.TerritoriesManager) + const { congregation, can } = await authenticateAndAuthorize(request, [Role.TerritoriesManager]) + const canManageTerritories = can(Role.TerritoriesManager) if (!canManageTerritories) { throw redirect('/') diff --git a/app/routes/_authenticated-layout.tsx b/app/routes/_authenticated-layout.tsx index 341a0c1..429133f 100644 --- a/app/routes/_authenticated-layout.tsx +++ b/app/routes/_authenticated-layout.tsx @@ -1,38 +1,25 @@ import { useEffect } from 'react' import { data } from 'react-router' import { toast } from 'sonner' -import { commitSession, verifySession } from '~/features/authentication/server/session.server' +import { commitSession } from '~/features/authentication/server/session.server' import { Role } from '~/features/authorization/model/roles.type' -import { verifyRole } from '~/features/authorization/server/verify-role.server' +import { authenticateAndAuthorize } from '~/shared/libs/auth.server' import { AppLayout } from '~/shared/ui/AppLayout' import type { Route } from './+types/_authenticated-layout' export async function loader({ request }: Route.LoaderArgs) { - const { session, congregation, currentUser } = await verifySession(request) - - const [ - canUploadDocument, - canManageBoard, - canViewPublishers, - canViewTerritories, - canViewProspection, - canManageTerritories, - canManageSettings, - canManageUsers, - canViewPrograms, - canViewActivity, - ] = await Promise.all([ - verifyRole(request, Role.BoardUploader), - verifyRole(request, Role.BoardValidator), - verifyRole(request, Role.PublisherViewer), - verifyRole(request, Role.TerritoriesViewer), - verifyRole(request, Role.ProspectionViewer), - verifyRole(request, Role.TerritoriesManager), - verifyRole(request, Role.Admin), - verifyRole(request, Role.SettingsUserManager), - verifyRole(request, Role.ProgramViewer), - verifyRole(request, Role.ActivityViewer), + const { session, congregation, currentUser, can } = await authenticateAndAuthorize(request, [ + Role.BoardUploader, + Role.BoardValidator, + Role.PublisherViewer, + Role.TerritoriesViewer, + Role.ProspectionViewer, + Role.TerritoriesManager, + Role.Admin, + Role.SettingsUserManager, + Role.ProgramViewer, + Role.ActivityViewer, ]) const messages = { success: session.get('success'), error: session.get('error') } @@ -41,16 +28,16 @@ export async function loader({ request }: Route.LoaderArgs) { { permissions: { canViewBoard: true, - canUploadDocument, - canManageBoard, - canViewPublishers, - canViewTerritories, - canViewProspection, - canManageTerritories, - canManageSettings, - canManageUsers, - canViewPrograms, - canViewActivity, + canUploadDocument: can(Role.BoardUploader), + canManageBoard: can(Role.BoardValidator), + canViewPublishers: can(Role.PublisherViewer), + canViewTerritories: can(Role.TerritoriesViewer), + canViewProspection: can(Role.ProspectionViewer), + canManageTerritories: can(Role.TerritoriesManager), + canManageSettings: can(Role.Admin), + canManageUsers: can(Role.SettingsUserManager), + canViewPrograms: can(Role.ProgramViewer), + canViewActivity: can(Role.ActivityViewer), isPlatformAdmin: currentUser.platformAdmin ?? false, }, congregationName: congregation.displayName ?? congregation.name, diff --git a/app/shared/libs/auth.server.test.ts b/app/shared/libs/auth.server.test.ts new file mode 100644 index 0000000..e7440b5 --- /dev/null +++ b/app/shared/libs/auth.server.test.ts @@ -0,0 +1,79 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import { Role } from '~/features/authorization/model/roles.type' + +vi.mock('~/features/authentication/server/session.server', () => ({ + verifySession: vi.fn(), +})) + +vi.mock('~/features/authorization/server/verify-role.server', () => ({ + verifyRole: vi.fn(), +})) + +vi.mock('~/shared/libs/db.server', () => ({ + restoreCongregationContext: vi.fn(), +})) + +const { authenticateAndAuthorize } = await import('./auth.server') +const { verifySession } = await import('~/features/authentication/server/session.server') +const { verifyRole } = await import('~/features/authorization/server/verify-role.server') +const { restoreCongregationContext } = await import('~/shared/libs/db.server') + +function makeRequest() { + return new Request('http://localhost/') +} + +const fakeSessionResult = { + currentUser: { id: 1, congregationId: 5, firstname: 'Test', lastname: 'User' }, + congregation: { id: 5, name: 'Test', slug: 'test' }, + session: {}, +} + +beforeEach(() => { + vi.resetAllMocks() + vi.mocked(verifySession).mockResolvedValue(fakeSessionResult as never) +}) + +describe('authenticateAndAuthorize', () => { + it('retourne les données de verifySession', async () => { + const result = await authenticateAndAuthorize(makeRequest()) + + expect(result.currentUser).toBe(fakeSessionResult.currentUser) + expect(result.congregation).toBe(fakeSessionResult.congregation) + expect(result.session).toBe(fakeSessionResult.session) + }) + + it('résout les permissions via verifyRole', async () => { + vi.mocked(verifyRole).mockImplementation((_req, role) => { + return Promise.resolve(role === Role.Admin) + }) + + const result = await authenticateAndAuthorize(makeRequest(), [Role.Admin, Role.BoardUploader]) + + expect(result.can(Role.Admin)).toBe(true) + expect(result.can(Role.BoardUploader)).toBe(false) + }) + + it('retourne false pour un rôle non demandé', async () => { + vi.mocked(verifyRole).mockResolvedValue(true as never) + + const result = await authenticateAndAuthorize(makeRequest(), [Role.Admin]) + + expect(result.can(Role.TerritoriesViewer)).toBe(false) + }) + + it('fonctionne sans rôles', async () => { + const result = await authenticateAndAuthorize(makeRequest()) + + expect(verifyRole).not.toHaveBeenCalled() + expect(result.can(Role.Admin)).toBe(false) + }) + + it('appelle restoreCongregationContext après la résolution des rôles', async () => { + vi.mocked(verifyRole).mockResolvedValue(true as never) + + await authenticateAndAuthorize(makeRequest(), [Role.Admin]) + + expect(restoreCongregationContext).toHaveBeenCalledWith(5) + }) +}) diff --git a/app/shared/libs/auth.server.ts b/app/shared/libs/auth.server.ts new file mode 100644 index 0000000..8240064 --- /dev/null +++ b/app/shared/libs/auth.server.ts @@ -0,0 +1,29 @@ +import { verifySession } from '~/features/authentication/server/session.server' +import type { Role } from '~/features/authorization/model/roles.type' +import { verifyRole } from '~/features/authorization/server/verify-role.server' + +import { restoreCongregationContext } from './db.server' + +/** + * Authenticates the user, checks roles, and restores the congregation context. + * + * Combines verifySession + verifyRole + restoreCongregationContext into a single call. + * Use this in all authenticated route loaders and actions. + */ +export async function authenticateAndAuthorize(request: Request, roles: Role[] = []) { + const { currentUser, congregation, session } = await verifySession(request) + + const resolved = await Promise.all(roles.map(async (role) => [role, await verifyRole(request, role)] as const)) + const permissions = new Set(resolved.filter(([, granted]) => granted).map(([role]) => role)) + + // Restore ALS context after all auth/role Prisma queries. + // The Prisma 7 pg adapter breaks AsyncLocalStorage propagation after async operations. + restoreCongregationContext(currentUser.congregationId) + + return { + currentUser, + congregation, + session, + can: (role: Role) => permissions.has(role), + } +} diff --git a/app/shared/libs/congregation.server.test.ts b/app/shared/libs/congregation.server.test.ts index 8ef293b..815b9ff 100644 --- a/app/shared/libs/congregation.server.test.ts +++ b/app/shared/libs/congregation.server.test.ts @@ -11,7 +11,7 @@ vi.mock('~/shared/libs/db.server', () => ({ vi.mock('react-router', () => ({ redirect: vi.fn((url: string) => { - // biome-ignore lint/style/useNamingConvention: en-tête HTTP standard + // biome-ignore lint/style/useNamingConvention: standard HTTP header throw new Response(null, { status: 302, headers: { Location: url } }) }), })) diff --git a/app/shared/libs/congregation.server.ts b/app/shared/libs/congregation.server.ts index 6ad7163..d70f8e9 100644 --- a/app/shared/libs/congregation.server.ts +++ b/app/shared/libs/congregation.server.ts @@ -73,10 +73,10 @@ export function getPlatformName(): string { } /** - * Résout l'assemblée locale correspondant au sous-domaine ou domaine personnalisé de la requête. + * Resolves the congregation matching the subdomain or custom domain from the request. * - * - Retourne `null` en mode mono-tenant ou si aucun slug n'est extrait de l'URL (domaine racine). - * - Redirige vers `/congregation-not-found` si un slug est présent mais ne correspond à aucune assemblée. + * - Returns `null` in single-tenant mode or if no slug is extracted from the URL (root domain). + * - Redirects to `/congregation-not-found` if a slug is present but doesn't match any congregation. */ export async function resolveCongregationFromRequest( request: Request, @@ -94,18 +94,18 @@ export async function resolveCongregationFromRequest( }) if (congregation) return congregation - // Le slug est présent dans l'URL mais ne correspond à aucune assemblée + // Slug is present in the URL but doesn't match any congregation throw redirect('/congregation-not-found') } - // Pas de slug — tenter la résolution par domaine personnalisé + // No slug — try resolving by custom domain const congregation = await unscopedDb.congregation.findFirst({ where: { domain: hostname }, select: { id: true, slug: true }, }) if (congregation) return congregation - // Domaine racine ou domaine inconnu sans slug — pas d'assemblée à résoudre + // Root domain or unknown domain without slug — no congregation to resolve return null } diff --git a/app/shared/libs/db.server.ts b/app/shared/libs/db.server.ts index ea10bb7..330e418 100644 --- a/app/shared/libs/db.server.ts +++ b/app/shared/libs/db.server.ts @@ -14,6 +14,23 @@ type CongregationContext = { } export const congregationContext = new AsyncLocalStorage() +// Fallback congregation ID when ALS context is lost. Used by the db extension +// as a safety net when concurrent loaders (run in parallel by React Router) +// break each other's ALS context via the Prisma 7 pg adapter. +let fallbackCongregationId: number | null = null + +/** + * Restores the congregation context in AsyncLocalStorage. + * + * The Prisma 7 pg adapter breaks AsyncLocalStorage propagation after async queries. + * Also stores the congregation ID in a module-level fallback for cases where + * concurrent loaders break each other's ALS context. + */ +export function restoreCongregationContext(congregationId: number) { + fallbackCongregationId = congregationId + congregationContext.enterWith({ congregationId }) +} + const SCOPED_MODELS = new Set([ 'User', 'Territory', @@ -49,7 +66,16 @@ const db = unscopedDb.$extends({ async $allOperations({ model, operation, args, query }) { if (!model || !SCOPED_MODELS.has(model)) return query(args) - const ctx = congregationContext.getStore() + let ctx = congregationContext.getStore() + + // If ALS context is lost (Prisma 7 pg adapter issue), recover from the fallback. + // This handles concurrent loaders run in parallel by React Router where one loader's + // Prisma queries break another loader's ALS context. + if (!ctx && fallbackCongregationId) { + ctx = { congregationId: fallbackCongregationId } + congregationContext.enterWith(ctx) + } + if (!ctx) { throw new Error( `Congregation context is required for ${model}.${operation} but was not set. ` + @@ -80,8 +106,8 @@ const db = unscopedDb.$extends({ const result = await query(args) - // Restaurer le contexte ALS après la requête — l'adaptateur pg de Prisma 7 - // peut casser la propagation d'AsyncLocalStorage lors des opérations async. + // Restore ALS context after query — the Prisma 7 pg adapter can break + // AsyncLocalStorage propagation during async operations. congregationContext.enterWith(ctx) return result