This commit is contained in:
Aotumuri
2025-06-01 16:16:12 +09:00
parent 02ca9c2c6f
commit 5b0cfa9603
4 changed files with 98 additions and 95 deletions
+30 -40
View File
@@ -71,47 +71,37 @@ export class TerritoryPatternsModal extends LitElement {
}
private checkPatternPermission(roles: string[]) {
if (
roles.includes("1286745100411473930") || // creator
roles.includes("1286738076386856991") || // admin
roles.includes("1338654590043820148") // mod
) {
return;
const patterns = territoryPatterns.pattern ?? {};
for (const [key, patternData] of Object.entries(patterns)) {
const roleGroup: string[] | string | undefined = patternData.role_group;
if (!roleGroup || (Array.isArray(roleGroup) && roleGroup.length === 0))
continue;
const groupList = Array.isArray(roleGroup) ? roleGroup : [roleGroup];
if (groupList.includes("all")) {
continue; // Allow all users
}
const isAllowed = groupList.some((required) => roles.includes(required));
if (!isAllowed) {
let reason: string;
if (groupList.includes("donor")) {
reason =
"This pattern is available only to donors (money haters or early access supporters).";
} else if (groupList.includes("staff")) {
reason = "This pattern is available only to moderators and above.";
} else {
reason = `This pattern is available only to specific roles. (${groupList.join(", ")})`;
}
this.setLockedPatterns([key], reason);
}
}
this.setLockedPatterns(
["evan", "openfront"],
"This pattern is available only to moderators and above.",
);
if (
roles.includes("1359441841371480176") || // money haters
roles.includes("1330243292306341969") // early access supporter
) {
return;
}
const restrictedForDonorsOnly = [
"diagonal",
"cross",
"mini_cross",
"horizontal_stripes",
"sparse_dots",
"diagonal_stripe",
"mountain_ridge",
"scattered_dots",
"circuit_board",
"vertical_bars",
".w.",
];
this.setLockedPatterns(
restrictedForDonorsOnly,
"This pattern is available only to donors (money haters or early access supporters).",
);
// Future permission logic here
return;
}
createRenderRoot() {
+1 -1
View File
@@ -77,7 +77,7 @@ export abstract class DefaultServerConfig implements ServerConfig {
jwtIssuer(): string {
const audience = this.jwtAudience();
return audience === "localhost"
? "http://localhost:8787"
? "https://api-worker-dev.evanpelle.workers.dev"
: `https://api.${audience}`;
}
async jwkPublicKey(): Promise<JWK> {
+60
View File
@@ -0,0 +1,60 @@
import { createRequire } from "module";
const require = createRequire(import.meta.url);
const territory_patterns = require("../../resources/cosmetic/territory_patterns.json");
type RoleGroups = Record<string, string[]>;
type PatternEntry = {
pattern: string;
role_group: string[];
};
type TerritoryPatterns = {
role_groups: RoleGroups;
pattern: Record<string, PatternEntry>;
};
const patternData = territory_patterns as TerritoryPatterns;
export class PrivilegeChecker {
private patternData: TerritoryPatterns;
constructor(patternData: TerritoryPatterns) {
this.patternData = patternData;
}
isPatternAllowed(base64: string, roleIDs: string[]): boolean {
const found = Object.entries(this.patternData.pattern).find(
([, entry]) => entry.pattern === base64,
);
if (!found) {
// fallback to staff privilege check
const staffRoles = this.patternData.role_groups["staff"] || [];
return roleIDs.some((role) => staffRoles.includes(role));
}
const [, entry] = found;
const allowedGroups = entry.role_group;
if (allowedGroups.includes("all")) {
return true;
}
for (const groupName of allowedGroups) {
const groupRoles = this.patternData.role_groups[groupName] || [];
if (roleIDs.some((role) => groupRoles.includes(role))) {
return true;
}
}
return false;
}
}
let cachedChecker: PrivilegeChecker | null = null;
export function getPrivilegeChecker(): PrivilegeChecker {
if (!cachedChecker) {
cachedChecker = new PrivilegeChecker(patternData);
}
return cachedChecker;
}
+7 -54
View File
@@ -2,7 +2,6 @@ import express, { NextFunction, Request, Response } from "express";
import rateLimit from "express-rate-limit";
import http from "http";
import ipAnonymize from "ip-anonymize";
import { createRequire } from "module";
import path from "path";
import { fileURLToPath } from "url";
import { WebSocket, WebSocketServer } from "ws";
@@ -22,9 +21,8 @@ import { GameManager } from "./GameManager";
import { gatekeeper, LimiterType } from "./Gatekeeper";
import { getUserMe, verifyClientToken } from "./jwt";
import { logger } from "./Logger";
import { getPrivilegeChecker } from "./Privilege";
import { initWorkerMetrics } from "./WorkerMetrics";
const require = createRequire(import.meta.url);
const territory_patterns = require("../../resources/territory_patterns.json");
const config = getServerConfigFromServer();
@@ -333,57 +331,12 @@ export function startWorker() {
}
if (clientMsg.pattern !== undefined) {
const isCreator = roles?.includes("1286745100411473930");
const isAdmin = roles?.includes("1286738076386856991");
const isMod = roles?.includes("1338654590043820148");
const isMoneyHater = roles?.includes("1359441841371480176");
const isEarlyAccess = roles?.includes("1330243292306341969");
const isAllowedBase64 = Object.values(
territory_patterns,
).includes(clientMsg.pattern);
const evanBlockedPatterns = [
territory_patterns["openfront"],
territory_patterns["evan"],
];
const isEvanPattern = evanBlockedPatterns.includes(
clientMsg.pattern,
);
const restrictedBase64Patterns = [
territory_patterns["diagonal"],
territory_patterns["cross"],
territory_patterns["mini_cross"],
territory_patterns["horizontal_stripes"],
territory_patterns["sparse_dots"],
territory_patterns["diagonal_stripe"],
territory_patterns["mountain_ridge"],
territory_patterns["scattered_dots"],
territory_patterns["circuit_board"],
territory_patterns["vertical_bars"],
territory_patterns[".w."],
];
const isRestrictedPattern = restrictedBase64Patterns.includes(
clientMsg.pattern,
);
if (!(isCreator || isAdmin || isMod)) {
if (isMoneyHater || isEarlyAccess) {
if (!isAllowedBase64 || isEvanPattern) {
log.warn(`pattern blocked (evan/openfront or unlisted)`);
return;
}
} else {
if (
!isAllowedBase64 ||
isRestrictedPattern ||
isEvanPattern
) {
log.warn(`pattern blocked (restricted or unlisted)`);
return;
}
}
const checker = getPrivilegeChecker();
if (!checker.isPatternAllowed(clientMsg.pattern, roles ?? [])) {
log.warn(
`pattern blocked (restricted or unlisted): ${clientMsg.pattern}`,
);
return;
}
}