Files
orion/docs/backend/rbac-quick-reference.md
Samir Boulahtit 3c7af0ccdf refactor: standardize markdown file naming to kebab-case convention
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>
2025-11-28 07:58:33 +01:00

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

@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']}")
// 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