Summary
The user ban capability is not actually implemented. There is a ban UI in the design template and an empty ban structure returned by the admin API, but no persistence, no create/list/delete endpoints, and no enforcement at login or registration. As a result, "banning" a user (or email/pattern/IP) has no effect — a banned user can still authenticate and obtain tokens.
This was discovered while building the per-scope email whitelist (allowed_emails). Filing for visibility; not implementing.
Evidence (current main)
- The ban data is a hardcoded empty constant, never backed by storage:
API/src/services/internal-admin.service.base.ts:35
export const emptyBans = { emails: [], patterns: [], ips: [], users: [] };
- The admin settings/read endpoints always return that constant:
API/src/services/internal-admin.service.base.ts:210 → bans: emptyBans
API/src/services/internal-admin.service.ts:64 → return { bans: emptyBans, apps: await getAdminApps() }
- The API schema advertises it as real backing data:
API/src/routes/root/schema.internal-admin.ts:376 ("Admin settings backing data for bans and apps").
- No ban model exists in
API/prisma/schema.prisma (no table to store bans).
- No admin endpoint to create/list/remove a ban (grep for ban CRUD under
API/src/routes/internal/admin/ returns nothing).
- No enforcement in the auth flow. The login gate
finalizeAuthenticatedUser → assertEmailDomainAllowedForLogin (API/src/services/login-domain-policy.service.ts) and the registration paths (auth-register.service.ts, social/social-login.service.ts) never read the ban lists. The status: 'blocked' results in those files are the registration domain policy, not bans.
- The only "ban" implementation is mock UI in the design template:
Docs/Admin/template-admin.html:199 ("Add Ban" modal) with hardcoded sample banned users (:357, :361-363). The React Admin app and API do not implement it.
Impact
- Operators believe they can ban abusive users/emails/domains/IPs, but the action is a no-op — banned principals retain full access.
- Silent security gap: there is no error or signal that the ban didn't take.
Suggested direction (for whoever picks this up)
Mirror the just-shipped allowed-emails whitelist pattern:
- Persistence: a
Ban table (scope: global and/or per client-domain/org/team to match the hierarchy), with fields for type (email | pattern | ip | user), value, reason, createdBy, createdAt.
- Admin API: create/list/delete endpoints under
/internal/admin/..., and have the settings endpoint return real bans instead of emptyBans. Keep /api + /llm in sync.
- Enforcement: check bans in the single login gate (
finalizeAuthenticatedUser) and in the registration paths, failing closed with the existing generic auth error (no enumeration). Decide precedence vs. the allowlist (a ban should override an allow).
- Admin UI: wire the existing "Add Ban" template modal to the new endpoints.
Notes
- Brief alignment: please confirm against
Docs/brief.md before implementing — the template implies ban is in scope, but the brief should be the source of truth for the exact scope (global vs per-tenant) and semantics.
Summary
The user ban capability is not actually implemented. There is a ban UI in the design template and an empty ban structure returned by the admin API, but no persistence, no create/list/delete endpoints, and no enforcement at login or registration. As a result, "banning" a user (or email/pattern/IP) has no effect — a banned user can still authenticate and obtain tokens.
This was discovered while building the per-scope email whitelist (allowed_emails). Filing for visibility; not implementing.
Evidence (current
main)API/src/services/internal-admin.service.base.ts:35API/src/services/internal-admin.service.base.ts:210→bans: emptyBansAPI/src/services/internal-admin.service.ts:64→return { bans: emptyBans, apps: await getAdminApps() }API/src/routes/root/schema.internal-admin.ts:376("Admin settings backing data for bans and apps").API/prisma/schema.prisma(no table to store bans).API/src/routes/internal/admin/returns nothing).finalizeAuthenticatedUser→assertEmailDomainAllowedForLogin(API/src/services/login-domain-policy.service.ts) and the registration paths (auth-register.service.ts,social/social-login.service.ts) never read the ban lists. Thestatus: 'blocked'results in those files are the registration domain policy, not bans.Docs/Admin/template-admin.html:199("Add Ban" modal) with hardcoded sample banned users (:357,:361-363). The React Admin app and API do not implement it.Impact
Suggested direction (for whoever picks this up)
Mirror the just-shipped allowed-emails whitelist pattern:
Bantable (scope: global and/or per client-domain/org/team to match the hierarchy), with fields for type (email|pattern|ip|user), value, reason, createdBy, createdAt./internal/admin/..., and have the settings endpoint return real bans instead ofemptyBans. Keep/api+/llmin sync.finalizeAuthenticatedUser) and in the registration paths, failing closed with the existing generic auth error (no enumeration). Decide precedence vs. the allowlist (a ban should override an allow).Notes
Docs/brief.mdbefore implementing — the template implies ban is in scope, but the brief should be the source of truth for the exact scope (global vs per-tenant) and semantics.