# RBAC Quick Reference Card **For Daily Development** | Keep this handy while coding --- ## Common Imports ```python # Authentication dependencies from app.api.deps import ( get_current_admin_from_cookie_or_header, get_current_store_from_cookie_or_header, require_store_permission, require_store_owner, get_user_permissions ) # Permission constants from app.core.permissions import StorePermissions # Exceptions from app.exceptions import ( InsufficientStorePermissionsException, StoreOwnerOnlyException ) # Services from app.services.store_team_service import store_team_service ``` --- ## Route Patterns ### Admin Route (Cookie OR Header) ```python @router.get("/admin/stores") def list_stores( user: User = Depends(get_current_admin_from_cookie_or_header) ): # user is authenticated admin ... ``` ### Admin API (Header Only) ```python @router.post("/api/v1/admin/stores") def create_store( user: User = Depends(get_current_admin_api) ): # user is authenticated admin (header required) ... ``` ### Store Route with Permission ```python @router.post("/store/{code}/products") def create_product( user: User = Depends(require_store_permission( StorePermissions.PRODUCTS_CREATE.value )) ): # user has products.create permission store = request.state.store ... ``` ### Owner-Only Route ```python @router.post("/store/{code}/team/invite") def invite_member( user: User = Depends(require_store_owner) ): # user is store owner store = request.state.store ... ``` ### Multi-Permission Route ```python @router.post("/store/{code}/products/bulk") def bulk_operation( user: User = Depends(require_all_store_permissions( StorePermissions.PRODUCTS_VIEW.value, StorePermissions.PRODUCTS_EDIT.value )) ): # user has ALL specified permissions ... ``` --- ## Permission Constants ### Quick Lookup ```python # Dashboard StorePermissions.DASHBOARD_VIEW # Products StorePermissions.PRODUCTS_VIEW StorePermissions.PRODUCTS_CREATE StorePermissions.PRODUCTS_EDIT StorePermissions.PRODUCTS_DELETE StorePermissions.PRODUCTS_IMPORT StorePermissions.PRODUCTS_EXPORT # Stock StorePermissions.STOCK_VIEW StorePermissions.STOCK_EDIT StorePermissions.STOCK_TRANSFER # Orders StorePermissions.ORDERS_VIEW StorePermissions.ORDERS_EDIT StorePermissions.ORDERS_CANCEL StorePermissions.ORDERS_REFUND # Customers StorePermissions.CUSTOMERS_VIEW StorePermissions.CUSTOMERS_EDIT StorePermissions.CUSTOMERS_DELETE StorePermissions.CUSTOMERS_EXPORT # Marketing StorePermissions.MARKETING_VIEW StorePermissions.MARKETING_CREATE StorePermissions.MARKETING_SEND # Reports StorePermissions.REPORTS_VIEW StorePermissions.REPORTS_FINANCIAL StorePermissions.REPORTS_EXPORT # Settings StorePermissions.SETTINGS_VIEW StorePermissions.SETTINGS_EDIT StorePermissions.SETTINGS_THEME StorePermissions.SETTINGS_DOMAINS # Team StorePermissions.TEAM_VIEW StorePermissions.TEAM_INVITE StorePermissions.TEAM_EDIT StorePermissions.TEAM_REMOVE # Imports StorePermissions.IMPORTS_VIEW StorePermissions.IMPORTS_CREATE StorePermissions.IMPORTS_CANCEL ``` --- ## User Helper Methods ```python # Check if admin user.is_admin # bool # Check if store user.is_store # bool # Check store ownership 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 # Check specific permission user.has_store_permission(store_id, "products.create") # bool ``` --- ## StoreUser Helper Methods ```python # Check if owner store_user.is_owner # bool # Check if team member store_user.is_team_member # bool # Check invitation status store_user.is_invitation_pending # bool # Check permission store_user.has_permission("products.create") # bool # Get all permissions store_user.get_all_permissions() # list[str] ``` --- ## Service Methods ### Team Management ```python # Invite team member store_team_service.invite_team_member( db=db, store=store, inviter=current_user, email="member@example.com", role_name="Staff", custom_permissions=None # Optional ) # Accept invitation store_team_service.accept_invitation( db=db, invitation_token=token, password="password123", first_name="John", last_name="Doe" ) # Remove team member store_team_service.remove_team_member( db=db, store=store, user_id=member_id ) # Update member role store_team_service.update_member_role( db=db, store=store, user_id=member_id, new_role_name="Manager", custom_permissions=None ) # Get team members members = store_team_service.get_team_members( db=db, store=store, include_inactive=False ) ``` --- ## Exception Handling ```python from app.exceptions import ( InsufficientStorePermissionsException, StoreOwnerOnlyException, StoreAccessDeniedException, InvalidInvitationTokenException, CannotRemoveStoreOwnerException, TeamMemberAlreadyExistsException ) # Raise permission error raise InsufficientStorePermissionsException( required_permission="products.create", store_code=store.store_code ) # Raise owner-only error raise StoreOwnerOnlyException( operation="team management", store_code=store.store_code ) # Raise access denied raise StoreAccessDeniedException( store_code=store.store_code, user_id=user.id ) ``` --- ## Frontend Permission Checks ### JavaScript/Alpine.js ```javascript // Check permission function hasPermission(permission) { const permissions = JSON.parse( localStorage.getItem('permissions') || '[]' ); return permissions.includes(permission); } // Conditional rendering {hasPermission('products.create') && ( )} // Disable button // Get permissions on login async function getPermissions() { const response = await fetch( '/api/v1/store/team/me/permissions', { headers: { 'Authorization': `Bearer ${token}` } } ); const data = await response.json(); localStorage.setItem( 'permissions', JSON.stringify(data.permissions) ); } ``` --- ## Testing Patterns ### Unit Test ```python def test_owner_has_all_permissions(): store_user = create_store_user(user_type="owner") assert store_user.has_permission("products.create") assert store_user.has_permission("team.invite") ``` ### Integration Test ```python def test_create_product_with_permission(client): user = create_user_with_permission("products.create") token = create_token(user) response = client.post( "/api/v1/store/ACME/products", json={"name": "Test"}, headers={"Authorization": f"Bearer {token}"} ) assert response.status_code == 201 ``` --- ## Common Mistakes to Avoid ### ❌ DON'T: Check permissions in service layer ```python # BAD def create_product(user, data): if not user.has_permission("products.create"): raise Exception() ``` ### ✅ DO: Check permissions at route level ```python # GOOD @router.post("/products") def create_product( user: User = Depends(require_store_permission("products.create")) ): return service.create_product(data) ``` --- ### ❌ DON'T: Use magic strings ```python # BAD require_store_permission("products.creat") # Typo! ``` ### ✅ DO: Use constants ```python # GOOD require_store_permission(StorePermissions.PRODUCTS_CREATE.value) ``` --- ### ❌ DON'T: Mix contexts ```python # BAD - Admin trying to access store route # This will be blocked automatically ``` ### ✅ DO: Use correct portal ```python # GOOD - Admins use /admin/*, stores use /store/* ``` --- ## Debugging Commands ### Check User Access ```python user = db.query(User).get(user_id) store = db.query(Store).get(store_id) print(f"Is owner: {user.is_owner_of(store.id)}") print(f"Is member: {user.is_member_of(store.id)}") print(f"Role: {user.get_store_role(store.id)}") print(f"Has products.create: {user.has_store_permission(store.id, 'products.create')}") ``` ### Decode JWT Token ```python import jwt 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"Expires: {decoded['exp']}") ``` ### Check Cookie ```javascript // In browser console document.cookie.split(';').forEach(c => console.log(c.trim())); ``` --- ## Role Presets | Role | Typical Permissions | |------|---------------------| | **Owner** | ALL (automatic) | | **Manager** | Most operations, no team management | | **Staff** | Products, orders, customers (CRUD) | | **Support** | Orders, customers (support focus) | | **Viewer** | Read-only access | | **Marketing** | Customers, marketing, reports | --- ## File Locations ``` app/ ├── api/ │ ├── deps.py ← All auth dependencies │ └── v1/ │ ├── admin/ │ │ └── auth.py ← Admin login │ ├── store/ │ │ ├── auth.py ← Store login │ │ └── team.py ← Team management │ └── public/ │ └── stores/auth.py ← Customer login │ ├── core/ │ └── permissions.py ← Permission constants │ ├── exceptions/ │ ├── admin.py │ ├── store.py │ └── auth.py │ ├── services/ │ ├── auth_service.py │ └── store_team_service.py ← Team management │ └── models/ └── database/ ├── user.py ← User model ├── store.py ← Store, StoreUser, Role └── customer.py ← Customer model ``` --- ## Status Codes | Code | Meaning | Common Cause | |------|---------|--------------| | 200 | OK | Success | | 201 | Created | Resource created | | 401 | Unauthorized | No/invalid token | | 403 | Forbidden | No permission | | 404 | Not Found | Resource not found | | 422 | Validation Error | Invalid input | --- ## Environment Variables ```bash JWT_SECRET_KEY=your-secret-key JWT_ALGORITHM=HS256 JWT_EXPIRATION=3600 # seconds (1 hour) ENVIRONMENT=development|staging|production ``` --- **Print and keep at your desk!** For full documentation: See [RBAC Developer Guide](../api/rbac.md)