load_roleperms() was setting self.rp_caches = {} before the async
DB query. During the await, other coroutines saw {} (not None),
skipped the load, and checked permissions against an empty dict,
causing intermittent 403 on random paths.
Fix: build in local dict first, assign atomically when complete.
load_path.py scripts across modules register paths like '/module/api/%'
using SQL LIKE wildcard, but check_roles_path() only recognized '**' as
wildcard suffix. This caused all %-terminated paths to be treated as
exact matches, resulting in 403 for any sub-path.
Now both '/module/api/%' and '/module/api/**' work as prefix wildcards.
When module_cache.rbac=false in config.json, LRUCache.get() always returns
None and LRUCache.set() is a no-op. This caused get_userroles() to store
roles into a disabled cache, then callers read back None, leading to
TypeError in check_roles_path() when iterating over None.
Fix: get_userroles() now returns the roles list directly. Callers use the
return value instead of relying solely on cache reads. Added safety
fallback to deny access if roles is somehow still None.
- login.ui: use actiontype:urlwidget for Form submit → code_login.dspy
- Removed custom JavaScript (phone_login.js)
- Send code button: minimal script action to fetch and set form value
- Login flow: bricks returns Message/Error/VBox widgets directly
- Multi-account selection: code_login.dspy returns VBox with Buttons
The _webbricks_=1 parameter was causing the response to be wrapped as a widget instead of plain JSON, which prevented the frontend from correctly parsing the response and setting the codeid field.
Now the API returns plain JSON {status: 'ok', data: {key: '...'}} and the form's hidden codeid field gets properly set after successful SMS generation.
The send verification code button was sending requests to https://token.opencomputing.cn/undefined/rbac/gen_sms_code.dspy because bricks.app.baseUrl doesn't exist in the bricks framework (it uses baseURI on widgets).
Fix: Use Jinja2 entire_url() template function like all other URLs in the file.
- Fixed syntax errors in userperm.py __init__ (removed broken 'this' reference
and incomplete method definition)
- Added 7 production-grade event handlers on UserPermissions:
- on_user_create/update/delete: invalidate specific user cache
- on_rolepermission_change: invalidate role-permission cache
- on_permission_change: invalidate role-permission cache
- on_role_change: invalidate ALL user + role-permission caches
- on_userrole_change: invalidate specific user cache by userid
- Added _bind_rbac_events() in init.py with 13 event bindings covering:
users C/U/D, rolepermission C/U/D, permission U, role C/U/D, userrole C/U/D
- All handlers have try/except error isolation to prevent one failure
from breaking other handlers
- Events auto-dispatched by sqlor after C/U/D operations (no service restart needed)
- Cleaned up unused imports (DBPools, exception)