本页面由机器翻译。发现错误了吗?帮助改进它。
Skip to content

SCIM 预配

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

企业版功能

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

先决条件

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

快速开始

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

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

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
    • Authentication:Bearer token(粘贴步骤 1 中的令牌)

身份验证

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

生成令牌

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

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

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

升级后重新发行令牌

旧版未版本控制的 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 进行部分更新。支持的操作:

操作路径
replaceactiveuserNameexternalIdemailsemails[type eq "work"].valuename.formatteddisplayName
addreplace
removeexternalIdemails

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

示例:通过 PATCH 停用和重新激活
json
// 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

支持以下操作:

操作路径效果
addmembers将用户添加到团队
removemembers[value eq "userId"]将用户移至 Default 团队
replacedisplayName重命名团队
replacemembers替换所有成员(被移除的成员移至 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/SchemasUser 和 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>