3 添加资源权限检查
在对特定的资源权限框架允许根据资源本身的特征做出决定,这意味着可以编写策略,例如允许拥有资源的用户进行操作,否则拒绝操作。
创建更新权限
让我们为文件添加一个新权限plugins/todo-list-common/src/permissions.ts
从前一节.
import { createPermission } from '@backstage/plugin-permission-common';
export const TODO_LIST_RESOURCE_TYPE = 'todo-item';
export const todoListCreatePermission = createPermission({
name: 'todo.list.create',
attributes: { action: 'create' },
});
export const todoListUpdatePermission = createPermission({
name: 'todo.list.update',
attributes: { action: 'update' },
resourceType: TODO_LIST_RESOURCE_TYPE,
});
export const todoListPermissions = [todoListCreatePermission];
export const todoListPermissions = [
todoListCreatePermission,
todoListUpdatePermission,
];
请注意,与todoListCreatePermission
,"......todoListUpdatePermission
权限包含一个resourceType
该字段向权限框架表明,该权限是在资源类型为'todo-item'
您可以使用任何您喜欢的字符串作为资源类型,只要您对每种类型的资源都使用相同的值。
为更新权限设置授权
首先,让我们编辑plugins/todo-list-backend/src/service/router.ts
的方法与上一节相同:
import { todoListCreatePermission } from '@internal/plugin-todo-list-common';
import {
todoListCreatePermission,
todoListUpdatePermission,
} from '@internal/plugin-todo-list-common';
// ...
const permissionIntegrationRouter = createPermissionIntegrationRouter({
permissions: [todoListCreatePermission],
permissions: [todoListCreatePermission, todoListUpdatePermission],
});
// ...
router.put('/todos', async (req, res) => {
const token = getBearerTokenFromAuthorizationHeader(
req.header('authorization'),
);
if (!isTodoUpdateRequest(req.body)) {
throw new InputError('Invalid payload');
}
const decision = (
await permissions.authorize(
[{ permission: todoListUpdatePermission, resourceRef: req.body.id }],
{
token,
},
)
)[0];
if (decision.result !== AuthorizeResult.ALLOW) {
throw new NotAllowedError('Unauthorized');
}
res.json(update(req.body));
});
**重要:**请注意,我们传递了一个额外的resourceRef
字段,用id
的值。
这样就可以根据资源的特征做出决定,但需要注意的是,策略作者将无法访问其权限策略中的资源 ref。 相反,策略将返回条件决定,我们现在需要在插件中支持这一点。
添加对条件决定的支持
安装缺失的模块:
$ yarn workspace @internal/plugin-todo-list-backend add zod
创建一个新的plugins/todo-list-backend/src/service/rules.ts
文件,并添加以下代码:
import { makeCreatePermissionRule } from '@backstage/plugin-permission-node';
import { TODO_LIST_RESOURCE_TYPE } from '@internal/plugin-todo-list-common';
import { z } from 'zod';
import { Todo, TodoFilter } from './todos';
export const createTodoListPermissionRule = makeCreatePermissionRule<
Todo,
TodoFilter,
typeof TODO_LIST_RESOURCE_TYPE
>();
export const isOwner = createTodoListPermissionRule({
name: 'IS_OWNER',
description: 'Should allow only if the todo belongs to the user',
resourceType: TODO_LIST_RESOURCE_TYPE,
paramsSchema: z.object({
userId: z.string().describe('User ID to match on the resource'),
}),
apply: (resource: Todo, { userId }) => {
return resource.author === userId;
},
toQuery: ({ userId }) => {
return {
property: 'author',
values: [userId],
};
},
});
export const rules = { isOwner };
makeCreatePermissionRule
是一个辅助工具,用于确保为该插件创建的规则使用一致的资源和查询类型。
注意:要支持由 backstage 集成者定义的自定义规则,必须从后端(backend)软件包中导出
createTodoListPermissionRule
并提供某种方式让自定义规则在后端启动前传入,很可能是通过createRouter
来实现。
我们创建了一个新的isOwner
规则,权限框架将自动使用该规则,每当返回的条件响应是对授权请求的响应,并附有resourceRef
具体而言apply
函数用于了解传递的资源是否应被授权。
让我们跳过toQuery
我们将在下一节再讨论这个问题。
现在,让我们通过编辑plugins/todo-list-backend/src/service/router.ts
这使用了createPermissionIntegrationRouter
助手,将权限框架所需的 API 添加到您的插件中。 您需要提供
getResources
: 一个函数,接受与传给authorize
的格式相同的resourceRefs
数组,并返回相应资源的数组。resourceType
: 与上述权限规则中使用的值相同。permissions
: 您的插件接受的权限列表。rules
: 您希望在条件决定中支持的所有权限规则的数组。
// ...
import {
TODO_LIST_RESOURCE_TYPE,
todoListCreatePermission,
todoListUpdatePermission,
} from '@internal/plugin-todo-list-common';
import { add, getAll, update } from './todos';
import { add, getAll, getTodo, update } from './todos';
import { rules } from './rules';
export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
const { logger, identity, permissions } = options;
const permissionIntegrationRouter = createPermissionIntegrationRouter({
permissions: [todoListCreatePermission, todoListUpdatePermission],
getResources: async resourceRefs => {
return resourceRefs.map(getTodo);
},
resourceType: TODO_LIST_RESOURCE_TYPE,
rules: Object.values(rules),
});
const router = Router();
router.use(express.json());
// ...
}
为政策制定者提供实用工具
现在我们有了新的资源类型和相应的规则,我们需要导出一些实用程序,供策略作者引用。
创建一个新的plugins/todo-list-backend/src/conditionExports.ts
文件,并添加以下代码:
import { TODO_LIST_RESOURCE_TYPE } from '@internal/plugin-todo-list-common';
import { createConditionExports } from '@backstage/plugin-permission-node';
import { rules } from './service/rules';
const { conditions, createConditionalDecision } = createConditionExports({
pluginId: 'todolist',
resourceType: TODO_LIST_RESOURCE_TYPE,
rules,
});
export const todoListConditions = conditions;
export const createTodoListConditionalDecision = createConditionalDecision;
确保todoListConditions
和createTodoListConditionalDecision
从todo-list-backend
通过编辑plugins/todo-list-backend/src/index.ts
:
export * from './service/router';
export * from './conditionExports';
export { exampleTodoListPlugin } from './plugin';
测试授权更新端点
让我们回到权限策略的处理函数,尝试用一个isOwner
状况
import {
BackstageIdentityResponse,
IdentityClient
} from '@backstage/plugin-auth-node';
import {
PermissionPolicy,
PolicyQuery,
} from '@backstage/plugin-permission-node';
import { isPermission } from '@backstage/plugin-permission-common';
import { todoListCreatePermission } from '@internal/plugin-todo-list-common';
import {
todoListCreatePermission,
todoListUpdatePermission,
} from '@internal/plugin-todo-list-common';
import {
todoListConditions,
createTodoListConditionalDecision,
} from '@internal/plugin-todo-list-backend';
async handle(
request: PolicyQuery,
_user?: BackstageIdentityResponse,
user?: BackstageIdentityResponse,
): Promise<PolicyDecision> {
if (isPermission(request.permission, todoListCreatePermission)) {
return {
result: AuthorizeResult.ALLOW,
};
}
if (isPermission(request.permission, todoListUpdatePermission)) {
return createTodoListConditionalDecision(
request.permission,
todoListConditions.isOwner({
userId: user?.identity.userEntityRef ?? '',
}),
);
}
return {
result: AuthorizeResult.ALLOW,
};
}
对于传入的更新请求,我们现在会返回一个我们在说
嘿,权限框架,我一个人做不了决定。 请转到 id 为
todolist
的插件,要求它应用这些条件。
为了检查一切是否按预期运行,每当您尝试编辑不是由您创建的项目时,您都会在用户界面上看到一个错误。 成功!