Access Control List — authentication, session management,
and permission validation for the Axion web platform.
Stateful server-side session management. Users authenticate
via login form or federated SSO (Auth0/Keycloak). The
session ID is stored in sys_user_session and validated
on each request. ACL permission maps are cached in Redis
with a 15-minute TTL.
loginname + loginpasswordlogin_user() verifies against sys_user (encrypted)_set_user_session() creates session UUIDsys_user_sessionvalidate_users() builds group/company/level mapsset_cached_acl()login_federated_user() receives IdP claims_extract_federated_acl() maps claims to groups_sync_federated_acl_to_user() updates sys_userlogin_session(session_guid) called per requestget_cached_acl() checks Redis
_restore_acl_state() → return (< 1ms)loginname from sys_user_sessionObjUser.Read()logout_user() clears session from sys_user_session
and invalidates Redis cache.
acl.validate_report("dashboard") # returns 1 or 0
Lookup order:
acl:user:{user}:reports)self.reports)acl.validate_form("login") # returns 1 or 0
Same pattern as reports with acl:user:{user}:forms.
Reports/forms with level <= 1 (or <= 2 without SSO)
are accessible without login via direct DB check.
| Key Pattern | Data | TTL |
|---|---|---|
acl:session:{session_key} |
Full ACL state (reports, forms, docs) | 15 min |
acl:reports:{package} |
Report → [allowed users] map | 15 min |
acl:forms:{package} |
Form → [allowed users] map | 15 min |
acl:reports:{package}:docs |
Document → [allowed users] map | 15 min |
acl:user:{package} |
User list with groups/levels | 15 min |
acl:user:{package}:groups |
Group → [users] map | 15 min |
acl:user:{package}:companies |
Company → [users] map | 15 min |
acl:user:{package}:levels |
Level → [users] map | 15 min |
acl:user:{user}:reports |
Per-user allowed report codes | 15 min |
acl:user:{user}:forms |
Per-user allowed form codes | 15 min |
TTL controlled by AclConstants.ACL_CACHE_TTL_SECS
(900 seconds = 15 minutes).
| Scenario | Latency |
|---|---|
| Repeat page view (session cache hit) | < 1ms |
| First page after login (cache miss) | ~20ms |
| Report-only page (lazy, no forms) | ~15ms |
| Per-user permission check (fast path) | < 1ms |
Reports, forms, and documents are NOT loaded eagerly.
Each is loaded on first access:
validate_report() → _ensure_reports_loaded()validate_form() → _ensure_forms_loaded()This means a report-only request never loads form ACLs.
| Method | Scope |
|---|---|
invalidate_acl(package) |
All ACL keys for a package |
invalidate_report_acl(package) |
Report cache only |
invalidate_form_acl(package) |
Form cache only |
invalidate_user_sessions(loginname) |
User's session + permission caches |
validate_users(force=1) |
Full rebuild from DB |
| Method | Purpose |
|---|---|
login_user() |
Password authentication |
login_session() |
Session validation (Redis fast path) |
login_federated_user() |
SSO authentication |
logout_user() |
Session termination |
validate_report() |
Check report permission |
validate_form() |
Check form permission |
validate_users() |
Build user/group/company/level maps |
validate_reports() |
Build report ACL map |
validate_forms() |
Build form ACL map |
validate_documents() |
Build document ACL map |
set_user() |
Set active user context |
create_report_list() |
Get reports accessible by user |
create_form_list() |
Get forms accessible by user |
get_cached_acl() |
Load session state from Redis |
set_cached_acl() |
Cache session state to Redis |
_restore_acl_state() |
Restore from cached state |
_ensure_reports_loaded() |
Lazy load reports |
_ensure_forms_loaded() |
Lazy load forms |
_cache_per_user_reports() |
Build user→reports reverse index |
_cache_per_user_forms() |
Build user→forms reverse index |
| Table | Purpose |
|---|---|
sys_user |
User accounts (managed by ObjPerson.yaml) |
sys_user_session |
Active sessions |
def_report |
Report definitions with ACL groups |
def_form |
Form definitions with ACL groups |
def_document |
Document definitions with ACL groups |
ACL constants in ObjConstants.AclConstants:
ACL_CACHE_TTL_SECS = 900 # 15 minutes
ACL_CACHE_PREFIX = "acl:"
ACL_SESSION_PREFIX = "acl:session:"
ACL_USER_PREFIX = "acl:user:"
ACL_REPORTS_PREFIX = "acl:reports:"
ACL_FORMS_PREFIX = "acl:forms:"
SESSION_SKELETON_KEY — hardcoded debug sessionANONYMOUS_USER — default unauthenticated userObjUser — user record managementObjPerson — person/user linking, sys_user schemaObjKeycloakResilient — Keycloak SSO syncWebServer — ACL initialization per requestObjConstants.AclConstants — cache TTL and key prefixescythonize -3 -a -i ObjAcl.py