feat(acls): added backend acls

This commit is contained in:
DecDuck
2025-02-04 13:15:34 +11:00
parent 256fbd6afa
commit 090d2e6586
70 changed files with 397 additions and 474 deletions

View File

@ -1,8 +1,11 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"auth:simple:invitation:delete",
]);
if (!allowed) throw createError({ statusCode: 403 });
const body = await readBody(h3);
const id = body.id;

View File

@ -1,8 +1,11 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"auth:simple:invitation:read",
]);
if (!allowed) throw createError({ statusCode: 403 });
await runTask("cleanup:invitations");

View File

@ -1,8 +1,11 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"auth:simple:invitation:new",
]);
if (!allowed) throw createError({ statusCode: 403 });
const body = await readBody(h3);
const isAdmin = body.isAdmin;
@ -30,7 +33,7 @@ export default defineEventHandler(async (h3) => {
isAdmin: isAdmin,
username: username,
email: email,
expires: expiresDate
expires: expiresDate,
},
});

View File

@ -1,8 +1,11 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"game:image:delete",
]);
if (!allowed) throw createError({ statusCode: 403 });
const body = await readBody(h3);
const gameId = body.gameId;

View File

@ -1,9 +1,12 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
import { handleFileUpload } from "~/server/internal/utils/handlefileupload";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"game:image:new",
]);
if (!allowed) throw createError({ statusCode: 403 });
const form = await readMultipartFormData(h3);
if (!form)

View File

@ -1,9 +1,12 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
import libraryManager from "~/server/internal/library";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"game:delete",
]);
if (!allowed) throw createError({ statusCode: 403 });
const query = getQuery(h3);
const gameId = query.id?.toString();

View File

@ -1,9 +1,12 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
import libraryManager from "~/server/internal/library";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"game:read",
]);
if (!allowed) throw createError({ statusCode: 403 });
const query = getQuery(h3);
const gameId = query.id?.toString();

View File

@ -1,8 +1,11 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"game:update",
]);
if (!allowed) throw createError({ statusCode: 403 });
const body = await readBody(h3);
const id = body.id;

View File

@ -1,9 +1,12 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
import { handleFileUpload } from "~/server/internal/utils/handlefileupload";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"game:update",
]);
if (!allowed) throw createError({ statusCode: 403 });
const form = await readMultipartFormData(h3);
if (!form)

View File

@ -1,8 +1,11 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"game:version:delete",
]);
if (!allowed) throw createError({ statusCode: 403 });
const body = await readBody(h3);
const gameId = body.id.toString();

View File

@ -1,8 +1,11 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"game:version:update",
]);
if (!allowed) throw createError({ statusCode: 403 });
const body = await readBody(h3);
const gameId = body.id?.toString();

View File

@ -1,8 +1,11 @@
import aclManager from "~/server/internal/acls";
import libraryManager from "~/server/internal/library";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"import:game:read",
]);
if (!allowed) throw createError({ statusCode: 403 });
const unimportedGames = await libraryManager.fetchAllUnimportedGames();
return { unimportedGames };

View File

@ -1,3 +1,4 @@
import aclManager from "~/server/internal/acls";
import libraryManager from "~/server/internal/library";
import {
GameMetadataSearchResult,
@ -5,8 +6,10 @@ import {
} from "~/server/internal/metadata/types";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"import:game:new",
]);
if (!allowed) throw createError({ statusCode: 403 });
const body = await readBody(h3);

View File

@ -1,8 +1,11 @@
import aclManager from "~/server/internal/acls";
import libraryManager from "~/server/internal/library";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"import:game:read",
]);
if (!allowed) throw createError({ statusCode: 403 });
const query = getQuery(h3);
const search = query.q?.toString();

View File

@ -1,8 +1,11 @@
import aclManager from "~/server/internal/acls";
import libraryManager from "~/server/internal/library";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"import:version:read",
]);
if (!allowed) throw createError({ statusCode: 403 });
const query = await getQuery(h3);
const gameId = query.id?.toString();

View File

@ -1,10 +1,13 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
import libraryManager from "~/server/internal/library";
import { parsePlatform } from "~/server/internal/utils/parseplatform";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"import:version:new",
]);
if (!allowed) throw createError({ statusCode: 403 });
const body = await readBody(h3);
const gameId = body.id;

View File

@ -1,8 +1,11 @@
import aclManager from "~/server/internal/acls";
import libraryManager from "~/server/internal/library";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, [
"import:version:read",
]);
if (!allowed) throw createError({ statusCode: 403 });
const query = await getQuery(h3);
const gameId = query.id?.toString();

View File

@ -1,6 +0,0 @@
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getUser(h3);
if (!user)
throw createError({ statusCode: 403, statusMessage: "Not authenticated" });
return { admin: user.admin };
});

View File

