如何建立一个为Okta配置的Flask SCIM服务器

403 阅读13分钟

在一个非常高的水平上,SCIM(跨身份管理系统)协议的存在是为了在各种独立的系统中提供和同步用户和组。在SCIM规范中,有一些细微的差别。在这篇文章中,我们将看一下一些基本的SCIM操作。我们将偷看Okta和使用PostgreSQL作为应用数据库的Flask构建的SCIM服务器之间的流程。即使你不是Python开发人员,希望你能从本指南中获得一些概念,用于你自己的SCIM实现。

设置Flask服务器和Postgres数据库

  1. 克隆这里的 repo,打开终端,cd 到项目根目录。

  2. virtualenv env 在根目录下创建一个新的 virtualenv 。

  3. source env/bin/activate 来运行虚拟环境。

  4. pip install -r requirements.txt 安装必要的Python包。

  5. 安装Postgres

  6. 创建一个新的Postgres数据库,命名为scim 。通过打开一个新的终端标签并输入psql postgres ,进入psql shell。用CREATE DATABASE scim; 创建数据库(运行\list 来仔细检查数据库是否已经创建)。

  7. 回到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 是一个表,便于在用户和组之间建立多对多的关系。)

  8. 现在一切都应该设置好了,可以在本地运行服务器。最后,运行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模板应用程序

  1. 在你的Okta仪表板上,进入应用->应用,然后点击浏览应用目录按钮。搜索SCIM 2.0测试应用(Header Auth),并在拉出该应用后点击添加按钮。

  2. 常规设置选项卡中,点击下一步

  3. 我们将把它设置为SWA应用程序,所以在登录选项标签中,点击安全网络认证

  4. 单击 "完成"。

  5. 配置选项卡上,点击配置API集成。

  6. 勾选启用API集成

  7. 在基本URL字段中,粘贴你在上面生成的ngrok网址,并将**/scim/v2加到最后。在API Token字段中,输入Bearer 123456789**。(稍后我们将讨论如何定制这个授权头,但开箱即用,SCIM服务器希望有这个值。)

  8. 点击Test API Credentials,你应该得到一个像下面这样的成功信息。

    你可以通过浏览http://localhost:4040 来检查ngrok日志,看看Okta的请求和SCIM服务器的响应。

  9. 点击保存。现在你的Provisoning标签看起来有点不同。

  10. 点击Provisioning to App旁边的编辑,并勾选: * 创建用户 * 更新用户属性 * 停用用户

保存

选项2:为现有的应用程序集成向导(AIW)应用程序启用SCIM配置。

如果你在上面设置了你的SCIM集成,请随意跳过这一部分,测试SCIM服务器

  1. 在你的Okta仪表盘上,进入应用->应用,然后点击创建应用集成按钮。对于这个设置,我们将选择SWA - 安全网络认证。点击下一步

  2. 你可以为应用程序名称应用程序登录页面URL添加任何你想要的内容,因为我们只是在研究SCIM功能,而不是这个应用程序的SWA方面。点击完成

  3. 在应用程序的 "常规"选项卡中,单击**"编辑",将 "供应 "从"无**"切换为 "SCIM"。点击保存

  4. 你的应用程序现在应该有一个供应标签。点击该标签,并填写整合设置,以符合下面的图片。使授权标题为123456789。你可以稍后在SCIM flask应用中改变这个。

  5. 点击测试连接器配置,你应该看到下面的成功确认。

  6. 你现在可以点击保存

    你可以导航到http://localhost:4040 ,看看Okta对这个请求的要求,以及SCIM服务器的响应。

  7. 现在你的Provisioning标签看起来有点不同。点击 "编辑"旁边的 "提供给应用程序"并勾选。

    • 创建用户
    • 更新用户属性
    • 停用用户

保存

现在你应该在Okta这边设置好,开始测试SCIM服务器。

我们刚才做了什么?

在我们继续测试我们的SCIM服务器之前,让我们谈谈我们在上面的设置步骤中做了什么。基本上我们设置了以下3个组件。

在这个设置中,Okta是用户数据的真实来源。SCIM服务器在本地打开了四个端点,由于ngrok的路由功能,Okta能够访问这些端点。这些端点是:

  • https://localhost:5000/scim/v2/Users
  • https://localhost:5000/scim/v2/Users/{user_id}
  • https://localhost:5000/scim/v2/Groups
  • https://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发送一个PUTPATCH/scim/v2/Users/{user_id}
  • 当分配或取消一个已经存在于外部应用数据库的用户时,Okta发送一个PUTPATCH/scim/v2/Users/{user_id}
  • 当改变一个被推送的Okta组的成员资格时,Okta会发送一个PUTPATCH/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功能。 有问题吗?你可以在下面的评论中留言!想保持联系?请关注我们的社交渠道