From df16f01fb2bb8ce69d309d825361806ddfa3f98c Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Thu, 29 Jan 2026 03:13:53 +0000
Subject: [PATCH] feat(access-control): Restrict machine creation to admins
Restricts the ability to create new pinball machines to users with the 'admin' role.
This is implemented by:
- Adding a server-side check in the `createMachineAction` to ensure the user is an admin.
- Conditionally rendering the "Add Machine" button on the machines list page based on the user's role.
- Adding a server-side redirect on the new machine page to prevent non-admins from accessing it directly.
Co-authored-by: timothyfroehlich <5819722+timothyfroehlich@users.noreply.github.com>
---
src/app/(app)/m/actions.ts | 13 ++++++-
src/app/(app)/m/new/page.tsx | 9 +++--
src/app/(app)/m/page.tsx | 50 +++++++++++++++++----------
src/test/unit/machine-actions.test.ts | 2 +-
4 files changed, 51 insertions(+), 23 deletions(-)
diff --git a/src/app/(app)/m/actions.ts b/src/app/(app)/m/actions.ts
index 188600ce..c29df5a7 100644
--- a/src/app/(app)/m/actions.ts
+++ b/src/app/(app)/m/actions.ts
@@ -93,6 +93,15 @@ export async function createMachineAction(
return err("UNAUTHORIZED", "User profile not found.");
}
+ // Access control: only admins can create machines
+ if (profile.role !== "admin") {
+ log.warn(
+ { userId: user.id, action: "createMachineAction" },
+ "Non-admin user attempted to create a machine"
+ );
+ return err("UNAUTHORIZED", "You must be an admin to create a machine.");
+ }
+
// Extract form data
const rawData = {
name: formData.get("name"),
@@ -117,7 +126,9 @@ export async function createMachineAction(
let finalOwnerId: string | undefined = undefined;
let finalInvitedOwnerId: string | undefined = undefined;
- if (profile.role === "admin" && ownerId) {
+ // At this point, user is guaranteed to be an admin.
+ // If an owner is specified, we resolve them. Otherwise, the admin is the owner.
+ if (ownerId) {
const isActive = await db.query.userProfiles.findFirst({
where: eq(userProfiles.id, ownerId),
});
diff --git a/src/app/(app)/m/new/page.tsx b/src/app/(app)/m/new/page.tsx
index f55c78a8..543f7537 100644
--- a/src/app/(app)/m/new/page.tsx
+++ b/src/app/(app)/m/new/page.tsx
@@ -38,11 +38,14 @@ export default async function NewMachinePage(): Promise
- No machines yet + No machines yet. + {isAdmin ? " Add the first one!" : ""}
- - - + {isAdmin && ( + + + + )}