2. 기본 권한 검사 추가
권한 검사의 결과가 리소스에 따라 바뀔 필요가 없다면 기본 권한 검사를 사용할 수 있어요. 이런 종류의 검사에서는 단순히 권한을 정의하고 그것으로 authorize를 호출하기만 하면 돼요.
출처: 문서
본문
권한 검사의 결과가 리소스에 따라 바뀔 필요가 없다면 기본 권한 검사를 사용할 수 있어요. 이런 종류의 검사에서는 단순히 권한을 정의하고 그것으로 authorize를 호출하기만 하면 돼요.
이 튜토리얼에서는 todo-backend의 create 엔드포인트를 인가하기 위해 기본 권한 검사를 사용할 거예요. 이렇게 하면 Backstage 통합자가 권한 정책을 조정해 각 사용자가 todo를 만들 권한이 있는지 제어할 수 있어요.
새 권한을 만드는 것으로 시작한 다음, todo 생성 중에 permission API를 사용해 그것으로 authorize를 호출할 거예요.
새 권한 만들기
plugins/todo-list-common/src/permissions.ts 파일로 이동해 첫 번째 권한을 추가해 보겠어요.
plugins/todo-list-common/src/permissions.ts
import { createPermission } from '@backstage/plugin-permission-common';export const tempExamplePermission = createPermission({ name: 'temp.example.noop', attributes: {},export const todoListCreatePermission = createPermission({ name: 'todo.list.create', attributes: { action: 'create' },});export const todoListPermissions = [tempExamplePermission];export const todoListPermissions = [todoListCreatePermission];
이 튜토리얼에서는 이 파일에서 모든 권한을 자동으로 내보냈어요(plugins/todo-list-common/src/index.ts 참고).
참고
플러그인이 인가하는 모든 권한은 "common-library" 패키지에서 내보내야 하므로 별도의 todo-list-common 패키지를 사용해요. 이렇게 하면 Backstage 통합자가 프론트엔드 구성 요소와 권한 정책 모두에서 이것들을 참조할 수 있어요.
새 권한을 사용한 인가
다음 모듈을 설치하세요.
$ yarn workspace @internal/plugin-todo-list-backend \ add @backstage/plugin-permission-common @backstage/plugin-permission-node @internal/plugin-todo-list-common
plugins/todo-list-backend/src/service/router.ts를 편집하세요.
plugins/todo-list-backend/src/service/router.ts
import { InputError } from '@backstage/errors';import { LoggerService, HttpAuthService } from '@backstage/backend-plugin-api';import { InputError, NotAllowedError } from '@backstage/errors';import { LoggerService, HttpAuthService, PermissionsService } from '@backstage/backend-plugin-api';import { AuthorizeResult } from '@backstage/plugin-permission-common';export interface RouterOptions { logger: LoggerService; httpAuth: HttpAuthService; permissions: PermissionsService;}export async function createRouter( options: RouterOptions,): Promise<express.Router> { const { logger, httpAuth } = options; const { logger, httpAuth, permissions } = options; const router = Router(); router.use(express.json()); router.get('/health', (_, response) => { logger.info('PONG!'); response.json({ status: 'ok' }); }); router.get('/todos', async (_req, res) => { res.json(getAll()); }); router.post('/todos', async (req, res) => { let author: string | undefined = undefined; const user = await identity.getIdentity({ request: req }); author = user?.identity.userEntityRef; const credentials = await httpAuth.credentials(req, { allow: ['user'] }); const decision = ( await permissions.authorize( [{ permission: todoListCreatePermission }], { credentials }, ) )[0]; if (decision.result === AuthorizeResult.DENY) { throw new NotAllowedError('Unauthorized'); } if (!isTodoCreateRequest(req.body)) { throw new InputError('Invalid payload'); } const todo = add({ title: req.body.title, author }); res.json(todo); }); // ...
plugins/todo-list-backend/src/plugin.ts에서 permissions 서비스를 전달하고 새 권한을 플러그인에 등록하세요.
plugins/todo-list-backend/src/plugin.ts
import { coreServices, createBackendPlugin } from '@backstage/backend-plugin-api';import { createRouter } from './service/router';import { todoListCreatePermission } from '@internal/plugin-todo-list-common';export const exampleTodoListPlugin = createBackendPlugin({ pluginId: 'todolist', register(env) { env.registerInit({ deps: { logger: coreServices.logger, httpAuth: coreServices.httpAuth, httpRouter: coreServices.httpRouter, permissions: coreServices.permissions, permissionsRegistry: coreServices.permissionsRegistry, }, async init({ logger, httpAuth, httpRouter }) { async init({ httpAuth, logger, httpRouter, permissions, permissionsRegistry }) { permissionsRegistry.addPermissions([todoListCreatePermission]); httpRouter.use( await createRouter({ logger, httpAuth, permissions, }), ); httpRouter.addAuthPolicy({ path: '/health', allow: 'unauthenticated', }); }, }); },});
바로 그거예요! 이제 플러그인이 완전히 구성됐어요. 권한을 거부해 로직을 테스트해 보겠어요.
인가된 create 엔드포인트 테스트
위 로직을 테스트하려면 Backstage 인스턴스의 통합자가 새로 만든 권한에 대해 DENY를 반환하도록 권한 정책을 변경해야 해요. Getting Started 단계에서 만든 권한 정책 모듈의 CustomPolicy 클래스를 갱신하세요.
import { PolicyDecision, isPermission, AuthorizeResult,} from '@backstage/plugin-permission-common';import { PermissionPolicy, PolicyQuery, PolicyQueryUser,} from '@backstage/plugin-permission-node';import { todoListCreatePermission } from '@internal/plugin-todo-list-common';export class CustomPolicy implements PermissionPolicy { async handle( request: PolicyQuery, _user?: PolicyQueryUser, ): Promise<PolicyDecision> { if (isPermission(request.permission, todoListCreatePermission)) { return { result: AuthorizeResult.DENY, }; } return { result: AuthorizeResult.ALLOW, }; }}
이제 새 Todo 항목을 만들려고 할 때마다 프론트엔드에 오류가 표시될 거예요.
다음으로 넘어가기 전에 결과를 ALLOW로 되돌리세요.
if (isPermission(request.permission, todoListCreatePermission)) { return { result: AuthorizeResult.DENY, result: AuthorizeResult.ALLOW, };}
이 시점에서 모든 것이 작동하지만 yarn tsc를 실행하면 오류가 나올 거예요. 이것을 고쳐 보겠어요.
plugins/todo-list-backend/src/service/router.test.ts를 정리하세요.
plugins/todo-list-backend/src/service/router.test.ts
import express from 'express';import request from 'supertest';import { createRouter } from './router';import { mockServices } from '@backstage/backend-test-utils';describe('createRouter', () => { let app: express.Express; beforeAll(async () => { const router = await createRouter({ logger: mockServices.logger.mock(), httpAuth: mockServices.httpAuth.mock(), permissions: mockServices.permissions.mock(), }); app = express().use(router); }); beforeEach(() => { jest.resetAllMocks(); }); describe('GET /health', () => { it('returns ok', async () => { const response = await request(app).get('/health'); expect(response.status).toEqual(200); expect(response.body).toEqual({ status: 'ok' }); }); });});
이제 yarn tsc를 실행하면 더 이상 오류가 없을 거예요.