@ -1,8 +1,9 @@
import aclManager from "~/server/internal/acls";
import libraryManager from "~/server/internal/library";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, ["library:read"]);
if (!allowed) throw createError({ statusCode: 403 });
const unimportedGames = await libraryManager.fetchAllUnimportedGames();
const games = await libraryManager.fetchGamesWithStatus();

View File

@ -1,8 +1,9 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getAdminUser(h3);
if (!user) throw createError({ statusCode: 403 });
const allowed = await aclManager.allowSystemACL(h3, ["user:read"]);
if (!allowed) throw createError({ statusCode: 403 });
const users = await prisma.user.findMany({});

View File

@ -2,6 +2,7 @@ import { AuthMec } from "@prisma/client";
import { JsonArray } from "@prisma/client/runtime/library";
import prisma from "~/server/internal/db/database";
import { checkHash } from "~/server/internal/security/simple";
import sessionHandler from "~/server/internal/session";
export default defineEventHandler(async (h3) => {
const body = await readBody(h3);
@ -31,7 +32,7 @@ export default defineEventHandler(async (h3) => {
if (!await checkHash(password, hash.toString()))
throw createError({ statusCode: 401, statusMessage: "Invalid username or password." });
await h3.context.session.setUserId(h3, authMek.userId, rememberMe);
await sessionHandler.setUserId(h3, authMek.userId, rememberMe);
return { result: true, userId: authMek.userId }
});

View File

@ -1,7 +1,8 @@
import clientHandler from "~/server/internal/clients/handler";
import sessionHandler from "~/server/internal/session";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await sessionHandler.getUserId(h3);
if (!userId) throw createError({ statusCode: 403 });
const query = getQuery(h3);

View File

@ -1,7 +1,8 @@
import clientHandler from "~/server/internal/clients/handler";
import sessionHandler from "~/server/internal/session";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await sessionHandler.getUserId(h3);
if (!userId) throw createError({ statusCode: 403 });
const body = await readBody(h3);

View File

@ -1,11 +1,11 @@
import aclManager from "~/server/internal/acls";
import userLibraryManager from "~/server/internal/userlibrary";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["collections:remove"]);
if (!userId)
throw createError({
statusCode: 403,
statusMessage: "Requires authentication",
});
const id = getRouterParam(h3, "id");

View File

@ -1,11 +1,11 @@
import aclManager from "~/server/internal/acls";
import userLibraryManager from "~/server/internal/userlibrary";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["collections:add"]);
if (!userId)
throw createError({
statusCode: 403,
statusMessage: "Requires authentication",
});
const id = getRouterParam(h3, "id");

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import userLibraryManager from "~/server/internal/userlibrary";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["collections:delete"]);
if (!userId)
throw createError({
statusCode: 403,

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import userLibraryManager from "~/server/internal/userlibrary";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["collections:read"]);
if (!userId)
throw createError({
statusCode: 403,

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import userLibraryManager from "~/server/internal/userlibrary";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["library:remove"]);
if (!userId)
throw createError({
statusCode: 403,

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import userLibraryManager from "~/server/internal/userlibrary";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["library:add"]);
if (!userId)
throw createError({
statusCode: 403,

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import userLibraryManager from "~/server/internal/userlibrary";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["collections:read"]);
if (!userId)
throw createError({
statusCode: 403,

View File

@ -1,11 +1,11 @@
import aclManager from "~/server/internal/acls";
import userLibraryManager from "~/server/internal/userlibrary";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["collections:new"]);
if (!userId)
throw createError({
statusCode: 403,
statusMessage: "Requires authentication",
});
const collections = await userLibraryManager.fetchCollections(userId);

View File

@ -1,11 +1,11 @@
import aclManager from "~/server/internal/acls";
import userLibraryManager from "~/server/internal/userlibrary";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["collections:read"]);
if (!userId)
throw createError({
statusCode: 403,
statusMessage: "Requires authentication",
});
const body = await readBody(h3);

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["store:read"]);
if (!userId) throw createError({ statusCode: 403 });
const gameId = getRouterParam(h3, "id");

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["notifications:delete"]);
if (!userId) throw createError({ statusCode: 403 });
const notificationId = getRouterParam(h3, "id");

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["notifications:read"]);
if (!userId) throw createError({ statusCode: 403 });
const notificationId = getRouterParam(h3, "id");

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["notifications:mark"]);
if (!userId) throw createError({ statusCode: 403 });
const notificationId = getRouterParam(h3, "id");

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["notifications:read"]);
if (!userId) throw createError({ statusCode: 403 });
const notifications = await prisma.notification.findMany({

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["notifications:mark"]);
if (!userId) throw createError({ statusCode: 403 });
await prisma.notification.updateMany({

View File

@ -1,6 +1,7 @@
import notificationSystem from "~/server/internal/notifications";
import session from "~/server/internal/session";
import { parse as parseCookies } from "cookie-es";
import aclManager from "~/server/internal/acls";
// TODO add web socket sessions for horizontal scaling
// Peer ID to user ID
@ -8,16 +9,10 @@ const socketSessions: { [key: string]: string } = {};
export default defineWebSocketHandler({
async open(peer) {
const cookies = peer.request?.headers?.get("Cookie");
if (!cookies) {
peer.send("unauthenticated");
return;
}
const parsedCookies = parseCookies(cookies);
const token = parsedCookies[session.getDropTokenCookie()];
const userId = await session.getUserIdRaw(token);
const userId = await aclManager.getUserIdACL(
{ headers: peer.request?.headers ?? new Headers() },
["notifications:listen"]
);
if (!userId) {
peer.send("unauthenticated");
return;

View File

@ -1,8 +1,10 @@
import aclManager from "~/server/internal/acls";
export default defineEventHandler(async (h3) => {
const id = getRouterParam(h3, "id");
if (!id) throw createError({ statusCode: 400, statusMessage: "Invalid ID" });
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["object:delete"]);
const result = await h3.context.objects.deleteWithPermission(id, userId);
return { success: result };

View File

@ -1,8 +1,10 @@
import aclManager from "~/server/internal/acls";
export default defineEventHandler(async (h3) => {
const id = getRouterParam(h3, "id");
if (!id) throw createError({ statusCode: 400, statusMessage: "Invalid ID" });
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["object:read"]);
const object = await h3.context.objects.fetchWithPermissions(id, userId);
if (!object)

View File

@ -1,3 +1,5 @@
import aclManager from "~/server/internal/acls";
export default defineEventHandler(async (h3) => {
const id = getRouterParam(h3, "id");
if (!id) throw createError({ statusCode: 400, statusMessage: "Invalid ID" });
@ -9,7 +11,7 @@ export default defineEventHandler(async (h3) => {
statusMessage: "Invalid upload",
});
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserIdACL(h3, ["object:update"]);
const buffer = Buffer.from(body);
const result = await h3.context.objects.writeWithPermissions(

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserACL(h3, ["store:read"]);
if (!userId) throw createError({ statusCode: 403 });
const developers = await prisma.developer.findMany({

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserACL(h3, ["store:read"]);
if (!userId) throw createError({ statusCode: 403 });
const publishers = await prisma.publisher.findMany({

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserACL(h3, ["store:read"]);
if (!userId) throw createError({ statusCode: 403 });
const games = await prisma.game.findMany({

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserACL(h3, ["store:read"]);
if (!userId) throw createError({ statusCode: 403 });
const games = await prisma.game.findMany({

View File

@ -1,7 +1,8 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const userId = await h3.context.session.getUserId(h3);
const userId = await aclManager.getUserACL(h3, ["store:read"]);
if (!userId) throw createError({ statusCode: 403 });
const versions = await prisma.gameVersion.findMany({

View File

@ -1,46 +1,39 @@
import session from "~/server/internal/session";
import taskHandler, { TaskMessage } from "~/server/internal/tasks";
import { parse as parseCookies } from "cookie-es";
import { MinimumRequestObject } from "~/server/h3";
// TODO add web socket sessions for horizontal scaling
// ID to admin
const adminSocketSessions: { [key: string]: boolean } = {};
const socketHeaders: { [key: string]: MinimumRequestObject } = {};
export default defineWebSocketHandler({
async open(peer) {
const cookies = peer.request?.headers?.get("Cookie");
if (!cookies) {
const request = peer.request;
if (!request) {
peer.send("unauthenticated");
return;
}
const parsedCookies = parseCookies(cookies);
const token = parsedCookies[session.getDropTokenCookie()];
const userId = await session.getUserIdRaw(token);
if (!userId) {
peer.send("unauthenticated");
return;
}
const admin = session.getAdminUser(token);
adminSocketSessions[peer.id] = admin !== undefined;
socketHeaders[peer.id] = {
headers: request.headers ?? new Headers(),
};
peer.send(`connect`);
},
message(peer, message) {
if (!peer.id) return;
if (adminSocketSessions[peer.id] === undefined) return;
if (socketHeaders[peer.id] === undefined) return;
const text = message.text();
if (text.startsWith("connect/")) {
const id = text.substring("connect/".length);
taskHandler.connect(peer.id, id, peer, adminSocketSessions[peer.id]);
taskHandler.connect(peer.id, id, peer, socketHeaders[peer.id]);
return;
}
},
close(peer, details) {
if (!peer.id) return;
if (adminSocketSessions[peer.id] === undefined) return;
delete adminSocketSessions[peer.id];
if (socketHeaders[peer.id] === undefined) return;
delete socketHeaders[peer.id];
taskHandler.disconnectAll(peer.id);
},

View File

@ -1,4 +1,6 @@
import aclManager from "~/server/internal/acls";
export default defineEventHandler(async (h3) => {
const user = await h3.context.session.getUser(h3);
const user = await aclManager.getUserACL(h3, ["read"]);
return user ?? null; // Need to specifically return null
});