Backstage Permissions Registry 核心服务详解:从 `createPermissionIntegrationRouter` 迁移到新式插件注册 Backstage Permissions Registry 核心服务详解从createPermissionIntegrationRouter迁移到新式插件注册【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage本篇文章深入解析 Backstage 后端系统New Backend System中的permissionsRegistry核心服务它是插件向权限框架注册权限permissions、规则rules与资源类型resource types的统一入口。你会掌握该服务的全部接口方法、完整的迁移路径从旧的createPermissionIntegrationRouter迁移过来、底层实现原理以及软件目录插件catalog如何实际使用它为你的插件接入细粒度授权能力提供可直接落地的方案。Permissions Registry 服务在权限体系中的定位Backstage 的权限框架允许你对插件功能进行细粒度的访问控制。要让一个插件的能力参与授权决策插件必须先把三类元数据告知权限系统权限Permission如 查看实体、规则Permission Rule如 实体所有者是 team-a以及资源类型Resource Type如catalog-entity。permissionsRegistry正是承载这一注册职责的核心服务。根据官方描述它允许你的插件注册新的权限、规则和资源类型并与权限框架集成是 权限框架总览 与后端插件之间的桥梁。在源码层面该服务的依赖定义位于 coreServices.ts服务引用标识为core.permissionsRegistryexport const permissionsRegistry createServiceRef import(./PermissionsRegistryService).PermissionsRegistryService ({ id: core.permissionsRegistry });也就是说任何基于新后端系统的插件都可以在registerInit的deps中直接声明coreServices.permissionsRegistry并注入使用无需额外的配置或初始化步骤。服务接口四个核心方法服务接口定义在 PermissionsRegistryService.ts共包含四个方法方法作用适用场景addPermissions(permissions: Permission[])向权限系统注册一批权限只注册权限、不涉及条件规则时addPermissionRules(rules)为某个资源类型注册条件过滤规则补充自定义规则可由插件自身或插件模块注册addResourceType(options)注册插件拥有的资源类型并绑定权限与规则需要支持条件授权conditional decisions的插件getPermissionRuleset(resourceRef)取回该资源已注册的规则集配合createConditionAuthorizer/createConditionTransformer使用addResourceType的完整选项addResourceType是核心方法其选项类型PermissionsRegistryServiceAddResourceTypeOptions包含以下字段resourceRef必填标识资源类型的PermissionResourceRef内部携带pluginId与resourceType两个关键信息。permissions可选该资源类型下可用的权限列表。rules必填该资源类型的条件过滤规则数组类型为PermissionRuleTResource, TQuery, TResourceType[]。例如软件目录中的isEntityOwner、hasAnnotation规则描述如何过滤一组资源并允许以具体参数如group:default/team-a、backstage.io/edit-url实例化条件。getResources可选根据资源引用标识批量加载资源对象的函数。注释明确说明如果不提供此函数权限系统将无法解析条件决策除非直接从插件请求资源。它是permission-backend在评估与当前插件相关的授权条件时回调的入口。以软件目录为例见 PermissionsRegistryService.ts 中的说明catalog 围绕具体的实体entities做条件访问控制其资源类型标识符为catalog-entity这个标识符只用于校验授权策略中的条件构造是否正确而非指向某个具体资源的引用。在插件中注册权限与资源类型要使用该服务只需在插件初始化时声明依赖并调用相应方法。例如一个拥有自定义资源类型的插件可以这样写import { coreServices, createBackendPlugin } from backstage/backend-plugin-api; export const examplePlugin createBackendPlugin({ pluginId: example, register(env) { env.registerInit({ deps: { logger: coreServices.logger, permissionsRegistry: coreServices.permissionsRegistry, }, async init({ logger, permissionsRegistry }) { logger.log(This is a silly example plugin with no functionality); permissionsRegistry.addResourceType({ resourceRef: RESOURCE_TYPE_MY_RESOURCE, permissions: [myResourcePermissions], rules: [myResourceRule], getResources: async resourceRefs { // 根据引用标识加载资源供权限后端评估条件使用 return resourceRefs.map(ref loadResource(ref)); }, }); }, }); }, });关于更完整的插件作者权限指南可参考 权限指南插件作者篇如果只想为已有插件添加自定义权限规则可直接查阅 自定义权限规则指南。从createPermissionIntegrationRouter迁移在新后端系统引入本服务之前插件通过createPermissionIntegrationRouter实现同样的注册功能并把返回的 router 挂载到自己的 HTTP 路由上。迁移的核心思路是删除对createPermissionIntegrationRouter的调用但把它收到的所有选项原样转交给permissionsRegistry服务。第一步移除旧路由注册export async function createRouter() { const router Router(); // 删除以下代码块 const permissionIntegrationRouter createPermissionIntegrationRouter({ resourceType: RESOURCE_TYPE_MY_RESOURCE, permissions: [myResourcePermissions], rules: [myResourceRule], }); router.use(permissionIntegrationRouter); // ... }第二步注入服务并注册相同选项export const examplePlugin createBackendPlugin({ pluginId: example, register(env) { env.registerInit({ deps: { logger: coreServices.logger, permissionsRegistry: coreServices.permissionsRegistry, // 新增依赖 }, async init({ logger, permissionsRegistry }) { logger.log(This is a silly example plugin with no functionality); // 与旧的 createPermissionIntegrationRouter 选项一一对应 permissionsRegistry.addResourceType({ resourceType: RESOURCE_TYPE_MY_RESOURCE, permissions: [myResourcePermissions], rules: [myResourceRule], }); }, }); }, });第三步按选项形态选择迁移方式官方文档针对不同选项形态给出了三种迁移映射如果旧代码只传了permissions选项不涉及资源类型与规则应改用permissionsRegistry.addPermissions而非addResourceType。如果旧代码通过resources选项传入了多个资源类型则应对每个资源类型分别调用一次permissionsRegistry.addResourceType。如果旧代码通过rules注册了规则对应使用addResourceType中的rules字段或独立调用addPermissionRules。这种选项平移到服务方法的设计使得迁移过程几乎是机械式的替换插件无需重写任何权限逻辑。底层实现服务工厂如何工作默认的服务工厂实现位于 permissionsRegistryServiceFactory.ts它揭示了几个值得注意的实现细节1. 内部仍然封装createPermissionIntegrationRouter工厂内部直接创建了一个createPermissionIntegrationRouter()所有注册方法addResourceType、addPermissions、addPermissionRules最终都转发给这个 router 实例。这保证了迁移前后行为完全一致——旧 API 的语义被完整保留只是注册入口从手动挂载路由变成了核心服务。2. 资源归属校验pluginId 强绑定工厂中的assertRefPluginId函数会校验传入的PermissionResourceRef.pluginId是否与当前插件的pluginId一致function assertRefPluginId(ref: PermissionResourceRef, pluginId: string) { if (ref.pluginId ! pluginId) { throw new Error( Resource type ${ref.resourceType} belongs to plugin ${ref.pluginId}, but was used with plugin ${pluginId}, ); } }也就是说插件不能注册属于其他插件的资源类型这一约束在addResourceType与getPermissionRuleset两个入口都会强制执行从源头避免了插件之间资源声明的互相污染。3. 注册锁定启动后禁止再注册工厂通过lifecycle.addStartupHook记录启动状态let started false; lifecycle.addStartupHook(() { started true; });一旦插件启动完成任何addResourceType/addPermissions/addPermissionRules调用都会抛出Cannot add permission resource types after the plugin has started之类的错误。注册必须在插件初始化阶段完成这也意味着插件模块modules同样可以在启动前通过该服务补充规则。4. 自动挂载条件应用端点工厂将内部 router 挂载到coreServices.httpRouter并额外在/.well-known/backstage/permissions/apply-conditions路径挂载了鉴权中间件该端点只允许user或service主体访问且user 主体必须带有真实身份actor否则抛出NotAllowedError。这是permission-backend在评估与插件相关的授权条件时调用的服务端 API也是getResources回调被触发的通道。仓库中的真实用例软件目录插件最典型的落地案例是软件目录插件。在 CatalogBuilder.ts 中catalog 构建其权限注册时const getResources async (resourceRefs: string[]) { const { items } await unauthorizedEntitiesCatalog.entitiesBatch({ credentials: await auth.getOwnServiceCredentials(), entityRefs: resourceRefs, }); return entitiesResponseToObjects(items).map(e e || undefined); }; permissionsRegistry.addResourceType({ resourceRef: catalogEntityPermissionResourceRef, getResources, permissions: [...catalogPermissions], rules: Object.values(catalogPermissionRules), });从中可以看到几个实践要点getResources使用服务自身的凭据auth.getOwnServiceCredentials()通过entitiesBatch批量加载实体避免绕过授权校验同一处还通过permissionsRegistry.getPermissionRuleset(...)取得规则集供条件授权器createConditionAuthorizer与条件转换器createConditionTransformer使用见 CatalogBuilder.ts。测试验证跨插件注册被拒绝服务工厂的测试用例 permissionsRegistryServiceFactory.test.ts 使用startTestBackend验证了资源归属约束当插件test试图注册pluginId: other的资源类型或读取其规则集时后端启动会直接失败并抛出Plugin test startup failed; caused by Error: Resource type some-resource belongs to plugin other, but was used with plugin test这一测试从行为层面确认了上述pluginId 强绑定约束的真实性与重要性是你在自研插件中编写类似注册逻辑时值得参考的边界条件。小结与推荐路径permissionsRegistry是新后端系统下插件接入权限框架的标准姿势。你可以据此规划自己的接入路线只注册权限使用addPermissions为已有资源类型补充规则使用addPermissionRules插件模块同样适用拥有自己的资源类型并支持条件授权使用addResourceType务必提供getResources以便permission-backend解析条件决策从旧系统迁移将createPermissionIntegrationRouter的选项逐一平移到上述服务方法删除手动router.use。如需更体系化的学习可继续阅读 权限框架总览、插件作者权限指南 与 自定义权限规则指南。【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考