diff --git a/docs/gns3-server/rbac-acl-implementation-guide.md b/docs/gns3-server/rbac-acl-implementation-guide.md new file mode 100644 index 000000000..5cfe5abb8 --- /dev/null +++ b/docs/gns3-server/rbac-acl-implementation-guide.md @@ -0,0 +1,571 @@ +# GNS3 RBAC + ACL Permission System Implementation Guide + +**Document Version**: 1.0 +**Created**: 2026-03-06 +**Applicable Version**: GNS3 Server v3.0+ + +--- + +## Table of Contents + +- [System Overview](#system-overview) +- [Core Concepts](#core-concepts) +- [Data Model](#data-model) +- [Permission Check Flow](#permission-check-flow) +- [Usage Examples](#usage-examples) +- [Best Practices](#best-practices) +- [Common Issues](#common-issues) + +--- + +## System Overview + +GNS3 Server implements a **two-tier permission control system** that combines **RBAC** (Role-Based Access Control) and **ACL** (Access Control List) features: + +``` +┌─────────────────────────────────────────────────────┐ +│ Tier 1: RBAC (Define Capabilities) │ +│ │ +│ Role → Privilege │ +│ Answers: "What operations can a user perform?" │ +└─────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────┐ +│ Tier 2: ACL (Explicit Authorization) │ +│ │ +│ Default: Deny All │ +│ Unless: ACE Explicitly Allows │ +│ Answers: "On which resources can these operations be used?" │ +└─────────────────────────────────────────────────────┘ +``` + +### Core Principle + +**Default Deny, Explicit Allow** + +Like network device ACLs, all access requests are denied by default unless explicitly allowed by an ACE (Access Control Entry). + +``` +No ACE → ❌ Access Denied +ACE with allowed=False → ❌ Access Denied +ACE with allowed=True → ✅ Access Allowed +``` + +--- + +## Core Concepts + +### 1. Privilege + +Privileges define the operations a user can perform. + +```python +Privilege Naming Format: . + +Examples: +- Project.Audit # View projects +- Project.Allocate # Create/delete projects +- Project.Modify # Modify projects +- Node.Console # Access node console +- Link.Capture # Capture link traffic +``` + +**Predefined Privileges**: 38 built-in privileges (see `gns3server/db/models/privileges.py`) + +### 2. Role + +Roles are collections of privileges that simplify permission management. + +```python +Built-in Roles: +- Administrator: All privileges +- User: Common privileges for projects, nodes, links, snapshots, etc. +- Auditor: Read-only privileges (*.Audit) +- Template manager: Template and symbol management +- User manager: User and group management +- ACL manager: Role and ACE management +- No Access: No privileges +``` + +### 3. User Group + +User groups are used to batch-manage users. + +```python +Built-in Groups: +- Administrators: Administrator group +- Users: Regular user group +``` + +### 4. ACE (Access Control Entry) + +ACEs are the core of access control, defining **on which resources which roles can be used**. + +```python +ACE Structure: +{ + "path": "/projects", # Resource path + "user_id": "uuid", # User ID (choose one with group_id) + "group_id": "uuid", # User group ID (choose one with user_id) + "role_id": "uuid", # Role ID + "allowed": true, # Whether to allow (default true) + "propagate": true, # Whether to propagate to child paths (default true) + "ace_type": "user" # "user" or "group" +} +``` + +**Important**: +- `path`: File system-style paths like `/projects`, `/projects/123` +- `role_id`: The role associated with the ACE, which defines available privileges +- `allowed`: Explicit allow or deny (default true) +- `propagate`: Whether permissions are inherited by child paths (default true) + +--- + +## Data Model + +### Entity Relationships + +``` +User ────< UserGroup > (many-to-many via user_group_map) + │ │ + │ └───< ACE (group_id) + │ + └───< ACE (user_id) + │ + ├── path (resource path) + ├── role → Role → Privilege (privilege) + ├── allowed (allow/deny) + └── propagate (whether to propagate) +``` + +### Database Tables + +| Table | Description | Key Fields | +|-------|-------------|------------| +| `users` | Users | `user_id`, `username`, `is_superadmin` | +| `user_groups` | User groups | `user_group_id`, `name` | +| `roles` | Roles | `role_id`, `name`, `is_builtin` | +| `privileges` | Privileges | `privilege_id`, `name` | +| `acl` (ACE) | Access Control Entries | `ace_id`, `path`, `user_id`, `group_id`, `role_id`, `allowed`, `propagate` | +| `privilege_role_map` | Role-privilege association | `privilege_id`, `role_id` | +| `user_group_map` | User-group association | `user_id`, `user_group_id` | + +--- + +## Permission Check Flow + +### Complete Flowchart + +``` +User Request: GET /projects/123, requires Project.Audit privilege + ↓ +┌─────────────────────────────────────────────────────┐ +│ 1. Extract Request Information │ +│ - User ID │ +│ - Path: /projects/123 │ +│ - Required privilege: Project.Audit │ +└─────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────┐ +│ 2. Special Check: Superadmin │ +│ If is_superadmin = True │ +│ → ✅ Allow directly (bypass RBAC + ACL) │ +└─────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────┐ +│ 3. Query User ACEs │ +│ SELECT * FROM ace │ +│ JOIN privilege_role_map ON ace.role_id = ... │ +│ JOIN privileges ON ... │ +│ WHERE │ +│ ace.user_id = │ +│ AND privileges.name = 'Project.Audit' │ +│ AND ace.path matches /projects/123 │ +└─────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────┐ +│ 4. Check User ACEs │ +│ If matching ACE found: │ +│ - if allowed = False → ❌ Deny │ +│ - if allowed = True → ✅ Allow │ +│ If not found: │ +│ → Continue checking group ACEs │ +└─────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────┐ +│ 5. Query Group ACEs │ +│ Query ACEs for all groups the user belongs to │ +│ (same logic as user ACEs) │ +└─────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────┐ +│ 6. Check Group ACEs │ +│ If matching group ACE found: │ +│ - if allowed = False → ❌ Deny │ +│ - if allowed = True → ✅ Allow │ +│ If not found: │ +│ → ❌ Access denied (deny by default) │ +└─────────────────────────────────────────────────────┘ +``` + +### Path Matching Rules + +Path matching follows the **specific-to-general** principle: + +``` +Request Path: /projects/123/nodes/456 + +Check Order: +1. /projects/123/nodes/456 (most specific) +2. /projects/123/nodes +3. /projects/123 +4. /projects +5. / (most general) +``` + +**Impact of propagate Parameter**: + +```python +# ACE 1: path="/projects", propagate=True +✅ Allow: /projects, /projects/123, /projects/123/nodes +# Permission propagates to all child paths + +# ACE 2: path="/projects", propagate=False +✅ Allow: /projects +❌ Deny: /projects/123, /projects/123/nodes +# Permission does not propagate, only exact match allowed +``` + +--- + +## Usage Examples + +### Scenario 1: Allow User to Access All Projects + +```python +# Create ACE +POST /v3/access/aces +{ + "path": "/projects", + "user_id": "550e8400-e29b-41d4-a716-446655440000", + "role_id": "", + "allowed": true, + "propagate": true +} + +# Result: User can access all projects (/projects/*) +``` + +### Scenario 2: Allow User to Access Only Specific Project + +```python +# Create ACE (exact path) +POST /v3/access/aces +{ + "path": "/projects/my-project-id", + "user_id": "550e8400-e29b-41d4-a716-446655440000", + "role_id": "", + "allowed": true, + "propagate": false +} + +# Result: User can only access /projects/my-project-id +# Cannot access other projects +``` + +### Scenario 3: Use Group Permissions + +```python +# Create ACE for group +POST /v3/access/aces +{ + "path": "/projects", + "group_id": "", + "role_id": "", + "allowed": true, + "propagate": true +} + +# Result: All members of "Users" group can access all projects +``` + +### Scenario 4: Explicitly Deny Specific Resource + +```python +# User can access all projects +ACE: path="/projects", user=A, allowed=true, propagate=true + +# But deny access to a specific secret project +ACE: path="/projects/secret", user=A, allowed=false + +# Result: User can access all projects except /projects/secret +``` + +### Scenario 5: Use Resource Pools + +```python +# Grant user access to resource pool +POST /v3/access/aces +{ + "path": "/pools/pool-123", + "user_id": "550e8400-e29b-41d4-a716-446655440000", + "role_id": "", + "allowed": true, + "propagate": true +} + +# Result: User can access all resources in pool-123 +``` + +--- + +## Best Practices + +### 1. Use Groups for Permission Management (Recommended) + +**Recommended** ✅: +```python +# Create ACE for "Users" group +ACE(path="/projects", group="Users", role="User", allowed=true) +``` + +**Not Recommended** ❌: +```python +# Create separate ACE for each user +ACE(path="/projects", user="user1", role="User", allowed=true) +ACE(path="/projects", user="user2", role="User", allowed=true) +ACE(path="/projects", user="user3", role="User", allowed=true) +# ... Repeat for hundreds of users +``` + +### 2. Use propagate to Reduce Configuration + +**Recommended** ✅: +```python +# Use propagate=True +ACE(path="/projects", group="Users", role="User", allowed=true, propagate=true) +# One ACE covers all projects and sub-resources +``` + +**Not Recommended** ❌: +```python +# Create separate ACE for each project +ACE(path="/projects/1", group="Users", role="User", allowed=true) +ACE(path="/projects/2", group="Users", role="User", allowed=true) +ACE(path="/projects/3", group="Users", role="User", allowed=true) +# ... Difficult to maintain +``` + +### 3. Use Default ACEs + +**Problem**: Fresh system install has no ACEs by default, users cannot access any resources. + +**Solution**: Create default ACEs for default user groups + +```python +# Initialization script +async def create_default_aces(): + users_group = await get_group_by_name("Users") + user_role = await get_role_by_name("User") + + # Create default ACE for "Users" group + await create_ace({ + "path": "/", + "group_id": users_group.id, + "role_id": user_role.id, + "allowed": true, + "propagate": true + }) +``` + +### 4. Audit Permission Configuration + +Regularly check ACE configuration: + +```python +# Query all ACEs +GET /v3/access/aces + +# Check user's actual permissions +GET /v3/access/users/me +# Returns user's groups, accessible pools, ACE list +``` + +--- + +## Common Issues + +### Q1: Why can't a user access resources even with role privileges? + +**A**: This is the most common issue. RBAC defines "what can be done," but ACL limits "where it can be done." + +**Checklist**: +1. Does the user have a matching ACE? +2. Does the ACE `path` match the request path? +3. Is the ACE `allowed` set to `true`? +4. Does the associated `role` have the required privilege? + +```bash +# Check user's ACEs +curl -X GET http://localhost:3080/v3/access/aces \ + -H "Authorization: Bearer " + +# Check user's groups +curl -X GET http://localhost:3080/v3/access/users/me \ + -H "Authorization: Bearer " +``` + +### Q2: What is the purpose of the propagate parameter? + +**A**: `propagate` controls whether permissions are inherited by child paths. + +- `propagate=true`: Permission propagates to all child paths +- `propagate=false`: Permission applies only to the exact path + +``` +ACE: path="/projects", propagate=true +→ Allow: /projects, /projects/1, /projects/1/nodes, ... + +ACE: path="/projects", propagate=false +→ Allow: /projects +→ Deny: /projects/1, /projects/1/nodes, ... +``` + +### Q3: What is the priority of user ACEs vs group ACEs? + +**A**: User ACEs take priority over group ACEs. + +```python +# User ACE +ACE(path="/projects", user=A, role=Auditor, allowed=true) + +# Group ACE (user's group) +ACE(path="/projects", group=Users, role=User, allowed=true) + +# Result: User ACE takes priority, user uses Auditor role +``` + +### Q4: How to deny access to specific resources? + +**A**: Create an ACE with `allowed=false`. + +```python +# User can access all projects +ACE(path="/projects", user=A, role=User, allowed=true, propagate=true) + +# But deny access to secret project +ACE(path="/projects/secret", user=A, role=User, allowed=false) +``` + +**Note**: The deny ACE path must be more specific (longer path). + +### Q5: Are superadmins subject to RBAC + ACL restrictions? + +**A**: No. Users with `is_superadmin=true` bypass all permission checks. + +```python +# Superadmin +{ + "username": "admin", + "is_superadmin": true +} + +# No ACE required to access any resource +``` + +### Q6: What is the path format for resource pools? + +**A**: Resource pools use the `/pools/{pool_id}` format. + +```python +# Grant user access to resource pool +ACE(path="/pools/pool-123", user=A, role=User, allowed=true) + +# Project paths within the pool +# /pools/pool-123/projects/project-1 +``` + +**Note**: There is an inconsistency in the code between `/pool` and `/pools`. Recommendation: use `/pools` (plural form). + +### Q7: How to debug permission issues? + +**A**: Enable debug logging and check the permission check flow. + +```python +# Enable debug logging +import logging +logging.getLogger("gns3server.db.repositories.rbac").setLevel(logging.DEBUG) + +# View logs +# DEBUG:gns3server.db.repositories.rbac:Checking user admin has privilege Project.Audit on '/projects/123' +``` + +--- + +## API Reference + +### Permission Check Related API Endpoints + +| Endpoint | Method | Description | Required Privilege | +|----------|--------|-------------|-------------------| +| `/v3/access/users/me` | GET | Get current user info (includes groups, pools, ACEs) | None (authenticated user) | +| `/v3/access/users` | GET | Get all users | User.Audit | +| `/v3/access/users/{user_id}` | GET | Get specific user | User.Audit | +| `/v3/access/groups` | GET | Get all groups | Group.Audit | +| `/v3/access/roles` | GET | Get all roles | Role.Audit | +| `/v3/access/privileges` | GET | Get all privileges | Role.Audit | +| `/v3/access/aces` | GET | Get all ACEs | ACE.Audit | +| `/v3/access/aces` | POST | Create ACE | ACE.Allocate | +| `/v3/access/aces/{ace_id}` | PUT | Update ACE | ACE.Modify | +| `/v3/access/aces/{ace_id}` | DELETE | Delete ACE | ACE.Allocate | + +--- + +## Code Reference + +| Component | File Path | +|-----------|-----------| +| Data Models | `gns3server/db/models/` | +| - Users and Groups | `users.py` | +| - Roles and Privileges | `roles.py`, `privileges.py` | +| - ACE | `acl.py` | +| RBAC Repository | `gns3server/db/repositories/rbac.py` | +| Permission Check Dependency | `gns3server/api/routes/controller/dependencies/rbac.py` | +| API Routes | `gns3server/api/routes/controller/` | +| - User Routes | `users.py` | +| - RBAC Routes | `roles.py`, `acl.py` | +| Schemas | `gns3server/schemas/controller/rbac.py` | + +--- + +## Summary + +GNS3's RBAC + ACL system is a powerful and flexible permission control framework: + +### Key Points + +1. **Two-Tier Protection**: RBAC defines capabilities, ACL limits scope +2. **Default Deny**: No ACE means access denied +3. **Explicit Allow**: Must have ACE (allowed=true) to access +4. **Role-Based**: ACEs grant privileges through roles +5. **Path Inheritance**: propagate controls permission propagation + +### Design Advantages + +- ✅ Fine-grained Control: Precise resource-level permissions +- ✅ Flexibility: Support user and group-level permissions +- ✅ Centralized Management: Define permissions centrally through roles +- ✅ High Security: Deny all by default, explicit allow + +### Caveats + +- ⚠️ New systems require default ACE creation +- ⚠️ Must configure ACEs for each user/group +- ⚠️ Regularly audit permission configurations +- ⚠️ Superadmin bypasses all restrictions + +--- + +**Document Version**: 1.0 +**Last Updated**: 2026-03-06 diff --git a/docs/gns3-server/rbac-acl-权限系统实现说明.md b/docs/gns3-server/rbac-acl-权限系统实现说明.md new file mode 100644 index 000000000..2967211fc --- /dev/null +++ b/docs/gns3-server/rbac-acl-权限系统实现说明.md @@ -0,0 +1,571 @@ +# GNS3 RBAC + ACL 权限系统实现说明 + +**文档版本**: 1.0 +**创建日期**: 2026-03-06 +**适用版本**: GNS3 Server v3.0+ + +--- + +## 目录 + +- [系统概述](#系统概述) +- [核心概念](#核心概念) +- [数据模型](#数据模型) +- [权限检查流程](#权限检查流程) +- [使用示例](#使用示例) +- [最佳实践](#最佳实践) +- [常见问题](#常见问题) + +--- + +## 系统概述 + +GNS3 Server 实现了一个**两层权限控制系统**,结合了 **RBAC**(基于角色的访问控制)和 **ACL**(访问控制列表)的特性: + +``` +┌─────────────────────────────────────────────────────┐ +│ 第 1 层:RBAC (定义能力) │ +│ │ +│ 角色 → 权限 │ +│ 回答问题:"用户能做什么操作?" │ +└─────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────┐ +│ 第 2 层:ACL (明确授权) │ +│ │ +│ 默认:拒绝所有 (Deny All) │ +│ 除非:ACE 明确允许 (Explicitly Allow) │ +│ 回答问题:"能在哪些资源上使用这些操作?" │ +└─────────────────────────────────────────────────────┘ +``` + +### 核心原则 + +**默认拒绝,明确允许** + +就像网络设备的 ACL,默认情况下拒绝所有访问请求,除非有明确的 ACE(访问控制条目)允许。 + +``` +没有 ACE → ❌ 拒绝访问 +有 ACE (allowed=False) → ❌ 拒绝访问 +有 ACE (allowed=True) → ✅ 允许访问 +``` + +--- + +## 核心概念 + +### 1. 权限 (Privilege) + +权限定义了用户可以执行的操作。 + +```python +权限命名格式: <资源>.<操作> + +示例: +- Project.Audit # 查看项目 +- Project.Allocate # 创建/删除项目 +- Project.Modify # 修改项目 +- Node.Console # 访问节点控制台 +- Link.Capture # 捕获链路流量 +``` + +**预定义权限**: 38 个内置权限(详见 `gns3server/db/models/privileges.py`) + +### 2. 角色 (Role) + +角色是权限的集合,用于简化权限管理。 + +```python +内置角色: +- Administrator: 所有权限 +- User: 项目、节点、链路、快照等常用权限 +- Auditor: 只读权限 (*.Audit) +- Template manager: 模板和符号管理 +- User manager: 用户和组管理 +- ACL manager: 角色和 ACE 管理 +- No Access: 无任何权限 +``` + +### 3. 用户组 (User Group) + +用户组用于批量管理用户。 + +```python +内置组: +- Administrators: 管理员组 +- Users: 普通用户组 +``` + +### 4. ACE (Access Control Entry) + +ACE 是访问控制的核心,定义了**在哪些资源上可以使用哪些角色**。 + +```python +ACE 结构: +{ + "path": "/projects", # 资源路径 + "user_id": "uuid", # 用户 ID (与 group_id 二选一) + "group_id": "uuid", # 用户组 ID (与 user_id 二选一) + "role_id": "uuid", # 角色 ID + "allowed": true, # 是否允许 (默认 true) + "propagate": true, # 是否传播到子路径 (默认 true) + "ace_type": "user" # "user" 或 "group" +} +``` + +**重要**: +- `path`: 使用文件系统风格的路径,如 `/projects`、`/projects/123` +- `role_id`: ACE 关联的角色,角色定义了可用权限 +- `allowed`: 显式允许或拒绝(默认 true) +- `propagate`: 权限是否继承到子路径(默认 true) + +--- + +## 数据模型 + +### 实体关系 + +``` +User ────< UserGroup > (many-to-many via user_group_map) + │ │ + │ └───< ACE (group_id) + │ + └───< ACE (user_id) + │ + ├── path (资源路径) + ├── role → Role → Privilege (权限) + ├── allowed (允许/拒绝) + └── propagate (是否传播) +``` + +### 数据库表 + +| 表名 | 说明 | 关键字段 | +|------|------|----------| +| `users` | 用户 | `user_id`, `username`, `is_superadmin` | +| `user_groups` | 用户组 | `user_group_id`, `name` | +| `roles` | 角色 | `role_id`, `name`, `is_builtin` | +| `privileges` | 权限 | `privilege_id`, `name` | +| `acl` (ACE) | 访问控制条目 | `ace_id`, `path`, `user_id`, `group_id`, `role_id`, `allowed`, `propagate` | +| `privilege_role_map` | 角色-权限关联 | `privilege_id`, `role_id` | +| `user_group_map` | 用户-组关联 | `user_id`, `user_group_id` | + +--- + +## 权限检查流程 + +### 完整流程图 + +``` +用户请求: GET /projects/123,需要 Project.Audit 权限 + ↓ +┌─────────────────────────────────────────────────────┐ +│ 1. 提取请求信息 │ +│ - 用户 ID │ +│ - 路径: /projects/123 │ +│ - 所需权限: Project.Audit │ +└─────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────┐ +│ 2. 特殊检查: Superadmin │ +│ 如果 is_superadmin = True │ +│ → ✅ 直接允许(绕过 RBAC + ACL) │ +└─────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────┐ +│ 3. 查询用户 ACE │ +│ SELECT * FROM ace │ +│ JOIN privilege_role_map ON ace.role_id = ... │ +│ JOIN privileges ON ... │ +│ WHERE │ +│ ace.user_id = │ +│ AND privileges.name = 'Project.Audit' │ +│ AND ace.path 匹配 /projects/123 │ +└─────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────┐ +│ 4. 检查用户 ACE │ +│ 如果找到匹配的 ACE: │ +│ - if allowed = False → ❌ 拒绝 │ +│ - if allowed = True → ✅ 允许 │ +│ 如果没找到: │ +│ → 继续检查组 ACE │ +└─────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────┐ +│ 5. 查询组 ACE │ +│ 查询用户所属的所有组的 ACE │ +│ (查询逻辑与用户 ACE 相同) │ +└─────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────┐ +│ 6. 检查组 ACE │ +│ 如果找到匹配的组 ACE: │ +│ - if allowed = False → ❌ 拒绝 │ +│ - if allowed = True → ✅ 允许 │ +│ 如果没找到: │ +│ → ❌ 拒绝访问(默认拒绝所有) │ +└─────────────────────────────────────────────────────┘ +``` + +### 路径匹配规则 + +路径匹配遵循**从具体到通用**的原则: + +``` +请求路径: /projects/123/nodes/456 + +检查顺序: +1. /projects/123/nodes/456 (最具体) +2. /projects/123/nodes +3. /projects/123 +4. /projects +5. / (最通用) +``` + +**propagate 参数的影响**: + +```python +# ACE 1: path="/projects", propagate=True +✅ 允许访问: /projects, /projects/123, /projects/123/nodes +# 权限传播到所有子路径 + +# ACE 2: path="/projects", propagate=False +✅ 允许访问: /projects +❌ 拒绝访问: /projects/123, /projects/123/nodes +# 权限不传播,只允许精确匹配 +``` + +--- + +## 使用示例 + +### 场景 1: 允许用户访问所有项目 + +```python +# 创建 ACE +POST /v3/access/aces +{ + "path": "/projects", + "user_id": "550e8400-e29b-41d4-a716-446655440000", + "role_id": "", + "allowed": true, + "propagate": true +} + +# 结果: 用户可以访问所有项目 (/projects/*) +``` + +### 场景 2: 允许用户只访问特定项目 + +```python +# 创建 ACE(精确路径) +POST /v3/access/aces +{ + "path": "/projects/my-project-id", + "user_id": "550e8400-e29b-41d4-a716-446655440000", + "role_id": "", + "allowed": true, + "propagate": false +} + +# 结果: 用户只能访问 /projects/my-project-id +# 不能访问其他项目 +``` + +### 场景 3: 使用组权限 + +```python +# 为组创建 ACE +POST /v3/access/aces +{ + "path": "/projects", + "group_id": "", + "role_id": "", + "allowed": true, + "propagate": true +} + +# 结果: "Users" 组的所有成员都可以访问所有项目 +``` + +### 场景 4: 显式拒绝特定资源 + +```python +# 用户可以访问所有项目 +ACE: path="/projects", user=A, allowed=true, propagate=true + +# 但拒绝访问某个秘密项目 +ACE: path="/projects/secret", user=A, allowed=false + +# 结果: 用户可以访问所有项目,除了 /projects/secret +``` + +### 场景 5: 使用资源池 + +```python +# 为用户分配资源池访问权限 +POST /v3/access/aces +{ + "path": "/pools/pool-123", + "user_id": "550e8400-e29b-41d4-a716-446655440000", + "role_id": "", + "allowed": true, + "propagate": true +} + +# 结果: 用户可以访问 pool-123 中的所有资源 +``` + +--- + +## 最佳实践 + +### 1. 使用组管理权限(推荐) + +**推荐** ✅: +```python +# 为 "Users" 组创建 ACE +ACE(path="/projects", group="Users", role="User", allowed=true) +``` + +**不推荐** ❌: +```python +# 为每个用户单独创建 ACE +ACE(path="/projects", user="user1", role="User", allowed=true) +ACE(path="/projects", user="user2", role="User", allowed=true) +ACE(path="/projects", user="user3", role="User", allowed=true) +# ... 为数百个用户重复 +``` + +### 2. 使用 propagate 减少配置 + +**推荐** ✅: +```python +# 使用 propagate=True +ACE(path="/projects", group="Users", role="User", allowed=true, propagate=true) +# 一个 ACE 覆盖所有项目和子资源 +``` + +**不推荐** ❌: +```python +# 为每个项目单独创建 ACE +ACE(path="/projects/1", group="Users", role="User", allowed=true) +ACE(path="/projects/2", group="Users", role="User", allowed=true) +ACE(path="/projects/3", group="Users", role="User", allowed=true) +# ... 难以维护 +``` + +### 3. 使用默认 ACE + +**问题**: 新安装的系统,默认没有任何 ACE,用户无法访问任何资源。 + +**解决方案**: 为默认用户组创建默认 ACE + +```python +# 初始化脚本 +async def create_default_aces(): + users_group = await get_group_by_name("Users") + user_role = await get_role_by_name("User") + + # 为 "Users" 组创建默认 ACE + await create_ace({ + "path": "/", + "group_id": users_group.id, + "role_id": user_role.id, + "allowed": true, + "propagate": true + }) +``` + +### 4. 审计权限配置 + +定期检查 ACE 配置: + +```python +# 查询所有 ACE +GET /v3/access/aces + +# 检查用户的实际权限 +GET /v3/access/users/me +# 返回用户所属组、可访问池、ACE 列表 +``` + +--- + +## 常见问题 + +### Q1: 为什么用户有角色权限,但无法访问资源? + +**A**: 这是最常见的问题。RBAC 定义了"能做什么",但 ACL 限定了"能在哪里做"。 + +**检查清单**: +1. 用户是否有匹配的 ACE? +2. ACE 的 `path` 是否匹配请求路径? +3. ACE 的 `allowed` 是否为 `true`? +4. ACE 关联的 `role` 是否有所需权限? + +```bash +# 检查用户的 ACE +curl -X GET http://localhost:3080/v3/access/aces \ + -H "Authorization: Bearer " + +# 检查用户的组 +curl -X GET http://localhost:3080/v3/access/users/me \ + -H "Authorization: Bearer " +``` + +### Q2: propagate 参数有什么用? + +**A**: `propagate` 控制权限是否继承到子路径。 + +- `propagate=true`: 权限传播到所有子路径 +- `propagate=false`: 权限只应用于精确路径 + +``` +ACE: path="/projects", propagate=true +→ 允许: /projects, /projects/1, /projects/1/nodes, ... + +ACE: path="/projects", propagate=false +→ 允许: /projects +→ 拒绝: /projects/1, /projects/1/nodes, ... +``` + +### Q3: 用户 ACE 和组 ACE 的优先级? + +**A**: 用户 ACE 优先于组 ACE。 + +```python +# 用户 ACE +ACE(path="/projects", user=A, role=Auditor, allowed=true) + +# 组 ACE(同一用户的组) +ACE(path="/projects", group=Users, role=User, allowed=true) + +# 结果: 用户 ACE 优先,用户使用 Auditor 角色 +``` + +### Q4: 如何拒绝访问特定资源? + +**A**: 创建 `allowed=false` 的 ACE。 + +```python +# 用户可以访问所有项目 +ACE(path="/projects", user=A, role=User, allowed=true, propagate=true) + +# 但拒绝访问秘密项目 +ACE(path="/projects/secret", user=A, role=User, allowed=false) +``` + +**注意**: 拒绝 ACE 的路径必须更具体(更长的路径)。 + +### Q5: Superadmin 是否受 RBAC + ACL 限制? + +**A**: 不受限制。`is_superadmin=true` 的用户绕过所有权限检查。 + +```python +# Superadmin +{ + "username": "admin", + "is_superadmin": true +} + +# 访问任何资源都不需要 ACE +``` + +### Q6: 资源池的路径格式是什么? + +**A**: 资源池使用 `/pools/{pool_id}` 格式。 + +```python +# 为用户分配资源池访问权限 +ACE(path="/pools/pool-123", user=A, role=User, allowed=true) + +# 池中的项目路径 +# /pools/pool-123/projects/project-1 +``` + +**注意**: 代码中有 `/pool` 和 `/pools` 的不一致问题,建议使用 `/pools`(复数形式)。 + +### Q7: 如何调试权限问题? + +**A**: 启用调试日志并检查权限检查流程。 + +```python +# 启用调试日志 +import logging +logging.getLogger("gns3server.db.repositories.rbac").setLevel(logging.DEBUG) + +# 查看日志 +# DEBUG:gns3server.db.repositories.rbac:Checking user admin has privilege Project.Audit on '/projects/123' +``` + +--- + +## API 参考 + +### 权限检查相关的 API 端点 + +| 端点 | 方法 | 说明 | 所需权限 | +|------|------|------|----------| +| `/v3/access/users/me` | GET | 获取当前用户信息(包含组、池、ACE) | 无(认证用户) | +| `/v3/access/users` | GET | 获取所有用户 | User.Audit | +| `/v3/access/users/{user_id}` | GET | 获取指定用户 | User.Audit | +| `/v3/access/groups` | GET | 获取所有组 | Group.Audit | +| `/v3/access/roles` | GET | 获取所有角色 | Role.Audit | +| `/v3/access/privileges` | GET | 获取所有权限 | Role.Audit | +| `/v3/access/aces` | GET | 获取所有 ACE | ACE.Audit | +| `/v3/access/aces` | POST | 创建 ACE | ACE.Allocate | +| `/v3/access/aces/{ace_id}` | PUT | 更新 ACE | ACE.Modify | +| `/v3/access/aces/{ace_id}` | DELETE | 删除 ACE | ACE.Allocate | + +--- + +## 代码位置参考 + +| 组件 | 文件路径 | +|------|----------| +| 数据模型 | `gns3server/db/models/` | +| - 用户和组 | `users.py` | +| - 角色和权限 | `roles.py`, `privileges.py` | +| - ACE | `acl.py` | +| RBAC 仓库 | `gns3server/db/repositories/rbac.py` | +| 权限检查依赖 | `gns3server/api/routes/controller/dependencies/rbac.py` | +| API 路由 | `gns3server/api/routes/controller/` | +| - 用户路由 | `users.py` | +| - RBAC 路由 | `roles.py`, `acl.py` | +| Schemas | `gns3server/schemas/controller/rbac.py` | + +--- + +## 总结 + +GNS3 的 RBAC + ACL 系统是一个强大且灵活的权限控制框架: + +### 关键要点 + +1. **两层防护**: RBAC 定义能力,ACL 限定范围 +2. **默认拒绝**: 没有 ACE = 拒绝访问 +3. **明确允许**: 必须有 ACE(allowed=true)才能访问 +4. **角色优先**: ACE 通过角色授予权限 +5. **路径继承**: propagate 控制权限传播 + +### 设计优势 + +- ✅ 细粒度控制:精确到具体资源的权限 +- ✅ 灵活性强:支持用户和组级别的权限 +- ✅ 集中管理:通过角色集中定义权限 +- ✅ 安全性高:默认拒绝所有,显式允许 + +### 注意事项 + +- ⚠️ 新系统需要创建默认 ACE +- ⚠️ 必须为每个用户/组配置 ACE +- ⚠️ 定期审计权限配置 +- ⚠️ Superadmin 绕过所有限制 + +--- + +**文档版本**: 1.0 +**最后更新**: 2026-03-06