Updated the roadmap document to reflect the implemented three-step permission check logic in the feature/simple-user-isolation branch. Added comprehensive design memory documenting: - Core problem and design conflicts - Three-step permission check implementation - Key design decisions and advantages - Use cases and scenarios - Design evolution process
9.9 KiB
RBAC User Isolation Roadmap
Overview
GNS3 3.0 ships with a complete RBAC framework (ACE + Role + Privilege models), but the user isolation layer is incomplete. Users can see resources they should not have access to because:
- Resource creation does not auto-grant the creator an ACE
- List endpoints return unfiltered results
- Two GET endpoints have RBAC checks bypassed via FIXME
This document has been updated to reflect the implemented solution in feature/simple-user-isolation branch.
Current State
| Resource | Route Check | List Filtering | Auto-ACE on Create | ACE Cleanup on Delete |
|---|---|---|---|---|
| Project | Project.Audit/Modify/Allocate |
Implemented — Three-step filtering | Not needed | Done |
| Template | Template.Audit FIXME |
None — all templates returned | Missing | Done |
| Node | Node.Audit/Modify/Allocate |
N/A (inherits project) | Inherits project | N/A |
| Link | Link.Audit/Modify/Allocate |
N/A (inherits project) | Inherits project | N/A |
| Drawing | Drawing.Audit/Modify/Allocate |
N/A (inherits project) | Inherits project | N/A |
| Snapshot | Snapshot.Audit/Allocate/Restore |
N/A (inherits project) | Inherits project | N/A |
| Image | Image.Audit/Allocate |
None — all images returned | Missing | Missing |
| Compute | Compute.Audit FIXME |
None — all computes returned | N/A (shared infra) | Done |
| Appliance | Appliance.Audit/Allocate |
None — all appliances returned | N/A (builtin) | N/A |
| Symbol | Symbol.Audit/Allocate |
None — all symbols returned | N/A (builtin) | N/A |
Implemented Solution: Project Isolation
Three-Step Permission Check Logic
Status: ✅ Implemented in feature/simple-user-isolation branch
# Step 1: ACE check - basic access permission
# Get projects user has ACE for
ace_projects = []
for project in controller.projects.values():
project_path = f"/projects/{project.id}"
if await rbac_repo.check_user_has_privilege(current_user.user_id, project_path, "Project.Audit"):
ace_projects.append(project)
# Step 2: Filter ace_projects by created_by - user's own projects
# Project sharing is only available through resource pools
user_projects = [p.asdict() for p in ace_projects if p.created_by == current_user.username]
projects.extend(user_projects)
# Step 3: Resource pool projects
# Projects shared through resource pools
user_pool_resources = await rbac_repo.get_user_pool_resources(current_user.user_id, "Project.Audit")
project_ids_in_pools = [str(r.resource_id) for r in user_pool_resources if r.resource_type == "project"]
pool_projects = [p.asdict() for p in controller.projects.values() if p.id in project_ids_in_pools]
projects.extend(pool_projects)
Key Design Principles
- ACE for basic access control: Controls whether user can access the system
- created_by for user isolation: Controls which specific resources user can access
- Resource pools for project sharing: The only mechanism for sharing projects between users
- No direct ACE sharing: Users cannot configure ACE directly on specific projects to share them
Advantages of This Approach
- Fault tolerance: Even with broad ACE configuration (
path: "/" + propagate: true), user isolation remains effective - Clear separation: Basic access, data ownership, and team sharing are clearly separated
- Simple mechanism: No complex auto-ACE or seen_project_ids tracking required
- Performance: Leverages existing created_by field, no schema changes needed
Updated Architecture
graph TD
subgraph Client
WebUI
CLI
end
subgraph "Controller API"
Auth[get_current_active_user]
Routes[Resource Routes]
ACE_Check[Step 1: ACE Check]
Owner_Filter[Step 2: Filter by created_by]
Pool_Check[Step 3: Resource Pools]
end
subgraph "RBAC Engine"
ACE[(ACE table)]
Role[(Role table)]
Privilege[(Privilege table)]
Checker[check_user_has_privilege]
end
WebUI --> Auth
CLI --> Auth
Auth --> Routes
Routes --> ACE_Check
ACE_Check --> Checker
ACE_Check --> Owner_Filter
Owner_Filter --> Pool_Check
Pool_Check --> Checker
Checker --> ACE
Checker --> Role
Role --> Privilege
Business Process
Project listing with three-step filtering
sequenceDiagram
actor U as User
participant API as GET /projects
participant Controller as Controller
participant RBAC as RBAC Engine
U->>API: List projects
API->>API: get_current_active_user (not superadmin)
API->>RBAC: Step 1: ACE check on each project
loop Each project
RBAC-->>API: ACE results
end
API->>API: Step 2: Filter by created_by
API-->>U: User's own projects
API->>RBAC: Step 3: Resource pool projects
RBAC-->>API: Pool projects
API-->>U: Combined list (own + pool)
Updated Phased Plan
✅ Phase 1 — MVP: Project isolation (COMPLETED)
Goal: Users only see projects they created or were granted access to through resource pools.
| Task | Files | Status | Detail |
|---|---|---|---|
| Fix project list filtering | projects.py — get_projects() |
✅ Implemented | Three-step filtering: ACE check → created_by filter → resource pools |
Result:
- ✅ Users can only see projects they created
- ✅ Team collaboration through resource pools works
- ✅ Fault tolerance: Works correctly even with broad ACE configurations
- ✅ No schema changes required
- ✅ ~30 lines changed
Phase 2 — Template isolation
Goal: Users see their own templates + builtin templates only.
| Task | Files | Detail |
|---|---|---|
| Apply same pattern to templates | templates.py — get_templates() |
Use same three-step filtering as projects |
Uncomment Template.Audit |
templates.py |
Restore has_privilege("Template.Audit") checks |
Dependency: Web UI must handle 403 from GET /templates/{id}. Mitigation: keep builtin templates unconditionally visible so the UI always has data.
Phase 3 — Image isolation (optional)
Goal: Users see only images they uploaded.
| Task | Files |
|---|---|
| Apply same pattern to images | images.py — get_images() |
| Fix image list filtering | images.py |
| ACE cleanup on delete | images.py — delete_image() |
Phase 4 — Default ACE for "Users" group (optional)
Goal: Users in "Users" group can create/list resources without admin ACE intervention.
| Task | Detail |
|---|---|
Default ACE on /projects |
Grant "Users" group → User role → /projects (propagate=False) |
Default ACE on /templates |
Grant "Users" group → User role → /templates (propagate=False) |
API Endpoints Changed
Phase 1 (Implemented)
| Method | Path | Change |
|---|---|---|
GET |
/v3/projects |
Three-step filtering: ACE → created_by → resource pools |
Phase 2 (Planned)
| Method | Path | Change |
|---|---|---|
GET |
/v3/templates |
Apply same three-step filtering |
GET |
/v3/templates/{id} |
Restore Template.Audit check |
Key Design Decisions
-
Project sharing through resource pools only: Users cannot configure ACE directly to share specific projects. All sharing must go through resource pools. This prevents permission configuration chaos and maintains clear ownership semantics.
-
No auto-ACE required: The three-step filtering logic works without needing automatic ACE creation on project creation. The created_by field provides sufficient ownership information.
-
Fault-tolerant to ACE configuration: Even if administrators configure broad ACE permissions (like
path: "/" + propagate: true), user isolation remains effective because Step 2 filters by created_by. -
No DB migration required: Uses existing ACE/role/privilege tables and created_by field. No schema changes needed.
-
Performance: Project list filtering is O(n) where n is the total number of projects. Each project requires one ACE check. Acceptable for < 500 projects. Can optimize later with batch ACE queries if needed.
Updated References
- Implementation:
gns3server/api/routes/controller/projects.py(feature/simple-user-isolation branch) - Discussion: https://github.com/GNS3/gns3-server/discussions/1949
- RBAC models:
gns3server/db/models/acl.py,roles.py,privileges.py - RBAC repository:
gns3server/db/repositories/rbac.py - Auth dependency:
gns3server/api/routes/controller/dependencies/authentication.py - RBAC dependency:
gns3server/api/routes/controller/dependencies/rbac.py - Resource pools:
gns3server/db/models/pools.pyandgns3server/db/repositories/pools.py
Implementation Notes
What Was Implemented
The feature/simple-user-isolation branch implements a robust user isolation mechanism that:
- Integrates with existing RBAC framework without breaking changes
- Leverages the created_by field that already exists in the Project model
- Uses three-step pipeline filtering to avoid complex seen_project_ids tracking
- Supports team collaboration through existing resource pool functionality
- Is fault-tolerant to ACE misconfiguration - broad ACE permissions don't break user isolation
What Was Not Implemented
The original roadmap's Phase 1 included auto-ACE creation on project creation. This was determined to be unnecessary because:
- The three-step filtering logic achieves user isolation without auto-ACE
- Auto-ACE would add complexity without significant benefit
- Project sharing through resource pools is cleaner than direct ACE configuration
Future Work
The same three-step filtering pattern can be applied to:
- Templates: Replace the FIXME comment with proper filtering logic
- Images: Apply the same pattern for user image isolation
- Other resources: Extend the pattern as needed
This implementation provides a solid foundation for user isolation in GNS3 3.0+ while maintaining compatibility with the existing RBAC framework.