Files
SnapOtter/apps/docs/zh-CN/guide/scim.md
T
SnapOtterandGitHub d10d0f544f fix: release QA hardening across processing, media, security, and CI gates (#649)
A release-readiness QA pass over the whole product. The commits split into
defects a user would hit and gates that were reporting green while measuring
nothing.

## Fixes that change behaviour

Rate limiting was bypassable on every install: TRUST_PROXY defaulted to true, so
request.ip came from a client-set header and a forged X-Forwarded-For got past
the login limiter. The default is now a private-network trust list.

A transient Postgres outage stranded in-flight jobs, leaving finished output on
disk with no row pointing at it. A reconciler now resolves those rows and adopts
the bytes rather than dropping the work.

A Redis connection that moved to a new address wedged every read-blocked
consumer, so completions stopped signalling while health still answered 200.
Socket timeouts plus subscriber pings recover it.

Installing more than one AI bundle left the shared venv multi-versioned and
silently broke three tools. The installer now reconciles distributions to one
version each.

Converting an image to JXL at quality 1 through 4 returned a 500, because
libjxl 0.7 rejects the distance those values compute. The quality is floored at
what the encoder honours. A missing ffmpeg was also reported to the user as a
corrupt upload; it now says the engine is unavailable.

RAW uploads reached an unpatched LibRaw on arm64, so it is built from source at
0.22.2, and the release scan was split so it can fail on an unfixed critical
instead of hiding it behind ignore-unfixed.

## Gates that could not fail

Two mutation lanes ran zero mutants because Stryker crawled the gitignored docs
build; coverage discarded its whole report on any failing test; the lint gate
skipped root tests, scripts, and two workspaces; and several generated matrices
counted a host missing ffmpeg as a passing tool. Each now measures what it
claims.

Full evidence and the outstanding release items are tracked locally and are not
part of this branch.
2026-07-27 15:37:30 +08:00

12 KiB
Raw Blame History

description, i18n_source_hash, i18n_provenance, i18n_output_hash, i18n_hash_version
description i18n_source_hash i18n_provenance i18n_output_hash i18n_hash_version
设置 SCIM 2.0 预配以将用户和组从你的身份提供商同步到 SnapOtter。涵盖 Okta、Azure AD / Entra ID 以及自定义集成。 06ee702b386e human d318d3799726 2

SCIM 预配

SnapOtter 实现了 SCIM 2.0System for Cross-domain Identity Management,跨域身份管理系统),用于自动化的用户和组预配。你的身份提供商可以自动创建、更新、停用和重新激活用户账户,并同步组成员关系。

::: tip 企业版功能 SCIM 预配需要带有 scim 功能的 enterprise 许可证。它在 team 计划中不可用。若没有该功能,所有 SCIM 端点(除发现端点外)都返回 403。 :::

先决条件

  • 一个可通过公网 URL 访问的正在运行的 SnapOtter 实例
  • 一个带有 scim 功能的 enterprise 许可证密钥
  • 内置 SnapOtter admin 帐户及其完整的有效权限集。委托的自定义角色或缺少任何管理权限的管理 API 密钥无法生成或撤销全局 SCIM 令牌。
  • 你的身份提供商的预配设置的管理员访问权限

快速开始

  1. 生成一个 SCIM bearer 令牌:
curl -X POST https://photos.example.com/api/v1/enterprise/scim/token \
  -H "Cookie: snapotter-session=YOUR_SESSION" \
  -H "Content-Type: application/json"

响应中包含该令牌。请立即保存;它无法再次获取。

{
  "token": "so_scim_v2_a1b2c3d4e5f6...",
  "message": "Save this token - it cannot be retrieved again"
}
  1. 在你的身份提供商中,配置 SCIM 预配,设置:
    • Base URLhttps://photos.example.com/api/v1/scim/v2
    • AuthenticationBearer token(粘贴步骤 1 中的令牌)

身份验证

SCIM 端点使用专用的 Bearer 令牌,与用户会话和 API 密钥分开。

生成令牌

POST /api/v1/enterprise/scim/token 生成新的 SCIM 代币。由于令牌可以跨实例配置和更改用户,因此此端点需要具有完整有效管理权限集的内置 admin 角色。将 users:manage 保留在自定义角色中是不够的。

令牌以明文形式仅返回一次。SnapOtter 只存储 scrypt 哈希值。如果你丢失了令牌,请吊销它并生成一个新的。

