-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
Problem Statement
OpenViking has implemented a basic multi-tenant RBAC system, but there are limitations in permission management:
- Fixed Roles: Only three built-in roles (ROOT, ADMIN, USER) are supported, which cannot meet complex enterprise permission requirements
- No Custom Permissions: Cannot create business-related roles like developer, viewer, project-manager
- No ACL: Current permission control is only based on space (user data space), cannot implement cross-user resource sharing
Proposed Solution
Goal: Extend on existing RBAC to support custom roles and ACL permission control.
Constraints:
- Existing user code works without modification (backward compatible)
- Admin can manage custom roles
- ACL supports directory-level sharing (inheritance)
- Storage uses AGFS JSON files
1. Overall Architecture
Request
│
▼
[Auth Middleware] ── Parse API Key → (account_id, user_id, role)
│
▼
[RBAC Guard] ── Check operation permissions by role (require_role)
│
▼
[RequestContext] ── UserIdentifier + Role injected as FastAPI dependency
│
▼
[Router] ── Pass RequestContext to Service
│
▼
[Service Layer] ── Request-level user context
│
├─► [VikingFS] ── Singleton, _uri_to_path isolates by account_id, _is_accessible filters permissions
│ │
│ └─► [ACL Manager] ── Locate authorization list by owner_space, check space/role authorization
│
└─► [VectorDB] ── Single collection, query injects account_id + owner_space filter
│
└─► [Role Manager] ── Role registry management (create/delete custom roles)
[Admin API] ── Role management + ACL management
│
├─► /accounts/{account_id}/roles ── Role CRUD
└─► /accounts/{account_id}/acls ── ACL sharing CRUD
Core Principles:
- Role Extension: Role changed from Enum to str type alias, supports built-in and custom roles
- ACL Dual Authorization: Supports grantee_space (user space) and grantee_role (role) two authorization dimensions
- ACL Organized by Owner: acls indexed by owner_space dimension, efficient query
- Backward Compatible: Original permission check logic takes priority, ACL as extension layer
2. Permission Check Flow
RequestContext
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ VikingFS._is_accessible(uri, ctx) │
├─────────────────────────────────────────────────────────────────────────┤
│ 1. Role check: if role in (ROOT, ADMIN) → allow │
│ 2. Scope check: if scope in (resources, temp) → allow │
│ 3. Space check: if uri's space == user's space → allow │
│ 4. ACL check: ┌─────────────────────────────────────────────────────┐ │
│ │ 4.1 Parse owner_space from URI │ │
│ │ - user/agent/session scope → extract space │ │
│ │ - resources scope → skip ACL (public resource) │ │
│ │ │ │
│ │ 4.2 Lookup ACL: acls[owner_space] │ │
│ │ - not found → no permission │ │
│ │ │ │
│ │ 4.3 Iterate owner's sharing list: │ │
│ │ for acl in acls[owner_space]: │ │
│ │ if uri.startswith(acl.path): │ │
│ │ for entry in acl.entries: │ │
│ │ if grantee_space in ctx.spaces: │ │
│ │ if permission in role_perms: return T │ │
│ │ if grantee_role == ctx.role: │ │
│ │ if permission in role_perms: return T │ │
│ └─────────────────────────────────────────────────────┘ │
│ 5. Default deny: return False │
└─────────────────────────────────────────────────────────────────────────┘
3. Role Management
3.1 Role Types
Two-layer role system:
- Built-in Roles: ROOT, ADMIN, USER (cannot be deleted, as system foundation)
- Custom Roles: Created and managed by Admin, e.g., developer, viewer, project-manager
Role permissions defined via permissions field:
read: Read permissionwrite: Read-write permission (includes read)delete: Delete permissionadmin: Admin permission
3.2 Role Storage
- Root Key:
ov.confserver section (static config) - Per-account role definitions:
/{account_id}/_system/roles.json - User role assignments:
/{account_id}/_system/users.json
Storage structure example:
// /acme/_system/roles.json —— Role definitions
{
"roles": {
"developer": { "description": "Developer", "permissions": ["read", "write"], "created_by": "alice" },
"viewer": { "description": "Viewer", "permissions": ["read"], "created_by": "alice" }
}
}
// /acme/_system/users.json —— User registry (extended role field)
{
"users": {
"alice": { "role": "admin", "key": "..." },
"bob": { "role": "developer", "key": "..." },
"charlie": { "role": "viewer", "key": "..." }
}
}3.3 Admin API
POST /api/v1/admin/accounts/{account_id}/roles Create custom role (ROOT, ADMIN)
GET /api/v1/admin/accounts/{account_id}/roles List all roles (ROOT, ADMIN)
PUT /api/v1/admin/accounts/{account_id}/roles/{role_id} Update role (ROOT, ADMIN)
DELETE /api/v1/admin/accounts/{account_id}/roles/{role_id} Delete custom role (ROOT, ADMIN)
4. ACL Management
4.1 Authorization Dimensions
ACL supports two authorization dimensions:
- grantee_space: Authorize to specific user space (user_space_name or agent_space_name)
- grantee_role: Authorize to specific role (any valid role, including custom roles)
Permission levels:
read: Read-only accesswrite: Read-write access (includes read)
4.2 ACL Storage
Design Consideration: ACL organized by authorizer (owner_space) dimension, not by path.
Advantages:
- Efficient Query: Given URI, directly locate the user's authorization list from acls[owner_space], avoid traversing entire ACL table
- Intuitive Management: Viewing "what resources a user shared" is more natural than "who accessed a resource"
File: /{account_id}/_system/acls.json
{
"acls": {
"alice_space": [
{
"path": "viking://resources/project-alpha/",
"entries": [
{ "grantee_space": "bob_space", "permission": "read" },
{ "grantee_role": "developer", "permission": "write" }
]
},
{
"path": "viking://agent/coding-agent/",
"entries": [
{ "grantee_space": "charlie_agent_space", "permission": "read" }
]
}
],
"bob_space": [
{
"path": "viking://resources/bob-private/",
"entries": [
{ "grantee_role": "viewer", "permission": "read" }
]
}
]
}
}Permission Check Flow:
User accesses URI: viking://resources/project-alpha/README.md
│
▼
1. Parse owner_space from URI:
- resources scope → no space, belongs to account public resource
- Need to infer owner from URI path (see below)
│
▼
2. Lookup ACL: acls[owner_space]
│
▼
3. Iterate owner's sharing list:
- Check uri.startswith(acl.path)
- Check grantee_space or grantee_role in entries
- Match → return corresponding permission
│
▼
4. No match → return no permission
URI to owner_space Mapping:
| URI Pattern | owner_space Resolution |
|---|---|
viking://resources/... |
Account-level public resource, no single owner, skip ACL check |
viking://user/{space}/... |
{space} is owner_space |
viking://agent/{space}/... |
{space} is owner_space |
viking://session/{space}/... |
{space} is owner_space |
4.3 Admin API
POST /api/v1/admin/accounts/{account_id}/acls Create sharing (ROOT, ADMIN)
GET /api/v1/admin/accounts/{account_id}/acls List all ACLs (ROOT, ADMIN)
DELETE /api/v1/admin/accounts/{account_id}/acls Delete sharing (ROOT, ADMIN)
5. Existing Code Changes
5.1 identity.py
Change: Role from Enum to str type alias
# Before
class Role(str, Enum):
ROOT = "root"
ADMIN = "admin"
USER = "user"
# After
Role = str # Type alias
ROOT = "root"
ADMIN = "admin"
USER = "user"
BUILTIN_ROLES = {ROOT, ADMIN, USER}5.2 api_keys.py
New:
- Integrate RoleManager
is_valid_role(account_id, role)methodget_role_permissions(account_id, role)method
Change:
register_user()supports any valid role
5.3 viking_fs.py
New:
_acl_managerattributeset_acl_manager()method_get_role_permissions()method
Change:
_is_accessible()adds ACL check logic
5.4 app.py
New:
- Initialize RoleManager and ACLManager
- Load all accounts' roles and ACLs
5.5 admin.py
New endpoints:
/accounts/{account_id}/roles- GET, POST, PUT, DELETE/accounts/{account_id}/acls- GET, POST, DELETE
6. Key Files
| File | Change Type | Description |
|---|---|---|
openviking/server/identity.py |
Modify | Role to str type, add built-in role constants |
openviking/server/role_manager.py |
New | Role registry management |
openviking/server/acl_manager.py |
New | ACL permission management |
openviking/server/api_keys.py |
Modify | Integrate RoleManager |
openviking/server/auth.py |
Modify | Permission check extension |
openviking/storage/viking_fs.py |
Modify | _is_accessible integrate ACL |
openviking/server/app.py |
Modify | Initialize RoleManager and ACLManager |
openviking/server/routers/admin.py |
Modify | Add role/ACL endpoints |
7. Implementation Order
Phase 1: Role System
T1: Identity Type Definition Update
Modify openviking/server/identity.py
- Role from Enum to str type alias
- Add built-in role constants ROOT, ADMIN, USER
- Add BUILTIN_ROLES set
T2: RoleManager
New openviking/server/role_manager.py
- Load/save roles.json
- create_role, delete_role, get_roles, is_valid_role, get_permissions
T3: APIKeyManager Integration
Modify openviking/server/api_keys.py
- Integrate RoleManager
- register_user supports custom roles
T4: Admin Router Role Endpoints
Modify openviking/server/routers/admin.py
- GET/POST/PUT/DELETE /accounts/{account_id}/roles
Phase 2: ACL System
T5: ACLManager
New openviking/server/acl_manager.py
- Load/save acls.json
- grant, revoke, check_access, delete_by_path
T6: VikingFS Integration
Modify openviking/storage/viking_fs.py
- Add _acl_manager
- _is_accessible adds ACL check
T7: App Initialization Integration
Modify openviking/server/app.py
- Initialize RoleManager and ACLManager
- Inject into VikingFS
T8: Admin Router ACL Endpoints
Modify openviking/server/routers/admin.py
- GET/POST/DELETE /accounts/{account_id}/acls
8. Verification Plan
T1: Role System Tests
- Create custom role "developer"
- Verify users can be assigned custom roles
- Verify custom role login has correct permissions
- Verify cannot delete built-in roles
- Verify ADMIN can manage custom roles
- Verify ADMIN can only operate on their own account
T2: ACL Tests
- alice shares viking://resources/project-alpha/ to bob (space authorization)
- alice shares directory to "developer" role (role authorization)
- bob uses space authorization to access shared directory → success
- User with "developer" role accesses role-authorized directory → success
- Unauthorized user access → 403
- After revoking authorization, access denied
T3: Regression Tests
- Existing tests adapt to new Role type
- Dev mode (no root_api_key) works normally
- Existing users (role=admin/user) login normally
9. Backward Compatibility
roles.jsonandacls.jsonare optional files, created automatically if not exist- Existing role field in users.json continues to work
- VikingFS original permission check logic takes priority, ACL as extension layer
- Dev mode (no root_api_key) not affected
Alternatives Considered
use VectorDB to storage acl entity
Feature Area
Storage/VectorDB
Use Case
Scenario 1: Project Team Sharing
Background
- account:
acme - alice is project lead (admin)
- bob, charlie are developers (developer)
- david is tester (tester)
Steps
1. alice creates roles
POST /api/v1/admin/accounts/acme/roles
{ "role_id": "developer", "description": "Developer", "permissions": ["read", "write"] }
POST /api/v1/admin/accounts/acme/roles
{ "role_id": "tester", "description": "Tester", "permissions": ["read"] }
2. alice registers users and assigns roles
POST /api/v1/admin/accounts/acme/users { "user_id": "bob", "role": "developer" }
POST /api/v1/admin/accounts/acme/users { "user_id": "charlie", "role": "developer" }
POST /api/v1/admin/accounts/acme/users { "user_id": "david", "role": "tester" }
3. alice creates shared directories
POST /api/v1/admin/accounts/acme/acls
{ "path": "viking://resources/project-alpha/", "grantee_role": "developer", "permission": "write" }
POST /api/v1/admin/accounts/acme/acls
{ "path": "viking://resources/project-alpha/", "grantee_role": "tester", "permission": "read" }
4. bob (developer) accesses project directory
- has write permission → can read/write
5. david (tester) accesses project directory
- has read permission → read-only
Scenario 2: Cross-User Resource Sharing
Background
- alice has private materials to share with bob
- Not through role, but direct authorization to user
Steps
1. alice shares private directory with bob
POST /api/v1/admin/accounts/acme/acls
{ "path": "viking://user/alice_space/docs/", "grantee_space": "bob_space", "permission": "read" }
2. bob accesses alice's private directory
GET /api/v1/fs/ls?uri=viking://user/alice_space/docs/
→ 200 OK (gains permission via ACL)
3. charlie attempts to access alice's private directory
GET /api/v1/fs/ls?uri=viking://user/alice_space/docs/
→ 403 Forbidden (no ACL authorization)
Scenario 3: Agent Knowledge Sharing
Background
- alice trained a coding-agent, wants to share with team members
- bob and charlie can both use alice's agent
Steps
1. alice shares agent directory
POST /api/v1/admin/accounts/acme/acls
{ "path": "viking://agent/coding-agent/", "grantee_space": "bob_agent_space", "permission": "read" }
POST /api/v1/admin/accounts/acme/acls
{ "path": "viking://agent/coding-agent/", "grantee_space": "charlie_agent_space", "permission": "read" }
2. bob uses alice's agent
GET /api/v1/fs/ls?uri=viking://agent/coding-agent/
→ 200 OK (accesses alice's agent space via ACL)
Scenario 4: Permission Revocation
Background
- david transfers from developer to viewer
- Need to revoke developer role permissions
Steps
Option 1: Modify user role
PUT /api/v1/admin/accounts/acme/users/david/role
{ "role": "viewer" }
Option 2: Delete ACL authorization
DELETE /api/v1/admin/accounts/acme/acls
{ "path": "viking://resources/project-alpha/", "grantee_space": "david_space" }
Scenario 5: Temporary Authorization
Background
- External auditor eve needs temporary access to certain resources
- Create temporary role or short-term ACL
Steps
1. alice creates temporary role
POST /api/v1/admin/accounts/acme/roles
{ "role_id": "auditor", "description": "Temporary Auditor", "permissions": ["read"] }
2. alice registers temporary user
POST /api/v1/admin/accounts/acme/users { "user_id": "eve", "role": "auditor" }
3. alice authorizes audit scope
POST /api/v1/admin/accounts/acme/acls
{ "path": "viking://resources/audit-2026Q1/", "grantee_role": "auditor", "permission": "read" }
4. After audit completes, delete user and role
DELETE /api/v1/admin/accounts/acme/users/eve
DELETE /api/v1/admin/accounts/acme/roles/auditor
### Example API (Optional)
```python
Additional Context
No response
Contribution
- I am willing to contribute to implementing this feature
Metadata
Metadata
Assignees
Labels
Type
Projects
Status