feat: implement complete RBAC access control with tests
Add 4-layer access control stack (subscription → module → menu → permissions): - P1: Wire requires_permission into menu sidebar filtering - P2: Expose window.USER_PERMISSIONS for Alpine.js client-side gating - P3: Add page-level permission guards on store routes - P4: Role CRUD API endpoints and role editor UI - P5: Audit trail for all role/permission changes Includes unit tests (menu permission filtering, role CRUD service) and integration tests (role API endpoints). All 404 core+tenancy tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -290,18 +290,61 @@ def get_store_context(
|
||||
**extra_context: Additional variables for template
|
||||
|
||||
Returns:
|
||||
Context dict for store pages
|
||||
Context dict for store pages (includes user_permissions list)
|
||||
"""
|
||||
# Resolve user permissions for the current store
|
||||
user_permissions = _resolve_store_user_permissions(db, current_user)
|
||||
|
||||
return get_context_for_frontend(
|
||||
FrontendType.STORE,
|
||||
request,
|
||||
db,
|
||||
user=current_user,
|
||||
store_code=store_code,
|
||||
user_permissions=user_permissions,
|
||||
**extra_context,
|
||||
)
|
||||
|
||||
|
||||
def _resolve_store_user_permissions(db: Session, current_user: Any) -> list[str]:
|
||||
"""
|
||||
Resolve the permission list for a store user.
|
||||
|
||||
Returns all permission IDs for the current user in their current store.
|
||||
Owners get all permissions. Members get permissions from their role.
|
||||
"""
|
||||
from app.modules.tenancy.models import User
|
||||
|
||||
store_id = getattr(current_user, "token_store_id", None)
|
||||
if not store_id:
|
||||
return []
|
||||
|
||||
user_id = getattr(current_user, "user_id", None) or getattr(
|
||||
current_user, "id", None
|
||||
)
|
||||
if not user_id:
|
||||
return []
|
||||
|
||||
user_model = db.query(User).filter(User.id == user_id).first()
|
||||
if not user_model:
|
||||
return []
|
||||
|
||||
# Owners get all permissions
|
||||
if user_model.is_owner_of(store_id):
|
||||
from app.modules.tenancy.services.permission_discovery_service import (
|
||||
permission_discovery_service,
|
||||
)
|
||||
|
||||
return list(permission_discovery_service.get_all_permission_ids())
|
||||
|
||||
# Members get permissions from their store role
|
||||
for membership in user_model.store_memberships:
|
||||
if membership.store_id == store_id and membership.is_active:
|
||||
return membership.get_all_permissions()
|
||||
|
||||
return []
|
||||
|
||||
|
||||
def get_storefront_context(
|
||||
request: Request,
|
||||
db: Session | None = None,
|
||||
|
||||
Reference in New Issue
Block a user