# Store RBAC System - Complete Guide ## Overview The store dashboard implements a **Role-Based Access Control (RBAC)** system that distinguishes between **Owners** and **Team Members**, with granular permissions for team members. --- ## User Types ### 1. Store Owner **Who:** The user who created the store account. **Characteristics:** - Has **ALL permissions** automatically (no role needed) - Cannot be removed or have permissions restricted - Can invite team members - Can create and manage roles - Identified by `StoreUser.user_type = "owner"` - Linked via `Store.owner_user_id → User.id` **Database:** ```python # StoreUser record for owner { "store_id": 1, "user_id": 5, "user_type": "owner", # ✓ Owner "role_id": None, # No role needed "is_active": True } ``` **Permissions:** - ✅ **All 75 permissions** (complete access) - See full list below --- ### 2. Team Members **Who:** Users invited by the store owner to help manage the store. **Characteristics:** - Have **limited permissions** based on assigned role - Must be invited via email - Invitation must be accepted before activation - Can be assigned one of the pre-defined roles or custom role - Identified by `StoreUser.user_type = "member"` - Permissions come from `StoreUser.role_id → Role.permissions` **Database:** ```python # StoreUser record for team member { "store_id": 1, "user_id": 7, "user_type": "member", # ✓ Team member "role_id": 3, # ✓ Role required "is_active": True, "invitation_token": None, # Accepted "invitation_accepted_at": "2024-11-15 10:30:00" } # Role record { "id": 3, "store_id": 1, "name": "Manager", "permissions": [ "dashboard.view", "products.view", "products.create", "products.edit", "orders.view", ... ] } ``` **Permissions:** - 🔒 **Limited** based on assigned role - Can have between 0 and 75 permissions - Common roles: Manager, Staff, Support, Viewer, Marketing --- ## Permission System ### All Available Permissions (75 total) ```python class StorePermissions(str, Enum): # Dashboard (1) DASHBOARD_VIEW = "dashboard.view" # Products (6) PRODUCTS_VIEW = "products.view" PRODUCTS_CREATE = "products.create" PRODUCTS_EDIT = "products.edit" PRODUCTS_DELETE = "products.delete" PRODUCTS_IMPORT = "products.import" PRODUCTS_EXPORT = "products.export" # Stock/Inventory (3) STOCK_VIEW = "stock.view" STOCK_EDIT = "stock.edit" STOCK_TRANSFER = "stock.transfer" # Orders (4) ORDERS_VIEW = "orders.view" ORDERS_EDIT = "orders.edit" ORDERS_CANCEL = "orders.cancel" ORDERS_REFUND = "orders.refund" # Customers (4) CUSTOMERS_VIEW = "customers.view" CUSTOMERS_EDIT = "customers.edit" CUSTOMERS_DELETE = "customers.delete" CUSTOMERS_EXPORT = "customers.export" # Marketing (3) MARKETING_VIEW = "marketing.view" MARKETING_CREATE = "marketing.create" MARKETING_SEND = "marketing.send" # Reports (3) REPORTS_VIEW = "reports.view" REPORTS_FINANCIAL = "reports.financial" REPORTS_EXPORT = "reports.export" # Settings (4) SETTINGS_VIEW = "settings.view" SETTINGS_EDIT = "settings.edit" SETTINGS_THEME = "settings.theme" SETTINGS_DOMAINS = "settings.domains" # Team Management (4) TEAM_VIEW = "team.view" TEAM_INVITE = "team.invite" TEAM_EDIT = "team.edit" TEAM_REMOVE = "team.remove" # Marketplace Imports (3) IMPORTS_VIEW = "imports.view" IMPORTS_CREATE = "imports.create" IMPORTS_CANCEL = "imports.cancel" ``` --- ## Pre-Defined Roles ### 1. Owner (All 75 permissions) **Use case:** Store owner (automatically assigned) - ✅ Full access to everything - ✅ Cannot be restricted - ✅ No role record needed (permissions checked differently) --- ### 2. Manager (43 permissions) **Use case:** Senior staff who manage most operations **Has access to:** - ✅ Dashboard, Products (all), Stock (all) - ✅ Orders (all), Customers (view, edit, export) - ✅ Marketing (all), Reports (all including financial) - ✅ Settings (view, theme) - ✅ Imports (all) **Does NOT have:** - ❌ `customers.delete` - Cannot delete customers - ❌ `settings.edit` - Cannot change core settings - ❌ `settings.domains` - Cannot manage domains - ❌ `team.*` - Cannot manage team members --- ### 3. Staff (10 permissions) **Use case:** Daily operations staff **Has access to:** - ✅ Dashboard view - ✅ Products (view, create, edit) - ✅ Stock (view, edit) - ✅ Orders (view, edit) - ✅ Customers (view, edit) **Does NOT have:** - ❌ Delete anything - ❌ Import/export - ❌ Marketing - ❌ Financial reports - ❌ Settings - ❌ Team management --- ### 4. Support (6 permissions) **Use case:** Customer support team **Has access to:** - ✅ Dashboard view - ✅ Products (view only) - ✅ Orders (view, edit) - ✅ Customers (view, edit) **Does NOT have:** - ❌ Create/delete products - ❌ Stock management - ❌ Marketing - ❌ Reports - ❌ Settings - ❌ Team management --- ### 5. Viewer (6 permissions) **Use case:** Read-only access for reporting/audit **Has access to:** - ✅ Dashboard (view) - ✅ Products (view) - ✅ Stock (view) - ✅ Orders (view) - ✅ Customers (view) - ✅ Reports (view) **Does NOT have:** - ❌ Edit anything - ❌ Create/delete anything - ❌ Marketing - ❌ Financial reports - ❌ Settings - ❌ Team management --- ### 6. Marketing (7 permissions) **Use case:** Marketing team focused on campaigns **Has access to:** - ✅ Dashboard (view) - ✅ Customers (view, export) - ✅ Marketing (all) - ✅ Reports (view) **Does NOT have:** - ❌ Products management - ❌ Orders management - ❌ Stock management - ❌ Financial reports - ❌ Settings - ❌ Team management --- ## Permission Checking Logic ### How Permissions Are Checked ```python # In User model (models/database/user.py) def has_store_permission(self, store_id: int, permission: str) -> bool: """Check if user has a specific permission in a store.""" # Step 1: Check if user is owner if self.is_owner_of(store_id): return True # ✅ Owners have ALL permissions # Step 2: Check team member permissions for vm in self.store_memberships: if vm.store_id == store_id and vm.is_active: if vm.role and permission in vm.role.permissions: return True # ✅ Permission found in role # No permission found return False ``` ### Permission Checking Flow ``` Request → Middleware → Extract store from URL ↓ Check user authentication ↓ Check if user is owner ├── YES → ✅ Allow (all permissions) └── NO ↓ Check if user is team member ├── NO → ❌ Deny └── YES ↓ Check if membership is active ├── NO → ❌ Deny └── YES ↓ Check if role has required permission ├── NO → ❌ Deny (403 Forbidden) └── YES → ✅ Allow ``` --- ## Using Permissions in Code ### 1. Require Specific Permission **When to use:** Endpoint needs one specific permission ```python from fastapi import APIRouter, Depends from app.api.deps import require_store_permission from app.core.permissions import StorePermissions from models.database.user import User router = APIRouter() @router.post("/products") def create_product( product_data: ProductCreate, user: User = Depends( require_store_permission(StorePermissions.PRODUCTS_CREATE.value) ) ): """ Create a product. Required permission: products.create ✅ Owner: Always allowed ✅ Manager: Allowed (has products.create) ✅ Staff: Allowed (has products.create) ❌ Support: Denied (no products.create) ❌ Viewer: Denied (no products.create) ❌ Marketing: Denied (no products.create) """ # Create product... pass ``` --- ### 2. Require ANY Permission **When to use:** Endpoint can be accessed with any of several permissions ```python @router.get("/dashboard") def view_dashboard( user: User = Depends( require_any_store_permission( StorePermissions.DASHBOARD_VIEW.value, StorePermissions.REPORTS_VIEW.value ) ) ): """ View dashboard. Required: dashboard.view OR reports.view ✅ Owner: Always allowed ✅ Manager: Allowed (has both) ✅ Staff: Allowed (has dashboard.view) ✅ Support: Allowed (has dashboard.view) ✅ Viewer: Allowed (has both) ✅ Marketing: Allowed (has both) """ # Show dashboard... pass ``` --- ### 3. Require ALL Permissions **When to use:** Endpoint needs multiple permissions ```python @router.post("/products/bulk-delete") def bulk_delete_products( user: User = Depends( require_all_store_permissions( StorePermissions.PRODUCTS_VIEW.value, StorePermissions.PRODUCTS_DELETE.value ) ) ): """ Bulk delete products. Required: products.view AND products.delete ✅ Owner: Always allowed ✅ Manager: Allowed (has both) ❌ Staff: Denied (no products.delete) ❌ Support: Denied (no products.delete) ❌ Viewer: Denied (no products.delete) ❌ Marketing: Denied (no products.delete) """ # Delete products... pass ``` --- ### 4. Require Owner Only **When to use:** Endpoint is owner-only (team management, critical settings) ```python from app.api.deps import require_store_owner @router.post("/team/invite") def invite_team_member( email: str, role_id: int, user: User = Depends(require_store_owner) ): """ Invite a team member. Required: Must be store owner ✅ Owner: Allowed ❌ Manager: Denied (not owner) ❌ All team members: Denied (not owner) """ # Invite team member... pass ``` --- ### 5. Get User Permissions **When to use:** Need to check permissions in business logic ```python from app.api.deps import get_user_permissions @router.get("/my-permissions") def list_my_permissions( permissions: list = Depends(get_user_permissions) ): """ Get all permissions for current user. Returns: - Owner: All 75 permissions - Team Member: Permissions from their role """ return {"permissions": permissions} ``` --- ## Database Schema ### StoreUser Table ```sql CREATE TABLE store_users ( id SERIAL PRIMARY KEY, store_id INTEGER NOT NULL REFERENCES stores(id), user_id INTEGER NOT NULL REFERENCES users(id), user_type VARCHAR NOT NULL, -- 'owner' or 'member' role_id INTEGER REFERENCES roles(id), -- NULL for owners invited_by INTEGER REFERENCES users(id), invitation_token VARCHAR, invitation_sent_at TIMESTAMP, invitation_accepted_at TIMESTAMP, is_active BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); ``` ### Role Table ```sql CREATE TABLE roles ( id SERIAL PRIMARY KEY, store_id INTEGER NOT NULL REFERENCES stores(id), name VARCHAR(100) NOT NULL, permissions JSON DEFAULT '[]', -- Array of permission strings created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); ``` --- ## Team Member Lifecycle ### 1. Invitation ``` Owner invites user → StoreUser created: { "user_type": "member", "is_active": False, "invitation_token": "abc123...", "invitation_sent_at": "2024-11-29 10:00:00", "invitation_accepted_at": null } ``` ### 2. Acceptance ``` User accepts invitation → StoreUser updated: { "is_active": True, "invitation_token": null, "invitation_accepted_at": "2024-11-29 10:30:00" } ``` ### 3. Active Member ``` Member can now access store dashboard with role permissions ``` ### 4. Deactivation ``` Owner deactivates member → StoreUser updated: { "is_active": False } ``` --- ## Common Use Cases ### Use Case 1: Dashboard Access **Q:** Can all users access the dashboard? **A:** Yes, if they have `dashboard.view` permission. - ✅ Owner: Always - ✅ Manager, Staff, Support, Viewer, Marketing: All have it - ❌ Custom role without `dashboard.view`: No --- ### Use Case 2: Product Management **Q:** Who can create products? **A:** Users with `products.create` permission. - ✅ Owner: Always - ✅ Manager: Yes (has permission) - ✅ Staff: Yes (has permission) - ❌ Support, Viewer, Marketing: No --- ### Use Case 3: Financial Reports **Q:** Who can view financial reports? **A:** Users with `reports.financial` permission. - ✅ Owner: Always - ✅ Manager: Yes (has permission) - ❌ Staff, Support, Viewer, Marketing: No --- ### Use Case 4: Team Management **Q:** Who can invite team members? **A:** Only the store owner. - ✅ Owner: Yes (owner-only operation) - ❌ All team members (including Manager): No --- ### Use Case 5: Settings Changes **Q:** Who can change store settings? **A:** Users with `settings.edit` permission. - ✅ Owner: Always - ❌ Manager: No (doesn't have permission) - ❌ All other roles: No --- ## Error Responses ### Missing Permission ```http HTTP 403 Forbidden { "error_code": "INSUFFICIENT_STORE_PERMISSIONS", "message": "You don't have permission to perform this action", "details": { "required_permission": "products.delete", "store_code": "orion" } } ``` ### Not Owner ```http HTTP 403 Forbidden { "error_code": "STORE_OWNER_ONLY", "message": "This operation requires store owner privileges", "details": { "operation": "team management", "store_code": "orion" } } ``` ### Inactive Membership ```http HTTP 403 Forbidden { "error_code": "INACTIVE_STORE_MEMBERSHIP", "message": "Your store membership is inactive" } ``` --- ## Summary ### Owner vs Team Member | Feature | Owner | Team Member | |---------|-------|-------------| | **Permissions** | All 75 (automatic) | Based on role (0-75) | | **Role Required** | No | Yes | | **Can Be Removed** | No | Yes | | **Team Management** | ✅ Yes | ❌ No | | **Critical Settings** | ✅ Yes | ❌ No (usually) | | **Invitation Required** | No (creates store) | Yes | ### Permission Hierarchy ``` Owner (75 permissions) └─ Manager (43 permissions) └─ Staff (10 permissions) └─ Support (6 permissions) └─ Viewer (6 permissions, read-only) Marketing (7 permissions, specialized) ``` ### Best Practices 1. **Use Constants:** Always use `StorePermissions.PERMISSION_NAME.value` 2. **Least Privilege:** Give team members minimum permissions needed 3. **Owner Only:** Keep sensitive operations owner-only 4. **Custom Roles:** Create custom roles for specific needs 5. **Regular Audit:** Review team member permissions regularly --- This RBAC system provides flexible, secure access control for store dashboards with clear separation between owners and team members.