diff --git a/src/app/(frontend)/account/actions.ts b/src/app/(frontend)/account/actions.ts index 0849011..5f9d519 100644 --- a/src/app/(frontend)/account/actions.ts +++ b/src/app/(frontend)/account/actions.ts @@ -1,6 +1,7 @@ "use server"; import config from "@payload-config"; +import { isPayloadUser } from "@/utils/access-control/isPayloadUser"; import { getPayload } from "payload"; import { emitGameEvent } from "@/utils/event-log/emit"; import { EventTypes } from "@/utils/event-log/eventTypes"; @@ -23,7 +24,7 @@ async function authenticate() { const hdrs = await headers(); const { user } = await payload.auth({ headers: hdrs, canSetHeaders: false }); - if (!user) { + if (!isPayloadUser(user)) { throw new Error("Unauthorized"); } diff --git a/src/app/(frontend)/helpdesk/actions.ts b/src/app/(frontend)/helpdesk/actions.ts index 1487767..71377ba 100644 --- a/src/app/(frontend)/helpdesk/actions.ts +++ b/src/app/(frontend)/helpdesk/actions.ts @@ -1,6 +1,7 @@ "use server"; import config from "@payload-config"; +import { isPayloadUser } from "@/utils/access-control/isPayloadUser"; import { getPayload } from "payload"; import type { Ticket } from "@/payload-types"; import { hasPermission } from "@/utils/access-control/hasPermission"; @@ -25,7 +26,7 @@ async function authenticate() { const hdrs = await headers(); const { user } = await payload.auth({ headers: hdrs, canSetHeaders: false }); - if (!user) { + if (!isPayloadUser(user)) { throw new Error("Unauthorized"); } diff --git a/src/app/(frontend)/layout.tsx b/src/app/(frontend)/layout.tsx index 8bce916..ef40b39 100644 --- a/src/app/(frontend)/layout.tsx +++ b/src/app/(frontend)/layout.tsx @@ -4,6 +4,7 @@ import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"; import { AppSidebar } from "@/components/frontend/blocks/AppSidebar"; import { getPayload } from "payload"; import config from "@payload-config"; +import { isPayloadUser } from "@/utils/access-control/isPayloadUser"; import { headers as nextHeaders } from "next/headers"; import { SiteHeader } from "@/components/frontend/SiteHeader"; import { Metadata } from "next"; @@ -50,7 +51,8 @@ export default async function RootLayout(props: { children: React.ReactNode }) { const payloadConfig = await config; const payload = await getPayload({ config }); - const { user } = await payload.auth({ headers, canSetHeaders: false }); + const { user: authUser } = await payload.auth({ headers, canSetHeaders: false }); + const user = isPayloadUser(authUser) ? authUser : null; const impersonating = (await cookies()).has(IMPERSONATION_ACTIVE_COOKIE); let xpLevel: ResolvedLevel = { diff --git a/src/app/(frontend)/locker/actions.ts b/src/app/(frontend)/locker/actions.ts index 0c20d18..cd4314e 100644 --- a/src/app/(frontend)/locker/actions.ts +++ b/src/app/(frontend)/locker/actions.ts @@ -1,6 +1,7 @@ "use server"; import config from "@payload-config"; +import { isPayloadUser } from "@/utils/access-control/isPayloadUser"; import { getPayload } from "payload"; import type { Asset, Loadout, LockerStorage, User } from "@/payload-types"; import { hasPermission } from "@/utils/access-control/hasPermission"; @@ -39,7 +40,7 @@ async function authenticate() { const hdrs = await headers(); const { user } = await payload.auth({ headers: hdrs, canSetHeaders: false }); - if (!user) { + if (!isPayloadUser(user)) { throw new Error("Unauthorized"); } diff --git a/src/app/(frontend)/locker/page.tsx b/src/app/(frontend)/locker/page.tsx index 9e390e8..b7be829 100644 --- a/src/app/(frontend)/locker/page.tsx +++ b/src/app/(frontend)/locker/page.tsx @@ -1,4 +1,5 @@ import config from "@payload-config"; +import { isPayloadUser } from "@/utils/access-control/isPayloadUser"; import { getPayload } from "payload"; import { headers as nextHeaders } from "next/headers"; import type { Asset, Loadout, LockerStorage } from "@/payload-types"; @@ -13,10 +14,11 @@ export const metadata = { export default async function LockerPage() { const payload = await getPayload({ config }); - const { user } = await payload.auth({ + const { user: authUser } = await payload.auth({ headers: await nextHeaders(), canSetHeaders: false, }); + const user = isPayloadUser(authUser) ? authUser : null; const userId = user?.id as number; const locker = await ensureLockerStorage(payload, userId); diff --git a/src/app/(frontend)/logistics/banking/actions.ts b/src/app/(frontend)/logistics/banking/actions.ts index aaf249a..5597ed4 100644 --- a/src/app/(frontend)/logistics/banking/actions.ts +++ b/src/app/(frontend)/logistics/banking/actions.ts @@ -1,6 +1,7 @@ "use server"; import config from "@payload-config"; +import { isPayloadUser } from "@/utils/access-control/isPayloadUser"; import { getPayload } from "payload"; import type { User } from "@/payload-types"; import { hasPermission } from "@/utils/access-control/hasPermission"; @@ -31,7 +32,7 @@ async function authenticate() { canSetHeaders: false, }); - if (!user) { + if (!isPayloadUser(user)) { throw new Error("Unauthorized"); } diff --git a/src/app/(frontend)/logistics/banking/page.tsx b/src/app/(frontend)/logistics/banking/page.tsx index cdebf8b..395110f 100644 --- a/src/app/(frontend)/logistics/banking/page.tsx +++ b/src/app/(frontend)/logistics/banking/page.tsx @@ -1,4 +1,5 @@ import config from "@payload-config"; +import { isPayloadUser } from "@/utils/access-control/isPayloadUser"; import { getPayload } from "payload"; import { headers as nextHeaders } from "next/headers"; import type { BankAccount, BankTransaction, Faction, Resource } from "@/payload-types"; @@ -13,10 +14,11 @@ export const metadata = { export default async function BankingPage() { const payload = await getPayload({ config }); - const { user } = await payload.auth({ + const { user: authUser } = await payload.auth({ headers: await nextHeaders(), canSetHeaders: false, }); + const user = isPayloadUser(authUser) ? authUser : null; const accountsRes = await payload.find({ collection: "bank-accounts", diff --git a/src/app/(frontend)/logistics/market/actions.ts b/src/app/(frontend)/logistics/market/actions.ts index e1cc99c..db7792a 100644 --- a/src/app/(frontend)/logistics/market/actions.ts +++ b/src/app/(frontend)/logistics/market/actions.ts @@ -1,6 +1,7 @@ "use server"; import config from "@payload-config"; +import { isPayloadUser } from "@/utils/access-control/isPayloadUser"; import { getPayload } from "payload"; import type { Asset, LockerStorage, MarketListing, MarketNegotiation, User } from "@/payload-types"; import { hasPermission } from "@/utils/access-control/hasPermission"; @@ -37,7 +38,7 @@ async function authenticate() { const hdrs = await headers(); const { user } = await payload.auth({ headers: hdrs, canSetHeaders: false }); - if (!user) { + if (!isPayloadUser(user)) { throw new Error("Unauthorized"); } diff --git a/src/app/(frontend)/roster/page.tsx b/src/app/(frontend)/roster/page.tsx index dfc1d50..65a62ec 100644 --- a/src/app/(frontend)/roster/page.tsx +++ b/src/app/(frontend)/roster/page.tsx @@ -1,4 +1,5 @@ import config from "@payload-config"; +import { isPayloadUser } from "@/utils/access-control/isPayloadUser"; import { getPayload } from "payload"; import { headers as nextHeaders } from "next/headers"; import type { Media, Rank, User } from "@/payload-types"; @@ -34,7 +35,8 @@ export type RosterAssignment = { export default async function RosterPage() { const headers = await nextHeaders(); const payload = await getPayload({ config }); - const { user: currentUser } = await payload.auth({ headers, canSetHeaders: false }); + const { user: authUser } = await payload.auth({ headers, canSetHeaders: false }); + const currentUser = isPayloadUser(authUser) ? authUser : null; const [profileRes, assignmentRes] = await Promise.all([ payload.find({ diff --git a/src/app/api/realtime/presence/route.ts b/src/app/api/realtime/presence/route.ts index e747f3d..9681a88 100644 --- a/src/app/api/realtime/presence/route.ts +++ b/src/app/api/realtime/presence/route.ts @@ -1,5 +1,6 @@ import { NextRequest, NextResponse } from "next/server"; import config from "@payload-config"; +import { isPayloadUser } from "@/utils/access-control/isPayloadUser"; import { getPayload } from "payload"; import { removePresence, touchPresence } from "@/lib/realtime/presence"; @@ -20,7 +21,7 @@ export async function POST(req: NextRequest) { const channel = channelFrom(req); if (!channel) return NextResponse.json({ ok: false, error: "Invalid channel." }, { status: 400 }); const { user } = await authenticate(req); - if (!user) return NextResponse.json({ ok: false }, { status: 401 }); + if (!isPayloadUser(user)) return NextResponse.json({ ok: false }, { status: 401 }); touchPresence(channel, { id: user.id as number, name: user.payloadDisplayName || user.username || `Pilot #${user.id}`, @@ -32,7 +33,7 @@ export async function DELETE(req: NextRequest) { const channel = channelFrom(req); if (!channel) return NextResponse.json({ ok: false, error: "Invalid channel." }, { status: 400 }); const { user } = await authenticate(req); - if (!user) return NextResponse.json({ ok: false }, { status: 401 }); + if (!isPayloadUser(user)) return NextResponse.json({ ok: false }, { status: 401 }); removePresence(channel, user.id as number); return NextResponse.json({ ok: true }); } diff --git a/src/collections/intelligence/Technologies.ts b/src/collections/intelligence/Technologies.ts index 9bc88cd..180e65e 100644 --- a/src/collections/intelligence/Technologies.ts +++ b/src/collections/intelligence/Technologies.ts @@ -1,5 +1,6 @@ import { CollectionConfig } from "payload"; import { requirePermission } from "@/utils/access-control/hasPermission"; +import { isPayloadUser } from "@/utils/access-control/isPayloadUser"; export const Technologies: CollectionConfig = { slug: "technologies", @@ -32,7 +33,9 @@ export const Technologies: CollectionConfig = { ], defaultValue: "in_progress", filterOptions: ({ options, req }) => { - const roles = (req.user?.roles as string[] | undefined) ?? []; + const roles = isPayloadUser(req.user) + ? ((req.user.roles as string[] | undefined) ?? []) + : []; const canReadAll = roles.includes("admin") || roles.includes("developer"); if (canReadAll) return options; return options.filter((option) => diff --git a/src/utils/access-control/hasRoles.ts b/src/utils/access-control/hasRoles.ts index a3edce4..2908a69 100644 --- a/src/utils/access-control/hasRoles.ts +++ b/src/utils/access-control/hasRoles.ts @@ -1,7 +1,10 @@ -import { User } from "@/payload-types"; +import { PayloadMcpApiKey, User } from "@/payload-types"; -export default function hasRoles(roles: NonNullable, user: User | null | undefined) { - if (!user) return false; +export default function hasRoles( + roles: NonNullable, + user: User | PayloadMcpApiKey | null | undefined, +) { + if (!user || user.collection !== "users") return false; return roles.some((role) => user.roles?.some((urole) => urole === role || urole === "developer")); } diff --git a/src/utils/access-control/isPayloadUser.ts b/src/utils/access-control/isPayloadUser.ts new file mode 100644 index 0000000..188f960 --- /dev/null +++ b/src/utils/access-control/isPayloadUser.ts @@ -0,0 +1,5 @@ +import type { PayloadMcpApiKey, User } from "@/payload-types"; + +export function isPayloadUser(user: User | PayloadMcpApiKey | null | undefined): user is User { + return !!user && user.collection === "users"; +}