feat: RBAC Phase 1 — consolidate user roles into 4-value enum
Some checks failed
CI / ruff (push) Successful in 11s
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / pytest (push) Has been cancelled

Consolidate User.role (2-value: admin/store) + User.is_super_admin (boolean)
into a single 4-value UserRole enum: super_admin, platform_admin,
merchant_owner, store_member. Drop stale StoreUser.user_type column.
Fix role="user" bug in merchant creation.

Key changes:
- Expand UserRole enum from 2 to 4 values with computed properties
  (is_admin, is_super_admin, is_platform_admin, is_merchant_owner, is_store_user)
- Add Alembic migration (tenancy_003) for data migration + column drops
- Remove is_super_admin from JWT token payload
- Update all auth dependencies, services, routes, templates, JS, and tests
- Update all RBAC documentation

66 files changed, 1219 unit tests passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 22:44:29 +01:00
parent ef21d47533
commit 1dcb0e6c33
67 changed files with 874 additions and 616 deletions

View File

@@ -155,23 +155,36 @@ StorePermissions.IMPORTS_CANCEL
---
## User Role Properties
```python
# Check if super admin (computed: role == "super_admin")
user.is_super_admin # bool
# Check if any admin (computed: role in ("super_admin", "platform_admin"))
user.is_admin # bool
# Check if platform admin (computed: role == "platform_admin")
user.is_platform_admin # bool
# Check if merchant owner (computed: role == "merchant_owner")
user.is_merchant_owner # bool
# Check if store-level user (computed: role in ("merchant_owner", "store_member"))
user.is_store_user # bool
```
## User Helper Methods
```python
# Check if admin
user.is_admin # bool
# Check if store
user.is_store # bool
# Check store ownership
# Check store ownership (via Merchant.owner_user_id)
user.is_owner_of(store_id) # bool
# Check store membership
user.is_member_of(store_id) # bool
# Get role in store
user.get_store_role(store_id) # str: "owner" | "member" | None
user.get_store_role(store_id) # str: "owner" | role name | None
# Check specific permission
user.has_store_permission(store_id, "products.create") # bool
@@ -182,7 +195,7 @@ user.has_store_permission(store_id, "products.create") # bool
## StoreUser Helper Methods
```python
# Check if owner
# Check if owner (derived from User.role and Merchant.owner_user_id)
store_user.is_owner # bool
# Check if team member
@@ -331,7 +344,8 @@ async function getPermissions() {
### Unit Test
```python
def test_owner_has_all_permissions():
store_user = create_store_user(user_type="owner")
user = create_user(role="merchant_owner")
store_user = create_store_user(user=user, store=store)
assert store_user.has_permission("products.create")
assert store_user.has_permission("team.invite")
```
@@ -423,8 +437,9 @@ token = "eyJ0eXAi..."
decoded = jwt.decode(token, verify=False)
print(f"User ID: {decoded['sub']}")
print(f"Username: {decoded['username']}")
print(f"Role: {decoded['role']}")
print(f"Role: {decoded['role']}") # super_admin, platform_admin, merchant_owner, or store_member
print(f"Expires: {decoded['exp']}")
# Note: is_super_admin is no longer in JWT tokens; derive from role == "super_admin"
```
### Check Cookie