Role-Based Access Control (RBAC)¶
Define roles, assign entitlements, and control per-database access.
Last Updated: April 2026
Overview¶
NornicDB implements a layered RBAC system:
- Roles — Built-in (admin, editor, viewer) and user-defined custom roles
- Global entitlements — Permissions that apply server-wide (read, write, admin, schema, etc.)
- Per-database access — Control which roles can see and access each database
- Per-database privileges — Fine-grained read/write control per (role, database) pair
- JWT authentication — Stateless token-based auth with password policies and account lockout
Roles¶
Built-in Roles¶
| Role | Entitlements | Description |
|---|---|---|
admin | read, write, create, delete, admin, schema, user_manage | Full access and system administration |
editor | read, write, create, delete | Read and write data |
viewer | read | Read-only access |
none | — | No access (disabled account) |
Built-in roles cannot be renamed or deleted.
Custom Roles¶
Create custom roles to model your organization's access patterns. Custom roles start with no global entitlements — assign per-database privileges to control what they can do.
List all roles:
Response:
Create a custom role:
curl -X POST http://localhost:7474/auth/roles \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "analyst"}'
Rename a custom role:
curl -X PATCH http://localhost:7474/auth/roles/analyst \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "data_analyst"}'
Renaming a role automatically updates the allowlist and all user assignments.
Delete a custom role:
curl -X DELETE http://localhost:7474/auth/roles/data_analyst \
-H "Authorization: Bearer $ADMIN_TOKEN"
A role cannot be deleted while any user is assigned to it.
Entitlements¶
Entitlements are the individual permissions that can be assigned to roles. Query the full canonical list from the API:
Global Entitlements¶
These apply server-wide and are assigned to roles by default (built-in roles) or via configuration (custom roles).
| ID | Name | What It Controls |
|---|---|---|
read | Read | Cypher MATCH, search, metrics, status, Bifrost/GraphQL reads, MCP reads |
write | Write | Cypher CREATE/DELETE/SET/MERGE, embed triggers, search rebuild, GraphQL mutations |
create | Create | Resource creation operations |
delete | Delete | Delete operations (e.g. GDPR delete) |
admin | Admin | Role management, database access/privileges, backup, GPU config, system admin |
schema | Schema | CREATE/DROP INDEX and CONSTRAINT via Bolt |
user_manage | User Management | Create, update, delete user accounts (distinct from admin) |
Per-Database Entitlements¶
These apply per database and are controlled through the allowlist and privileges APIs.
| ID | Name | What It Controls |
|---|---|---|
database_see | Database: See | Database appears in SHOW DATABASES and the catalogue |
database_access | Database: Access | Role can run queries against this database |
database_read | Database: Read | Read operations (MATCH, property reads) on this database |
database_write | Database: Write | Write operations (CREATE, DELETE, SET, MERGE) on this database |
Per-Database Access Control¶
Database Allowlist¶
The allowlist controls which roles can see and access which databases. If no allowlist is configured for a role, that role can access all databases (empty list = all).
View current allowlist:
Response:
[
{"role": "analyst", "databases": ["analytics", "reporting"]},
{"role": "viewer", "databases": ["public_data"]}
]
Set allowlist for a single role:
curl -X PUT http://localhost:7474/auth/access/databases \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"role": "analyst",
"databases": ["analytics", "reporting"]
}'
Set allowlist for multiple roles at once:
curl -X PUT http://localhost:7474/auth/access/databases \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mappings": [
{"role": "analyst", "databases": ["analytics", "reporting"]},
{"role": "auditor", "databases": ["audit_logs"]}
]
}'
Per-Database Privileges¶
The privileges matrix provides fine-grained read and write control per (role, database) pair. When no privilege entry exists for a combination, access falls back to the role's global permissions (e.g. editor = read+write, viewer = read-only).
View current privileges:
Response:
[
{"role": "analyst", "database": "analytics", "read": true, "write": false},
{"role": "analyst", "database": "reporting", "read": true, "write": true},
{"role": "auditor", "database": "audit_logs", "read": true, "write": false}
]
Set privileges:
curl -X PUT http://localhost:7474/auth/access/privileges \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{"role": "analyst", "database": "analytics", "read": true, "write": false},
{"role": "analyst", "database": "reporting", "read": true, "write": true}
]'
How Access Is Resolved¶
When a user queries a database, NornicDB resolves access in this order:
- Allowlist check — Can this user's role(s) see and access this database?
- Privileges check — Is there a (role, database) privilege entry? If yes, use it.
- Global fallback — If no privilege entry, use the role's global permissions (admin/editor = read+write, viewer = read-only).
When a new database is created, the admin role and the creating user's roles are automatically granted full access.
User Management¶
Create Users¶
curl -X POST http://localhost:7474/auth/users \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"username": "alice",
"password": "SecurePass123!",
"roles": ["analyst"]
}'
Response:
{
"username": "alice",
"email": "alice@localhost",
"roles": ["analyst"],
"created_at": "2026-04-01T10:30:00Z",
"disabled": false
}
Update User Roles¶
curl -X PUT http://localhost:7474/auth/users/alice \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"roles": ["analyst", "viewer"]}'
List Users¶
Disable a User¶
curl -X PUT http://localhost:7474/auth/users/alice \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"disabled": true}'
Delete a User¶
User Profile (Self-Service)¶
Users can update their own password and profile without admin privileges:
# Change password
curl -X POST http://localhost:7474/auth/password \
-H "Authorization: Bearer $USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"old_password": "OldPass123!",
"new_password": "NewSecurePass456!"
}'
# Update profile metadata
curl -X PUT http://localhost:7474/auth/profile \
-H "Authorization: Bearer $USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "alice@example.com",
"metadata": {"department": "Engineering", "team": "Data"}
}'
User Storage¶
User accounts are stored as nodes in the system database:
- Persisted immediately on create/update/delete
- Loaded automatically on server startup
- Included in database backups
- Internal database IDs and password hashes are never exposed in API responses
Authentication¶
Login (Get Token)¶
curl -X POST http://localhost:7474/auth/token \
-d "grant_type=password&username=alice&password=SecurePass123!"
Response:
Using Tokens¶
# Authorization header
curl http://localhost:7474/db/nornicdb/tx/commit \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
# Or API key header
curl http://localhost:7474/db/nornicdb/tx/commit \
-H "X-API-Key: ndb_sk_abc123..."
API Keys¶
For service-to-service communication:
Security Features¶
Account Lockout¶
After repeated failed login attempts, accounts are locked automatically:
Password Policy¶
Passwords are hashed using bcrypt with automatic salt generation. Plain-text passwords are never stored.
Session Management¶
Tokens can be revoked (logout) by adding them to a server-side blacklist. Revoked tokens are rejected on subsequent requests.
Configuration¶
auth:
enabled: true
# JWT
jwt_secret: "${NORNICDB_JWT_SECRET}" # Min 32 characters
jwt_expiry: 24h
# Password policy
min_password_length: 12
require_uppercase: true
require_number: true
require_special: true
bcrypt_cost: 10
# Security
max_failed_attempts: 5
lockout_duration: 15m
# Required: JWT signing secret (min 32 characters)
export NORNICDB_JWT_SECRET="your-super-secret-jwt-key-min-32-chars"
# Optional: Disable auth for development
export NORNICDB_NO_AUTH=true
Endpoint Protection¶
| Endpoint | Required Permission |
|---|---|
GET /health | None (public) |
GET /status | read |
GET /metrics | read |
POST /db/{db}/tx/commit | read or write (per-database) |
POST /nornicdb/search | read |
POST /auth/users | user_manage |
GET/POST /auth/roles | admin |
GET/PUT /auth/access/databases | admin |
GET/PUT /auth/access/privileges | admin |
GET /auth/entitlements | read |
DELETE /nornicdb/gdpr/* | admin |
GET /admin/* | admin |
Audit Integration¶
All authentication and access events are logged:
{
"timestamp": "2026-04-01T10:30:00Z",
"event_type": "LOGIN",
"user_id": "usr_abc123",
"username": "alice",
"ip_address": "192.168.1.100",
"user_agent": "Mozilla/5.0...",
"success": true
}
See Audit Logging for full details.
Compliance Mapping¶
| Requirement | NornicDB Feature |
|---|---|
| GDPR Art. 32 | Access controls, authentication |
| HIPAA §164.312(a)(1) | Unique user identification |
| HIPAA §164.312(d) | Person or entity authentication |
| FISMA AC-2 | Account management |
| SOC2 CC6.1 | Logical access controls |
See Also¶
- Entitlements Reference — Canonical list of all assignable entitlements
- Per-Database RBAC & Lockout Recovery — Allowlist details and admin lockout recovery
- Encryption — Data protection at rest
- Audit Logging — Access and event audit trails
- HIPAA Compliance — Healthcare compliance mapping
- Multi-Database Guide — System database and user storage