> ## Documentation Index
> Fetch the complete documentation index at: https://docs.snackbase.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Security Model

> Role-based access control, field-level permissions, and rule engine

SnackBase provides a comprehensive security model with role-based access control, field-level permissions, and a powerful rule engine. This guide explains the security architecture, authorization flows, and best practices.

## Overview

SnackBase security operates on **multiple layers** to ensure data protection:

| Layer                    | Purpose              | Mechanism                             |
| ------------------------ | -------------------- | ------------------------------------- |
| **Authentication**       | Verify user identity | JWT tokens, Argon2id password hashing |
| **Account Isolation**    | Separate tenant data | Row-level filtering via `account_id`  |
| **Authorization**        | Control user actions | RBAC + Permission system              |
| **Field-Level Security** | Hide sensitive data  | Field-level access control            |
| **Audit Logging**        | Track all actions    | Immutable audit logs (coming soon)    |

## Authentication vs Authorization

Understanding the distinction is critical:

| Aspect             | Authentication        | Authorization             |
| ------------------ | --------------------- | ------------------------- |
| **Question**       | Who are you?          | What can you do?          |
| **Mechanism**      | JWT tokens, passwords | Roles, permissions, rules |
| **Timing**         | Once per session      | Every request             |
| **Failure Result** | 401 Unauthorized      | 403 Forbidden             |

### Example Scenario

```
Authentication (Who are you?):
├── User provides credentials
├── System verifies identity
└── Result: "You are alice@acme.com"

Authorization (What can you do?):
├── User requests DELETE /api/v1/posts/123
├── System checks permissions
├── User has "editor" role
├── Editor role does NOT have "delete" permission
└── Result: 403 Forbidden - "You cannot delete posts"
```

## Role-Based Access Control (RBAC)

SnackBase uses **RBAC** as the foundation of authorization.

### RBAC Hierarchy

```
Account (AB1001)
│
├── Users
│   ├── alice@acme.com
│   ├── bob@acme.com
│   └── jane@acme.com
│
├── Roles
│   ├── admin
│   │   └── Permissions: All operations on all collections
│   ├── editor
│   │   └── Permissions: Create, Read, Update on posts only
│   └── viewer
│       └── Permissions: Read on posts only
│
└── Role Assignments
    ├── alice@acme.com → admin
    ├── bob@acme.com → editor
    └── jane@acme.com → viewer
```

### Default Roles

| Role       | Description                | Typical Permissions                          |
| ---------- | -------------------------- | -------------------------------------------- |
| **admin**  | Full administrative access | All operations on all collections            |
| **editor** | Content creator/manager    | Create, Read, Update on specific collections |
| **viewer** | Read-only access           | Read on specific collections                 |

### Custom Roles

You can create custom roles for any purpose:

```json theme={null}
{
  "name": "moderator",
  "description": "Can moderate user-generated content",
  "permissions": [
    {
      "collection": "comments",
      "create": false,
      "read": true,
      "update": true,
      "delete": true
    },
    {
      "collection": "users",
      "create": false,
      "read": true,
      "update": false,
      "delete": false
    }
  ]
}
```

## Permission System

Permissions define **what operations** a user can perform on **which collections**.

### Permission Matrix

For a role with permissions:

| Collection | Create | Read | Update | Delete |
| ---------- | ------ | ---- | ------ | ------ |
| posts      | ✅      | ✅    | ✅      | ❌      |
| comments   | ✅      | ✅    | ✅      | ✅      |
| users      | ❌      | ✅    | ❌      | ❌      |

### Permission Structure

```json theme={null}
{
  "id": "perm_abc123",
  "role_id": "role_editor",
  "collection": "posts",
  "create": true,
  "read": true,
  "update": true,
  "delete": false,
  "fields": ["title", "content", "status"],
  "rules": {
    "create": "@has_role('editor')",
    "update": "@owns_record() or @has_role('admin')"
  }
}
```

### Wildcard Collections

Use `*` to grant permissions on **all collections**:

```json theme={null}
{
  "role": "admin",
  "collection": "*",
  "create": true,
  "read": true,
  "update": true,
  "delete": true
}
```

This grants admin full access to ALL collections, including future ones.

### Permission Caching

Permissions are cached for **5 minutes** to improve performance:

```
┌──────────────────┐
│ First Request    │
│ Check permissions│
│ from database    │
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│ Cache for 5 min  │
│ Subsequent       │
│ requests use     │
│ cached perms     │
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│ After 5 min or   │
│ permission change│
│ Cache invalidated│
└──────────────────┘
```

## Rule Engine

SnackBase includes a **powerful rule engine** for fine-grained access control.

### Rule Syntax

Rules use a custom DSL (Domain Specific Language):

