593 lines
10 KiB
Markdown
593 lines
10 KiB
Markdown
# Ubuntu User Management API
|
||
|
||
## 基本信息
|
||
|
||
- 服务地址示例: `http://127.0.0.1:8000`
|
||
- API 版本: `1.0.0`
|
||
- 文档地址: `/docs`
|
||
- OpenAPI 地址: `/openapi.json`
|
||
- 请求与响应格式: `application/json`
|
||
|
||
## 鉴权
|
||
|
||
所有业务接口都需要 Bearer Token。
|
||
|
||
请求头:
|
||
|
||
```http
|
||
Authorization: Bearer <TOKEN>
|
||
```
|
||
|
||
`TOKEN` 来自 `.env`:
|
||
|
||
```env
|
||
TOKEN=your-token
|
||
SERVER_NAME=user-manage-api
|
||
```
|
||
|
||
鉴权失败响应:
|
||
|
||
```json
|
||
{
|
||
"detail": {
|
||
"code": "unauthorized",
|
||
"message": "Invalid bearer token."
|
||
}
|
||
}
|
||
```
|
||
|
||
## 通用错误格式
|
||
|
||
业务错误:
|
||
|
||
```json
|
||
{
|
||
"code": "not_found",
|
||
"message": "user not found"
|
||
}
|
||
```
|
||
|
||
参数错误:
|
||
|
||
```json
|
||
{
|
||
"code": "invalid_parameter",
|
||
"message": "..."
|
||
}
|
||
```
|
||
|
||
常见错误码:
|
||
|
||
| HTTP 状态码 | code | 描述 |
|
||
| --- | --- | --- |
|
||
| 400 | `invalid_parameter` | 请求参数格式或校验失败 |
|
||
| 400 | `invalid_home_dir` | `home_dir` 不在 `HOME_BASE_DIR` 下 |
|
||
| 401 | `unauthorized` | 未提供 token 或 token 不正确 |
|
||
| 404 | `not_found` | 用户或用户组不存在,或被可见性规则隐藏 |
|
||
| 409 | `resource_conflict` | 用户或用户组已存在 |
|
||
| 422 | `precondition_failed` | 前置条件不满足,例如用户组仍有成员 |
|
||
| 423 | `user_locked` | 用户已锁定,只允许查看,不允许修改 |
|
||
| 500 | `system_command_error` | 系统命令执行失败 |
|
||
| 503 | `system_timeout` | 系统命令超时 |
|
||
| 503 | `system_permission_denied` | 系统命令权限不足 |
|
||
|
||
## 可见性规则
|
||
|
||
用户和用户组会受 `.env` 中的白名单、黑名单和 UID/GID 范围限制。
|
||
|
||
优先级:
|
||
|
||
```text
|
||
白名单 > 黑名单 > UID/GID 范围
|
||
```
|
||
|
||
相关配置:
|
||
|
||
```env
|
||
WHITELIST_USERS=
|
||
WHITELIST_GROUPS=
|
||
HIDDEN_USERS=root,daemon,nobody
|
||
HIDDEN_GROUPS=root,daemon,nogroup
|
||
LOCKED_USERS=
|
||
USER_UID_MIN=1000
|
||
USER_UID_MAX=60000
|
||
GROUP_GID_MIN=1000
|
||
GROUP_GID_MAX=60000
|
||
```
|
||
|
||
说明:
|
||
|
||
- 白名单中的用户/用户组始终可见并允许操作。
|
||
- 黑名单中的用户/用户组会被隐藏并禁止操作,除非同时在白名单中。
|
||
- 不在白名单和黑名单时,用户按 UID 范围判断,用户组按 GID 范围判断。
|
||
- 被隐藏的用户或用户组对 API 表现为 `404 not_found`。
|
||
- `LOCKED_USERS` 中的用户可以查看,但不能创建同名用户、删除、改密码、添加用户组或移除用户组。
|
||
|
||
## Home 目录规则
|
||
|
||
相关配置:
|
||
|
||
```env
|
||
HOME_BASE_DIR=/home
|
||
LINK_HOME_DIR=
|
||
```
|
||
|
||
创建用户时:
|
||
|
||
- `home_dir` 为空时,默认使用 `HOME_BASE_DIR/username`。
|
||
- `home_dir` 不为空时,必须位于 `HOME_BASE_DIR` 下。
|
||
- `LINK_HOME_DIR` 为空时,用户目录直接创建在 `HOME_BASE_DIR` 下。
|
||
- `LINK_HOME_DIR` 不为空时,实际目录创建在 `LINK_HOME_DIR/username`,并在 `HOME_BASE_DIR/username` 创建软链接。
|
||
|
||
删除用户时:
|
||
|
||
- 会删除用户账号。
|
||
- 如果用户 home 是软链接,则删除该软链接。
|
||
- 如果用户 home 是普通目录,不会删除目录内容。
|
||
|
||
## 数据模型
|
||
|
||
### UserCreateRequest
|
||
|
||
| 字段 | 类型 | 必填 | 默认值 | 描述 |
|
||
| --- | --- | --- | --- | --- |
|
||
| `username` | string | 是 | - | 用户名。格式: `^[a-z_][a-z0-9_-]{0,31}$` |
|
||
| `password_hash` | string | 是 | - | 预先生成的 Linux 密码 hash,长度 10 到 512 |
|
||
| `primary_group` | string/null | 否 | `null` | 主用户组。格式同用户组名 |
|
||
| `groups` | string[] | 否 | `[]` | 附加用户组,会自动去重 |
|
||
| `shell` | string | 否 | `/bin/bash` | 登录 shell |
|
||
| `home_dir` | string/null | 否 | `null` | 用户 home 路径,必须在 `HOME_BASE_DIR` 下 |
|
||
|
||
### UserSummary
|
||
|
||
| 字段 | 类型 | 描述 |
|
||
| --- | --- | --- |
|
||
| `username` | string | 用户名 |
|
||
| `uid` | integer | 用户 UID |
|
||
| `gid` | integer | 用户主 GID |
|
||
| `home_dir` | string | 用户 home 路径 |
|
||
| `shell` | string | 登录 shell |
|
||
|
||
### GroupCreateRequest
|
||
|
||
| 字段 | 类型 | 必填 | 描述 |
|
||
| --- | --- | --- | --- |
|
||
| `groupname` | string | 是 | 用户组名。格式: `^[a-z_][a-z0-9_-]{0,31}$` |
|
||
|
||
### GroupSummary
|
||
|
||
| 字段 | 类型 | 描述 |
|
||
| --- | --- | --- |
|
||
| `groupname` | string | 用户组名 |
|
||
| `gid` | integer | 用户组 GID |
|
||
| `members` | string[] | 用户组成员 |
|
||
|
||
### UserGroupsUpdateRequest
|
||
|
||
| 字段 | 类型 | 必填 | 默认值 | 描述 |
|
||
| --- | --- | --- | --- | --- |
|
||
| `groups` | string[] | 是 | - | 用户组列表,至少 1 个,会自动去重 |
|
||
| `mode` | string | 否 | `append` | `append` 表示追加,`replace` 表示替换全部附加组 |
|
||
|
||
### UserPasswordUpdateRequest
|
||
|
||
| 字段 | 类型 | 必填 | 描述 |
|
||
| --- | --- | --- | --- |
|
||
| `password_hash` | string | 是 | 新密码的 Linux hash,长度 10 到 512。API 不接收明文密码 |
|
||
|
||
### ApiResponse
|
||
|
||
| 字段 | 类型 | 描述 |
|
||
| --- | --- | --- |
|
||
| `status` | string | 固定为 `ok` |
|
||
| `message` | string | 操作结果描述 |
|
||
|
||
## API 列表
|
||
|
||
### 健康检查
|
||
|
||
```http
|
||
GET /health
|
||
```
|
||
|
||
描述:
|
||
|
||
返回服务器名称和在线状态。服务器名称来自 `.env` 中的 `SERVER_NAME`。
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"server_name": "user-manage-api",
|
||
"status": "online"
|
||
}
|
||
```
|
||
|
||
字段说明:
|
||
|
||
| 字段 | 类型 | 描述 |
|
||
| --- | --- | --- |
|
||
| `server_name` | string | 服务器名称,由 `SERVER_NAME` 配置 |
|
||
| `status` | string | 固定为 `online` |
|
||
|
||
### 创建用户
|
||
|
||
```http
|
||
POST /users
|
||
```
|
||
|
||
描述:
|
||
|
||
创建系统用户。密码必须由调用方提前生成 hash,API 不处理明文密码。
|
||
|
||
请求体:
|
||
|
||
```json
|
||
{
|
||
"username": "alice",
|
||
"password_hash": "$6$salt$hash",
|
||
"primary_group": null,
|
||
"groups": ["dev"],
|
||
"shell": "/bin/bash",
|
||
"home_dir": "/home/alice"
|
||
}
|
||
```
|
||
|
||
最小请求体:
|
||
|
||
```json
|
||
{
|
||
"username": "alice",
|
||
"password_hash": "$6$salt$hash"
|
||
}
|
||
```
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"message": "User created."
|
||
}
|
||
```
|
||
|
||
注意:
|
||
|
||
- `username` 在黑名单中且不在白名单中时禁止创建。
|
||
- `primary_group` 和 `groups` 中的用户组必须存在且可见。
|
||
- `home_dir` 必须在 `HOME_BASE_DIR` 下。
|
||
- 启用 `LINK_HOME_DIR` 后,账号 home 仍为 `HOME_BASE_DIR/username`,实际目录位于 `LINK_HOME_DIR/username`。
|
||
|
||
### 删除用户
|
||
|
||
```http
|
||
DELETE /users/{username}
|
||
```
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 描述 |
|
||
| --- | --- | --- |
|
||
| `username` | string | 要删除的用户名 |
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"message": "User deleted."
|
||
}
|
||
```
|
||
|
||
注意:
|
||
|
||
- 用户不存在或被隐藏时返回 `404`。
|
||
- 用户在 `LOCKED_USERS` 中时返回 `423 user_locked`。
|
||
- 删除账号后,如果 home 是软链接,会删除该软链接。
|
||
- 如果 home 是普通目录,不会删除目录内容。
|
||
|
||
### 修改用户密码
|
||
|
||
```http
|
||
PATCH /users/{username}/password
|
||
```
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 描述 |
|
||
| --- | --- | --- |
|
||
| `username` | string | 要修改密码的用户名 |
|
||
|
||
请求体:
|
||
|
||
```json
|
||
{
|
||
"password_hash": "$6$rounds=5000$salt$hash"
|
||
}
|
||
```
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"message": "User password updated."
|
||
}
|
||
```
|
||
|
||
注意:
|
||
|
||
- `password_hash` 必须是调用方提前生成的 Linux 密码 hash。
|
||
- 用户不存在或被隐藏时返回 `404`。
|
||
- 用户在 `LOCKED_USERS` 中时返回 `423 user_locked`。
|
||
|
||
### 获取用户列表
|
||
|
||
```http
|
||
GET /users
|
||
```
|
||
|
||
描述:
|
||
|
||
返回可见用户列表。会应用白名单、黑名单和 UID 范围规则。
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
[
|
||
{
|
||
"username": "alice",
|
||
"uid": 1000,
|
||
"gid": 1000,
|
||
"home_dir": "/home/alice",
|
||
"shell": "/bin/bash"
|
||
}
|
||
]
|
||
```
|
||
|
||
### 获取用户详情
|
||
|
||
```http
|
||
GET /users/{username}
|
||
```
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 描述 |
|
||
| --- | --- | --- |
|
||
| `username` | string | 用户名 |
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"username": "alice",
|
||
"uid": 1000,
|
||
"gid": 1000,
|
||
"home_dir": "/home/alice",
|
||
"shell": "/bin/bash"
|
||
}
|
||
```
|
||
|
||
### 创建用户组
|
||
|
||
```http
|
||
POST /groups
|
||
```
|
||
|
||
请求体:
|
||
|
||
```json
|
||
{
|
||
"groupname": "dev"
|
||
}
|
||
```
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"message": "Group created."
|
||
}
|
||
```
|
||
|
||
注意:
|
||
|
||
- `groupname` 在黑名单中且不在白名单中时禁止创建。
|
||
|
||
### 删除用户组
|
||
|
||
```http
|
||
DELETE /groups/{groupname}
|
||
```
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 描述 |
|
||
| --- | --- | --- |
|
||
| `groupname` | string | 要删除的用户组名 |
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"message": "Group deleted."
|
||
}
|
||
```
|
||
|
||
注意:
|
||
|
||
- 用户组不存在或被隐藏时返回 `404`。
|
||
- 用户组仍有成员时返回 `422 precondition_failed`。
|
||
|
||
### 获取用户组列表
|
||
|
||
```http
|
||
GET /groups
|
||
```
|
||
|
||
描述:
|
||
|
||
返回可见用户组列表。会应用白名单、黑名单和 GID 范围规则。
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
[
|
||
{
|
||
"groupname": "dev",
|
||
"gid": 1000,
|
||
"members": ["alice"]
|
||
}
|
||
]
|
||
```
|
||
|
||
### 获取用户组详情
|
||
|
||
```http
|
||
GET /groups/{groupname}
|
||
```
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 描述 |
|
||
| --- | --- | --- |
|
||
| `groupname` | string | 用户组名 |
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"groupname": "dev",
|
||
"gid": 1000,
|
||
"members": ["alice"]
|
||
}
|
||
```
|
||
|
||
### 添加或替换用户附加组
|
||
|
||
```http
|
||
POST /users/{username}/groups
|
||
```
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 描述 |
|
||
| --- | --- | --- |
|
||
| `username` | string | 用户名 |
|
||
|
||
请求体:
|
||
|
||
```json
|
||
{
|
||
"groups": ["dev", "ops"],
|
||
"mode": "append"
|
||
}
|
||
```
|
||
|
||
参数说明:
|
||
|
||
| 字段 | 类型 | 描述 |
|
||
| --- | --- | --- |
|
||
| `groups` | string[] | 要添加或替换的用户组列表 |
|
||
| `mode` | string | `append` 追加用户组,`replace` 替换全部附加组 |
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"message": "User groups updated."
|
||
}
|
||
```
|
||
|
||
注意:
|
||
|
||
- 用户不存在或被隐藏时返回 `404`。
|
||
- 用户在 `LOCKED_USERS` 中时返回 `423 user_locked`。
|
||
- 目标用户组不存在或被隐藏时返回 `404`。
|
||
|
||
### 移除用户附加组
|
||
|
||
```http
|
||
DELETE /users/{username}/groups
|
||
```
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 描述 |
|
||
| --- | --- | --- |
|
||
| `username` | string | 用户名 |
|
||
|
||
请求体:
|
||
|
||
```json
|
||
{
|
||
"groups": ["dev"],
|
||
"mode": "append"
|
||
}
|
||
```
|
||
|
||
说明:
|
||
|
||
`mode` 字段会被接收,但删除操作只使用 `groups`。
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"message": "User groups removed."
|
||
}
|
||
```
|
||
|
||
注意:
|
||
|
||
- 用户不存在或被隐藏时返回 `404`。
|
||
- 用户在 `LOCKED_USERS` 中时返回 `423 user_locked`。
|
||
|
||
### 获取用户所属用户组
|
||
|
||
```http
|
||
GET /users/{username}/groups
|
||
```
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 类型 | 描述 |
|
||
| --- | --- | --- |
|
||
| `username` | string | 用户名 |
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"username": "alice",
|
||
"groups": ["dev", "ops"]
|
||
}
|
||
```
|
||
|
||
说明:
|
||
|
||
返回的 `groups` 会过滤掉不可见用户组。
|
||
|
||
## curl 示例
|
||
|
||
```bash
|
||
curl -X GET 'http://127.0.0.1:8000/users' \
|
||
-H 'Authorization: Bearer <TOKEN>'
|
||
```
|
||
|
||
```bash
|
||
curl -X POST 'http://127.0.0.1:8000/users' \
|
||
-H 'Authorization: Bearer <TOKEN>' \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"username": "alice",
|
||
"password_hash": "$6$rounds=5000$salt$hash",
|
||
"shell": "/bin/bash"
|
||
}'
|
||
```
|