docs: README accuracy pass + Security section
Document the auth/RBAC/SSRF hardening in this branch: mandatory strong SECRET_KEY (server refuses weak/default), admin-only + masked /api-keys, admin-only /webhooks with SSRF guard, project role hierarchy, and auth added to previously-open endpoints. Fixed stale Issues→tasks model. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
232
README.md
232
README.md
@@ -1,100 +1,163 @@
|
|||||||
# HarborForge Backend
|
# HarborForge Backend
|
||||||
|
|
||||||
Agent/人类协同任务管理平台 - FastAPI 后端
|
The core REST API for HarborForge — an Agent/人类协同任务管理平台 (Agent/Human collaborative task-management platform).
|
||||||
|
|
||||||
## API Endpoints (38)
|
Part of the [HarborForge](../README.md) platform.
|
||||||
|
|
||||||
### Auth
|
- **Role:** core REST API — users, projects, tasks, milestones, proposals, RBAC, webhooks, worklogs, notifications, monitor telemetry.
|
||||||
- `POST /auth/token` - 登录获取 JWT token
|
- **Stack:** Python 3.11 · FastAPI · SQLAlchemy · MySQL
|
||||||
- `GET /auth/me` - 获取当前用户信息
|
- **Port:** `8000`
|
||||||
|
|
||||||
### Issues
|
The service reads its database configuration from the AbstractWizard config volume (falling back to env/defaults) and authenticates requests with JWT (HS256) signed by `SECRET_KEY`.
|
||||||
|
|
||||||
> Issues 和 Search 列表接口返回分页格式:`{items, total, page, page_size, total_pages}`
|
## Run / Build
|
||||||
> Issues 支持排序参数:`sort_by` (created_at/priority/title/due_date/status), `sort_order` (asc/desc)
|
|
||||||
> Issues 支持额外过滤:`assignee_id`, `tag`
|
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
> Issues 和 Search 列表接口返回分页格式:
|
```bash
|
||||||
> Issues 支持排序参数: (created_at/priority/title/due_date/status), (asc/desc)
|
docker build -t harborforge-backend .
|
||||||
> Issues 支持额外过滤:,
|
docker run -p 8000:8000 \
|
||||||
- `POST /issues` - 创建 issue(支持 resolution 决议案类型)
|
-e SECRET_KEY="$(openssl rand -hex 32)" \
|
||||||
- `GET /issues` - 列表(分页、排序、按 assignee/tag 过滤)(支持按 project/status/type 过滤)
|
-v /path/to/config:/config \
|
||||||
- `GET /issues/{id}` - 详情
|
harborforge-backend
|
||||||
- `PATCH /issues/{id}` - 更新
|
```
|
||||||
- `DELETE /issues/{id}` - 删除
|
|
||||||
- `POST /issues/{id}/transition` - 状态变更(触发 webhook)
|
|
||||||
- `GET /search/issues?q=keyword` - 搜索
|
|
||||||
|
|
||||||
### Comments
|
### Local (uvicorn)
|
||||||
- `POST /comments` - 创建评论
|
|
||||||
- `GET /issues/{id}/comments` - 列表
|
|
||||||
- `PATCH /comments/{id}` - 更新
|
|
||||||
- `DELETE /comments/{id}` - 删除
|
|
||||||
|
|
||||||
### Projects
|
```bash
|
||||||
- `POST /projects` - 创建
|
pip install -r requirements.txt
|
||||||
- `GET /projects` - 列表
|
export SECRET_KEY="$(openssl rand -hex 32)"
|
||||||
- `GET /projects/{id}` - 详情
|
uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||||
- `PATCH /projects/{id}` - 更新
|
```
|
||||||
- `DELETE /projects/{id}` - 删除
|
|
||||||
|
|
||||||
### Project Members
|
On startup the app creates/migrates the schema, runs AbstractWizard
|
||||||
- `POST /projects/{id}/members` - 添加成员
|
initialization (admin user, default project, default roles), and starts a
|
||||||
- `GET /projects/{id}/members` - 列表
|
background monitor-polling thread.
|
||||||
- `DELETE /projects/{id}/members/{user_id}` - 移除
|
|
||||||
|
|
||||||
### Users
|
## Configuration
|
||||||
- `POST /users` - 注册
|
|
||||||
- `GET /users` - 列表
|
|
||||||
- `GET /users/{id}` - 详情
|
|
||||||
- `PATCH /users/{id}` - 更新
|
|
||||||
|
|
||||||
### Webhooks
|
Environment variables (also loadable from a `.env` file):
|
||||||
- `POST /webhooks` - 创建
|
|
||||||
- `GET /webhooks` - 列表
|
|
||||||
- `GET /webhooks/{id}` - 详情
|
|
||||||
- `PATCH /webhooks/{id}` - 更新
|
|
||||||
- `DELETE /webhooks/{id}` - 删除
|
|
||||||
- `GET /webhooks/{id}/logs` - 投递日志
|
|
||||||
|
|
||||||
### System
|
| Variable | Default | Description |
|
||||||
- `GET /health` - 健康检查
|
|----------|---------|-------------|
|
||||||
- `GET /version` - 版本信息
|
| `SECRET_KEY` | *(none — must be set)* | JWT signing key (HS256). The server **refuses to start** with a weak/default/short value. |
|
||||||
- `GET /dashboard/stats` - 统计面板
|
| `DATABASE_URL` | `mysql+pymysql://harborforge:harborforge_pass@mysql:3306/harborforge` | Fallback DB URL when the wizard config volume is absent. |
|
||||||
|
| `ALGORITHM` | `HS256` | JWT algorithm. |
|
||||||
|
| `ACCESS_TOKEN_EXPIRE_MINUTES` | `30` | Access-token lifetime. |
|
||||||
|
| `LOG_LEVEL` | `INFO` | Log level. |
|
||||||
|
| `CONFIG_DIR` | `/config` | AbstractWizard config volume directory. |
|
||||||
|
| `CONFIG_FILE` | `harborforge.json` | Config file name within `CONFIG_DIR`. |
|
||||||
|
|
||||||
### Milestones
|
Database resolution order: **wizard config volume** (`$CONFIG_DIR/$CONFIG_FILE` → `database` block) → `DATABASE_URL` env → built-in default.
|
||||||
- `POST /milestones` - 创建里程碑
|
|
||||||
- `GET /milestones` - 列表(支持按 project/status 过滤)
|
|
||||||
- `GET /milestones/{id}` - 详情
|
|
||||||
- `PATCH /milestones/{id}` - 更新
|
|
||||||
- `DELETE /milestones/{id}` - 删除
|
|
||||||
- `GET /milestones/{id}/issues` - 里程碑下的 issue 列表
|
|
||||||
- `GET /milestones/{id}/progress` - 里程碑完成进度
|
|
||||||
|
|
||||||
### Notifications
|
## Security
|
||||||
- `GET /notifications` - 列表(支持 user_id, unread_only 过滤)
|
|
||||||
- `GET /notifications/count` - 未读通知计数
|
|
||||||
- `POST /notifications/{id}/read` - 标记已读
|
|
||||||
- `POST /notifications/read-all` - 全部标记已读
|
|
||||||
|
|
||||||
### Issue Assignment
|
The current code enforces the following security posture. These are
|
||||||
- `POST /issues/{id}/assign` - 指派 issue(自动发送通知)
|
operational requirements, not optional hardening.
|
||||||
|
|
||||||
### Webhook Retry
|
### Mandatory strong `SECRET_KEY`
|
||||||
- `POST /webhooks/{id}/retry/{log_id}` - 重试失败的 webhook 投递
|
|
||||||
|
|
||||||
### Time Tracking (Work Logs)
|
`app/core/config.py` validates `SECRET_KEY` at import time and **raises and
|
||||||
- `POST /worklogs` - 记录工时
|
refuses to start** if the value is empty, shorter than 32 characters, or a
|
||||||
- `GET /issues/{id}/worklogs` - 某 issue 的工时记录
|
known default/placeholder (e.g. `change-me-in-production`, `secret`,
|
||||||
- `GET /issues/{id}/worklogs/summary` - 某 issue 工时汇总
|
`changeme`). Operators **must** provide a strong random key:
|
||||||
- `GET /users/{id}/worklogs` - 某用户的工时记录
|
|
||||||
- `DELETE /worklogs/{id}` - 删除工时记录
|
|
||||||
- `GET /projects/{id}/worklogs/summary` - 项目工时汇总(按用户分组)
|
|
||||||
|
|
||||||
### Export
|
```bash
|
||||||
- `GET /export/issues` - 导出 issues CSV
|
openssl rand -hex 32
|
||||||
- `GET /issues/overdue` - 逾期未完成的 issue
|
```
|
||||||
|
|
||||||
|
A weak signing key allows JWT forgery and full authentication bypass, so this
|
||||||
|
check is intentionally fatal.
|
||||||
|
|
||||||
|
### API-key management is admin-only and masked
|
||||||
|
|
||||||
|
The `/api-keys` endpoints (`POST`, `GET`, `DELETE /api-keys/{id}`) all require
|
||||||
|
a global admin (`require_admin`). Listing **never returns the full secret** —
|
||||||
|
keys are masked to a short prefix/suffix (e.g. `abc123…9f`). The full key is
|
||||||
|
only returned once, on creation.
|
||||||
|
|
||||||
|
### Webhooks router is admin-only with SSRF protection
|
||||||
|
|
||||||
|
The entire `/webhooks` router is mounted with `dependencies=[Depends(require_admin)]`,
|
||||||
|
so every webhook endpoint (create/list/get/update/delete/logs/retry) requires a
|
||||||
|
global admin. Webhook delivery (`app/services/webhook.py`) validates the target
|
||||||
|
URL before sending:
|
||||||
|
|
||||||
|
- Only `http`/`https` schemes are allowed.
|
||||||
|
- The host is DNS-resolved and **every** resolved address is rejected if it is
|
||||||
|
private, loopback, link-local, multicast, reserved, or unspecified
|
||||||
|
(SSRF protection).
|
||||||
|
- HTTP redirects are disabled (`follow_redirects=False`).
|
||||||
|
|
||||||
|
### Project role hierarchy enforcement
|
||||||
|
|
||||||
|
`check_project_role` in `app/api/rbac.py` enforces a real, ordered role
|
||||||
|
hierarchy rather than a flat membership check:
|
||||||
|
|
||||||
|
```
|
||||||
|
guest(0) < viewer(1) < member(2) < dev(3) < mgr(4) < admin(5)
|
||||||
|
```
|
||||||
|
|
||||||
|
A caller below the required rank is denied with `403`, and any unknown role on
|
||||||
|
either side is denied by default. Global admins bypass project-level checks.
|
||||||
|
|
||||||
|
### Authentication on previously open endpoints
|
||||||
|
|
||||||
|
The following endpoints now require an authenticated caller
|
||||||
|
(JWT bearer token **or** `X-API-Key`) and enforce ownership/permission:
|
||||||
|
|
||||||
|
- `DELETE /milestones/{id}` — requires milestone-edit permission.
|
||||||
|
- `POST /worklogs` — worklogs are always attributed to the caller; only admins
|
||||||
|
may log time for another user.
|
||||||
|
- `DELETE /worklogs/{id}` — caller-scoped; non-admins cannot delete another
|
||||||
|
user's worklog.
|
||||||
|
- `POST /tasks/{task_code}/assign` and `POST /tasks/batch/assign`.
|
||||||
|
- `GET /activity`.
|
||||||
|
- `GET /export/tasks`.
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
- `POST /auth/token` — OAuth2 password grant; returns a JWT bearer token.
|
||||||
|
- Authenticated requests send `Authorization: Bearer <token>` **or**
|
||||||
|
`X-API-Key: <key>` (API keys map to a user and are created by admins).
|
||||||
|
- `GET /auth/me` — current user.
|
||||||
|
- `GET /auth/me/permissions`, `GET /auth/me/apikey-permissions` — permission introspection.
|
||||||
|
|
||||||
|
## Key API Areas
|
||||||
|
|
||||||
|
| Area | Prefix / Routes | Notes |
|
||||||
|
|------|-----------------|-------|
|
||||||
|
| Auth | `/auth/*` | token, current user, permission introspection |
|
||||||
|
| Users | `/users` | registration, list/detail/update (list & mutate are admin-only) |
|
||||||
|
| Projects | `/projects` | CRUD, members (`/projects/{id}/members`), worklog summary |
|
||||||
|
| Project members | `/projects/{id}/members` | add/list/remove with role |
|
||||||
|
| Milestones | `/projects/{id}/milestones`, `/milestones/{id}` | CRUD, items, progress |
|
||||||
|
| Milestone actions | preflight / freeze / start / close | lifecycle transitions |
|
||||||
|
| Tasks | `/tasks` | CRUD, transition, take, assign, batch transition/assign, tags, search |
|
||||||
|
| Comments | `/comments`, `/tasks/{id}/comments` | CRUD |
|
||||||
|
| Proposals | `/projects/{code}/proposals` | propose / accept / reject / reopen (legacy `/proposes`) |
|
||||||
|
| Essentials | proposal essentials | feature/improvement/refactor items |
|
||||||
|
| Meetings | `/meetings` | create/list/detail/update/delete/attend |
|
||||||
|
| Roles & RBAC | `/roles` | roles, permissions, role-permission assignment |
|
||||||
|
| Webhooks | `/webhooks` | **admin-only**; CRUD, logs, retry (SSRF-guarded delivery) |
|
||||||
|
| API keys | `/api-keys` | **admin-only**; create/list (masked)/revoke |
|
||||||
|
| Worklogs | `/worklogs`, `/tasks/{id}/worklogs`, `/users/{id}/worklogs` | time tracking & summaries |
|
||||||
|
| Notifications | `/notifications` | list, unread count, mark read / read-all |
|
||||||
|
| Activity | `/activity` | activity log (authenticated) |
|
||||||
|
| Export | `/export/tasks` | CSV export (authenticated) |
|
||||||
|
| Calendar | `/calendar` | scheduling / time slots |
|
||||||
|
| Monitor | `/monitor` | public overview, admin providers/servers, heartbeat telemetry |
|
||||||
|
| Dashboard | `/dashboard/stats` | aggregate statistics |
|
||||||
|
| System | `/health`, `/version`, `/config/status` | health, version, init status |
|
||||||
|
|
||||||
|
## Task Types
|
||||||
|
|
||||||
|
| Type | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| issue | 普通任务 |
|
||||||
|
| story | 用户故事 |
|
||||||
|
| test | 测试用例 |
|
||||||
|
| resolution | 决议案(Agent 僵局提交)|
|
||||||
|
|
||||||
## CLI
|
## CLI
|
||||||
|
|
||||||
@@ -102,18 +165,9 @@ The legacy Python CLI (`cli.py`) has been retired. Use the Go-based `hf` CLI ins
|
|||||||
|
|
||||||
See [HarborForge.Cli](../HarborForge.Cli/README.md) for installation and usage.
|
See [HarborForge.Cli](../HarborForge.Cli/README.md) for installation and usage.
|
||||||
|
|
||||||
## 技术栈
|
## Tech Stack
|
||||||
|
|
||||||
- Python 3.11 + FastAPI
|
- Python 3.11 + FastAPI
|
||||||
- SQLAlchemy + MySQL
|
- SQLAlchemy + MySQL (auto schema create/migrate on startup)
|
||||||
- JWT (python-jose)
|
- JWT (python-jose, HS256) + bcrypt password hashing
|
||||||
- Docker
|
- Docker
|
||||||
|
|
||||||
## Issue Types
|
|
||||||
|
|
||||||
| Type | 用途 |
|
|
||||||
|------|------|
|
|
||||||
| task | 普通任务 |
|
|
||||||
| story | 用户故事 |
|
|
||||||
| test | 测试用例 |
|
|
||||||
| resolution | 决议案(Agent 僵局提交)|
|
|
||||||
|
|||||||
Reference in New Issue
Block a user