```python theme={null}
# Simple comparisons
user.id == "user_abc123"
user.email == "admin@example.com"

# Role checks
@has_role("admin")
@has_any_role(["admin", "moderator"])

# Record ownership
@owns_record()

# Field comparisons
status in ["draft", "published"]
priority >= 3

# Logical operators
@has_role("admin") or @owns_record()
@has_role("editor") and status == "draft"
not status == "archived"

# Complex expressions
(@has_role("admin") or @owns_record()) and not status == "locked"
```

### Built-in Functions

| Function                 | Description                 | Example                                 |
| ------------------------ | --------------------------- | --------------------------------------- |
| `@has_role(role)`        | User has specific role      | `@has_role("admin")`                    |
| `@has_any_role([roles])` | User has any of these roles | `@has_any_role(["admin", "moderator"])` |
| `@owns_record()`         | User created this record    | `@owns_record()`                        |
| `@is_superadmin()`       | User is superadmin          | `@is_superadmin()`                      |

### Rule Evaluation Context

Rules have access to:

| Variable  | Description           | Example                              |
| --------- | --------------------- | ------------------------------------ |
| `user`    | Current user object   | `user.id`, `user.email`              |
| `record`  | Record being accessed | `record.created_by`, `record.status` |
| `context` | Request context       | `context.account_id`                 |

### Permission Rules Example

```json theme={null}
{
  "collection": "posts",
  "update": true,
  "rules": {
    "update": "(@owns_record() and status in ['draft', 'pending']) or @has_role('admin')"
  }
}
```

**Translation**: Users can update posts if:

* They created the post AND status is draft/pending, OR
* They have admin role

## Field-Level Security

SnackBase supports **field-level access control** to hide sensitive data.

### Field Visibility

Restrict which fields a role can see:

```json theme={null}
{
  "role": "viewer",
  "collection": "users",
  "read": true,
  "fields": ["name", "email"],
  "excluded_fields": ["phone", "ssn", "salary"]
}
```

Users with this role will receive:

```json theme={null}
// Response (excluded fields filtered out)
{
  "id": "user_abc123",
  "name": "Alice Johnson",
  "email": "alice@example.com"
  // phone, ssn, salary NOT included
}
```

### Field-Level Rules

Apply rules to specific fields:

```json theme={null}
{
  "collection": "users",
  "field_rules": {
    "salary": {
      "read": "@has_role('admin') or @owns_record()",
      "write": "@has_role('hr') or @is_superadmin()"
    },
    "email": {
      "read": "true",
      "write": "@has_role('admin')"
    }
  }
}
```

## Account Isolation

Account isolation is the **foundation of SnackBase security**.

### Multi-Tenant Isolation

All data is automatically isolated by `account_id`:

```sql theme={null}
-- User from AB1001 requests posts
SELECT * FROM posts WHERE account_id = 'AB1001';

-- User from XY2048 requests posts
SELECT * FROM posts WHERE account_id = 'XY2048';
```

Users cannot see or access data from other accounts.

### Enforcement Layers

Account isolation is enforced at **multiple layers**:

| Layer              | Mechanism                           | Example                    |
| ------------------ | ----------------------------------- | -------------------------- |
| **Database**       | `account_id` column in WHERE clause | `WHERE account_id = ?`     |
| **Repository**     | Automatic filtering in queries      | `posts.find_all(context)`  |
| **API Middleware** | Validates account in token          | Token contains account\_id |
| **Hooks**          | Built-in account\_isolation\_hook   | Cannot be disabled         |

### Cross-Account Access Prevention

Attempting to access another account's data:

```bash theme={null}
# User from AB1001 tries to access XY2048 data
GET /api/v1/posts?account_id=XY2048

# Result: 403 Forbidden
# The account_id filter is overridden and reset to AB1001
```

The system **ignores** malicious `account_id` parameters.

## Security Best Practices

### 1. Principle of Least Privilege

Grant minimum required permissions:

```json theme={null}
// ❌ Too permissive
{
  "role": "viewer",
  "collection": "*",
  "delete": true  // Viewers shouldn't delete!
}

// ✅ Correct
{
  "role": "viewer",
  "collection": "posts",
  "read": true,
  "create": false,
  "update": false,
  "delete": false
}
```

### 2. Use Rules for Fine-Grained Control

Leverage the rule engine for complex scenarios:

```json theme={null}
{
  "rules": {
    "update": "@owns_record() or @has_role('admin')",
    "delete": "@has_role('admin') and not record.status == 'locked'"
  }
}
```

### 3. Implement Field-Level Security

Hide sensitive fields by default:

```json theme={null}
{
  "collection": "users",
  "excluded_fields": ["password_hash", "ssn", "salary"]
}
```

### 4. Regular Permission Audits

Periodically review and update permissions:

* Remove unused roles
* Tighten overly permissive rules
* Document permission rationale
* Use audit logs (when available) to track access

### 5. Use Wildcards Carefully

Wildcard permissions (`*`) are powerful but dangerous:

