Security Model

December 31, 2025 · View on GitHub

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.


Table of Contents


Overview

SnackBase security operates on multiple layers to ensure data protection:

LayerPurposeMechanism
AuthenticationVerify user identityJWT tokens, Argon2id password hashing
Account IsolationSeparate tenant dataRow-level filtering via account_id
AuthorizationControl user actionsRBAC + Permission system
Field-Level SecurityHide sensitive dataField-level access control
Audit LoggingTrack all actionsImmutable audit logs (coming soon)

Screenshot Placeholder 1

Description: A layered security diagram showing five concentric circles: Authentication (inner), Account Isolation, Authorization, Field-Level Security, and Audit Logging (outer).


Security Architecture

Request Security Flow

┌──────────────┐
│ Client       │
│ Request      │
└──────┬───────┘


┌─────────────────────────────────┐
│ 1. Authentication Middleware    │
│    - Verify JWT token           │
│    - Extract user & account      │
│    - Check token expiration      │
└──────┬──────────────────────────┘


┌─────────────────────────────────┐
│ 2. Account Isolation Hook       │
│    - Inject account_id filter   │
│    - Enforce row-level security │
└──────┬──────────────────────────┘


┌─────────────────────────────────┐
│ 3. Authorization Middleware     │
│    - Load user permissions      │
│    - Check collection access    │
│    - Evaluate operation rules   │
└──────┬──────────────────────────┘


┌─────────────────────────────────┐
│ 4. Field-Level Security         │
│    - Filter sensitive fields    │
│    - Apply field rules          │
└──────┬──────────────────────────┘


┌─────────────────────────────────┐
│ 5. Business Logic               │
│    - Execute operation          │
│    - Return filtered data       │
└─────────────────────────────────┘

Screenshot Placeholder 2

Description: A vertical flow diagram showing the request passing through five security layers before reaching business logic.


Authentication vs Authorization

Understanding the distinction is critical:

AspectAuthenticationAuthorization
QuestionWho are you?What can you do?
MechanismJWT tokens, passwordsRoles, permissions, rules
TimingOnce per sessionEvery request
Failure Result401 Unauthorized403 Forbidden

Screenshot Placeholder 3

Description: A comparison table showing authentication vs authorization with visual icons representing each concept.

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"

Screenshot Placeholder 4

Description: A split-screen example showing authentication (left) and authorization (right) with their respective questions and results.


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

Screenshot Placeholder 5

Description: A tree diagram showing the RBAC hierarchy with Account at top, branching to Users, Roles, and Role Assignments.

Default Roles

RoleDescriptionTypical Permissions
adminFull administrative accessAll operations on all collections
editorContent creator/managerCreate, Read, Update on specific collections
viewerRead-only accessRead on specific collections

Screenshot Placeholder 6

Description: A table showing default roles with their descriptions and typical permission sets.

Custom Roles

You can create custom roles for any purpose:

{
  "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
    }
  ]
}

Screenshot Placeholder 7

Description: JSON showing a custom "moderator" role with specific permissions for comments and users collections.


Permission System

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

Permission Matrix

For a role with permissions:

CollectionCreateReadUpdateDelete
posts
comments
users

Screenshot Placeholder 8

Description: A visual permission matrix grid showing checkboxes for Create, Read, Update, Delete permissions across multiple collections.

Permission Structure

{
  "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')"
  }
}

Screenshot Placeholder 9

Description: JSON showing a complete permission object with CRUD flags, field restrictions, and rule expressions.

Wildcard Collections

Use * to grant permissions on all collections:

{
  "role": "admin",
  "collection": "*",
  "create": true,
  "read": true,
  "update": true,
  "delete": true
}

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

Screenshot Placeholder 10

Description: A code example showing wildcard collection permission with a visual highlight of the asterisk.

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│
└──────────────────┘

Screenshot Placeholder 11

Description: A timeline diagram showing permission caching with cache hit period and invalidation.


Rule Engine

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

Rule Syntax

Rules use a custom DSL (Domain Specific Language):

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

Screenshot Placeholder 12

Description: A code example showing various rule syntax patterns with comments explaining each.

Built-in Functions

FunctionDescriptionExample
@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()

Screenshot Placeholder 13

Description: A table showing all built-in rule functions with their descriptions and usage examples.

Rule Evaluation Context

Rules have access to:

