feat(groups): add fail-closed admission management
CI / validate (push) Successful in 5m13s
Release / release (push) Successful in 7m8s

This commit is contained in:
dmg
2026-08-01 18:36:45 -04:00
parent 19a5d04178
commit b88097c15a
32 changed files with 1910 additions and 29 deletions
+41 -2
View File
@@ -1,15 +1,17 @@
import { randomUUID } from "node:crypto";
import { isRequestTimestampFresh, verifyHashedToken } from "@minecraft-account-manager/auth";
import { enabledAccessGroup, isRequestTimestampFresh, verifyHashedToken } from "@minecraft-account-manager/auth";
import { problemDetails, velocityAccessRequestSchema } from "@minecraft-account-manager/contracts";
import {
appSettings,
events,
groups,
ipObservations,
minecraftAccounts,
pluginCredentials,
pluginRequests,
userGroupMemberships,
} from "@minecraft-account-manager/database";
import { and, eq, isNull, lt, sql } from "drizzle-orm";
import { and, eq, isNull, lt, or, sql } from "drizzle-orm";
import { NextResponse } from "next/server";
import { db } from "@/lib/database";
import { isUniqueConstraintViolation } from "@/lib/database-errors";
@@ -210,6 +212,42 @@ async function handleVelocityAccess(request: Request) {
return { allowed: false as const, message: denialMessage };
}
const assignedGroups = await tx
.select({ id: groups.id, name: groups.name, accessEnabled: groups.accessEnabled })
.from(groups)
.leftJoin(userGroupMemberships, eq(userGroupMemberships.groupId, groups.id))
.where(or(eq(groups.isDefault, true), eq(userGroupMemberships.userId, account.userId)));
const enabledGroup = enabledAccessGroup(assignedGroups);
if (!enabledGroup) {
await tx.insert(events).values({
id: randomUUID(),
source: `/velocity/${input.serverId}`,
type: "games.minecraft.account-manager.game.login.denied",
subject: `minecraft-account/${account.id}`,
time: occurredAt,
actorUserId: account.userId,
data: {
username: input.username,
reason: "group_access_disabled",
ipIntelligence: auditIpData,
},
ipAddress: input.ipAddress,
correlationId: input.requestId,
});
await tx.insert(ipObservations).values({
userId: account.userId,
minecraftAccountId: account.id,
source: "game",
ipAddress: input.ipAddress,
minecraftUuid: input.minecraftUuid,
username: input.username,
classification: intelligence.classification,
observedAt: occurredAt,
});
return { allowed: false as const, message: "Your account group does not currently have server access." };
}
if (account.minecraftUuid !== input.minecraftUuid || account.username !== input.username) {
await tx
.update(minecraftAccounts)
@@ -259,6 +297,7 @@ async function handleVelocityAccess(request: Request) {
previousUsername: account.username === input.username ? null : account.username,
uuidBackfilled: account.minecraftUuid === null,
ipIntelligence: auditIpData,
accessGroup: enabledGroup.name,
},
ipAddress: input.ipAddress,
correlationId: input.requestId,