1
0
Fork 0

fix(security): restrict deposit/withdraw to admin/dev, allow transfer/reorganize for users

- addResource, removeResource, placeResourceOnGrid, removeGridItem
  now require admin or developer role (game master operations)
- transferResource, moveGridItem, mergeGridStacks, rotateGridItem,
  splitGridStack, autoArrangeGrid require user, admin, or developer
  (logistics operations that don't change total inventory)
- Guests are excluded from all mutating operations
This commit is contained in:
Jason Fraley 2026-07-28 14:17:52 -04:00
parent 01ac20f44f
commit 5d146f22bf

View file

@ -3,6 +3,7 @@
import config from "@payload-config"; import config from "@payload-config";
import { getPayload } from "payload"; import { getPayload } from "payload";
import { GameStructure, Resource, Structure } from "@/payload-types"; import { GameStructure, Resource, Structure } from "@/payload-types";
import hasRoles from "@/utils/access-control/hasRoles";
interface ActionResult { interface ActionResult {
success: boolean; success: boolean;
@ -64,7 +65,10 @@ export async function addResource(
amount: number, amount: number,
): Promise<ActionResult> { ): Promise<ActionResult> {
try { try {
const { payload } = await authenticate(); const { payload, user } = await authenticate();
if (!hasRoles(["admin"], user)) {
return { success: false, error: "Insufficient permissions. Admin or Developer role required for deposit." };
}
if (amount <= 0) { if (amount <= 0) {
return { success: false, error: "Amount must be greater than zero." }; return { success: false, error: "Amount must be greater than zero." };
@ -233,7 +237,10 @@ export async function removeResource(
amount: number, amount: number,
): Promise<ActionResult> { ): Promise<ActionResult> {
try { try {
const { payload } = await authenticate(); const { payload, user } = await authenticate();
if (!hasRoles(["admin"], user)) {
return { success: false, error: "Insufficient permissions. Admin or Developer role required for withdrawal." };
}
if (amount <= 0) { if (amount <= 0) {
return { success: false, error: "Amount must be greater than zero." }; return { success: false, error: "Amount must be greater than zero." };
@ -303,7 +310,10 @@ export async function transferResource(
amount: number, amount: number,
): Promise<ActionResult> { ): Promise<ActionResult> {
try { try {
const { payload } = await authenticate(); const { payload, user } = await authenticate();
if (!hasRoles(["admin"], user) && !hasRoles(["user"], user)) {
return { success: false, error: "Insufficient permissions. User, Admin, or Developer role required for transfer." };
}
if (amount <= 0) { if (amount <= 0) {
return { success: false, error: "Amount must be greater than zero." }; return { success: false, error: "Amount must be greater than zero." };
@ -387,7 +397,10 @@ export async function placeResourceOnGrid(
rotated: boolean, rotated: boolean,
): Promise<GridActionResult> { ): Promise<GridActionResult> {
try { try {
const { payload } = await authenticate(); const { payload, user } = await authenticate();
if (!hasRoles(["admin"], user)) {
return { success: false, error: "Insufficient permissions. Admin or Developer role required for placement." };
}
if (amount <= 0) return { success: false, error: "Amount must be greater than zero." }; if (amount <= 0) return { success: false, error: "Amount must be greater than zero." };
@ -479,7 +492,10 @@ export async function moveGridItem(
rotated?: boolean, rotated?: boolean,
): Promise<GridActionResult> { ): Promise<GridActionResult> {
try { try {
const { payload } = await authenticate(); const { payload, user } = await authenticate();
if (!hasRoles(["admin"], user) && !hasRoles(["user"], user)) {
return { success: false, error: "Insufficient permissions. User, Admin, or Developer role required." };
}
const structure = await getStructure(payload, structureId); const structure = await getStructure(payload, structureId);
const existing = [...(structure.storedResources ?? [])]; const existing = [...(structure.storedResources ?? [])];
const idx = existing.findIndex((e) => e.id === entryId); const idx = existing.findIndex((e) => e.id === entryId);
@ -541,7 +557,10 @@ export async function mergeGridStacks(
targetId: string, targetId: string,
): Promise<GridActionResult> { ): Promise<GridActionResult> {
try { try {
const { payload } = await authenticate(); const { payload, user } = await authenticate();
if (!hasRoles(["admin"], user) && !hasRoles(["user"], user)) {
return { success: false, error: "Insufficient permissions. User, Admin, or Developer role required." };
}
const structure = await getStructure(payload, structureId); const structure = await getStructure(payload, structureId);
const existing = [...(structure.storedResources ?? [])]; const existing = [...(structure.storedResources ?? [])];
@ -593,7 +612,10 @@ export async function rotateGridItem(
entryId: string, entryId: string,
): Promise<GridActionResult> { ): Promise<GridActionResult> {
try { try {
const { payload } = await authenticate(); const { payload, user } = await authenticate();
if (!hasRoles(["admin"], user) && !hasRoles(["user"], user)) {
return { success: false, error: "Insufficient permissions. User, Admin, or Developer role required." };
}
const structure = await getStructure(payload, structureId); const structure = await getStructure(payload, structureId);
const existing = [...(structure.storedResources ?? [])]; const existing = [...(structure.storedResources ?? [])];
const idx = existing.findIndex((e) => e.id === entryId); const idx = existing.findIndex((e) => e.id === entryId);
@ -653,7 +675,10 @@ export async function removeGridItem(
entryId: string, entryId: string,
): Promise<GridActionResult> { ): Promise<GridActionResult> {
try { try {
const { payload } = await authenticate(); const { payload, user } = await authenticate();
if (!hasRoles(["admin"], user)) {
return { success: false, error: "Insufficient permissions. Admin or Developer role required for removal." };
}
const structure = await getStructure(payload, structureId); const structure = await getStructure(payload, structureId);
const existing = [...(structure.storedResources ?? [])]; const existing = [...(structure.storedResources ?? [])];
const idx = existing.findIndex((e) => e.id === entryId); const idx = existing.findIndex((e) => e.id === entryId);
@ -676,7 +701,10 @@ export async function splitGridStack(
splitAmount: number, splitAmount: number,
): Promise<GridActionResult> { ): Promise<GridActionResult> {
try { try {
const { payload } = await authenticate(); const { payload, user } = await authenticate();
if (!hasRoles(["admin"], user) && !hasRoles(["user"], user)) {
return { success: false, error: "Insufficient permissions. User, Admin, or Developer role required." };
}
if (splitAmount <= 0) return { success: false, error: "Split amount must be positive." }; if (splitAmount <= 0) return { success: false, error: "Split amount must be positive." };
@ -755,7 +783,10 @@ export async function splitGridStack(
export async function autoArrangeGrid(structureId: number): Promise<GridActionResult> { export async function autoArrangeGrid(structureId: number): Promise<GridActionResult> {
try { try {
const { payload } = await authenticate(); const { payload, user } = await authenticate();
if (!hasRoles(["admin"], user) && !hasRoles(["user"], user)) {
return { success: false, error: "Insufficient permissions. User, Admin, or Developer role required." };
}
const structure = await getStructure(payload, structureId); const structure = await getStructure(payload, structureId);
const gridW = structure.type.xSize ?? 0; const gridW = structure.type.xSize ?? 0;
const gridH = structure.type.ySize ?? 0; const gridH = structure.type.ySize ?? 0;