ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Open edX OAuth 令牌组织隔离设计:在 JWT 中纳入 Organizations 过滤器(ADR 0007 深度解析)

Open edX OAuth 令牌组织隔离设计:在 JWT 中纳入 Organizations 过滤器(ADR 0007 深度解析) Open edX OAuth 令牌组织隔离设计在 JWT 中纳入 Organizations 过滤器ADR 0007 深度解析【免费下载链接】openedx-platformThe Open edX LMS Studio, powering education sites around the world!项目地址: https://gitcode.com/GitHub_Trending/ed/openedx-platform导读本文以 Open edX 平台 oauth_dispatch 应用 的架构决策记录 0007-include-organizations-in-tokens.rst 为核心剖析 Open edX 如何让外部组织的应用DOT Application通过 OAuth 令牌安全地按组织维度访问 API 数据应用与组织关联、JWT 中引入filters声明、授权审批页面向用户明示授权范围。读完本文你将掌握该决策的完整背景、数据模型与 JWT 载荷设计、三种授权类型的令牌形态差异以及当前仓库中ApplicationAccess、filters字段的实际落地实现。一、决策背景组织访问 API 的现状与痛点1.1 外部应用对组织级 API 访问的诉求在引入本决策之前Open edX 的 API 存在明显的访问能力断层外部 edX 应用希望通过Client Credentials 授权类型进行服务器到服务器server-to-server的 API 调用以获取数据但当时 API 通常只对全局 staff 用户返回数据这类用户实际上拥有对系统的管理级只读权限这种全有或全无all-or-nothing的能力无法满足 edX 合作伙伴组织的需求——一个组织需要能访问自己课程/学习者的数据同时绝不能触及其他组织的数据。另一个典型场景是部分组织为自家学习者构建了独立门户以 edX 作为身份提供者IdP和底层 LMS。出于各种原因它们希望在自有门户上向学习者展示 edX 数据但当时无法通过 API 访问单个学习者的数据。根因在于API 端点缺少可靠的信息来根据请求方应用的组织归属去限制/过滤 API 返回结果。这正是本 ADR 要解决的核心问题——为 API 提供组织维度的过滤依据。1.2 edX 系统中的三类组织关系决策文档将 edX 系统中存在的组织关系归纳为三种类型它们决定了组织对数据的访问模式组织类型角色定位典型访问方式内容提供者Content Provider为课程、项目等提供内容的合作伙伴组织希望获取所有选修其课程的学习者数据a) 通过Client Credentials 授权类型调用批量 API如后台同步数据b) 通过Authorization Code 授权类型以 edX 为 IdP为已登录用户提供用户级 API如在自建门户展示用户数据用户提供者User Provider通过 SSO 门户把用户注册进 edX 的企业组织组织而非 edX是身份提供者访问其全部用户的数据学分提供者Credit Provider认可 edX 课程/项目学分的机构或雇主用户选择性授予该组织访问其 edX 记录与信息的权限二、核心决策三项设计共同构建组织隔离为了让 DOT Application 能访问自身组织的数据同时避免无意或恶意地获取其他组织的数据ADR 提出了三项配套决策应用需要与自身组织建立关联组织信息需要与签发的令牌进行密码学绑定授权审批表单authorization approval form需要向授权用户展示组织信息。2.1 决策一为 DOT 应用关联可用组织创建一个可配置的、Application 级别的可用组织available organizations设置其定位与 Application 级别的可用 scopes见 0006-enforce-scopes-in-LMS-APIs.rst类似引入一个新的数据模型把可用组织与 DOT Application 关联起来新模型通过外键Foreign Key指向 Organization 表本质上是 Organizations 与 DOT Applications 之间的多对多关系新模型还包含一个组织类型organization type列取值为content_provider、user_provider、credit_provider等最初阶段只使用content_provider。源码佐证这一决策最初落地的模型是ApplicationOrganization见 models.py其中定义了RELATION_TYPE_CONTENT_ORG content_org及唯一的关系类型选项(content_org, Content Provider)并通过unique_together (application, relation_type, organization)约束同一应用对同一组织、同一关系类型只允许一条记录。该模型当前已被标记为DEPRECATED不再使用其演进过程详见下文从 ApplicationOrganization 到 ApplicationAccess.filters小节。2.2 决策二组织与用户作为 OAuth 令牌过滤器与应用关联的组织将被写入 JWT 中新增的filters字段filters字段的值包含组织类型与标识格式如下content_org:Microsoft对于代表用户创建的令牌即非Client Credentials 授权类型签发的令牌令牌被进一步限定到授权用户本人因此会额外追加一个值为me的user过滤器user:me扩展JwtBuilder的build_token功能把 filters 纳入令牌 payload。由于该 payload经过密码学签名令牌中的 scopes 被绑定并限制在 filters 范围内无法被篡改由于 filters 位于令牌内部任何收到令牌的依赖方包括任意微服务都能基于 filters 强制限制 scopes。API 端点将根据指定的 filters 限制其 payload 中返回的字段/记录。2.3 决策三授权审批表单展示组织信息当系统向用户呈现授权审批的中间页interstitial authorization approval form请求用户为某个 DOT Application 授权时如果该 Application 与某个 Organization 关联则应将 Organization 的值展示给用户。这使得用户能清楚地意识到授予该应用的访问权限被限定在组织所属范围内。源码佐证EdxOAuth2AuthorizationView见 dot_overrides/views.py在auto_even_if_expired分支中通过ApplicationAccess.get_filter_values(application, ApplicationAccess.CONTENT_ORG_FILTER_NAME)读取该应用配置的 content org 过滤器并将其放入kwargs[content_orgs]传入授权页模板当ApplicationAccess.DoesNotExist未配置访问策略时则回退为空列表。对应测试见 test_views.py其中配置了content_org:test content org与other_filter:filter_val两组过滤器。三、令牌形态示例两种授权类型的差异3.1 Client Credentials服务器到服务器授权类型当可信应用发起服务器到服务器调用时JWT 中包含该应用的service user信息filters字段携带应用关联的组织标识与类型{ scopes: [grades:read, enrollments:read], filters: [content_org:Microsoft], version: 1.0, preferred_username: microsoft_service_user, ... }3.2 Authorization Code 与 Password代表用户授权类型当用户已批准的应用或可信移动应用代表该用户发起调用时令牌中包含用户信息filters字段除组织过滤器外还追加user:me{ scopes: [grades:read, enrollments:read], filters: [content_org:Microsoft, user:me], version: 1.0, preferred_username: ajay_mehta, ... }源码佐证上述两种形态在当前实现中由 adapters/dot.py 的get_authorization_filters统一生成先收集ApplicationAccess.filters中配置的全部过滤器再根据授权类型决定是否追加user:me——Client Credentials 类型的应用不加允许批量获取所有用户数据其余类型一律追加。签发时 jwt.py 的create_jwt_token_dict通过filtersoauth_adapter.get_authorization_filters(client)将其写入 JWT payload见_create_jwt中的filters: filters or []jwt.py。测试 test_views.py 验证了 JWT 中确实包含预期的 scopes 与 filters。四、当前仓库中的落地实现4.1ApplicationAccess模型scopes 与 filters 统一管理随着后续 ADR 0011-scope-filter-support.rst 的演进当前主模型是 models.py 中的ApplicationAccess它通过一对一外键关联 DOT Application并同时承载scopes与filters两个字段CONTENT_ORG_FILTER_NAME content_org声明 content org 过滤器形如content_org:org_name例如content_org:SchoolX表示凡是关心该过滤器的端点其响应都应依据过滤器值进行过滤scopes ListCharField(...)应用被允许请求的 scope 列表最多 25 个每个最长 32 字符filters ListCharField(...)应用被允许请求的过滤器列表同样最多 25 个可为空nullTrue, blankTrue类方法get_scopes/get_filters/get_filter_values分别用于读取 scopes、filters以及按过滤器名提取取值get_filter_values内部通过filter_constraint.split(:, 1)解析name:value形式见 models.py。该模型的filters字段由迁移 0008_applicationaccess_filters.py 于 2020 年引入。4.2 从 ApplicationOrganization 到 ApplicationAccess.filtersADR 0011 明确指出content_org过滤器的初版实现即ApplicationOrganization模型难以支持与组织无关的新过滤器类型因此做出了两项演进决策在ApplicationAccess上增加filters字段以便快速支持新过滤器类型弃用并移除ApplicationOrganization模型它只能处理极小的过滤器子集。由此带来的好处过滤器通常与 scopes 存在关联现在二者可在同一个管理后台页面中配置让 OAuth 应用的安全定义更简单直观。按照 models.py 中的注释ApplicationOrganization已计划在 Juniper 版本后移除存量数据需迁移为ApplicationAccess中的content_org:ORG NAME过滤器记录。沿用决策文档中的示例现在的配置方式是Scopes: grades:read,enrollments:read Filters: content_org:Microsoft签发出的 JWT 将包含{ scopes: [grades:read, enrollments:read], filters: [content_org:Microsoft, user:me], ... }注意使用给定 OAuth Application 签发的每一个 JWT 访问令牌都会包含为该应用定义的全部过滤器自过滤器初版引入起即如此这与授权请求实际请求的 scope 无关。4.3 管理后台配置入口在 Django Admin 中admin.py 注册了ApplicationAccessAdmin其list_display [application, scopes, filters]运维人员可以在同一屏内为一个 DOT Application 配置 scopes 与 filters。此外create_dot_application管理命令见 create_dot_application.py支持通过--scopes参数在创建应用时一并写入ApplicationAccess可用作初始化的便捷入口。五、决策后果与安全考量5.1 正向影响逐步淘汰 Restricted Application因为组织关联建立在 DOT Application 上而非 Restricted Application 上未来可以彻底移除 Restricted Application 机制任意依赖方可执行组织级限制将组织值及其类型放进令牌后任何收到令牌的依赖方包括微服务都能把 scopes 限制在组织范围内执行职责分层清晰独立的filters字段明确了各层级的职责边界层级职责API 端点声明required scopes所需范围基础 Django Permission 类强制校验required scopesAPI 网关未来可额外强制校验required scopesAPI 端点强制校验required filters所需过滤器5.2 未来引入新过滤器类型时的安全预案当未来引入新过滤器类型时必须防止不认识新过滤器的旧端点不执行它导致的安全漏洞可选方案高敏感端点直接拒签对安全性要求极高的端点应拒绝任何包含无法识别过滤器的令牌多阶段分步发布等所有微服务与相关端点都升级到可识别新过滤器后再进行令牌的主版本升级只有所有相关端点更新完毕后才签发带新过滤器的令牌。5.3 被否决的备选方案把过滤器类型嵌入 scopes决策文档还评估过一种备选方案——将过滤器类型嵌入 scopes 字段例如grades:read:content_org该方案的优势在于更安全旧端点会自动拒绝其无法识别的 scope 中的新过滤器类型且允许令牌为不同 scope 指定不同过滤器。但该方案最终被否决理由有二增加了理解与解析 scope 值的无谓混淆保持 filters 独立可以让它随时间的推移独立演化、增长变得更复杂而不必强行把值塞进 scope 表达式。源码佐证grant_type 演进filters与 scopes 解耦的设计思想在后来的 ADR 中延续——例如 0014-add-grant-type-in-jwt-payload.rst 在 JWT payload 中单独增加了grant_type字段。当前 jwt.py 的 payload 构建同时包含scopes、filters、grant_type、is_restricted等独立字段验证了各声明字段独立演化的设计取向。六、总结ADR 0007 为 Open edX 的 OAuth 生态引入了组织感知能力通过DOT Application 与组织关联、JWTfilters声明密码学绑定、授权审批表单明示组织范围三管齐下让内容提供者、用户提供者、学分提供者等组织可以在不越权的前提下访问自身数据。当前仓库已将其演进为ApplicationAccess模型的scopesfilters统一配置模式content_org:组织名与user:me过滤器随 JWT 一同签发任何依赖方含微服务都能据此执行组织与用户粒度的数据过滤。对于希望在 Open edX 之上构建组织级数据访问的开发者与平台运维者而言理解这套模型是正确配置 OAuth 应用访问策略的前提。进一步阅读决策文档原文0007-include-organizations-in-tokens.rst后续演进0011-scope-filter-support.rst关联决策0006-enforce-scopes-in-LMS-APIs.rst核心实现models.py、jwt.py、adapters/dot.py、dot_overrides/views.py验证测试test_views.py【免费下载链接】openedx-platformThe Open edX LMS Studio, powering education sites around the world!项目地址: https://gitcode.com/GitHub_Trending/ed/openedx-platform创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表