feat(admission): add group VPN exceptions
CI / validate (push) Successful in 5m45s
Release / release (push) Successful in 7m21s

This commit is contained in:
dmg
2026-08-02 10:16:16 -04:00
parent 24808b0f8c
commit 71856bb869
34 changed files with 1820 additions and 138 deletions
@@ -0,0 +1,33 @@
import { describe, expect, it } from "vitest";
import { admissionDenialMessage, DEFAULT_ADMISSION_MESSAGES, parseAdmissionMessages } from "./admission-settings";
describe("admission message settings", () => {
it("normalizes three independently configured denial messages", () => {
const formData = new FormData();
formData.set("registrationMessage", " Register your account before joining. ");
formData.set("groupAccessDeniedMessage", "This group cannot access the server right now.");
formData.set("vpnDeniedMessage", "VPN access requires a host-approved exception.");
expect(parseAdmissionMessages(formData)).toEqual({
registrationMessage: "Register your account before joining.",
groupAccessDeniedMessage: "This group cannot access the server right now.",
vpnDeniedMessage: "VPN access requires a host-approved exception.",
});
});
it("selects the configured message for each admission denial reason", () => {
expect(admissionDenialMessage("not_registered", DEFAULT_ADMISSION_MESSAGES)).toBe(DEFAULT_ADMISSION_MESSAGES.registrationMessage);
expect(admissionDenialMessage("group_access_disabled", DEFAULT_ADMISSION_MESSAGES)).toBe(DEFAULT_ADMISSION_MESSAGES.groupAccessDeniedMessage);
expect(admissionDenialMessage("anonymized_network_disallowed", DEFAULT_ADMISSION_MESSAGES)).toBe(DEFAULT_ADMISSION_MESSAGES.vpnDeniedMessage);
});
it("rejects missing, short, overlong, or control-character messages", () => {
for (const invalid of ["short", "a".repeat(501), "Denied\nInjected"] as const) {
const formData = new FormData();
formData.set("registrationMessage", DEFAULT_ADMISSION_MESSAGES.registrationMessage);
formData.set("groupAccessDeniedMessage", DEFAULT_ADMISSION_MESSAGES.groupAccessDeniedMessage);
formData.set("vpnDeniedMessage", invalid);
expect(parseAdmissionMessages(formData)).toBeNull();
}
});
});
+37
View File
@@ -0,0 +1,37 @@
export interface AdmissionMessages {
registrationMessage: string;
groupAccessDeniedMessage: string;
vpnDeniedMessage: string;
}
export const DEFAULT_ADMISSION_MESSAGES: AdmissionMessages = {
registrationMessage: "Please register your Minecraft account before joining.",
groupAccessDeniedMessage: "Your account group does not currently have server access. Contact a host if you believe this is a mistake.",
vpnDeniedMessage: "VPN, proxy, and Tor connections are not allowed. Contact a host to request an exception.",
} as const;
export function admissionDenialMessage(
reason: "not_registered" | "group_access_disabled" | "anonymized_network_disallowed",
messages: AdmissionMessages,
) {
if (reason === "not_registered") return messages.registrationMessage;
if (reason === "group_access_disabled") return messages.groupAccessDeniedMessage;
return messages.vpnDeniedMessage;
}
const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/;
function messageValue(formData: FormData, name: string) {
const value = String(formData.get(name) ?? "").trim();
return value.length >= 10 && value.length <= 500 && !CONTROL_CHARACTERS.test(value)
? value
: null;
}
export function parseAdmissionMessages(formData: FormData) {
const registrationMessage = messageValue(formData, "registrationMessage");
const groupAccessDeniedMessage = messageValue(formData, "groupAccessDeniedMessage");
const vpnDeniedMessage = messageValue(formData, "vpnDeniedMessage");
if (!registrationMessage || !groupAccessDeniedMessage || !vpnDeniedMessage) return null;
return { registrationMessage, groupAccessDeniedMessage, vpnDeniedMessage };
}