본문 바로가기
WIKI 기술 지식 베이스

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를 실행하면 더 이상 오류가 없을 거예요.

더 알아보기 (Learn more)