```json theme={null}
// ⚠️ Use with caution
{
  "collection": "*",
  "delete": true  // Can delete from ALL collections!
}

// ✅ Prefer explicit collections
{
  "collection": "posts",
  "delete": true
}
```

### 6. Test Permission Changes

Always test permission changes in development:

```python theme={null}
def test_editor_cannot_delete_posts():
    editor_user = create_user(role="editor")
    client = login_as(editor_user)

    response = client.delete("/api/v1/posts/123")

    assert response.status_code == 403
```

### 7. Monitor and Alert

Monitor for suspicious activity:

* Repeated failed authorization attempts
* Unusual access patterns
* Permission escalation attempts
* Cross-account access attempts

## Common Security Scenarios

### Scenario 1: User Can Only Edit Their Own Posts

```json theme={null}
{
  "role": "author",
  "collection": "posts",
  "create": true,
  "read": true,
  "update": true,
  "delete": true,
  "rules": {
    "update": "@owns_record()",
    "delete": "@owns_record() and not status == 'published'"
  }
}
```

### Scenario 2: Moderators Can Edit All Comments

```json theme={null}
{
  "role": "moderator",
  "collection": "comments",
  "create": false,
  "read": true,
  "update": true,
  "delete": true,
  "field_rules": {
    "author_ip": {
      "read": "@has_role('admin')"
    }
  }
}
```

### Scenario 3: Public Read, Private Write

```json theme={null}
{
  "role": "anonymous",
  "collection": "posts",
  "read": true,
  "create": false,
  "update": false,
  "delete": false,
  "excluded_fields": ["draft_notes", "internal_status"]
}
```

## Security Headers

SnackBase implements **defense-in-depth** security by automatically setting HTTP security headers on all responses.

### Automatic Headers

All responses include the following security headers:

| Header                        | Value                                 | Purpose                                            |
| ----------------------------- | ------------------------------------- | -------------------------------------------------- |
| **X-Content-Type-Options**    | `nosniff`                             | Prevents MIME type sniffing attacks                |
| **X-Frame-Options**           | `DENY`                                | Prevents clickjacking by blocking iframe embedding |
| **X-XSS-Protection**          | `1; mode=block`                       | Enables browser XSS protection (legacy browsers)   |
| **Strict-Transport-Security** | `max-age=31536000; includeSubDomains` | Enforces HTTPS (production only)                   |
| **Content-Security-Policy**   | Configurable                          | Prevents XSS and injection attacks                 |
| **Permissions-Policy**        | Configurable                          | Restricts browser features                         |
| **Referrer-Policy**           | `strict-origin-when-cross-origin`     | Controls referrer information                      |

### Content Security Policy (CSP)

The default CSP is designed for maximum security while supporting the Admin UI:

```
default-src 'self'; 
script-src 'self'; 
style-src 'self' 'unsafe-inline'; 
img-src 'self' data:; 
font-src 'self'; 
connect-src 'self'; 
frame-ancestors 'none'
```

**Key Directives**:

* `default-src 'self'`: Only load resources from same origin
* `script-src 'self'`: Block inline scripts and external scripts
* `style-src 'self' 'unsafe-inline'`: Allow inline styles (for React)
* `img-src 'self' data:`: Allow images from same origin and data URIs
* `frame-ancestors 'none'`: Prevent iframe embedding

### Customization

Customize security headers via environment variables:

```bash theme={null}
# Customize CSP for external CDN
SNACKBASE_CSP_POLICY="default-src 'self'; script-src 'self' https://cdn.example.com"

# Adjust HSTS max-age
SNACKBASE_HSTS_MAX_AGE=63072000  # 2 years

# Customize Permissions Policy
SNACKBASE_PERMISSIONS_POLICY="geolocation=(), camera=(), microphone=()"
```

### Environment-Aware Behavior

Security headers adapt to the environment:

| Environment     | HSTS Header        | HTTPS Redirect             |
| --------------- | ------------------ | -------------------------- |
| **Development** | ❌ Not set          | ❌ Disabled                 |
| **Production**  | ✅ Set with max-age | ⚙️ Optional (configurable) |

This prevents HSTS issues during local development while enforcing HTTPS in production.

## Summary

| Concept                             | Key Takeaway                                                                      |
| ----------------------------------- | --------------------------------------------------------------------------------- |
| **Security Layers**                 | Authentication → Account Isolation → Authorization → Field-Level Security → Audit |
| **Authentication vs Authorization** | Authentication = Who are you? Authorization = What can you do?                    |
| **RBAC**                            | Users → Roles → Permissions → Collections                                         |
| **Permission System**               | CRUD permissions per collection, wildcard support, 5-minute cache                 |
| **Rule Engine**                     | Custom DSL for fine-grained control with built-in functions                       |
| **Field-Level Security**            | Hide sensitive fields, field-specific rules                                       |
| **Account Isolation**               | Automatic via account\_id, enforced at multiple layers                            |
| **Best Practices**                  | Least privilege, use rules, hide sensitive data, audit permissions                |
