diff --git a/docs/gns3-copilot/todo/acl-web-ui-implementation-guide.md b/docs/gns3-copilot/todo/acl-web-ui-implementation-guide.md
new file mode 100644
index 000000000..2bbc73f5f
--- /dev/null
+++ b/docs/gns3-copilot/todo/acl-web-ui-implementation-guide.md
@@ -0,0 +1,1206 @@
+# ACL Web UI Implementation Guide
+
+**Document Status**: Design Phase
+**Priority**: High
+**Created**: 2026-03-06
+**Target Audience**: Frontend Developers
+
+---
+
+## Table of Contents
+
+- [Feature Overview](#feature-overview)
+- [API Endpoints Details](#api-endpoints-details)
+- [Data Structures](#data-structures)
+- [Frontend Implementation Guide](#frontend-implementation-guide)
+- [UI/UX Design Recommendations](#uiux-design-recommendations)
+- [Common Scenarios](#common-scenarios)
+- [Error Handling](#error-handling)
+- [Example Code](#example-code)
+
+---
+
+## Feature Overview
+
+### Objective
+
+Implement ACL (Access Control List) management functionality in the GNS3 Web UI, allowing administrators to manage user permissions through a graphical interface.
+
+### Core Features
+
+1. **View ACL List** - Display all ACEs (Access Control Entries)
+2. **Create ACE** - Add new access control entries
+3. **Edit ACE** - Modify existing ACE properties
+4. **Delete ACE** - Remove unwanted access control entries
+5. **Get Available Endpoints** - Retrieve list of resources that can be configured with ACL
+
+### User Flow
+
+```
+Administrator Login
+ ↓
+Navigate to "Permission Management" page
+ ↓
+View existing ACL list
+ ↓
+Select action:
+ - Create new ACE → Fill form → Submit
+ - Edit existing ACE → Modify form → Save
+ - Delete ACE → Confirm → Delete
+```
+
+---
+
+## API Endpoints Details
+
+### Base URL
+
+```
+http://localhost:3080/v3/access/aces
+```
+
+### 1. Get All ACEs
+
+**Endpoint**: `GET /v3/access/aces`
+
+**Privilege**: `ACE.Audit`
+
+**Request Example**:
+```javascript
+const response = await fetch('/v3/access/aces', {
+ headers: {
+ 'Authorization': `Bearer ${token}`
+ }
+});
+const aces = await response.json();
+```
+
+**Response Example**:
+```json
+[
+ {
+ "ace_id": "550e8400-e29b-41d4-a716-446655440000",
+ "path": "/projects",
+ "user_id": "650e8400-e29b-41d4-a716-446655440001",
+ "group_id": null,
+ "role_id": "750e8400-e29b-41d4-a716-446655440002",
+ "allowed": true,
+ "propagate": true,
+ "ace_type": "user",
+ "created_at": "2026-03-06T10:00:00Z",
+ "updated_at": "2026-03-06T10:00:00Z"
+ }
+]
+```
+
+---
+
+### 2. Get Single ACE
+
+**Endpoint**: `GET /v3/access/aces/{ace_id}`
+
+**Privilege**: `ACE.Audit`
+
+**Request Example**:
+```javascript
+const response = await fetch(`/v3/access/aces/${aceId}`, {
+ headers: {
+ 'Authorization': `Bearer ${token}`
+ }
+});
+const ace = await response.json();
+```
+
+---
+
+### 3. Create ACE
+
+**Endpoint**: `POST /v3/access/aces`
+
+**Privilege**: `ACE.Allocate`
+
+**Request Example**:
+```javascript
+const newAce = {
+ path: "/projects",
+ user_id: "550e8400-e29b-41d4-a716-446655440000",
+ role_id: "750e8400-e29b-41d4-a716-446655440002",
+ allowed: true,
+ propagate: true
+};
+
+const response = await fetch('/v3/access/aces', {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(newAce)
+});
+const createdAce = await response.json();
+```
+
+**Response**: `201 Created` + Created ACE object
+
+---
+
+### 4. Update ACE ⭐ Core Feature
+
+**Endpoint**: `PUT /v3/access/aces/{ace_id}`
+
+**Privilege**: `ACE.Modify`
+
+**Request Example**:
+```javascript
+const updatedAce = {
+ path: "/projects/updated",
+ allowed: false,
+ propagate: false
+};
+
+const response = await fetch(`/v3/access/aces/${aceId}`, {
+ method: 'PUT',
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(updatedAce)
+});
+const updated = await response.json();
+```
+
+**Important**:
+- Supports **partial updates** - only submit fields to modify
+- `user_id` and `group_id` cannot both be present (choose one)
+- `path` must match an existing API endpoint
+
+---
+
+### 5. Delete ACE
+
+**Endpoint**: `DELETE /v3/access/aces/{ace_id}`
+
+**Privilege**: `ACE.Allocate`
+
+**Request Example**:
+```javascript
+const response = await fetch(`/v3/access/aces/${aceId}`, {
+ method: 'DELETE',
+ headers: {
+ 'Authorization': `Bearer ${token}`
+ }
+});
+// Response: 204 No Content
+```
+
+---
+
+### 6. Get Available Endpoints (for Path Selection)
+
+**Endpoint**: `GET /v3/access/aces/endpoints`
+
+**Privilege**: `ACE.Audit`
+
+**Request Example**:
+```javascript
+const response = await fetch('/v3/access/aces/endpoints', {
+ headers: {
+ 'Authorization': `Bearer ${token}`
+ }
+});
+const endpoints = await response.json();
+```
+
+**Response Example**:
+```json
+[
+ {
+ "endpoint": "/",
+ "name": "All endpoints",
+ "endpoint_type": "root"
+ },
+ {
+ "endpoint": "/projects",
+ "name": "All projects",
+ "endpoint_type": "project"
+ },
+ {
+ "endpoint": "/projects/123e4567-e89b-12d3-a456-426614174000",
+ "name": "Project \"My Project\"",
+ "endpoint_type": "project"
+ },
+ {
+ "endpoint": "/projects/123e4567-e89b-12d3-a456-426614174000/nodes",
+ "name": "All nodes in project \"My Project\"",
+ "endpoint_type": "node"
+ },
+ {
+ "endpoint": "/pools",
+ "name": "All resource pools",
+ "endpoint_type": "pool"
+ }
+]
+```
+
+---
+
+### 7. Get Related Data
+
+#### Get Users List
+
+**Endpoint**: `GET /v3/access/users`
+
+**Privilege**: `User.Audit`
+
+```javascript
+const response = await fetch('/v3/access/users', {
+ headers: { 'Authorization': `Bearer ${token}` }
+});
+const users = await response.json();
+```
+
+#### Get User Groups List
+
+**Endpoint**: `GET /v3/access/groups`
+
+**Privilege**: `Group.Audit`
+
+```javascript
+const response = await fetch('/v3/access/groups', {
+ headers: { 'Authorization': `Bearer ${token}` }
+});
+const groups = await response.json();
+```
+
+#### Get Roles List
+
+**Endpoint**: `GET /v3/access/roles`
+
+**Privilege**: `Role.Audit`
+
+```javascript
+const response = await fetch('/v3/access/roles', {
+ headers: { 'Authorization': `Bearer ${token}` }
+});
+const roles = await response.json();
+```
+
+---
+
+## Data Structures
+
+### ACE Object
+
+```typescript
+interface ACE {
+ ace_id: string; // UUID
+ path: string; // Resource path, e.g., "/projects"
+ user_id?: string; // User ID (choose one with group_id)
+ group_id?: string; // User group ID (choose one with user_id)
+ role_id: string; // Role ID
+ allowed: boolean; // Whether allowed (default true)
+ propagate: boolean; // Whether to propagate to child paths (default true)
+ ace_type: "user" | "group"; // "user" or "group"
+ created_at: string; // ISO 8601 timestamp
+ updated_at: string; // ISO 8601 timestamp
+}
+```
+
+### ACECreate (Creating ACE)
+
+```typescript
+interface ACECreate {
+ path: string;
+ user_id?: string;
+ group_id?: string;
+ role_id: string;
+ allowed?: boolean;
+ propagate?: boolean;
+}
+```
+
+### ACEUpdate (Updating ACE)
+
+```typescript
+interface ACEUpdate {
+ path?: string;
+ user_id?: string;
+ group_id?: string;
+ role_id?: string;
+ allowed?: boolean;
+ propagate?: boolean;
+}
+```
+
+### Endpoint Object
+
+```typescript
+interface Endpoint {
+ endpoint: string; // Endpoint path
+ name: string; // Display name
+ endpoint_type: string; // "root" | "project" | "node" | "link" | "user" | "group" | "role" | "pool"
+}
+```
+
+---
+
+## Frontend Implementation Guide
+
+### Recommended Tech Stack
+
+- **Framework**: React / Vue / Angular
+- **State Management**: Redux / Vuex / NgRx
+- **UI Components**: Material-UI / Ant Design / Element Plus
+- **HTTP Client**: Axios / Fetch API
+
+### Core Component Design
+
+#### 1. ACE List Component
+
+```
+
+ ├── # Filter
+ ├── # ACE table
+ │ ├──
+ │ ├──
+ │ └── ...
+ └── # Create button
+```
+
+#### 2. ACE Form Component (Create/Edit)
+
+```
+ # or mode="edit"
+ ├── # Path selector
+ ├── # User/group selector
+ ├── # Role selector
+ ├── # Allow/deny toggle
+ ├── # Propagate switch
+ └──
+```
+
+#### 3. Endpoint Selector Component
+
+```
+
+ ├── # Tree structure showing endpoints
+ │ ├──
+ │ ├──
+ │ └── ...
+ └── # Search box
+```
+
+---
+
+### State Management
+
+```typescript
+// Redux example
+interface ACLState {
+ aces: ACE[];
+ endpoints: Endpoint[];
+ users: User[];
+ groups: Group[];
+ roles: Role[];
+ loading: boolean;
+ error: string | null;
+}
+
+// Actions
+const loadACEs = () => async (dispatch) => {
+ dispatch({ type: 'ACL_LOAD_START' });
+ try {
+ const aces = await api.getACEs();
+ dispatch({ type: 'ACL_LOAD_SUCCESS', payload: aces });
+ } catch (error) {
+ dispatch({ type: 'ACL_LOAD_ERROR', payload: error.message });
+ }
+};
+
+const createACE = (ace: ACECreate) => async (dispatch) => {
+ const newAce = await api.createACE(ace);
+ dispatch({ type: 'ACL_CREATE_SUCCESS', payload: newAce });
+};
+
+const updateACE = (aceId: string, updates: ACEUpdate) => async (dispatch) => {
+ const updated = await api.updateACE(aceId, updates);
+ dispatch({ type: 'ACL_UPDATE_SUCCESS', payload: updated });
+};
+
+const deleteACE = (aceId: string) => async (dispatch) => {
+ await api.deleteACE(aceId);
+ dispatch({ type: 'ACL_DELETE_SUCCESS', payload: aceId });
+};
+```
+
+---
+
+## UI/UX Design Recommendations
+
+### 1. List Page Layout
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ Permission Management [New ACE] │
+├─────────────────────────────────────────────────────────┤
+│ Filters: [User/Group] [Role] [Path Type] [Status] │
+├─────────────────────────────────────────────────────────┤
+│ ┌───────────────────────────────────────────────────┐ │
+│ │ Path │ User/Group │ Role │ Allow │ Action│ │
+│ ├───────────────────────────────────────────────────┤ │
+│ │ /projects │ user1 │ User │ ✓ │ Edit │ │
+│ │ /projects/123 │ group1 │ Auditor│ ✓ │ Edit │ │
+│ │ /pools/456 │ user2 │ User │ ✗ │ Edit │ │
+│ └───────────────────────────────────────────────────┘ │
+│ [Load More] │
+└─────────────────────────────────────────────────────────┘
+```
+
+### 2. Create/Edit Form
+
+```
+┌────────────────────────────────────────────┐
+│ New Access Control Entry (ACE) [Cancel]│
+├────────────────────────────────────────────┤
+│ │
+│ Resource Path * │
+│ ┌──────────────────────────────────────┐ │
+│ │ ▼ Select Endpoint [Search]│ │
+│ └──────────────────────────────────────┘ │
+│ Hint: Select the resource path to control │
+│ │
+│ Apply To * │
+│ ○ User □ User Group │
+│ ┌──────────────────────────────────────┐ │
+│ │ ▼ Select User │ │
+│ └──────────────────────────────────────┘ │
+│ │
+│ Role * │
+│ ┌──────────────────────────────────────┐ │
+│ │ User ▼ │ │
+│ └──────────────────────────────────────┘ │
+│ Note: This role includes Project.Audit │
+│ and other privileges │
+│ │
+│ Permission Settings │
+│ ○ Allow Access ● Deny Access │
+│ │
+│ □ Propagate to Child Paths │
+│ Note: When checked, permission applies │
+│ to all child resources │
+│ │
+│ [Cancel] [Save] │
+└────────────────────────────────────────────┘
+```
+
+### 3. Endpoint Selector (Tree Structure)
+
+```
+┌────────────────────────────────────────────┐
+│ Select Resource Path [Search] │
+├────────────────────────────────────────────┤
+│ 📁 / (All endpoints) │
+│ 📁 /projects (All projects) │
+│ 📁 My Project │
+│ 📁 /nodes (All nodes) │
+│ 📄 Router1 │
+│ 📄 Switch1 │
+│ 📁 /links (All links) │
+│ 📁 /pools (All resource pools) │
+│ 📄 Production Pool │
+│ 📁 /templates (All templates) │
+└────────────────────────────────────────────┘
+```
+
+### 4. Interaction Design Points
+
+#### Loading States
+```javascript
+// Show skeleton screen
+
+
+// Or progress bar
+
+```
+
+#### Error Messages
+```javascript
+// Form validation error
+
+ Path cannot be empty
+
+
+// API error
+
+```
+
+#### Confirmation Dialog
+```javascript
+// Delete confirmation
+
+```
+
+---
+
+## Common Scenarios
+
+### Scenario 1: Create User-Level ACE
+
+```javascript
+async function createUserACE() {
+ const newAce = {
+ path: "/projects",
+ user_id: selectedUserId,
+ role_id: userRoleId,
+ allowed: true,
+ propagate: true
+ };
+
+ try {
+ const ace = await api.createACE(newAce);
+ showSuccess('ACE created successfully');
+ loadACEList(); // Refresh list
+ } catch (error) {
+ showError(`Creation failed: ${error.message}`);
+ }
+}
+```
+
+### Scenario 2: Create Group-Level ACE
+
+```javascript
+async function createGroupACE() {
+ const newAce = {
+ path: "/templates",
+ group_id: usersGroupId,
+ role_id: userRoleId,
+ allowed: true,
+ propagate: false
+ };
+
+ try {
+ const ace = await api.createACE(newAce);
+ showSuccess('Group ACE created successfully');
+ loadACEList();
+ } catch (error) {
+ showError(`Creation failed: ${error.message}`);
+ }
+}
+```
+
+### Scenario 3: Edit Existing ACE
+
+```javascript
+async function editACE(aceId, updates) {
+ try {
+ const updated = await api.updateACE(aceId, updates);
+ showSuccess('ACE updated successfully');
+ loadACEList();
+ } catch (error) {
+ showError(`Update failed: ${error.message}`);
+ }
+}
+
+// Example: Toggle allowed status
+async function toggleAllowed(ace) {
+ await editACE(ace.ace_id, {
+ allowed: !ace.allowed
+ });
+}
+
+// Example: Change path
+async function changePath(aceId, newPath) {
+ await editACE(aceId, {
+ path: newPath
+ });
+}
+```
+
+### Scenario 4: Delete ACE
+
+```javascript
+async function deleteACE(aceId) {
+ if (!confirm('Are you sure you want to delete this ACE?')) {
+ return;
+ }
+
+ try {
+ await api.deleteACE(aceId);
+ showSuccess('ACE deleted successfully');
+ loadACEList();
+ } catch (error) {
+ showError(`Deletion failed: ${error.message}`);
+ }
+}
+```
+
+### Scenario 5: Batch Operations
+
+```javascript
+// Batch delete
+async function batchDelete(aceIds) {
+ const promises = aceIds.map(id => api.deleteACE(id));
+ await Promise.all(promises);
+ showSuccess(`Deleted ${aceIds.length} ACEs`);
+ loadACEList();
+}
+
+// Batch toggle allowed status
+async function batchToggleAllowed(aceIds, allowed) {
+ const promises = aceIds.map(id =>
+ api.updateACE(id, { allowed })
+ );
+ await Promise.all(promises);
+ showSuccess(`Updated ${aceIds.length} ACEs`);
+ loadACEList();
+}
+```
+
+---
+
+## Error Handling
+
+### Common Error Types
+
+| HTTP Status | Error Description | Handling |
+|-------------|-------------------|----------|
+| 400 Bad Request | Path doesn't match any endpoint | Prompt user to select valid path |
+| 400 Bad Request | Both user_id and group_id present | Prompt user to choose one |
+| 403 Forbidden | Insufficient privileges | Prompt user to contact administrator |
+| 404 Not Found | ACE doesn't exist | Refresh list, notify resource deleted |
+| 422 Unprocessable Entity | Validation failed | Display specific field errors |
+
+### Error Handling Example
+
+```javascript
+async function handleAPICall(apiFunction) {
+ try {
+ const result = await apiFunction();
+ return { success: true, data: result };
+ } catch (error) {
+ if (error.response) {
+ const { status, data } = error.response;
+
+ switch (status) {
+ case 400:
+ return {
+ success: false,
+ error: data.detail || 'Invalid request parameters'
+ };
+
+ case 403:
+ return {
+ success: false,
+ error: 'Insufficient privileges, please contact administrator'
+ };
+
+ case 404:
+ return {
+ success: false,
+ error: 'Resource not found, may have been deleted'
+ };
+
+ case 422:
+ return {
+ success: false,
+ error: formatValidationError(data.detail)
+ };
+
+ default:
+ return {
+ success: false,
+ error: 'Unknown error, please try again later'
+ };
+ }
+ }
+
+ return {
+ success: false,
+ error: 'Network error, please check connection'
+ };
+ }
+}
+
+function formatValidationError(errors) {
+ // Format validation error messages
+ return errors.map(err => err.msg).join(', ');
+}
+```
+
+---
+
+## Example Code
+
+### Complete React Component Example
+
+```jsx
+import React, { useState, useEffect } from 'react';
+import {
+ Table,
+ Button,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ Select,
+ MenuItem,
+ Switch,
+ FormControlLabel,
+ TextField,
+ IconButton
+} from '@mui/material';
+import { Edit, Delete, Add } from '@mui/icons-material';
+
+// API Client
+const api = {
+ async getACEs(token) {
+ const res = await fetch('/v3/access/aces', {
+ headers: { 'Authorization': `Bearer ${token}` }
+ });
+ if (!res.ok) throw new Error(res.statusText);
+ return res.json();
+ },
+
+ async getEndpoints(token) {
+ const res = await fetch('/v3/access/aces/endpoints', {
+ headers: { 'Authorization': `Bearer ${token}` }
+ });
+ if (!res.ok) throw new Error(res.statusText);
+ return res.json();
+ },
+
+ async getUsers(token) {
+ const res = await fetch('/v3/access/users', {
+ headers: { 'Authorization': `Bearer ${token}` }
+ });
+ if (!res.ok) throw new Error(res.statusText);
+ return res.json();
+ },
+
+ async getGroups(token) {
+ const res = await fetch('/v3/access/groups', {
+ headers: { 'Authorization': `Bearer ${token}` }
+ });
+ if (!res.ok) throw new Error(res.statusText);
+ return res.json();
+ },
+
+ async getRoles(token) {
+ const res = await fetch('/v3/access/roles', {
+ headers: { 'Authorization': `Bearer ${token}` }
+ });
+ if (!res.ok) throw new Error(res.statusText);
+ return res.json();
+ },
+
+ async createACE(token, data) {
+ const res = await fetch('/v3/access/aces', {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(data)
+ });
+ if (!res.ok) {
+ const error = await res.json();
+ throw new Error(error.detail || 'Creation failed');
+ }
+ return res.json();
+ },
+
+ async updateACE(token, aceId, data) {
+ const res = await fetch(`/v3/access/aces/${aceId}`, {
+ method: 'PUT',
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(data)
+ });
+ if (!res.ok) {
+ const error = await res.json();
+ throw new Error(error.detail || 'Update failed');
+ }
+ return res.json();
+ },
+
+ async deleteACE(token, aceId) {
+ const res = await fetch(`/v3/access/aces/${aceId}`, {
+ method: 'DELETE',
+ headers: { 'Authorization': `Bearer ${token}` }
+ });
+ if (!res.ok) throw new Error('Deletion failed');
+ }
+};
+
+// ACE Form Component
+function ACEForm({ token, ace, onSave, onCancel }) {
+ const [endpoints, setEndpoints] = useState([]);
+ const [users, setUsers] = useState([]);
+ const [groups, setGroups] = useState([]);
+ const [roles, setRoles] = useState([]);
+ const [formData, setFormData] = useState({
+ path: ace?.path || '',
+ user_id: ace?.user_id || '',
+ group_id: ace?.group_id || '',
+ role_id: ace?.role_id || '',
+ allowed: ace?.allowed ?? true,
+ propagate: ace?.propagate ?? true,
+ ace_type: ace?.ace_type || 'user'
+ });
+
+ useEffect(() => {
+ Promise.all([
+ api.getEndpoints(token),
+ api.getUsers(token),
+ api.getGroups(token),
+ api.getRoles(token)
+ ]).then(([endpoints, users, groups, roles]) => {
+ setEndpoints(endpoints);
+ setUsers(users);
+ setGroups(groups);
+ setRoles(roles);
+ });
+ }, [token]);
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+
+ const data = {
+ ...formData,
+ [formData.ace_type === 'user' ? 'user_id' : 'group_id']:
+ formData.ace_type === 'user' ? formData.user_id : formData.group_id
+ };
+ delete data.ace_type;
+ delete data.user_id;
+ delete data.group_id;
+
+ if (formData.ace_type === 'user') {
+ data.user_id = formData.user_id;
+ } else {
+ data.group_id = formData.group_id;
+ }
+
+ try {
+ if (ace) {
+ await api.updateACE(token, ace.ace_id, data);
+ } else {
+ await api.createACE(token, data);
+ }
+ onSave();
+ } catch (error) {
+ alert(error.message);
+ }
+ };
+
+ return (
+
+ );
+}
+
+// ACE List Component
+export default function ACLManagement({ token }) {
+ const [aces, setAces] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [dialogOpen, setDialogOpen] = useState(false);
+ const [editingACE, setEditingACE] = useState(null);
+
+ const loadACEs = async () => {
+ setLoading(true);
+ try {
+ const data = await api.getACEs(token);
+ setAces(data);
+ } catch (error) {
+ alert(`Loading failed: ${error.message}`);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ loadACEs();
+ }, [token]);
+
+ const handleCreate = () => {
+ setEditingACE(null);
+ setDialogOpen(true);
+ };
+
+ const handleEdit = (ace) => {
+ setEditingACE(ace);
+ setDialogOpen(true);
+ };
+
+ const handleDelete = async (aceId) => {
+ if (!confirm('Are you sure you want to delete this ACE?')) return;
+
+ try {
+ await api.deleteACE(token, aceId);
+ loadACEs();
+ } catch (error) {
+ alert(`Deletion failed: ${error.message}`);
+ }
+ };
+
+ const handleSave = () => {
+ setDialogOpen(false);
+ loadACEs();
+ };
+
+ return (
+
+
+
Access Control List (ACL)
+ }
+ onClick={handleCreate}
+ >
+ New ACE
+
+
+
+ {loading ? (
+
Loading...
+ ) : (
+
+
+
+ | Path |
+ User/Group |
+ Role |
+ Allowed |
+ Propagate |
+ Actions |
+
+
+
+ {aces.map((ace) => (
+
+ | {ace.path} |
+ {ace.ace_type === 'user' ? ace.user_id : ace.group_id} |
+ {ace.role_id} |
+ {ace.allowed ? '✓' : '✗'} |
+ {ace.propagate ? '✓' : '✗'} |
+
+ handleEdit(ace)}>
+
+
+ handleDelete(ace.ace_id)}>
+
+
+ |
+
+ ))}
+
+
+ )}
+
+
+
+ );
+}
+```
+
+---
+
+## Implementation Checklist
+
+### Basic Features
+- [ ] Fetch and display ACE list
+- [ ] Create new ACE
+- [ ] Edit existing ACE
+- [ ] Delete ACE
+- [ ] Fetch and display available endpoints
+
+### Form Features
+- [ ] Path selector (dropdown/tree)
+- [ ] User/group selector
+- [ ] Role selector
+- [ ] Allow/deny toggle
+- [ ] Propagate switch
+- [ ] Form validation
+
+### User Experience
+- [ ] Loading state display
+- [ ] Error notifications
+- [ ] Success notifications
+- [ ] Delete confirmation dialog
+- [ ] List filtering/search
+
+### Advanced Features
+- [ ] Batch operations
+- [ ] ACE export/import
+- [ ] Permission preview
+- [ ] Change history
+
+---
+
+## References
+
+### Related Documentation
+- [RBAC + ACL Permission System Implementation Guide](../gns3-server/rbac-acl-权限系统实现说明.md)
+- [RBAC + ACL Implementation Guide](../gns3-server/rbac-acl-implementation-guide.md)
+
+### API Specifications
+- OpenAPI/Swagger: `/static/swagger-ui-bundle.js`
+- ReDoc: `/static/redoc.standalone.js`
+
+### Test Data
+```javascript
+// Example ACEs for testing
+const exampleACEs = [
+ {
+ path: "/projects",
+ ace_type: "user",
+ user_id: "admin-user-id",
+ role_id: "user-role-id",
+ allowed: true,
+ propagate: true
+ },
+ {
+ path: "/projects/sensitive",
+ ace_type: "group",
+ group_id: "auditors-group-id",
+ role_id: "auditor-role-id",
+ allowed: false,
+ propagate: false
+ }
+];
+```
+
+---
+
+**Document Version**: 1.0
+**Last Updated**: 2026-03-06