RBAC documentation

This commit is contained in:
2025-11-15 20:59:22 +01:00
parent e3ed4a3295
commit 424450b802
5 changed files with 4130 additions and 0 deletions

View File

@@ -0,0 +1,298 @@
# RBAC Implementation - Deliverables Index
## Overview
This package contains a complete RBAC (Role-Based Access Control) implementation for your multi-tenant e-commerce platform. All files are ready to be integrated into your codebase.
## 📚 Developer Documentation
### For Your Development Team
**[RBAC_DEVELOPER_GUIDE.md](RBAC_DEVELOPER_GUIDE.md)** ⭐ **Primary Documentation**
- Complete developer guide (800+ lines)
- Architecture overview and design principles
- Database schema with detailed explanations
- Permission system structure
- Authentication and authorization flows
- Team management workflows
- Extensive code examples for all scenarios
- Best practices and patterns
- Comprehensive testing guidelines
- Troubleshooting section
- **Give this to your developers** - Everything they need to know
**[RBAC_QUICK_REFERENCE.md](RBAC_QUICK_REFERENCE.md)** 📋 **Quick Reference**
- One-page reference for daily development
- Common imports and patterns
- Permission constants lookup
- Helper methods cheat sheet
- Frontend integration snippets
- Testing patterns
- Debugging commands
- **Print and keep at desk** - For quick lookups
### For Implementation Planning
**[RBAC_IMPLEMENTATION_SUMMARY.md](RBAC_IMPLEMENTATION_SUMMARY.md)**
- Executive summary and key recommendations
- Architecture overview and permission hierarchy
- Security best practices
- Common pitfalls to avoid
- Implementation checklist
- Q&A section
- **For planning** - Read this first for the big picture
**[RBAC_VISUAL_GUIDE.md](RBAC_VISUAL_GUIDE.md)**
- System architecture diagrams
- Team invitation flow
- Permission check flow
- Database relationship diagrams
- Permission naming conventions
- Role preset visualizations
- Security boundaries
- **For understanding** - Visual learners start here
## 🗄️ Database Models
### **user_model_improved.py**
Updated User model with:
- Clarified role field (admin/vendor only)
- Email verification field
- Helper methods for permission checking
- Owner/member checking methods
- Vendor-specific role retrieval
**Where to integrate:** Replace/update your `models/database/user.py`
### **vendor_user_improved.py**
Enhanced VendorUser model with:
- `user_type` field (owner/member distinction)
- Invitation system fields
- Permission checking methods
- Owner identification
**Where to integrate:** Update your `models/database/vendor.py` (VendorUser class)
## 🔐 Permission System
### **permissions.py**
Complete permission system including:
- `VendorPermissions` enum (all available permissions)
- `PermissionGroups` class (preset role configurations)
- `PermissionChecker` utility class
- Helper functions
**Where to integrate:** Create as `app/core/permissions.py`
## ⚠️ Exception Handling
### **vendor_exceptions.py**
Vendor-specific exceptions:
- `VendorAccessDeniedException`
- `InsufficientVendorPermissionsException`
- `VendorOwnerOnlyException`
- `CannotRemoveVendorOwnerException`
- `TeamMemberAlreadyExistsException`
- `InvalidInvitationTokenException`
- And more...
**Where to integrate:** Add to your `app/exceptions/` directory
## 🛠️ Dependencies & Route Guards
### **deps_permissions.py**
FastAPI dependencies for permission checking:
- `require_vendor_permission(permission)`
- `require_vendor_owner()`
- `require_any_vendor_permission(*permissions)`
- `require_all_vendor_permissions(*permissions)`
- `get_user_permissions()`
**Where to integrate:** Add to your existing `app/api/deps.py`
## 🔧 Service Layer
### **vendor_team_service.py**
Complete team management service:
- `invite_team_member()` - Send team invitations
- `accept_invitation()` - Activate invited accounts
- `remove_team_member()` - Remove team members
- `update_member_role()` - Change member roles
- `get_team_members()` - List all team members
- Helper methods for token generation and role management
**Where to integrate:** Create as `app/services/vendor_team_service.py`
## 📡 API Routes
### **team_routes_example.py**
Complete team management API routes:
- GET `/team/members` - List team members
- POST `/team/invite` - Invite new member
- POST `/team/accept-invitation` - Accept invitation
- DELETE `/team/members/{user_id}` - Remove member
- PUT `/team/members/{user_id}/role` - Update member role
- GET `/team/me/permissions` - Get current user permissions
- Example product routes with permission checks
**Where to integrate:** Create as `app/api/v1/vendor/team.py`
## 🗃️ Database Migration
### **rbac_migration_guide.md**
Comprehensive migration guide:
- Schema changes required
- Alembic migration script
- Data migration steps (6 steps with SQL)
- Post-migration checklist
- Verification queries
- Rollback plan
**Use this:** Before deploying to production
## 📊 File Structure
```
your-project/
├── app/
│ ├── api/
│ │ ├── deps.py ← Add deps_permissions.py content
│ │ └── v1/
│ │ ├── admin/
│ │ └── vendor/
│ │ └── team.py ← Add team_routes_example.py
│ │
│ ├── core/
│ │ └── permissions.py ← Add permissions.py
│ │
│ ├── exceptions/
│ │ └── vendor.py ← Add vendor_exceptions.py content
│ │
│ └── services/
│ └── vendor_team_service.py ← Add vendor_team_service.py
└── models/
└── database/
├── user.py ← Update with user_model_improved.py
└── vendor.py ← Update VendorUser from vendor_user_improved.py
```
## 🚀 Quick Start Integration
### Step 1: Review & Plan (30 minutes)
1. Read `RBAC_IMPLEMENTATION_SUMMARY.md`
2. Review `RBAC_VISUAL_GUIDE.md` for architecture
3. Check your current codebase against recommendations
### Step 2: Database Migration (1-2 hours)
1. Follow `rbac_migration_guide.md`
2. Create Alembic migration
3. Test in development environment
4. Run migration
### Step 3: Integrate Models (1 hour)
1. Update `models/database/user.py` with improved User model
2. Update `models/database/vendor.py` with improved VendorUser
3. Test model loading
### Step 4: Add Permission System (30 minutes)
1. Create `app/core/permissions.py`
2. Import in your application
3. Test permission constants
### Step 5: Add Exceptions (15 minutes)
1. Add vendor exceptions to `app/exceptions/`
2. Import in relevant modules
### Step 6: Add Dependencies (30 minutes)
1. Add permission checking functions to `app/api/deps.py`
2. Test dependencies work
### Step 7: Add Service Layer (30 minutes)
1. Create `app/services/vendor_team_service.py`
2. Test service methods
### Step 8: Add Routes (30 minutes)
1. Create team management routes
2. Add permission checks to existing routes
3. Test all endpoints
### Step 9: Frontend Integration (2-4 hours)
1. Update login flow to fetch permissions
2. Add UI elements for team management
3. Show/hide features based on permissions
4. Create invitation acceptance page
### Step 10: Testing (2-3 hours)
1. Test all permission combinations
2. Test invitation flow
3. Test owner protections
4. Test admin blocking
5. Test multi-vendor access
## 📝 Implementation Notes
### Priority Changes (Must Do)
1. **User.role clarification** - Critical for security
2. **VendorUser.user_type** - Required for owner distinction
3. **Permission checking in routes** - Security requirement
4. **Invitation system** - Required for team management
### Optional Enhancements
1. **Custom permissions UI** - Allow owners to create custom roles
2. **Permission analytics** - Track permission usage
3. **Team activity logs** - Audit trail for team actions
4. **Email templates** - Professional invitation emails
## 🆘 Support & Questions
### Common Issues
**Q: Migration fails?**
A: Check the verification queries in the migration guide. Likely data inconsistency.
**Q: Permission checking not working?**
A: Ensure middleware sets `request.state.vendor` correctly.
**Q: Owner can't access routes?**
A: Check that owner has `VendorUser` entry with `user_type='owner'`.
**Q: Invitation emails not sending?**
A: Implement `_send_invitation_email()` in service (marked as TODO).
### Next Steps for You
1. ✅ Review all documentation
2. ✅ Plan integration timeline
3. ✅ Set up development environment
4. ✅ Run database migration in dev
5. ✅ Integrate code changes
6. ✅ Test thoroughly
7. ✅ Deploy to staging
8. ✅ User acceptance testing
9. ✅ Deploy to production
## 📞 Your Questions Answered
Based on your original question:
**✅ Admin Creation:** Admins created by super admins on backend *(already correct)*
**✅ Vendor Owner Creation:** Auto-created when vendor is created *(implement in vendor creation logic)*
**✅ Team Member Invitation:** Email-based invitation system *(vendor_team_service.py provides this)*
**✅ Customer Registration:** Self-registration on shop *(separate Customer model is correct)*
**✅ Role-Based Access:** Full RBAC system *(permissions.py + dependencies)*
**✅ Multi-Tenant Isolation:** Vendor-scoped roles and permissions *(VendorUser + Role models)*
## 🎉 You're Ready!
You now have everything needed to implement a production-ready RBAC system. All code is written, tested patterns are provided, and comprehensive documentation is included.
Good luck with your implementation! 🚀

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,474 @@
# RBAC Implementation Recommendations - Summary
## Executive Summary
Your current authentication and authorization system is well-structured. Here are my recommendations to enhance it for proper role-based access control (RBAC) in your multi-tenant e-commerce platform.
## Key Recommendations
### 1. ✅ Clarify User.role Field
**Current Issue:** The `User.role` field is used for both platform-level and vendor-specific roles.
**Solution:**
- `User.role` should ONLY contain: `"admin"` or `"vendor"`
- Vendor-specific roles (manager, staff, etc.) belong in `VendorUser.role`
- Customers are separate in the `Customer` model (already correct)
**Benefits:**
- Clear separation of concerns
- Easier to manage platform-level access
- Prevents confusion between contexts
### 2. ✅ Add VendorUser.user_type Field
**Why:** Distinguish between vendor owners and team members at the database level.
**Implementation:**
```python
class VendorUserType(str, enum.Enum):
OWNER = "owner"
TEAM_MEMBER = "member"
```
**Benefits:**
- Owners have automatic full permissions
- Team members use role-based permissions
- Easy to query for owners vs. members
- Prevents accidentally removing owners
### 3. ✅ Implement Invitation System
**Components Needed:**
- `invitation_token` field in VendorUser
- `invitation_sent_at` and `invitation_accepted_at` timestamps
- Email sending service
- Invitation acceptance endpoint
**Flow:**
1. Owner invites team member via email
2. System creates User account (inactive) and VendorUser (pending)
3. Invitation email sent with unique token
4. Team member clicks link, sets password, activates account
5. VendorUser.is_active set to True
### 4. ✅ Define Permission Constants
**Create:** `app/core/permissions.py` with:
- `VendorPermissions` enum (all available permissions)
- `PermissionGroups` class (preset role permissions)
- `PermissionChecker` utility class
**Example Permissions:**
```python
PRODUCTS_VIEW = "products.view"
PRODUCTS_CREATE = "products.create"
PRODUCTS_DELETE = "products.delete"
ORDERS_VIEW = "orders.view"
TEAM_INVITE = "team.invite" # Owner only
SETTINGS_EDIT = "settings.edit" # Owner/Manager only
```
### 5. ✅ Add Permission Checking Dependencies
**Create FastAPI dependencies:**
- `require_vendor_permission(permission)` - Single permission
- `require_vendor_owner()` - Owner only
- `require_any_vendor_permission(*permissions)` - Any of list
- `require_all_vendor_permissions(*permissions)` - All of list
- `get_user_permissions()` - Get user's permission list
**Usage in Routes:**
```python
@router.post("/products")
def create_product(
user: User = Depends(require_vendor_permission("products.create"))
):
# User verified to have products.create permission
...
```
### 6. ✅ Create Vendor Team Service
**Service Responsibilities:**
- `invite_team_member()` - Send invitations
- `accept_invitation()` - Activate accounts
- `remove_team_member()` - Deactivate members
- `update_member_role()` - Change permissions
- `get_team_members()` - List team
**Why Service Layer:**
- Business logic separate from routes
- Reusable across different contexts
- Easier to test
- Consistent error handling
### 7. ✅ Add Helper Methods to Models
**User Model:**
```python
@property
def is_admin(self) -> bool
def is_owner_of(self, vendor_id: int) -> bool
def is_member_of(self, vendor_id: int) -> bool
def get_vendor_role(self, vendor_id: int) -> str
def has_vendor_permission(self, vendor_id: int, permission: str) -> bool
```
**VendorUser Model:**
```python
@property
def is_owner(self) -> bool
def has_permission(self, permission: str) -> bool
def get_all_permissions(self) -> list
```
### 8. ✅ Enhanced Exception Handling
**Add Vendor-Specific Exceptions:**
- `VendorAccessDeniedException` - No access to vendor
- `InsufficientVendorPermissionsException` - Missing permission
- `VendorOwnerOnlyException` - Owner-only operation
- `CannotRemoveVendorOwnerException` - Prevent owner removal
- `InvalidInvitationTokenException` - Bad invitation
- `MaxTeamMembersReachedException` - Team size limit
## Architecture Overview
```
┌─────────────────────────────────────────────────────────┐
│ Platform Level │
│ User.role: "admin" or "vendor" │
│ - Admins: Full platform access │
│ - Vendors: Access to their vendor(s) │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Vendor Level │
│ VendorUser.user_type: "owner" or "member" │
│ - Owners: Full vendor access (all permissions) │
│ - Members: Role-based access (limited permissions) │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Role Level │
│ Role.permissions: ["products.view", ...] │
│ - Presets: Manager, Staff, Support, Viewer, Marketing │
│ - Custom: Owner can define custom roles │
└─────────────────────────────────────────────────────────┘
```
## Permission Hierarchy
```
Owner (VendorUser.user_type = "owner")
└─> ALL permissions automatically
Team Member (VendorUser.user_type = "member")
└─> Role.permissions (from VendorUser.role_id)
├─> Manager: Most permissions except critical settings/team
├─> Staff: Products, orders, customers (read/write)
├─> Support: Orders, customers (support focus)
├─> Viewer: Read-only access
└─> Custom: Owner-defined permission sets
```
## Security Best Practices
### 1. **Cookie Path Isolation** (Already Implemented ✓)
- Admin: `/admin` path
- Vendor: `/vendor` path
- Customer: `/shop` path
- Prevents cross-context cookie leakage
### 2. **Role Checking at Route Level**
```python
# Good - Check at route definition
@router.post("/products")
def create_product(
user: User = Depends(require_vendor_permission("products.create"))
):
...
# Bad - Check inside handler
@router.post("/products")
def create_product(user: User):
if not has_permission(...): # Don't do this
raise Exception()
```
### 3. **Block Admin from Vendor Routes**
```python
# In vendor auth endpoints
if user.role == "admin":
raise InvalidCredentialsException(
"Admins cannot access vendor portal"
)
```
### 4. **Owner Cannot Be Removed**
```python
if vendor_user.is_owner:
raise CannotRemoveVendorOwnerException(vendor.vendor_code)
```
### 5. **Inactive Users Have No Permissions**
```python
if not user.is_active or not vendor_user.is_active:
return False # No permissions
```
## Frontend Integration
### 1. **Get User Permissions on Load**
```javascript
// On vendor dashboard load
const response = await fetch('/api/v1/vendor/team/me/permissions');
const { permissions } = await response.json();
// Store in state
localStorage.setItem('permissions', JSON.stringify(permissions));
```
### 2. **Show/Hide UI Elements**
```javascript
// Check permission before rendering
function canViewProducts() {
const permissions = JSON.parse(localStorage.getItem('permissions'));
return permissions.includes('products.view');
}
// In template
{canViewProducts() && <ProductsList />}
```
### 3. **Disable Actions Without Permission**
```javascript
<button
disabled={!permissions.includes('products.delete')}
onClick={handleDelete}
>
Delete Product
</button>
```
## Database Schema Summary
```
users
├── id (PK)
├── email (unique)
├── username (unique)
├── role ('admin' | 'vendor') ← Platform role only
├── is_active
└── is_email_verified ← New field
vendors
├── id (PK)
├── vendor_code (unique)
├── owner_user_id (FK → users.id)
└── ...
vendor_users (junction table with role info)
├── id (PK)
├── vendor_id (FK → vendors.id)
├── user_id (FK → users.id)
├── user_type ('owner' | 'member') ← New field
├── role_id (FK → roles.id, nullable) ← NULL for owners
├── invitation_token ← New field
├── invitation_sent_at ← New field
├── invitation_accepted_at ← New field
└── is_active
roles (vendor-specific)
├── id (PK)
├── vendor_id (FK → vendors.id)
├── name (e.g., 'Manager', 'Staff')
└── permissions (JSON: ["products.view", ...])
customers (separate, vendor-scoped)
├── id (PK)
├── vendor_id (FK → vendors.id)
├── email (unique within vendor)
└── ... (separate auth from User)
```
## Implementation Checklist
### Phase 1: Database & Models
- [ ] Add `is_email_verified` to User model
- [ ] Update User.role to only accept 'admin'/'vendor'
- [ ] Add `user_type` to VendorUser model
- [ ] Add invitation fields to VendorUser
- [ ] Make VendorUser.role_id nullable
- [ ] Create permissions.py with constants
- [ ] Add helper methods to User model
- [ ] Add helper methods to VendorUser model
- [ ] Run database migration
### Phase 2: Services & Logic
- [ ] Create vendor_team_service.py
- [ ] Implement invite_team_member()
- [ ] Implement accept_invitation()
- [ ] Implement remove_team_member()
- [ ] Implement update_member_role()
- [ ] Add vendor-specific exceptions
- [ ] Set up email sending for invitations
### Phase 3: API & Dependencies
- [ ] Add permission checking dependencies to deps.py
- [ ] Update vendor auth endpoint to block admins
- [ ] Create team management routes
- [ ] Add permission checks to existing routes
- [ ] Create invitation acceptance endpoint (public)
- [ ] Add /me/permissions endpoint
### Phase 4: Testing
- [ ] Test owner has all permissions
- [ ] Test team members respect role permissions
- [ ] Test invitation flow end-to-end
- [ ] Test cannot remove owner
- [ ] Test admin blocked from vendor routes
- [ ] Test permission checking in all routes
- [ ] Test inactive users have no access
### Phase 5: Frontend
- [ ] Load user permissions on login
- [ ] Show/hide UI based on permissions
- [ ] Implement invitation acceptance page
- [ ] Add team management UI (owner only)
- [ ] Add role selector when inviting
- [ ] Show permission lists for roles
## Migration Steps
1. **Backup database**
2. **Run Alembic migration** (see migration guide)
3. **Update User roles** (convert 'user' to 'vendor')
4. **Set vendor owners** in vendor_users
5. **Create default roles** for all vendors
6. **Assign roles** to existing team members
7. **Verify migration** with SQL queries
8. **Test system** thoroughly
9. **Deploy** to production
## Common Pitfalls to Avoid
### ❌ Don't: Mix Platform and Vendor Roles
```python
# Bad
user.role = "manager" # Vendor role in User table
```
```python
# Good
user.role = "vendor" # Platform role
vendor_user.role.name = "manager" # Vendor role
```
### ❌ Don't: Check Permissions in Business Logic
```python
# Bad
def create_product(user, product_data):
if not has_permission(user, "products.create"):
raise Exception()
# ... create product
```
```python
# Good
@router.post("/products")
def create_product(
product_data: ProductCreate,
user: User = Depends(require_vendor_permission("products.create"))
):
# Permission already checked by dependency
return service.create_product(user, product_data)
```
### ❌ Don't: Allow Owner Removal
```python
# Bad
def remove_team_member(vendor, user_id):
vendor_user = get_vendor_user(vendor, user_id)
vendor_user.is_active = False
```
```python
# Good
def remove_team_member(vendor, user_id):
vendor_user = get_vendor_user(vendor, user_id)
if vendor_user.is_owner:
raise CannotRemoveVendorOwnerException(vendor.vendor_code)
vendor_user.is_active = False
```
### ❌ Don't: Forget to Check is_active
```python
# Bad
def has_permission(user, vendor_id, permission):
return permission in user.get_vendor_role(vendor_id).permissions
```
```python
# Good
def has_permission(user, vendor_id, permission):
if not user.is_active:
return False
vendor_user = get_vendor_user(user, vendor_id)
if not vendor_user or not vendor_user.is_active:
return False
return vendor_user.has_permission(permission)
```
## Questions & Answers
**Q: Can a user be a team member of multiple vendors?**
A: Yes! A user can have multiple VendorUser entries, one per vendor. Each has independent role/permissions.
**Q: Can a vendor have multiple owners?**
A: No. Each vendor has one owner (Vendor.owner_user_id). The owner can delegate management via Manager role, but there's only one true owner.
**Q: What happens to team members when owner changes?**
A: Team members remain. Update Vendor.owner_user_id and both old/new owner's VendorUser.user_type.
**Q: Can admins access vendor dashboards?**
A: No. Admins are blocked from vendor routes. This is intentional security.
**Q: How do customers authenticate?**
A: Customers use the Customer model with separate authentication. They're vendor-scoped and can't access vendor/admin areas.
**Q: Can permissions be customized per team member?**
A: Yes! When inviting, pass `custom_permissions` array to override role presets.
**Q: How do invitation links work?**
A: Invitation token is a secure random string stored in VendorUser. Link format: `/vendor/invitation/accept?token=<token>`. Token is single-use and expires in 7 days.
## Next Steps
1. Review all generated files in `/home/claude/`
2. Integrate changes into your codebase
3. Create and run database migration
4. Update existing routes with permission checks
5. Implement team management UI
6. Test thoroughly in development
7. Deploy to staging for user acceptance testing
8. Deploy to production with monitoring
## Support
All implementation files have been created in `/home/claude/`:
- `user_model_improved.py` - Updated User model
- `vendor_user_improved.py` - Updated VendorUser model
- `permissions.py` - Permission system
- `vendor_exceptions.py` - Vendor exceptions
- `deps_permissions.py` - Permission dependencies
- `vendor_team_service.py` - Team management service
- `team_routes_example.py` - Example routes
- `rbac_migration_guide.md` - Database migration guide
These are ready to be integrated into your project structure.

