Skip to main content

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,我们建议您使用相同的形状,以方便互操作。如果不是,您需要实现一个函数,将嵌套对象转换成您自己的格式。

创建读取权限

让我们为插件添加另一个权限。

plugins/todo-list-backend/src/service/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 todoListReadPermission = createPermission({
name: 'todos.list.read',
attributes: { action: 'read' },
resourceType: TODO_LIST_RESOURCE_TYPE,
});

export const todoListPermissions = [
todoListCreatePermission,
todoListUpdatePermission,
todoListReadPermission,
];

使用有条件的政策决定

到目前为止,我们只使用了PermissionEvaluator.authorize方法,该方法将在返回结果之前评估条件判定。 在这一步中,我们希望在插件内评估条件判定,因此我们将使用PermissionEvaluator.authorizeConditional而不是

plugins/todo-list-backend/src/service/router.ts
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许可。

packages/backend/src/plugins/permission.ts
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
}),
);
}

保存权限策略的更改后,用户界面应只显示您创建的待办事项。