# 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 Role Properties

```python
# Check if super admin (computed: role == "super_admin")
user.is_super_admin  # bool

# Check if any admin (computed: role in ("super_admin", "platform_admin"))
user.is_admin  # bool

# Check if platform admin (computed: role == "platform_admin")
user.is_platform_admin  # bool

# Check if merchant owner (computed: role == "merchant_owner")
user.is_merchant_owner  # bool

# Check if store-level user (computed: role in ("merchant_owner", "store_member"))
user.is_store_user  # bool
```

## User Helper Methods

```python
# Check store ownership (via Merchant.owner_user_id)
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" | role name | None

# Check specific permission
user.has_store_permission(store_id, "products.create")  # bool
```

---

## StoreUser Helper Methods

```python
# Check if owner (derived from User.role and Merchant.owner_user_id)
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') && (
    <CreateButton />
)}

// Disable button
<button disabled={!hasPermission('products.edit')}>
    Edit
</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():
    user = create_user(role="merchant_owner")
    store_user = create_store_user(user=user, store=store)
    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']}")  # super_admin, platform_admin, merchant_owner, or store_member
print(f"Expires: {decoded['exp']}")
# Note: is_super_admin is no longer in JWT tokens; derive from role == "super_admin"
```

### 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)
