mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-10-02 08:07:40 -05:00
Move app to apps/web-react in pnpm workspace layout
This commit is contained in:
127
apps/web-react/app/features/api/core/perms.test.ts
Normal file
127
apps/web-react/app/features/api/core/perms.test.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import * as ApiTokenFactory from "~/db/seed/factories/ApiTokenFactory";
|
||||
import * as TournamentOrganizationFactory from "~/db/seed/factories/TournamentOrganizationFactory";
|
||||
import * as UserFactory from "~/db/seed/factories/UserFactory";
|
||||
import * as UserRepository from "~/features/user-page/UserRepository.server";
|
||||
import * as ApiRepository from "../ApiRepository.server";
|
||||
import { checkUserHasApiAccess } from "./perms";
|
||||
|
||||
describe("Permission logic consistency between findAllApiTokens and checkUserHasApiAccess", () => {
|
||||
test("both functions grant access for isApiAccesser flag", async () => {
|
||||
const { id } = await UserFactory.create(null, { roles: ["API_ACCESSER"] });
|
||||
|
||||
await ApiTokenFactory.create({ userId: id });
|
||||
const tokens = await ApiRepository.findAllApiTokens();
|
||||
|
||||
const user = await UserRepository.findLeanById(id);
|
||||
const hasAccess = await checkUserHasApiAccess(user!);
|
||||
|
||||
expect(tokens).toHaveLength(1);
|
||||
expect(hasAccess).toBe(true);
|
||||
});
|
||||
|
||||
test("both functions grant access for isTournamentOrganizer flag", async () => {
|
||||
const { id } = await UserFactory.create(
|
||||
{},
|
||||
{ roles: ["TOURNAMENT_ORGANIZER"] },
|
||||
);
|
||||
|
||||
await ApiTokenFactory.create({ userId: id });
|
||||
const tokens = await ApiRepository.findAllApiTokens();
|
||||
|
||||
const user = await UserRepository.findLeanById(id);
|
||||
const hasAccess = await checkUserHasApiAccess(user!);
|
||||
|
||||
expect(tokens).toHaveLength(1);
|
||||
expect(hasAccess).toBe(true);
|
||||
});
|
||||
|
||||
test("both functions grant access for patronTier >= 2", async () => {
|
||||
const { id } = await UserFactory.create(null, { patronTier: 2 });
|
||||
|
||||
await ApiTokenFactory.create({ userId: id });
|
||||
const tokens = await ApiRepository.findAllApiTokens();
|
||||
|
||||
const user = await UserRepository.findLeanById(id);
|
||||
const hasAccess = await checkUserHasApiAccess(user!);
|
||||
|
||||
expect(tokens).toHaveLength(1);
|
||||
expect(hasAccess).toBe(true);
|
||||
});
|
||||
|
||||
test("both functions deny access for patronTier < 2", async () => {
|
||||
const { id } = await UserFactory.create(null, { patronTier: 1 });
|
||||
|
||||
await ApiTokenFactory.create({ userId: id });
|
||||
const tokens = await ApiRepository.findAllApiTokens();
|
||||
|
||||
const user = await UserRepository.findLeanById(id);
|
||||
const hasAccess = await checkUserHasApiAccess(user!);
|
||||
|
||||
expect(tokens).toHaveLength(0);
|
||||
expect(hasAccess).toBe(false);
|
||||
});
|
||||
|
||||
test("both functions grant access for ADMIN/ORGANIZER/STREAMER of established org", async () => {
|
||||
const [owner, admin, organizer, streamer] = await UserFactory.createMany(4);
|
||||
|
||||
await TournamentOrganizationFactory.create(
|
||||
{ ownerId: owner.id },
|
||||
{
|
||||
isEstablished: true,
|
||||
members: [
|
||||
{ userId: admin.id, role: "ADMIN" },
|
||||
{ userId: organizer.id, role: "ORGANIZER" },
|
||||
{ userId: streamer.id, role: "STREAMER" },
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
for (const userId of [admin.id, organizer.id, streamer.id]) {
|
||||
await ApiTokenFactory.create({ userId });
|
||||
const tokens = await ApiRepository.findAllApiTokens();
|
||||
|
||||
const user = await UserRepository.findLeanById(userId);
|
||||
const hasAccess = await checkUserHasApiAccess(user!);
|
||||
|
||||
expect(tokens.length).toBeGreaterThan(0);
|
||||
expect(hasAccess).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
test("both functions deny access for MEMBER of established org", async () => {
|
||||
const [owner, member] = await UserFactory.createMany(2);
|
||||
|
||||
await TournamentOrganizationFactory.create(
|
||||
{ ownerId: owner.id },
|
||||
{ isEstablished: true, members: [{ userId: member.id, role: "MEMBER" }] },
|
||||
);
|
||||
|
||||
await ApiTokenFactory.create({ userId: member.id });
|
||||
const tokens = await ApiRepository.findAllApiTokens();
|
||||
|
||||
const user = await UserRepository.findLeanById(member.id);
|
||||
const hasAccess = await checkUserHasApiAccess(user!);
|
||||
|
||||
expect(tokens).toHaveLength(0);
|
||||
expect(hasAccess).toBe(false);
|
||||
});
|
||||
|
||||
test("both functions deny access for ADMIN of non-established org", async () => {
|
||||
const [owner, member] = await UserFactory.createMany(2);
|
||||
|
||||
await TournamentOrganizationFactory.create(
|
||||
{ ownerId: owner.id },
|
||||
{ members: [{ userId: member.id, role: "ADMIN" }] },
|
||||
);
|
||||
|
||||
await ApiTokenFactory.create({ userId: member.id });
|
||||
const tokens = await ApiRepository.findAllApiTokens();
|
||||
|
||||
const user = await UserRepository.findLeanById(member.id);
|
||||
const hasAccess = await checkUserHasApiAccess(user!);
|
||||
|
||||
expect(tokens).toHaveLength(0);
|
||||
expect(hasAccess).toBe(false);
|
||||
});
|
||||
});
|
||||
23
apps/web-react/app/features/api/core/perms.ts
Normal file
23
apps/web-react/app/features/api/core/perms.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { AuthenticatedUser } from "~/features/auth/core/user.server";
|
||||
import * as TournamentOrganizationRepository from "~/features/tournament-organization/TournamentOrganizationRepository.server";
|
||||
|
||||
/**
|
||||
* Checks whether a user has permission to access the API.
|
||||
* A user has API access if they either have the API_ACCESSER role (includes supporters),
|
||||
* or are an admin/organizer/streamer of an established tournament organization.
|
||||
*
|
||||
* @param user - The authenticated user to check permissions for
|
||||
* @returns True if the user has API access, false otherwise
|
||||
*/
|
||||
export async function checkUserHasApiAccess(user: AuthenticatedUser) {
|
||||
// NOTE: permissions logic also exists in ApiRepository.findAllApiTokens function
|
||||
if (user.roles.includes("API_ACCESSER")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const orgs = await TournamentOrganizationRepository.findByUserId(user.id, {
|
||||
roles: ["ADMIN", "ORGANIZER", "STREAMER"],
|
||||
});
|
||||
|
||||
return orgs.some((org) => org.isEstablished);
|
||||
}
|
||||
Reference in New Issue
Block a user