在一个非常高的水平上,SCIM(跨身份管理系统)协议的存在是为了在各种独立的系统中提供和同步用户和组。在SCIM规范中,有一些细微的差别。在这篇文章中,我们将看一下一些基本的SCIM操作。我们将偷看Okta和使用PostgreSQL作为应用数据库的Flask构建的SCIM服务器之间的流程。即使你不是Python开发人员,希望你能从本指南中获得一些概念,用于你自己的SCIM实现。
设置Flask服务器和Postgres数据库
-
克隆这里的 repo,打开终端,
cd到项目根目录。 -
用
virtualenv env在根目录下创建一个新的 virtualenv 。 -
用
source env/bin/activate来运行虚拟环境。 -
用
pip install -r requirements.txt安装必要的Python包。 -
安装Postgres。
-
创建一个新的Postgres数据库,命名为
scim。通过打开一个新的终端标签并输入psql postgres,进入psqlshell。用CREATE DATABASE scim;创建数据库(运行\list来仔细检查数据库是否已经创建)。 -
回到flask应用根目录下的终端标签。运行以下命令,在
scim数据库中创建迁移和表:-
python manage.py db init -
python manage.py db migrate -
python manage.py db upgrade
随意跳回到你的Postgres标签,运行
\c scim来导航到scim db,然后\dt来查看你的新表。groups,users,link。(link是一个表,便于在用户和组之间建立多对多的关系。) -
-
现在一切都应该设置好了,可以在本地运行服务器。最后,运行
python app.py来完成。现在你应该在http://localhost:5000上运行你的SCIM服务器了。
设置Ngrok将请求从Okta路由到本地主机
一旦你安装了ngrok,运行./ngrok http 5000 ,创建一个从ngrok到http://localhost:5000 的隧道。复制ngrok创建的https 转发URL,因为你以后会需要它。
创建和配置你的Okta应用程序
现在是时候在Okta创建一个新的SCIM集成了。如果你的SCIM应用已经在Okta那边设置好了,可以跳过前面的 测试SCIM服务器.有两种选择可以在这个服务器上使用,我总是推荐第一种,那就是使用Okta SCIM模板应用程序。
选项1:SCIM模板应用程序
-
在你的Okta仪表板上,进入应用->应用,然后点击浏览应用目录按钮。搜索SCIM 2.0测试应用(Header Auth),并在拉出该应用后点击添加按钮。
-
在常规设置选项卡中,点击下一步。
-
我们将把它设置为SWA应用程序,所以在登录选项标签中,点击安全网络认证。
-
单击 "完成"。
-
在配置选项卡上,点击配置API集成。
-
勾选启用API集成。
-
在基本URL字段中,粘贴你在上面生成的ngrok网址,并将**/scim/v2加到最后。在API Token字段中,输入Bearer 123456789**。(稍后我们将讨论如何定制这个授权头,但开箱即用,SCIM服务器希望有这个值。)
-
点击Test API Credentials,你应该得到一个像下面这样的成功信息。
你可以通过浏览
http://localhost:4040来检查ngrok日志,看看Okta的请求和SCIM服务器的响应。 -
点击保存。现在你的Provisoning标签看起来有点不同。
-
点击Provisioning to App旁边的编辑,并勾选: * 创建用户 * 更新用户属性 * 停用用户
并保存。
选项2:为现有的应用程序集成向导(AIW)应用程序启用SCIM配置。
如果你在上面设置了你的SCIM集成,请随意跳过这一部分,测试SCIM服务器。
-
在你的Okta仪表盘上,进入应用->应用,然后点击创建应用集成按钮。对于这个设置,我们将选择SWA - 安全网络认证。点击下一步。
-
你可以为应用程序名称和应用程序登录页面URL添加任何你想要的内容,因为我们只是在研究SCIM功能,而不是这个应用程序的SWA方面。点击完成。
-
在应用程序的 "常规"选项卡中,单击**"编辑",将 "供应 "从"无**"切换为 "SCIM"。点击保存。
-
你的应用程序现在应该有一个供应标签。点击该标签,并填写整合设置,以符合下面的图片。使授权标题为123456789。你可以稍后在SCIM flask应用中改变这个。
-
点击测试连接器配置,你应该看到下面的成功确认。
-
你现在可以点击保存。
你可以导航到
http://localhost:4040,看看Okta对这个请求的要求,以及SCIM服务器的响应。 -
现在你的Provisioning标签看起来有点不同。点击 "编辑"旁边的 "提供给应用程序"并勾选。
- 创建用户
- 更新用户属性
- 停用用户
并保存。
现在你应该在Okta这边设置好,开始测试SCIM服务器。
我们刚才做了什么?
在我们继续测试我们的SCIM服务器之前,让我们谈谈我们在上面的设置步骤中做了什么。基本上我们设置了以下3个组件。
在这个设置中,Okta是用户数据的真实来源。SCIM服务器在本地打开了四个端点,由于ngrok的路由功能,Okta能够访问这些端点。这些端点是:
https://localhost:5000/scim/v2/Usershttps://localhost:5000/scim/v2/Users/{user_id}https://localhost:5000/scim/v2/Groupshttps://localhost:5000/scim/v2/Groups/{group_id}
这些端点根据Okta的请求触发CRUD操作。这些调用的一些例子。
CREATE
Okta的例子:
- 当一个用户被分配到Okta应用时,Okta会在
POST。/scim/v2/Users /scim/v2/Users当一个组被分配到Okta应用中时,Okta会对该组的每个成员进行反复的POSTs。- 当使用推送组来添加一个新的组到Okta应用时,Okta会发送一个
POST到/scim/v2/Groups
阅读
Okta实例:
- 当从外部应用导入用户和组时,Okta发送一个
GET到/scim/v2/Users和/或GET到/scim/v2/Groups - 当从外部应用中获取所有(或过滤)用户时,Okta发送一个
GET到/scim/v2/Users - 当从外部应用中获取一个用户时,Okta发送一个
GET到/scim/v2/Users/{user_id} - 当从外部应用获得所有(或过滤)组时,Okta发送一个
GET到/scim/v2/Groups - 当从外部应用获得一个单独的组时,Okta会发送一个
GET到/scim/v2/Groups/{group_id}
除了导入之外,这些操作都是由Okta在创建、更新或删除操作之前发起的。你可以把这些调用看成是同步验证步骤。
更新
Okta的例子:
- 当更新Okta的用户属性时,Okta发送一个
PUT或PATCH到/scim/v2/Users/{user_id} - 当分配或取消一个已经存在于外部应用数据库的用户时,Okta发送一个
PUT或PATCH到/scim/v2/Users/{user_id} - 当改变一个被推送的Okta组的成员资格时,Okta会发送一个
PUT或PATCH到/scim/v2/Groups/{group_id}
DELETE
Okta的例子:
- 当在推送组标签中选择 "解除推送组的链接 "后,选择 "在目标应用中删除该组 "时,okta会发送一个
DELETE到/scim/v2/Groups/{group_id}
让我们继续测试SCIM服务器,看看这些调用的实际效果吧
测试SCIM服务器
注意:我在下面的步骤中使用的是SCIM模板集成。如果你使用的是AIW版本,在一些调用中可能会有细微的差别。更多信息在这里。
-
导航到
http://localhost:4040,查看Okta和SCIM服务器之间发生的所有请求和响应。为了简洁起见,我将在下面的例子中截断我的部分。 -
从上面的章节中,你看到我们在授权头中设置了SCIM集成的承载器123456789。你可以在app.py文件中,在函数auth_required的下面一行,把这个标头改成你想要的样子。
if request.headers['Authorization'].split('Bearer ')[1] == '123456789':
用你想要的任何独特的值替换123456789,并确保在你的Okta集成的供应标签中更新这个。
注意:这只是为了测试时的方便 - 千万不要在源代码中存储敏感的凭证。更多信息在这里。
认证的存在是为了保护你的端点免受不必要的请求。在本指南中,我们设置了头授权,但你也可以使用基本认证或OAuth。本教程不会对SCIM的这方面进行太深入的研究。
指派一个用户
在分配标签下,点击分配->分配给人。我分配了用户**obi-wan.kenobi@iamciam.dev。**下面是Okta向我的SCIM服务器发出的请求和响应。
- Okta请求:
GET /scim/v2/Users?filter=userName%20eq%20%22obi-wan.kenobi%40iamciam.dev%22&startIndex=1&count=100
Okta将首先运行一个带有userName过滤查询参数的GET,以查看用户是否已经存在于外部应用程序中,因为还没有用户的外部ID来链接他们和Okta用户。
- SCIM服务器响应:
200 OK
{
"Resources": [],
"itemsPerPage": 0,
"schemas": [
"urn:ietf:params:scim:api:messages:2.0:ListResponse"
],
"startIndex": 1,
"totalResults": 0
}
这个响应告诉Okta,该用户目前不存在于数据库中,因此通知下一个调用--
POST,以创建该用户。
- Okta请求:
POST /scim/v2/Users
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User"
],
"userName": "obi-wan.kenobi@iamciam.dev",
"name": {
"givenName": "Obi-Wan",
"familyName": "Kenobi"
},
"emails": [
{
"primary": true,
"value": "obi-wan.kenobi@iamciam.dev",
"type": "work"
}
],
"displayName": "Obi-Wan Kenobi",
"locale": "en-US",
"externalId": "00use6vjvehodmsQb4x6",
"groups": [],
"password": "gl^m&qWZ",
"active": true
}
注意:上述密码值是由Okta随机生成的,因为许多外部应用在创建用户时需要这个值。由于应用程序是联合的,这个密码不能用于登录。
- SCIM服务器响应。
201 CREATED
{
"active": true,
"displayName": "Obi-Wan Kenobi",
"emails": [
{
"primary": true,
"type": "work",
"value": "obi-wan.kenobi@iamciam.dev"
}
],
"externalId": "00use6vjvehodmsQb4x6",
"groups": [],
"id": "289383d9-ff3a-48bb-99ea-3048762267c7",
"locale": "en-US",
"meta": {
"resourceType": "User"
},
"name": {
"familyName": "Kenobi",
"givenName": "Obi-Wan",
"middleName": null
},
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User"
],
"userName": "obi-wan.kenobi@iamciam.dev"
}
- 现在,如果你导航到你的本地scim数据库并运行
select * from users;,你应该在数据库中看到分配的用户。
取消指定一个用户
让我们继续,取消我们刚刚在Okta中分配的用户。在Assignments下,点击我们刚刚分配的用户旁边的X。
请求/回复应该是这样的。
- Okta请求
PATCH /scim/v2/Users/289383d9-ff3a-48bb-99ea-3048762267c7
{
"schemas": [
"urn:ietf:params:scim:api:messages:2.0:PatchOp"
],
"Operations": [
{
"op": "replace",
"value": {
"active": false
}
}
]
}
Okta并没有删除一个未分配的用户,而是将他们的
active值更新为false。
- SCIM服务器响应
204 NO CONTENT
- 当然,如果你在scim db中运行同样的
select * from users;,你会看到该用户现在是active: false。
指派一个组
在Assignments标签下,点击Assign-> Assignto Groups。我分配了组Droids。注意,这种行为与上面分配单个用户的方式类似。Okta会遍历群组成员,并在外部SCIM服务器中创建用户 - 但群组本身不会被创建。这是在推送组中完成的,我们将在后面处理。下面是Okta对SCIM服务器的请求和响应。
- Okta请求。
GET /scim/v2/Users?filter=userName%20eq%20%22c-3po%40iamciam.dev%22&startIndex=1&count=100
- SCIM服务器响应。
200 OK
{
"Resources": [],
"itemsPerPage": 0,
"schemas": [
"urn:ietf:params:scim:api:messages:2.0:ListResponse"
],
"startIndex": 1,
"totalResults": 0
}
- Okta请求。
POST /scim/v2/Users
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User"
],
"userName": "c-3po@iamciam.dev",
"name": {
"givenName": "C",
"familyName": "3PO"
},
"emails": [
{
"primary": true,
"value": "c-3po@iamciam.dev",
"type": "work"
}
],
"displayName": "C 3PO",
"locale": "en-US",
"externalId": "00usemeo53HWWPYy14x6",
"groups": [],
"password": "wA&&cprB",
"active": true
}
- SCIM服务器响应。
201 CREATED
{
"active": true,
"displayName": "C 3PO",
"emails": [
{
"primary": true,
"type": "work",
"value": "c-3po@iamciam.dev"
}
],
"externalId": "00usemeo53HWWPYy14x6",
"groups": [],
"id": "478c4cf4-1aa3-41a1-93da-4c154c5955e0",
"locale": "en-US",
"meta": {
"resourceType": "User"
},
"name": {
"familyName": "3PO",
"givenName": "C",
"middleName": null
},
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User"
],
"userName": "c-3po@iamciam.dev"
}
- 我的Droids组中有3个成员。C-3P0, R2-D2, 和BB-8。上述请求/响应链在其他两个用户身上重复出现。现在当我在scim数据库中运行
select * from users;,我可以看到分配的用户。
推进组
如上所述,在Okta中分配一个组只是将用户添加到我的SCIM服务器,而不是组本身。推送组是Okta特有的功能。它让你可以将Okta群组成员分配到SCIM应用中,而不会自动将该群组添加到外部应用中。更多信息可以在这里找到 为了将一个组添加到我们的外部应用程序中,在你的Okta应用程序集成中标签到推送组。点击 "推送组"->"按名称查找组",选择你上面分配的组。确保立即推送组成员资格被选中,然后点击保存。Okta应该进行两次调用--一次是创建组本身,另一次是更新组成员。下面是我的Droids群组推送的请求和响应。
- Okta请求。
POST /scim/v2/Groups
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:Group"
],
"displayName": "Droids",
"members": [
{
"value": "cc2573a1-e1c4-491d-a3db-ae4a2e078d61",
"display": "bb-8@iamciam.dev"
},
{
"value": "478c4cf4-1aa3-41a1-93da-4c154c5955e0",
"display": "c-3po@iamciam.dev"
},
{
"value": "34e14e58-f0fe-4e9d-aeb2-0da25fa6626d",
"display": "r2-d2@iamciam.dev"
}
]
}
- *SCIM Server Response:*
```http
201 CREATED
{
"displayName": "Droids",
"id": "b446521a-a65b-4c0b-a5ee-0a15e8e3e908",
"members": [],
"meta": {
"resourceType": "Group"
},
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:Group"
]
}
- Okta请求:
PATCH /scim/v2/Groups/b446521a-a65b-4c0b-a5ee-0a15e8e3e908
{
"schemas": [
"urn:ietf:params:scim:api:messages:2.0:PatchOp"
],
"Operations": [
{
"op": "add",
"path": "members",
"value": [
{
"value": "cc2573a1-e1c4-491d-a3db-ae4a2e078d61",
"display": "bb-8@iamciam.dev"
},
{
"value": "478c4cf4-1aa3-41a1-93da-4c154c5955e0",
"display": "c-3po@iamciam.dev"
},
{
"value": "34e14e58-f0fe-4e9d-aeb2-0da25fa6626d",
"display": "r2-d2@iamciam.dev"
}
]
}
]
}
- SCIM服务器响应:
200 OK
{
"displayName": "Droids",
"id": "b446521a-a65b-4c0b-a5ee-0a15e8e3e908",
"members": [
{
"display": "c-3po@iamciam.dev",
"value": "478c4cf4-1aa3-41a1-93da-4c154c5955e0"
},
{
"display": "bb-8@iamciam.dev",
"value": "cc2573a1-e1c4-491d-a3db-ae4a2e078d61"
},
{
"display": "r2-d2@iamciam.dev",
"value": "34e14e58-f0fe-4e9d-aeb2-0da25fa6626d"
}
],
"meta": {
"resourceType": "Group"
},
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:Group"
]
}
有了这些,你现在应该在运行select * from groups; 后在SCIM数据库中看到你的组。
你可以查看链接表,它显示了用户和组之间的多对多关系。你可以通过select * from link; 来查看这个。
总结
这涵盖了Okta的SCIM集成的基本功能。其他用例还没有经过广泛的测试,可能需要随着时间的推移进行调整,但希望本指南能帮助开发者更好地理解Okta的SCIM功能。 有问题吗?你可以在下面的评论中留言!想保持联系?请关注我们的社交渠道