4 授权访问分页数据
授权GET /todos
与更新端点类似,可以根据每个资源的特性授权访问。 不过,我们需要为该端点授权一个资源列表。
一种可能的解决方案是利用批处理功能授权所有待办事项,然后只返回已作出决定的待办事项。ALLOW
:
router.get('/todos', async (req, res) => {
const token = IdentityClient.getBearerToken(req.header('authorization'));
res.json(getAll());
const items = getAll();
const decisions = await permissions.authorize(
items.map(({ id }) => ({
permission: todoListReadPermission,
resourceRef: id,
})),
);
const filteredItems = decisions.filter(
decision => decision.result === AuthorizeResult.ALLOW,
);
res.json(filteredItems);
});
这种方法适用于简单的情况,但它有一个缺点:它迫使我们预先检索所有元素并逐一授权。 这就迫使插件实现处理分页等问题,而这些问题目前是由数据源处理的。
为了避免这种情况,权限框架支持在数据源中过滤项目。 在本部分教程中,我们将介绍使用该行为所需的步骤。
注意:为了以这种方式执行授权过滤,数据源必须允许使用 AND、OR 和 NOT 操作符对过滤器进行逻辑组合。 权限框架返回的条件决定使用 嵌套对象 来组合条件。如果您要从头开始实现过滤器 API,我们建议您使用相同的形状,以方便互操作。如果不是,您需要实现一个函数,将嵌套对象转换成您自己的格式。
创建读取权限
让我们为插件添加另一个权限。
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 todoListReadPermission = createPermission({
name: 'todos.list.read',
attributes: { action: 'read' },
resourceType: TODO_LIST_RESOURCE_TYPE,
});
export const todoListPermissions = [
todoListCreatePermission,
todoListUpdatePermission,
todoListReadPermission,
];
使用有条件的政策决定
到目前为止,我们只使用了PermissionEvaluator.authorize
方法,该方法将在返回结果之前评估条件判定。 在这一步中,我们希望在插件内评估条件判定,因此我们将使用PermissionEvaluator.authorizeConditional
而不是
import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';
import {
createPermissionIntegrationRouter,
createConditionTransformer,
ConditionTransformer,
} from '@backstage/plugin-permission-node';
import { add, getAll, getTodo, update } from './todos';
import { add, getAll, getTodo, TodoFilter, update } from './todos';
import {
TODO_LIST_RESOURCE_TYPE,
todoListCreatePermission,
todoListUpdatePermission,
todoListReadPermission,
} from './permissions';
// ...
const permissionIntegrationRouter = createPermissionIntegrationRouter({
permissions: [todoListCreatePermission, todoListUpdatePermission],
permissions: [todoListCreatePermission, todoListUpdatePermission, todoListReadPermission],
getResources: async resourceRefs => {
return resourceRefs.map(getTodo);
},
resourceType: TODO_LIST_RESOURCE_TYPE,
rules: Object.values(rules),
});
// ...
const transformConditions: ConditionTransformer<TodoFilter> = createConditionTransformer(Object.values(rules));
router.get('/todos', async (_req, res) => {
router.get('/todos', async (req, res) => {
const token = getBearerTokenFromAuthorizationHeader(
req.header('authorization'),
);
const decision = (
await permissions.authorizeConditional([{ permission: todoListReadPermission }], {
token,
})
)[0];
if (decision.result === AuthorizeResult.DENY) {
throw new NotAllowedError('Unauthorized');
}
if (decision.result === AuthorizeResult.CONDITIONAL) {
const filter = transformConditions(decision.conditions);
res.json(getAll(filter));
} else {
res.json(getAll());
}
res.json(getAll());
});
为了使处理有条件决定的过程更容易,权限框架提供了一个createConditionTransformer
该函数接受一个权限规则数组,并返回一个转换器函数,该函数使用toQuery
方法定义在每条规则上。
因为TodoFilter
如果过滤器的结构不同,我们就需要在将其传递给 api 之前对其进行进一步转换。
测试授权读取端点
让我们更新权限策略,每当有一个todoListReadPermission
在这种情况下,我们可以重复使用为todosListCreate
许可。
import {
todoListCreatePermission,
todoListUpdatePermission,
todoListReadPermission,
} from '@internal/plugin-todo-list-common';
if (isPermission(request.permission, todoListUpdatePermission)) {
if (
isPermission(request.permission, todoListUpdatePermission) ||
isPermission(request.permission, todoListReadPermission)
) {
return createTodoListConditionalDecision(
request.permission,
todoListConditions.isOwner({
userId: user?.identity.userEntityRef
}),
);
}
保存权限策略的更改后,用户界面应只显示您创建的待办事项。