View File

@@ -0,0 +1,513 @@
# 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_vendor_from_cookie_or_header,
require_vendor_permission,
require_vendor_owner,
get_user_permissions
)
# Permission constants
from app.core.permissions import VendorPermissions
# Exceptions
from app.exceptions import (
InsufficientVendorPermissionsException,
VendorOwnerOnlyException
)
# Services
from app.services.vendor_team_service import vendor_team_service
```
---
## Route Patterns
### Admin Route (Cookie OR Header)
```python
@router.get("/admin/vendors")
def list_vendors(
user: User = Depends(get_current_admin_from_cookie_or_header)
):
# user is authenticated admin
...
```
### Admin API (Header Only)
```python
@router.post("/api/v1/admin/vendors")
def create_vendor(
user: User = Depends(get_current_admin_api)
):
# user is authenticated admin (header required)
...
```
### Vendor Route with Permission
```python
@router.post("/vendor/{code}/products")
def create_product(
user: User = Depends(require_vendor_permission(
VendorPermissions.PRODUCTS_CREATE.value
))
):
# user has products.create permission
vendor = request.state.vendor
...
```
### Owner-Only Route
```python
@router.post("/vendor/{code}/team/invite")
def invite_member(
user: User = Depends(require_vendor_owner)
):
# user is vendor owner
vendor = request.state.vendor
...
```
### Multi-Permission Route
```python
@router.post("/vendor/{code}/products/bulk")
def bulk_operation(
user: User = Depends(require_all_vendor_permissions(
VendorPermissions.PRODUCTS_VIEW.value,
VendorPermissions.PRODUCTS_EDIT.value
))
):
# user has ALL specified permissions
...
```
---
## Permission Constants
### Quick Lookup
```python
# Dashboard
VendorPermissions.DASHBOARD_VIEW
# Products
VendorPermissions.PRODUCTS_VIEW
VendorPermissions.PRODUCTS_CREATE
VendorPermissions.PRODUCTS_EDIT
VendorPermissions.PRODUCTS_DELETE
VendorPermissions.PRODUCTS_IMPORT
VendorPermissions.PRODUCTS_EXPORT
# Stock
VendorPermissions.STOCK_VIEW
VendorPermissions.STOCK_EDIT
VendorPermissions.STOCK_TRANSFER
# Orders
VendorPermissions.ORDERS_VIEW
VendorPermissions.ORDERS_EDIT
VendorPermissions.ORDERS_CANCEL
VendorPermissions.ORDERS_REFUND
# Customers
VendorPermissions.CUSTOMERS_VIEW
VendorPermissions.CUSTOMERS_EDIT
VendorPermissions.CUSTOMERS_DELETE
VendorPermissions.CUSTOMERS_EXPORT
# Marketing
VendorPermissions.MARKETING_VIEW
VendorPermissions.MARKETING_CREATE
VendorPermissions.MARKETING_SEND
# Reports
VendorPermissions.REPORTS_VIEW
VendorPermissions.REPORTS_FINANCIAL
VendorPermissions.REPORTS_EXPORT
# Settings
VendorPermissions.SETTINGS_VIEW
VendorPermissions.SETTINGS_EDIT
VendorPermissions.SETTINGS_THEME
VendorPermissions.SETTINGS_DOMAINS
# Team
VendorPermissions.TEAM_VIEW
VendorPermissions.TEAM_INVITE
VendorPermissions.TEAM_EDIT
VendorPermissions.TEAM_REMOVE
# Imports
VendorPermissions.IMPORTS_VIEW
VendorPermissions.IMPORTS_CREATE
VendorPermissions.IMPORTS_CANCEL
```
---
## User Helper Methods
```python
# Check if admin
user.is_admin # bool
# Check if vendor
user.is_vendor # bool
# Check vendor ownership
user.is_owner_of(vendor_id) # bool
# Check vendor membership
user.is_member_of(vendor_id) # bool
# Get role in vendor
user.get_vendor_role(vendor_id) # str: "owner" | "member" | None
# Check specific permission
user.has_vendor_permission(vendor_id, "products.create") # bool
```
---
## VendorUser Helper Methods
```python
# Check if owner
vendor_user.is_owner # bool
# Check if team member
vendor_user.is_team_member # bool
# Check invitation status
vendor_user.is_invitation_pending # bool
# Check permission
vendor_user.has_permission("products.create") # bool
# Get all permissions
vendor_user.get_all_permissions() # list[str]
```
---
## Service Methods
### Team Management
```python
# Invite team member
vendor_team_service.invite_team_member(
db=db,
vendor=vendor,
inviter=current_user,
email="member@example.com",
role_name="Staff",
custom_permissions=None # Optional
)
# Accept invitation
vendor_team_service.accept_invitation(
db=db,
invitation_token=token,
password="password123",
first_name="John",
last_name="Doe"
)
# Remove team member
vendor_team_service.remove_team_member(
db=db,
vendor=vendor,
user_id=member_id
)
# Update member role
vendor_team_service.update_member_role(
db=db,
vendor=vendor,
user_id=member_id,
new_role_name="Manager",
custom_permissions=None
)
# Get team members
members = vendor_team_service.get_team_members(
db=db,
vendor=vendor,
include_inactive=False
)
```
---
## Exception Handling
```python
from app.exceptions import (
InsufficientVendorPermissionsException,
VendorOwnerOnlyException,
VendorAccessDeniedException,
InvalidInvitationTokenException,
CannotRemoveVendorOwnerException,
TeamMemberAlreadyExistsException
)
# Raise permission error
raise InsufficientVendorPermissionsException(
required_permission="products.create",
vendor_code=vendor.vendor_code
)
# Raise owner-only error
raise VendorOwnerOnlyException(
operation="team management",
vendor_code=vendor.vendor_code
)
# Raise access denied
raise VendorAccessDeniedException(
vendor_code=vendor.vendor_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') && (
<CreateButton />
)}
// Disable button
<button disabled={!hasPermission('products.edit')}>
Edit
</button>
// Get permissions on login
async function getPermissions() {
const response = await fetch(
'/api/v1/vendor/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():
vendor_user = create_vendor_user(user_type="owner")
assert vendor_user.has_permission("products.create")
assert vendor_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/vendor/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_vendor_permission("products.create"))
):
return service.create_product(data)
```
---
### ❌ DON'T: Use magic strings
```python
# BAD
require_vendor_permission("products.creat") # Typo!
```
### ✅ DO: Use constants
```python
# GOOD
require_vendor_permission(VendorPermissions.PRODUCTS_CREATE.value)
```
---
### ❌ DON'T: Mix contexts
```python
# BAD - Admin trying to access vendor route
# This will be blocked automatically
```
### ✅ DO: Use correct portal
```python
# GOOD - Admins use /admin/*, vendors use /vendor/*
```
---
## Debugging Commands
### Check User Access
```python
user = db.query(User).get(user_id)
vendor = db.query(Vendor).get(vendor_id)
print(f"Is owner: {user.is_owner_of(vendor.id)}")
print(f"Is member: {user.is_member_of(vendor.id)}")
print(f"Role: {user.get_vendor_role(vendor.id)}")
print(f"Has products.create: {user.has_vendor_permission(vendor.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
│ ├── vendor/
│ │ ├── auth.py ← Vendor login
│ │ └── team.py ← Team management
│ └── public/
│ └── vendors/auth.py ← Customer login
├── core/
│ └── permissions.py ← Permission constants
├── exceptions/
│ ├── admin.py
│ ├── vendor.py
│ └── auth.py
├── services/
│ ├── auth_service.py
│ └── vendor_team_service.py ← Team management
└── models/
└── database/
├── user.py ← User model
├── vendor.py ← Vendor, VendorUser, 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.md`

View File

@@ -0,0 +1,316 @@
# RBAC Architecture Visual Guide
## System Overview
```
┌─────────────────────────────────────────────────────────────────┐
│ PLATFORM LEVEL │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Admin Users │ │ Vendor Users │ │
│ │ role="admin" │ │ role="vendor" │ │
│ │ │ │ │ │
│ │ • Full platform │ │ • Can own/join │ │
│ │ access │ │ vendors │ │
│ │ • Cannot access │ │ • Cannot access │ │
│ │ vendor portal │ │ admin portal │ │
│ └──────────────────┘ └──────────────────┘ │
│ │ │
└──────────────────────────────────────────────┼──────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ VENDOR LEVEL │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Vendor: ACME │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────────────┐ │ │
│ │ │ Owner │ │ Team Members │ │ │
│ │ │ user_type= │ │ user_type= │ │ │
│ │ │ "owner" │ │ "member" │ │ │
│ │ │ │ │ │ │ │
│ │ │ • All perms │ │ • Role-based perms │ │ │
│ │ │ • Can invite │ │ • Manager/Staff/etc │ │ │
│ │ │ • Can remove │ │ • Can be invited │ │ │
│ │ │ • Cannot be │ │ • Can be removed │ │ │
│ │ │ removed │ │ │ │ │
│ │ └──────────────┘ └──────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ Roles │ │ │
│ │ │ │ │ │
│ │ │ • Manager (many perms) │ │ │
│ │ │ • Staff (moderate perms) │ │ │
│ │ │ • Support (limited perms) │ │ │
│ │ │ • Viewer (read-only) │ │ │
│ │ │ • Custom (owner-defined) │ │ │
│ │ └──────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ CUSTOMER LEVEL │
│ (Separate from Users) │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Customers (per vendor) │ │
│ │ │ │
│ │ • Vendor-scoped authentication │ │
│ │ • Can self-register │ │
│ │ • Access own account + shop catalog │ │
│ │ • Cannot access admin/vendor portals │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
## Team Invitation Flow
```
┌──────────┐
│ Owner │
│ (ACME) │
└────┬─────┘
│ 1. Click "Invite Team Member"
│ Email: jane@example.com
│ Role: Manager
┌─────────────────────┐
│ System Creates: │
│ • User account │
│ • VendorUser │
│ • Invitation token │
└────┬────────────────┘
│ 2. Email sent to jane@example.com
┌──────────────────────┐
│ Jane clicks link │
│ /invitation/accept? │
│ token=abc123... │
└────┬─────────────────┘
│ 3. Jane sets password
│ Enters name
┌─────────────────────┐
│ Account Activated: │
│ • User.is_active │
│ • VendorUser. │
│ is_active │
└────┬────────────────┘
│ 4. Jane can now login
┌──────────────────────┐
│ Jane logs in to │
│ ACME vendor portal │
│ with Manager perms │
└──────────────────────┘
```
## Permission Check Flow
```
┌────────────────┐
│ User makes │
│ request to: │
│ POST /products │
└───────┬────────┘
┌────────────────────────────────┐
│ FastAPI Dependency: │
│ require_vendor_permission( │
│ "products.create" │
│ ) │
└───────┬────────────────────────┘
│ 1. Get vendor from request.state
│ 2. Get user from JWT
┌────────────────────────────────┐
│ Is user a member of vendor? │
└───────┬────────────────────────┘
├─ No ──> ❌ VendorAccessDeniedException
▼ Yes
┌────────────────────────────────┐
│ Is user the owner? │
└───────┬────────────────────────┘
├─ Yes ──> ✅ Allow (owners have all perms)
▼ No
┌────────────────────────────────┐
│ Get user's role and │
│ permissions from VendorUser │
└───────┬────────────────────────┘
┌────────────────────────────────┐
│ Does role contain │
│ "products.create"? │
└───────┬────────────────────────┘
├─ No ──> ❌ InsufficientVendorPermissionsException
▼ Yes
┌────────────────────────────────┐
│ ✅ Allow request │
│ Handler executes │
└────────────────────────────────┘
```
## Database Relationships
```
┌──────────────────┐
│ users │
│ │
│ id (PK) │◄────┐
│ email │ │
│ role │ │
│ ('admin' or │ │
│ 'vendor') │ │
└──────────────────┘ │
│ │
│ owner_user_id │
│ │
▼ │
┌──────────────────┐ │
│ vendors │ │
│ │ │
│ id (PK) │ │
│ vendor_code │ │
│ owner_user_id ──┼─────┘
└──────────────────┘
┌──────────────────┐ ┌──────────────────┐
│ vendor_users │ │ roles │
│ │ │ │
│ id (PK) │ │ id (PK) │
│ vendor_id (FK) │ │ vendor_id (FK) │
│ user_id (FK) │ │ name │
│ role_id (FK) ───┼────►│ permissions │
│ user_type │ │ (JSON) │
│ ('owner' or │ └──────────────────┘
│ 'member') │
│ invitation_* │
│ is_active │
└──────────────────┘
Separate hierarchy:
┌──────────────────┐
│ customers │
│ │
│ id (PK) │
│ vendor_id (FK) │
│ email │
│ hashed_password │
│ (vendor-scoped) │
└──────────────────┘
```
## Permission Naming Convention
```
Resource.Action
Examples:
✓ dashboard.view
✓ products.view
✓ products.create
✓ products.edit
✓ products.delete
✓ products.import
✓ products.export
✓ orders.view
✓ orders.edit
✓ orders.cancel
✓ orders.refund
✓ customers.view
✓ customers.edit
✓ reports.financial
✓ team.invite
✓ team.remove
✓ settings.edit
```
## Role Presets
```
┌──────────────────────────────────────────────────────────────┐
│ OWNER │
│ ALL PERMISSIONS (automatic, not stored in role) │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ MANAGER │
│ Most permissions except: │
│ • team.invite/remove (owner only) │
│ • Critical settings (owner only) │
│ │
│ Has: products.*, orders.*, customers.*, reports.* │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ STAFF │
│ Day-to-day operations: │
│ • products.view/create/edit │
│ • stock.view/edit │
│ • orders.view/edit │
│ • customers.view │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ SUPPORT │
│ Customer service focus: │
│ • orders.view/edit │
│ • customers.view/edit │
│ • products.view (read-only) │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ VIEWER │
│ Read-only access: │
│ • *.view permissions only │
│ • No edit/create/delete │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ MARKETING │
│ Marketing & analytics focus: │
│ • customers.view/export │
│ • marketing.* (all marketing actions) │
│ • reports.view │
└──────────────────────────────────────────────────────────────┘
```
## Security Boundaries
```
❌ BLOCKED ✅ ALLOWED
Admin → Vendor Portal Admin → Admin Portal
Vendor → Admin Portal Vendor → Vendor Portal
Customer → Admin Portal Customer → Shop Catalog
Customer → Vendor Portal Customer → Own Account
Cookie Isolation:
admin_token (path=/admin) ← Only sent to /admin/*
vendor_token (path=/vendor) ← Only sent to /vendor/*
customer_token (path=/shop) ← Only sent to /shop/*
```