Renamed all documentation files to follow kebab-case naming standard: - UPPERCASE files → lowercase (e.g., RBAC.md → rbac.md) - snake_case files → kebab-case (e.g., icons_guide.md → icons-guide.md) - SCREAMING_SNAKE_CASE → kebab-case (e.g., DATABASE_SETUP_GUIDE.md → database-setup-guide.md) Files renamed (15 total): API Documentation: - api/RBAC.md → api/rbac.md Architecture: - architecture/API_CONSOLIDATION_PROPOSAL.md → api-consolidation-proposal.md - architecture/API_MIGRATION_STATUS.md → api-migration-status.md Development: - development/AUTH_DEPENDENCIES_GUIDE.md → auth-dependencies-guide.md - development/CUSTOMER_AUTHENTICATION_IMPLEMENTATION.md → customer-authentication-implementation.md - development/CUSTOMER_AUTH_SUMMARY.md → customer-auth-summary.md - development/icons_guide.md → icons-guide.md Database Seeder: - database-seeder/DATABASE_INIT_GUIDE.md → database-init-guide.md - database-seeder/DATABASE_QUICK_REFERENCE_GUIDE.md → database-quick-reference-guide.md - database-seeder/DATABASE_SEEDER_DOCUMENTATION.md → database-seeder-documentation.md - database-seeder/MAKEFILE_DATABASE_SEEDER.md → makefile-database-seeder.md Error Rendering: - error-rendering/ERROR_RENDERING_DEVELOPER_DOCUMENTATION.md → error-rendering-developer-documentation.md - error-rendering/HTML_ERROR_RENDERING_FLOW_DIAGRAM.md → html-error-rendering-flow-diagram.md Getting Started: - getting-started/DATABASE_QUICK_REFERENCE.md → database-quick-reference.md - getting-started/DATABASE_SETUP_GUIDE.md → database-setup-guide.md Updates: - Updated all references in mkdocs.yml - Updated all cross-references in markdown files - Verified mkdocs builds without warnings or errors Standard: Use kebab-case (lowercase-with-hyphens) for all markdown files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
10 KiB
10 KiB
RBAC Quick Reference Card
For Daily Development | Keep this handy while coding
Common Imports
# 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)
@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)
@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
@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
@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
@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
# 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
# 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
# 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
# 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
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
// 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
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
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
# BAD
def create_product(user, data):
if not user.has_permission("products.create"):
raise Exception()
✅ DO: Check permissions at route level
# 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
# BAD
require_vendor_permission("products.creat") # Typo!
✅ DO: Use constants
# GOOD
require_vendor_permission(VendorPermissions.PRODUCTS_CREATE.value)
❌ DON'T: Mix contexts
# BAD - Admin trying to access vendor route
# This will be blocked automatically
✅ DO: Use correct portal
# GOOD - Admins use /admin/*, vendors use /vendor/*
Debugging Commands
Check User Access
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
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
// 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
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