Skip to content

User Roles & RBAC Overview ​

The platform has exactly three application roles: Student, Instructor and Admin. This page explains where roles come from, how they are checked, and what happens when someone tries something they are not allowed to do. The role pages (Student, Instructor, Admin) and the comparison matrix describe what each role can actually do, traced from the code rather than inferred from the role names.

Authentication vs authorization ​

AuthenticationAuthorization
Question it answersWho are you?What are you allowed to do?
Handled byKeycloak (username and password), NextAuth in the portalPortal middleware and pages (user experience), FastAPI guards (the real boundary)
ResultA signed access token and a portal session cookieAllow, redirect, or an HTTP error (401, 403, 404)
Codecyberrange/portal/src/lib/auth.ts, cyberrange/src/provisioning/auth.py (verify_token)cyberrange/portal/src/middleware.ts, lib/routeRoles.ts, cyberrange/src/provisioning/auth.py (require_app_role, require_role, require_owner, require_owner_or_admin)

Plain language

Authentication is the security guard checking your ID at the door. Authorization is which rooms your badge opens. This platform checks the badge twice: the portal will not show you a room you cannot enter, and the API refuses the request anyway if you try to reach it another way.

The three roles at a glance ​

RoleWho it is forLands onSummary of powers
StudentBSIT Network & Cybersecurity learners/dashboardRun one lab at a time, use the terminal, get scored, submit reports and screenshots, see only their own data
InstructorFaculty supervising labs/instructorEverything a student can do for themselves, plus read-only oversight of every live lab, every student's progress, SIEM alerts and check-failure reasons, grading of reviews, and the anonymized research export
AdminPlatform operators/adminEverything an instructor can do, plus account and role management in Keycloak, marking test accounts out of research data, the identified research export, force-destroying any lab, system health and the audit log

Source for landing pages: landingPathForRole() in cyberrange/portal/src/lib/routeRoles.ts. The landing page is also enforced afterwards: an instructor or admin who opens /dashboard is sent to /instructor or /admin (redirectPathForRole(), pull request 176). On first sign-in, students and instructors see a one-time orientation at /orientation with steps for their role; admins skip it (pull requests 184, 188 and 311, cyberrange/portal/src/app/orientation/page.tsx).

Where roles come from ​

  • Roles are Keycloak realm roles named exactly student, instructor and admin in the realm cyber-range (cyberrange/deploy/host/create_demo_accounts.sh, cyberrange/docs/AUTH-03-role-contract.md).
  • Each account is meant to hold exactly one application role. The demo accounts student_demo, instructor_demo and admin_demo are created with one role each, and the Admin Users page assigns a single role (role is one of the three values in CreateUserRequest and SetRoleRequest, cyberrange/src/provisioning/users_router.py).
  • Every token also contains Keycloak's own roles (offline_access, uma_authorization, default-roles-cyber-range). These are ignored by every check.
  • There is no self-registration. Accounts are created by an admin or by the setup scripts (users_router.py docstring).

How roles are read during authentication ​

WhereHow roles are readCode
PortalAfter sign-in, NextAuth stores Keycloak's access token in the encrypted session. decodeRoles() reads realm_access.roles from the token payload (the token was already validated during the code exchange). Roles are re-read on every token refresh, so a role change appears at the next refresh.cyberrange/portal/src/lib/auth.ts
APIEvery request's bearer token is sent to Keycloak's introspection endpoint. Roles are the union of realm_access.roles and resource_access.portal.roles. Results are cached for at most 60 seconds, and the cache is cleared for a user when an admin disables them or changes their role.cyberrange/src/provisioning/auth.py, introspect_cache.py, users_router.py
Terminal bridgeSame introspection (shared cache module), then an ownership check through the APIcyberrange/bridge-src/bridge.py

RBAC architecture ​

Authentication to authorization, end to end ​

What happens on an unauthorized attempt ​

SituationWhere it is stoppedResult the user seesCode
Not signed in, opens a protected pageEdge middlewareRedirect to /login?callbackUrl=...cyberrange/portal/src/middleware.ts
Signed in but no application roleEdge middleware and login routingRedirect to /no-rolemiddleware.ts, lib/routeRoles.ts
Student opens /instructor or /adminEdge middleware (and AuthGate on the client)Redirect to /dashboardlib/routeRoles.ts
Instructor opens /admin, or an instructor or admin opens /dashboardEdge middleware (and AuthGate)Redirect to their own landing page (/instructor or /admin)redirectPathForRole() in lib/routeRoles.ts
Instructor asks for an identified knowledge-gain export (anonymize=false) or for excluded accountsexport_knowledge_gain()403, audited as KNOWLEDGE_GAIN_EXPORTED DENIEDpods_router.py
BFF call without a sessionproxyToApi()401 Unauthorizedcyberrange/portal/src/lib/apiProxy.ts
Missing or malformed bearer token at the APIverify_token()401cyberrange/src/provisioning/auth.py
Expired or revoked tokenverify_token()401 Invalid or expired tokenauth.py
Keycloak unreachableverify_token()503 Auth service unavailableauth.py
Authenticated, no application role, calls the API directlyrequire_app_role on the router403auth.py, pods_router.py
Student calls an instructor or admin endpointrequire_role()403 Forbidden: Insufficient privilegesauth.py
Someone touches another user's lab or reviewrequire_owner() / owner-or-staff checks404 (existence is hidden)auth.py, pods_router.py
Admin tries to disable, re-role or reset their own account_reject_self()Request refused and audited as DENIEDusers_router.py
Non-admin attempts a force-destroyrequire_role(["admin"]) runs first403, and nothing is written to the audit logpods_router.py
Terminal WebSocket with a bad token or someone else's labbridge.pyError message in the terminal, connection closed with code 1008cyberrange/bridge-src/bridge.py

Multi-role and no-role policy ​

  • No application role: treated as unauthorized everywhere (portal redirect to /no-role, API 403).
  • More than one application role: not expected, and not prevented by the type system. The portal lands the user on the highest role (admin, then instructor, then student). The API grants the union of all roles.

Source: cyberrange/docs/AUTH-03-role-contract.md, landingPathForRole() in cyberrange/portal/src/lib/routeRoles.ts.

Planned / Not Yet Implemented ​

CapabilityStatusEvidence
Instructor or admin editing scenarios, milestones or points in the UINot implementedScenario catalog is static in cyberrange/portal/src/hooks/useScenarios.ts; no API route writes scenarios
Deleting user accountsNot implementedusers_router.py has create, enable/disable, role, password, MFA reset and research-exclusion routes only
/settings, /profile, /progress pagesNot implementedListed in the middleware matcher, but no pages exist
Flag submission for hybrid (non-final) milestonesAPI onlyFlagSubmission is shown only on the flag-only final tasks (Scenario 1 and 2 Task 5); other milestones' flags can only be submitted through the API
Self-service MFA recoveryNot implementedA user who loses their authenticator needs an Admin Reset MFA; Keycloak recovery codes are a preview feature and there is no SMTP for email reset
Admin terminal into a student lab from the UIBackend onlyThe bridge accepts admin tokens for any lab (see Admin), but no admin page offers it

Cyber Range capstone documentation (Capstone 2 · Group 2 · A4102). Documents main at commit 6971373.