同一时间只有一个 SCIM 令牌处于活动状态。生成新令牌会替换先前的令牌。

::: warning 升级后重新发行令牌 旧版未版本控制的 SCIM 令牌将被拒绝。升级到颁发 so_scim_v2_... 令牌的版本后,请生成新令牌并更新您的身份提供商,然后再恢复配置。 :::

吊销令牌

DELETE /api/v1/enterprise/scim/token 撤销当前的 SCIM 令牌。它具有与令牌生成相同的完整内置管理要求。

速率限制

SCIM 端点按每个令牌每分钟 1000 个请求进行速率限制。超过此限制会返回 HTTP 429。

支持的资源

SCIM 资源 SnapOtter 概念 创建 读取 更新 删除
User 用户账户 软删除
Group 团队

::: warning SCIM Group 映射到 SnapOtter 团队,而非角色。SCIM 无法设置用户的角色。所有通过 SCIM 创建的用户都会被分配 user 角色。要更改用户的角色,请使用 SnapOtter 管理界面。 :::

用户操作

创建用户

POST /api/v1/scim/v2/Users

创建一个新的用户账户,其 authProvider 设置为 scim,角色为 user。该用户会被分配到 Default 团队。如果 activefalse,则角色改为设置为 disabled

必需属性:userName。可选属性:externalIdemailsactive(默认为 true)。

列出和筛选用户

GET /api/v1/scim/v2/Users

返回分页的用户列表。支持 startIndexcount 查询参数(每页最多 200 条结果)。

筛选仅支持 eq(等于),可用于以下属性:

  • userName eq "jane"
  • externalId eq "ext-12345"

其他筛选运算符和属性会返回 HTTP 400。

获取用户

GET /api/v1/scim/v2/Users/:id

通过 SnapOtter 用户 ID 返回单个用户。

替换用户

PUT /api/v1/scim/v2/Users/:id

替换用户的属性。支持 userNameexternalIdemailsactive。用户名更改会检查冲突(如果新用户名已被另一用户占用,则返回 409)。

修补用户

PATCH /api/v1/scim/v2/Users/:id

使用 SCIM PatchOp 进行部分更新。支持的操作:

操作 路径
replace activeuserNameexternalIdemailsemails[type eq "work"].valuename.formatteddisplayName
add replace
remove externalIdemails

name.formatteddisplayName 路径为兼容性而被接受,但不会产生持久效果(SnapOtter 不单独存储显示名称)。

无值的 replace 操作(即值为不含 path 的对象)也受支持,可用键为 userNameexternalIdemailsactive

停用用户(软删除)

DELETE /api/v1/scim/v2/Users/:id

SnapOtter 不会通过 SCIM 硬删除用户。DELETE 会执行软停用:

  1. 用户的角色从其当前值(例如 editor)更改为 disabled:editor,同时保留原始角色。
  2. 用户的密码被清除。
  3. 所有活动会话被吊销。
  4. 所有 API 密钥被吊销。

该用户无法再登录或使用任何 API 密钥。其数据(文件、历史记录)会被保留。

重新激活用户

要重新激活先前已停用的用户,请发送带有 active: truePUTPATCH 请求。SnapOtter 会恢复停用前的原始角色(例如 disabled:editor 再次变为 editor)。如果无法确定原始角色,则回退为 user

::: details 示例:通过 PATCH 停用和重新激活

// Deactivate
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [
    { "op": "replace", "path": "active", "value": false }
  ]
}

// Reactivate
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [
    { "op": "replace", "path": "active", "value": true }
  ]
}

:::

组操作

SCIM Group 映射到 SnapOtter 团队。创建组会创建一个团队。组成员关系控制用户所属的团队。

创建组

POST /api/v1/scim/v2/Groups

必需:displayName。可选:members{ value: userId } 数组)。

列出和筛选组

GET /api/v1/scim/v2/Groups

筛选仅支持 displayName eq "..."。分页使用 startIndexcount(每页最多 200 条结果)。

获取组

GET /api/v1/scim/v2/Groups/:id

替换组

PUT /api/v1/scim/v2/Groups/:id

替换组名称和完整的成员列表。不在新列表中的现有成员会被移至 Default 团队。

修补组

PATCH /api/v1/scim/v2/Groups/:id

支持以下操作:

操作 路径 效果
add members 将用户添加到团队
remove members[value eq "userId"] 将用户移至 Default 团队
replace displayName 重命名团队
replace members 替换所有成员(被移除的成员移至 Default 团队)

