Files
WebDAV/docs/REQUIREMENTS.md

88 lines
4.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# davbox · 需求文档
## 0. 定位
用户需要一个**概念只有两个**的自建 WebDAV 服务:
- **admin**:管理账号(新增 / 删除 / 改密码 / 看目录与用量)
- **client**:账号登录后管理自己的文件
- **主要消费者是 App**:思源笔记、Joplin 等通过 WebDAV 同步
设计分工(关键):**WebDAV 协议不自研**(底盘用 Go 官方 `golang.org/x/net/webdav`),
**产品层 100% 自研**(账号模型、目录隔离、admin 页、client 页)。
## 1. 阶段一:底盘(本次委托范围)
**做**:一个 Go 二进制,读账号配置,按账号隔离地提供完整 WebDAV 服务。
**不做**(留给阶段二/三):admin 页、client 页、配额、HTTPS、SSO、日志文件、数据库。
阶段一唯一目的:**证明底盘与隔离是可信的**。
### 硬性边界(违反任何一条即视为任务失败)
- **绝不** 执行 `systemctl` / `service` 任何操作
- **绝不** 执行 `pkill` / `killall` / `kill` 任何**已存在**的进程
- **绝不** 触碰任何已在运行的端口(80 / 443 等)
- **绝不** 修改仓库之外的任何系统配置(服务、容器、nginx、cron、其他项目的文件)
- **不新增第三方依赖**:只允许 `golang.org/x/net/webdav`
- 只准修改本仓库内的文件
- 服务只准监听 `127.0.0.1:18900`
- 测试进程自己起自己收,按 PID 收尾
### 交付物
```
./
├── cmd/davbox/main.go # 入口
├── internal/account/ # 账号加载 + Basic 认证 + 路由
├── internal/fs/ # 按账号 chroot 的文件系统封装
├── go.mod # module davbox
├── accounts.json # 示例:app1 / app2
└── davbox # go build 产物
```
### 功能要求
1. **配置**:`accounts.json` 为数组,每项
`{ "user": "app1", "pass": "...", "root": "/abs/path", "readonly": false }`
2. **认证**:HTTP Basic,口令用 `crypto/subtle` 常量时间比较;
未认证/失败 → `401`,**必须带 `WWW-Authenticate: Basic realm="davbox"`**(客户端靠它重试)
3. **URL 空间**:`/<user>/...` → 剥离前缀映射到该账号 `root` 下的相对路径
例:账号 `app1`(root=`/srv/dav/app1`)收 `PUT /app1/notes/a.txt` → 落盘 `/srv/dav/app1/notes/a.txt`
4. **越权必须失败**:访问他人前缀、`../`、`%2e%2e`、双重编码、绝对路径逃逸 → 401/403/404 之一,绝不允许成功
5. **只读账号**:PUT / DELETE / MKCOL / MOVE / COPY / PROPPATCH → 403;GET / PROPFIND 正常
6. **六个动词可用**:GET / PUT / DELETE / MKCOL / MOVE / PROPFIND(PROPFIND 正确处理 `Depth: 0` 与 `Depth: 1` 并返回合法 `207 Multi-Status`)
7. **启动参数**:`-addr 127.0.0.1:18900 -c accounts.json`;日志到 stdout,不写文件
8. `root` 不存在时自动 `MkdirAll`
9. Basic 认证通过后的请求**不得**把口令写进日志
### 验收标准(逐条实测,不看自述)
| # | 项目 | 通过条件 |
|---|---|---|
| 1 | 六动词 | GET / PUT / DELETE / MKCOL / MOVE / PROPFIND 全部返回正确状态码 |
| 2 | 跨账号读 | app1 读 app2 目录 → 401/403/404 |
| 3 | 跨账号写 | app1 写 app2 目录 → 401/403/404 |
| 4 | `../` 逃逸 | 错误码,且磁盘不产生越狱文件 |
| 5 | `%2e%2e` 逃逸 | 同上 |
| 6 | 落盘位置 | app1 写的内容只在 app1 的 root 里 |
| 7 | 未认证 | 401 且响应头含 `WWW-Authenticate` |
| 8 | 只读账号 | 写 403 / 读 200-207 |
| 9 | 空闲内存 | RSS < 30 MB |
| 10 | 收尾 | 只监听 127.0.0.1:18900;按 PID 可干净停掉 |
| 11 | 合规(加分项) | 若能跑通 litmus 基础集更好(`internal/litmus_test_server.go` 有现成入口) |
## 2. 阶段二:admin 页 + client 页(下一轮委托)
- **admin 页**(登录:独立管理员账号)
- 列表:应用名 / 目录 / 已用空间 / 只读开关 / 最近活动
- 操作:新增应用(自动生成强口令 + 建目录)、改密码、改只读、停用、删除(目录保留)
- 每行一个「复制连接信息」按钮:地址 + 用户名 + 口令,一次复制走
- **client 页**(登录:应用账号)
- 浏览 / 上传 / 下载 / 删除 / 新建文件夹 / 重命名;面包屑导航;移动端可用(不隐藏导航)
- 视觉:Swiss 极简;文案中文、克制,不放技术说明
## 3. 阶段三(可选,等你点名)
配额限制、用量趋势、分享链接(走带 token 的公开链接那套口径)、审计日志页