feat(admission): add scheduled group access
CI / validate (push) Successful in 6m15s
Release / release (push) Successful in 7m49s

This commit is contained in:
dmg
2026-08-02 14:04:48 -04:00
parent eb1c5b6de4
commit c131465ff5
32 changed files with 2446 additions and 59 deletions
+1
View File
@@ -33,6 +33,7 @@ This OKF bundle is the product record for implemented and proposed behavior. Sto
* [US-017 — Control admission with groups](us-017-group-access.md) - Each user has one effective group that explicitly controls Minecraft access.
* [US-018 — Monitor community account activity](us-018-admin-dashboard.md) - Administrators review daily users, confirmed connections, locations, denials, and risky networks.
* [US-019 — Manage groups efficiently](us-019-admin-group-management.md) - Administrators manage group identity, policies, membership, and creation through focused confirmed workflows.
* [US-020 — Schedule group access in UTC](us-020-scheduled-group-access.md) - Enabled groups may be restricted to recurring weekly UTC windows with static denial-message templates.
# Tracking
+1
View File
@@ -2,6 +2,7 @@
## 2026-08-02
* **Extend**: Add recurring UTC group-access windows, browser-local schedule editing, and validated static denial-message variables.
* **Refine**: Replace admin group cards with a policy table, confirmed modal workflows, editable group details, and reusable effective-member management.
* **Add**: Provide Users-page group assignment, effective-group VPN/proxy/Tor exceptions for game admission, and independent configurable denial messages.
* **Fix**: Treat malformed ProxyCheck proxy signals as unknown and classify every authenticated Velocity login before identity resolution.
+3
View File
@@ -23,11 +23,14 @@ As a registered player, I want the Velocity proxy to recognize my approved Java
- [x] Successful fallback backfills UUID and canonical username.
- [x] Changed usernames are persisted and audited.
- [x] Registered players are allowed only when their single effective group has access enabled; explicit assignments override the default group.
- [x] Disabled group access overrides every schedule; enabled groups with weekly windows admit logins only during an active UTC window.
- [x] Schedule policy is checked before VPN/proxy/Tor policy and is enforced only at login.
- [x] Unknown players, group-disabled players, API failures, malformed responses, and unauthorized requests fail closed with registration guidance.
- [x] The plugin records the real Velocity connection IP and supports Java Edition online mode only.
- [x] After admission, Velocity reports `PostLoginEvent` as best-effort authenticated telemetry without disconnecting an admitted player when reporting fails.
- [x] Confirmed-connection reports use fresh timestamps and database replay protection.
- [x] Group-disabled and VPN/proxy/Tor-policy denials return distinct operator-configured messages.
- [x] Schedule denials return the configured static template with the effective group, player, and next UTC window.
- [x] The default anonymized-network message directs the player to contact a host for an exception.
- [x] API failures, malformed responses, and unauthorized requests retain fail-closed plugin fallback behavior.
+4 -2
View File
@@ -21,8 +21,9 @@ As an administrator, I want operational settings and audit visibility, so that I
- [x] Event views show type, subject, IP, classification, and approximate location when available.
- [x] Admin console access itself creates an audit event with the SSO identity.
- [x] Settings, users, and events are linked from the shared admin navigation.
- [x] Administrators can independently configure registration-required, group-access-disabled, and VPN/proxy/Tor-denied game messages.
- [x] Every message is validated server-side and has a safe default.
- [x] Administrators can independently configure registration-required, group-access-disabled, schedule-denied, and VPN/proxy/Tor-denied game-message templates.
- [x] Registration, group, and network templates accept only `{player}` and `{group}`; schedule templates also accept `{next_start}` and `{next_end}`.
- [x] Every template is validated server-side and has a safe default.
- [x] Admission-message changes are audited with the administrator identity without logging credentials.
# Implementation
@@ -31,6 +32,7 @@ As an administrator, I want operational settings and audit visibility, so that I
- [`apps/web/src/app/admin/(console)/actions.ts`](../apps/web/src/app/admin/%28console%29/actions.ts)
- [`apps/web/src/lib/admission-settings.ts`](../apps/web/src/lib/admission-settings.ts)
- [`packages/database/drizzle/0004_zippy_silver_centurion.sql`](../packages/database/drizzle/0004_zippy_silver_centurion.sql)
- [`packages/database/drizzle/0005_young_vertigo.sql`](../packages/database/drizzle/0005_young_vertigo.sql)
- [`apps/web/src/app/admin/(console)/events/page.tsx`](../apps/web/src/app/admin/%28console%29/events/page.tsx)
# Validation
+3
View File
@@ -19,6 +19,8 @@ As an administrator, I want to organize registered users into access groups, so
- [x] The `everyone` group remains created with Minecraft access disabled.
- [x] Administrators can create groups with access disabled by default and move users between groups.
- [x] Administrators can enable or disable Minecraft admission for each group.
- [x] Disabled Minecraft admission always denies group members; enabled admission may additionally be restricted by recurring UTC windows.
- [x] Groups without windows retain unrestricted scheduling, and groups with windows admit logins only during an active window.
- [x] Admission follows only the user's effective group; default and explicit-group access are never combined.
- [x] Administrators can delete non-default groups, returning affected users to `everyone`.
- [x] The protected default group cannot be deleted.
@@ -40,6 +42,7 @@ As an administrator, I want to organize registered users into access groups, so
- [`packages/database/drizzle/0002_simple_queen_noir.sql`](../packages/database/drizzle/0002_simple_queen_noir.sql)
- [`packages/database/drizzle/0003_smiling_silver_samurai.sql`](../packages/database/drizzle/0003_smiling_silver_samurai.sql)
- [`packages/database/drizzle/0004_zippy_silver_centurion.sql`](../packages/database/drizzle/0004_zippy_silver_centurion.sql)
- [`packages/database/drizzle/0005_young_vertigo.sql`](../packages/database/drizzle/0005_young_vertigo.sql)
- [`apps/web/src/app/admin/(console)/groups/page.tsx`](../apps/web/src/app/admin/%28console%29/groups/page.tsx)
- [`apps/web/src/app/admin/(console)/groups/[groupId]/page.tsx`](../apps/web/src/app/admin/%28console%29/groups/%5BgroupId%5D/page.tsx)
- [`apps/web/src/app/api/velocity/access/route.ts`](../apps/web/src/app/api/velocity/access/route.ts)
+3
View File
@@ -24,6 +24,8 @@ As an administrator, I want a concise group policy table and focused group detai
- [x] Non-default groups can be deleted only after modal confirmation, returning all affected users to `everyone`.
- [x] Group identity, policy, creation, and deletion mutations commit atomically with their audit events.
- [x] Modal controls support keyboard operation, focus management, cancellation, and clear pending state.
- [x] Group details summarize recurring UTC access windows in the browser's local timezone.
- [x] Administrators use a confirmed modal to add, remove, and replace multiple non-overlapping windows.
# Implementation
@@ -32,6 +34,7 @@ As an administrator, I want a concise group policy table and focused group detai
- [`apps/web/src/app/admin/(console)/groups/actions.ts`](../apps/web/src/app/admin/%28console%29/groups/actions.ts)
- [`apps/web/src/components/admin-modal-form.tsx`](../apps/web/src/components/admin-modal-form.tsx)
- [`apps/web/src/components/group-policy-control.tsx`](../apps/web/src/components/group-policy-control.tsx)
- [`apps/web/src/components/group-schedule-editor.tsx`](../apps/web/src/components/group-schedule-editor.tsx)
- [`apps/web/src/lib/group-management.ts`](../apps/web/src/lib/group-management.ts)
# Validation
+51
View File
@@ -0,0 +1,51 @@
---
type: User Story
title: Schedule group access in UTC
description: Administrators restrict enabled groups to recurring weekly UTC windows and provide static denial-message templates.
tags: [admin, groups, scheduling, velocity, templates, security]
timestamp: 2026-08-02T17:42:26Z
story_id: US-020
status: verified
---
# User Story
As an administrator, I want an enabled group to have recurring access windows, so that its members can join only during approved weekly periods and receive useful denial guidance.
# Acceptance Criteria
- [x] A group can have zero or more recurring weekly access windows stored and evaluated in UTC.
- [x] The browser shows each UTC window's current equivalent in the administrator's local timezone while clearly identifying UTC as authoritative.
- [x] Administrators can add and remove multiple windows, including windows that cross the end of the UTC week.
- [x] Window starts are inclusive and window ends are exclusive.
- [x] No configured windows preserve unrestricted scheduling behavior while Minecraft access is enabled.
- [x] Disabled Minecraft access always denies admission, regardless of schedule.
- [x] Enabled Minecraft access with configured windows allows login only inside an active window.
- [x] VPN/proxy/Tor policy is evaluated only after group access and schedule policy pass.
- [x] Schedule enforcement occurs at login and does not disconnect an existing session when a window ends.
- [x] Schedule changes require confirmation, reauthorize the administrator, and commit atomically with an audit event.
- [x] Malformed or overlapping schedule data is rejected; malformed persisted policy fails closed.
- [x] Registration, group-disabled, and VPN/proxy/Tor templates support `{player}` and `{group}`.
- [x] Schedule-denied templates additionally support `{next_start}` and `{next_end}` for the earliest upcoming UTC window.
- [x] Unknown template variables, control characters, and invalid lengths are rejected server-side.
- [x] Registration denials use `everyone` when no effective group can be resolved.
# Implementation
- [`packages/database/src/schema.ts`](../packages/database/src/schema.ts)
- [`packages/database/drizzle/0005_young_vertigo.sql`](../packages/database/drizzle/0005_young_vertigo.sql)
- [`apps/web/src/lib/group-schedule.ts`](../apps/web/src/lib/group-schedule.ts)
- [`apps/web/src/lib/admission-settings.ts`](../apps/web/src/lib/admission-settings.ts)
- [`apps/web/src/app/admin/(console)/groups/[groupId]/page.tsx`](../apps/web/src/app/admin/%28console%29/groups/%5BgroupId%5D/page.tsx)
- [`apps/web/src/app/api/velocity/access/route.ts`](../apps/web/src/app/api/velocity/access/route.ts)
# Validation
UTC recurrence, multiple-window selection, local conversion, malformed schedules, template validation, policy precedence, and schedule-editor interactions are covered by automated tests. Drizzle generation, migration preflight, TypeScript, lint, build, security checks, and OKF validation must pass.
# Related Stories
- [Enforce registration at Velocity](us-009-velocity-admission.md)
- [Operate settings and audit views](us-012-admin-operations.md)
- [Control Minecraft admission with groups](us-017-group-access.md)
- [Manage groups efficiently](us-019-admin-group-management.md)