删除组

DELETE /api/v1/scim/v2/Groups/:id

删除团队。被删除团队的所有成员都会被移至 Default 团队。用户不会被停用或删除。

IdP 设置

Okta

  1. 在 Okta 管理控制台中,打开你的 SnapOtter 应用程序(或创建一个)。
  2. 转到 Provisioning 选项卡,然后点击 Configure API Integration
  3. 勾选 Enable API Integration 并输入:
    • Base URLhttps://photos.example.com/api/v1/scim/v2
    • API Token:上面生成的 SCIM bearer 令牌
  4. 点击 Test API Credentials,然后点击 Save
  5. Provisioning > To App 下,启用:
    • Create Users
    • Update User Attributes
    • Deactivate Users
  6. Push Groups 下,配置要作为 SnapOtter 团队同步的 Okta 组。

Azure AD / Entra ID

  1. 在 Azure 门户中,转到你的 SnapOtter 企业应用程序。
  2. 转到 Provisioning,并将 Provisioning Mode 设置为 Automatic
  3. Admin Credentials 下,输入:
    • Tenant URLhttps://photos.example.com/api/v1/scim/v2
    • Secret Token:上面生成的 SCIM bearer 令牌
  4. 点击 Test Connection,然后点击 Save
  5. Mappings 下,配置用户和组的属性映射。默认设置通常有效,但请确认 userName 按预期映射到 userPrincipalNamemail
  6. Provisioning Status 设置为 On 并保存。

Azure 会按固定的同步周期(通常每 40 分钟)预配用户和组。

发现端点

以下三个端点无需身份验证即可访问,用于描述 SCIM 服务器的功能:

端点 说明
GET /api/v1/scim/v2/ServiceProviderConfig 服务器功能和支持的特性
GET /api/v1/scim/v2/Schemas User 和 Group 模式定义
GET /api/v1/scim/v2/ResourceTypes 可用的资源类型(User、Group

ServiceProviderConfig 会公告以下功能:

功能 是否支持
Patch
Bulk
Filter 是(最多 200 条结果,仅 eq 运算符)
Change password
Sort
ETag

限制

  • 筛选:仅支持 eq 运算符。复杂筛选、and/or 运算符、co(包含)和 sw(以...开头)均未实现。
  • 批量操作:不支持。
  • Sort 和 ETag:不支持。
  • 角色SCIM 无法分配 SnapOtter 角色。所有预配的用户都会获得 user 角色。
  • MAX_USERSMAX_USERS 环境变量限制不会在 SCIM 用户创建时强制执行。如果你需要限制用户数量,请在你的 IdP 中管理分配。
  • 单一令牌:同一时间只能有一个 SCIM 令牌处于活动状态。如果多个 IdP 需要 SCIM 访问权限,它们必须共享该令牌。
  • 组即团队:SCIM Group 对应团队,而非角色或权限组。

故障排除

403 "SCIM provisioning requires an enterprise license with the scim feature"

你的许可证不包含 scim 功能,或者未配置许可证。SCIM 需要 enterprise 计划许可证。请确认已设置 SNAPOTTER_LICENSE_KEY 且许可证包含 scim 功能。

401 "Bearer token required"

SCIM 请求未包含 Authorization: Bearer <token> 标头。请检查你的 IdP 的预配配置。

401 "Invalid token"

令牌格式错误、使用已停用的未版本化格式或与存储的哈希不匹配。生成当前的 so_scim_v2_... 令牌并在 IdP 的配置设置中更新该令牌。

401 "SCIM not configured"

尚未生成任何 SCIM 令牌。请使用 POST /api/v1/enterprise/scim/token 端点创建一个。

409 "User already exists" / "userName already taken"

已存在同名用户名的用户。当 IdP 重试失败的创建操作时,可能会发生这种情况。请在 SnapOtter 管理面板中检查是否有重复的用户名。

429 "SCIM rate limit exceeded"

IdP 发送的请求超过每分钟 1000 个。这通常发生在大型初始同步期间。大多数 IdP 会在速率限制窗口重置后自动重试。如果问题持续存在,请检查你的 IdP 的预配同步间隔。

用户已取消预配但未从界面中移除

SCIM DELETE 是软停用。已停用的用户仍会以禁用状态出现在管理员用户列表中。这是有意为之,以便保留他们的数据。他们的角色显示为 disabled:<original-role>