VariableDescriptionExample
userCurrent user objectuser.id, user.email
recordRecord being accessedrecord.created_by, record.status
contextRequest contextcontext.account_id

Screenshot Placeholder 14

Description: A table showing rule evaluation context variables with their available properties.

Permission Rules Example

{
  "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

Screenshot Placeholder 15

Description: A visual decision tree diagram showing the rule logic with branches for ownership check and admin check.


Field-Level Security

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

Field Visibility

Restrict which fields a role can see:

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

Users with this role will receive:

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

Screenshot Placeholder 16

Description: A side-by-side comparison showing full user record (left) vs filtered response (right) with sensitive fields redacted.

Field-Level Rules

Apply rules to specific fields:

{
  "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')"
    }
  }
}

Screenshot Placeholder 17

Description: A code example showing field-level rules with different permissions for salary and email fields.


Account Isolation

Account isolation is the foundation of SnackBase security.

Multi-Tenant Isolation

All data is automatically isolated by account_id:

-- 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.

Screenshot Placeholder 18

Description: A database diagram showing two account partitions (AB1001, XY2048) with data separated, and a query showing results from one partition only.

Enforcement Layers

Account isolation is enforced at multiple layers:

LayerMechanismExample
Databaseaccount_id column in WHERE clauseWHERE account_id = ?
RepositoryAutomatic filtering in queriesposts.find_all(context)
API MiddlewareValidates account in tokenToken contains account_id
HooksBuilt-in account_isolation_hookCannot be disabled

Screenshot Placeholder 19

Description: A layered diagram showing account isolation enforcement at Database, Repository, API Middleware, and Hooks layers.

Cross-Account Access Prevention

Attempting to access another account's data:

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

Screenshot Placeholder 20

Description: A sequence diagram showing a malicious cross-account request being blocked and the account_id being reset to the user's actual account.


Security Best Practices

1. Principle of Least Privilege

Grant minimum required permissions:

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

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

Screenshot Placeholder 21

Description: A code comparison showing overly permissive (bad) vs minimal required permissions (good) with visual indicators.

2. Use Rules for Fine-Grained Control

Leverage the rule engine for complex scenarios:

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

Screenshot Placeholder 22

Description: Code example showing best practice rule usage with ownership checks and admin role checks.

3. Implement Field-Level Security

Hide sensitive fields by default:

{
  "collection": "users",
  "excluded_fields": ["password_hash", "ssn", "salary"]
}

Screenshot Placeholder 23

Description: Code example showing field exclusion for sensitive user data.

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

Screenshot Placeholder 24

Description: A checklist or flowchart showing permission audit process with steps: Review Roles → Analyze Permissions → Tighten Rules → Document Changes.

5. Use Wildcards Carefully

Wildcard permissions (*) are powerful but dangerous:

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

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

Screenshot Placeholder 25

Description: Code comparison showing risky wildcard usage vs safer explicit collection permission.

6. Test Permission Changes

Always test permission changes in development:

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

Screenshot Placeholder 26

Description: A code example showing a test case for verifying permission restrictions.

7. Monitor and Alert

Monitor for suspicious activity:

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

Screenshot Placeholder 27

Description: A dashboard mockup showing security monitoring with metrics for failed attempts, unusual patterns, and alerts.


Common Security Scenarios

Scenario 1: User Can Only Edit Their Own Posts

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

Screenshot Placeholder 28

Description: A use case diagram showing the author role workflow with permission boundaries.

Scenario 2: Moderators Can Edit All Comments

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

Screenshot Placeholder 29

Description: A use case diagram showing moderator permissions with IP address hidden from non-admins.

Scenario 3: Public Read, Private Write

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

Screenshot Placeholder 30

Description: A use case diagram showing public access with restricted write operations and hidden internal fields.


Summary

ConceptKey Takeaway
Security LayersAuthentication → Account Isolation → Authorization → Field-Level Security → Audit
Authentication vs AuthorizationAuthentication = Who are you? Authorization = What can you do?
RBACUsers → Roles → Permissions → Collections
Permission SystemCRUD permissions per collection, wildcard support, 5-minute cache
Rule EngineCustom DSL for fine-grained control with built-in functions
Field-Level SecurityHide sensitive fields, field-specific rules
Account IsolationAutomatic via account_id, enforced at multiple layers
Best PracticesLeast privilege, use rules, hide sensitive data, audit permissions


Questions? Check the FAQ or open an issue on GitHub.