Merged PR 139: 認証・認可を宣言的に扱える仕組みを既存処理に適用する
## 概要 [Task1830: 認証・認可を宣言的に扱える仕組みを既存処理に適用する](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1830) - 既存処理について、認証認可をガードで実施するよう以下のAPIに適用しました。 - ユーザー一覧 - GET /users - ユーザー追加 - POST /users/signup/ - 音声ファイルアップロード先取得 - GET /files/audio/upload-location/ - ライセンス注文 - POST /licenses/orders ロールガードのテストで定数を使うように修正 ## レビューポイント - 対応APIの抜け漏れはないか - 対応内容に問題はないか - 付与権限 - トークン内容取得 ## UIの変更 - なし ## 動作確認状況 - ローカルで確認
This commit is contained in:
parent
15ee0c2e98
commit
1082a48fe9
@ -1,11 +0,0 @@
|
||||
/**
|
||||
* 権限の過不足を確認する
|
||||
* @param {string[]}
|
||||
* @return {boolean}
|
||||
*/
|
||||
// XXX: deprecated 削除予定
|
||||
export const confirmPermission = (authority: string): boolean => {
|
||||
console.log(authority);
|
||||
|
||||
return true;
|
||||
};
|
||||
@ -1,17 +1,18 @@
|
||||
import { ADMIN_ROLES, USER_ROLES } from '../../../constants';
|
||||
import { RoleGuard } from './roleguards';
|
||||
|
||||
describe('RoleGuard', () => {
|
||||
it('1つの許可Roleが設定時、完全に一致するroleを持つ場合、許可される', () => {
|
||||
const guards = RoleGuard.requireds({ roles: ['author'] });
|
||||
const guards = RoleGuard.requireds({ roles: [USER_ROLES.AUTHOR] });
|
||||
expect(guards.checkRole('author')).toBeTruthy();
|
||||
});
|
||||
it('1つの許可Roleが設定時、その許可roleを含むroleを持つ場合、許可される', () => {
|
||||
const guards = RoleGuard.requireds({ roles: ['author'] });
|
||||
const guards = RoleGuard.requireds({ roles: [USER_ROLES.AUTHOR] });
|
||||
// 'author admin'が許可リスト(author)に含まれるので許可
|
||||
expect(guards.checkRole('author admin')).toBeTruthy();
|
||||
});
|
||||
it('author OR adminの許可Roleが設定時、その許可roleを含むroleを持つ場合、許可される', () => {
|
||||
const guards = RoleGuard.requireds({ roles: ['author', 'admin'] });
|
||||
const guards = RoleGuard.requireds({ roles: [USER_ROLES.AUTHOR, ADMIN_ROLES.ADMIN] });
|
||||
// authorが許可リスト([authorまたはadmin])に含まれるので許可
|
||||
expect(guards.checkRole('author')).toBeTruthy();
|
||||
// adminが許可リスト([authorまたはadmin])に含まれるので許可
|
||||
@ -20,19 +21,25 @@ describe('RoleGuard', () => {
|
||||
expect(guards.checkRole('author admin')).toBeTruthy();
|
||||
});
|
||||
it('author OR adminの許可Roleが設定時、その許可roleを含むroleを持たない場合、拒否される', () => {
|
||||
const guards = RoleGuard.requireds({ roles: ['author', 'admin'] });
|
||||
const guards = RoleGuard.requireds({
|
||||
roles: [USER_ROLES.AUTHOR, ADMIN_ROLES.ADMIN],
|
||||
});
|
||||
// typistが許可リスト([authorまたはadmin])に含まれないので拒否
|
||||
expect(guards.checkRole('typist')).toBeFalsy();
|
||||
});
|
||||
it('author AND adminの許可Roleが設定時、その許可roleを含むroleを持つ場合、許可される', () => {
|
||||
const guards = RoleGuard.requireds({ roles: [['author', 'admin']] });
|
||||
const guards = RoleGuard.requireds({
|
||||
roles: [[USER_ROLES.AUTHOR, ADMIN_ROLES.ADMIN]],
|
||||
});
|
||||
// 'author admin'が許可リスト([authorかつadmin])に含まれるので許可
|
||||
expect(guards.checkRole('author admin')).toBeTruthy();
|
||||
// 'typist author admin'が許可リスト([authorかつadmin])に含まれるので許可
|
||||
expect(guards.checkRole('typist author admin')).toBeTruthy();
|
||||
});
|
||||
it('author AND adminの許可Roleが設定時、その許可roleに合致しないroleを持つ場合、拒否される', () => {
|
||||
const guards = RoleGuard.requireds({ roles: [['author', 'admin']] });
|
||||
const guards = RoleGuard.requireds({
|
||||
roles: [[USER_ROLES.AUTHOR, ADMIN_ROLES.ADMIN]],
|
||||
});
|
||||
// authorが許可リスト([authorかつadmin])に含まれないので拒否
|
||||
expect(guards.checkRole('author')).toBeFalsy();
|
||||
// adminが許可リスト([authorかつadmin])に含まれないので拒否
|
||||
@ -42,7 +49,7 @@ describe('RoleGuard', () => {
|
||||
});
|
||||
it('(author AND admin) OR typistの許可Roleが設定時、その許可roleを含むroleを持つ場合、許可される', () => {
|
||||
const guards = RoleGuard.requireds({
|
||||
roles: [['author', 'admin'], 'typist'],
|
||||
roles: [[USER_ROLES.AUTHOR, ADMIN_ROLES.ADMIN], USER_ROLES.TYPIST],
|
||||
});
|
||||
// typistが許可リスト(typist)に含まれないので許可
|
||||
expect(guards.checkRole('typist')).toBeTruthy();
|
||||
@ -53,7 +60,7 @@ describe('RoleGuard', () => {
|
||||
});
|
||||
it('(author AND admin) OR typistの許可Roleが設定時、その許可roleを含むroleを持たない場合、拒否される', () => {
|
||||
const guards = RoleGuard.requireds({
|
||||
roles: [['author', 'admin'], 'typist'],
|
||||
roles: [[USER_ROLES.AUTHOR, ADMIN_ROLES.ADMIN], USER_ROLES.TYPIST],
|
||||
});
|
||||
// authorが許可リスト([authorかつadmin])に含まれないので拒否
|
||||
expect(guards.checkRole('author')).toBeFalsy();
|
||||
|
||||
@ -98,9 +98,9 @@ export class AuthController {
|
||||
})
|
||||
async accessToken(@Req() req): Promise<AccessTokenResponse> {
|
||||
const refreshToken = retrieveAuthorizationToken(req);
|
||||
if (refreshToken !== undefined) {
|
||||
if (!refreshToken) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
makeErrorResponse('E000107'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
@ -145,7 +145,11 @@ export class AuthService {
|
||||
|
||||
const token = verify<RefreshToken>(refreshToken, pubkey);
|
||||
if (isVerifyError(token)) {
|
||||
throw new Error(`${token.reason} | ${token.message}`);
|
||||
this.logger.error(`${token.reason} | ${token.message}`);
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000101'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const accessToken = sign<AccessToken>(
|
||||
|
||||
@ -30,6 +30,7 @@ import {
|
||||
} from './types/types';
|
||||
import { AuthGuard } from '../../common/guards/auth/authguards';
|
||||
import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
import { USER_ROLES } from '../../constants';
|
||||
|
||||
@ApiTags('files')
|
||||
@Controller('files')
|
||||
@ -63,7 +64,7 @@ export class FilesController {
|
||||
})
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(RoleGuard.requireds({ roles: ['author'] }))
|
||||
@UseGuards(RoleGuard.requireds({ roles: [USER_ROLES.AUTHOR] }))
|
||||
@Post('audio/upload-finished')
|
||||
async uploadFinished(
|
||||
@Headers('authorization') authorization: string,
|
||||
@ -134,12 +135,14 @@ export class FilesController {
|
||||
'ログイン中ユーザー用のBlob Storage上の音声ファイルのアップロード先アクセスURLを取得します',
|
||||
})
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(RoleGuard.requireds({ roles: [USER_ROLES.AUTHOR] }))
|
||||
async uploadLocation(
|
||||
@Headers('authorization') authorization: string,
|
||||
@Query() query: AudioUploadLocationRequest,
|
||||
// クエリパラメータ AudioUploadLocationRequest は空であるため内部で使用しない。
|
||||
// 使用しないことを宣言するために先頭にプレフィックス_(アンダースコア)をつけている
|
||||
@Query() _query: AudioUploadLocationRequest,
|
||||
): Promise<AudioUploadLocationResponse> {
|
||||
const {} = query;
|
||||
//TODO Guardsで認証するからここではデコードするだけ
|
||||
const token = authorization.substring(
|
||||
'Bearer '.length,
|
||||
authorization.length,
|
||||
|
||||
@ -8,21 +8,22 @@ import {
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiBearerAuth,
|
||||
ApiOperation,
|
||||
ApiResponse,
|
||||
ApiTags,
|
||||
ApiOperation,
|
||||
ApiBearerAuth,
|
||||
} from '@nestjs/swagger';
|
||||
import { Request } from 'express';
|
||||
import { decode } from 'jsonwebtoken';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import { ErrorResponse } from '../../common/error/types/types';
|
||||
import { AuthGuard } from '../../common/guards/auth/authguards';
|
||||
import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
import { LicensesService } from './licenses.service';
|
||||
import { CreateOrdersResponse, CreateOrdersRequest } from './types/types';
|
||||
import { Request } from 'express';
|
||||
import { retrieveAuthorizationToken } from '../../common/http/helper';
|
||||
import { AccessToken } from '../../common/token';
|
||||
import { LicensesService } from './licenses.service';
|
||||
import { CreateOrdersRequest, CreateOrdersResponse } from './types/types';
|
||||
import { AuthGuard } from '../../common/guards/auth/authguards';
|
||||
import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
import { ADMIN_ROLES } from '../../constants';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
@ApiTags('licenses')
|
||||
@Controller('licenses')
|
||||
@ -51,7 +52,7 @@ export class LicensesController {
|
||||
@ApiOperation({ operationId: 'createOrders' })
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(RoleGuard.requireds({ roles: ['admin', 'author'] }))
|
||||
@UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
|
||||
@Post('/orders')
|
||||
async createOrders(
|
||||
@Req() req: Request,
|
||||
@ -60,22 +61,13 @@ export class LicensesController {
|
||||
console.log(req.header('Authorization'));
|
||||
console.log(body);
|
||||
|
||||
// AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない
|
||||
const accessToken = retrieveAuthorizationToken(req);
|
||||
|
||||
//アクセストークンが存在しない場合のエラー
|
||||
if (accessToken == undefined) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000107'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
const decodedToken = decode(accessToken, {
|
||||
json: true,
|
||||
}) as AccessToken;
|
||||
const payload = jwt.decode(accessToken, { json: true }) as AccessToken;
|
||||
|
||||
// ライセンス注文処理
|
||||
await this.licensesService.licenseOrders(
|
||||
decodedToken,
|
||||
payload,
|
||||
body.poNumber,
|
||||
body.orderCount,
|
||||
);
|
||||
|
||||
@ -16,30 +16,31 @@ import {
|
||||
ApiTags,
|
||||
} from '@nestjs/swagger';
|
||||
import { Request } from 'express';
|
||||
import { decode } from 'jsonwebtoken';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import { ErrorResponse } from '../../common/error/types/types';
|
||||
import { AuthGuard } from '../../common/guards/auth/authguards';
|
||||
import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
import { retrieveAuthorizationToken } from '../../common/http/helper';
|
||||
import { AccessToken } from '../../common/token';
|
||||
import {
|
||||
isSortDirection,
|
||||
isTaskListSortableAttribute,
|
||||
} from '../../common/types/sort';
|
||||
import {
|
||||
ConfirmRequest,
|
||||
ConfirmResponse,
|
||||
GetRelationsResponse,
|
||||
GetSortCriteriaRequest,
|
||||
GetSortCriteriaResponse,
|
||||
GetUsersResponse,
|
||||
PostSortCriteriaRequest,
|
||||
PostSortCriteriaResponse,
|
||||
SignupRequest,
|
||||
SignupResponse,
|
||||
PostSortCriteriaRequest,
|
||||
PostSortCriteriaResponse,
|
||||
GetSortCriteriaRequest,
|
||||
GetSortCriteriaResponse,
|
||||
} from './types/types';
|
||||
import { UsersService } from './users.service';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { AuthGuard } from '../../common/guards/auth/authguards';
|
||||
import {
|
||||
isSortDirection,
|
||||
isTaskListSortableAttribute,
|
||||
} from '../../common/types/sort';
|
||||
import { ADMIN_ROLES } from '../../constants';
|
||||
import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
|
||||
@ApiTags('users')
|
||||
@Controller('users')
|
||||
@ -111,21 +112,13 @@ export class UsersController {
|
||||
@ApiOperation({ operationId: 'getUsers' })
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(RoleGuard.requireds({ roles: ['admin', 'author'] }))
|
||||
@UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
|
||||
@Get()
|
||||
async getUsers(@Req() req: Request): Promise<GetUsersResponse> {
|
||||
console.log(req.header('Authorization'));
|
||||
|
||||
const accessToken = retrieveAuthorizationToken(req);
|
||||
|
||||
// アクセストークンが存在しない場合のエラー
|
||||
if (accessToken == undefined) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000107'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
const decodedToken = decode(accessToken, { json: true }) as AccessToken;
|
||||
const decodedToken = jwt.decode(accessToken, { json: true }) as AccessToken;
|
||||
|
||||
const users = await this.usersService.getUsers(decodedToken);
|
||||
return { users };
|
||||
@ -153,9 +146,9 @@ export class UsersController {
|
||||
})
|
||||
@ApiOperation({ operationId: 'signup' })
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(RoleGuard.requireds({ roles: ['admin', 'author'] }))
|
||||
@Post('/signup')
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
|
||||
async signup(
|
||||
@Req() req: Request,
|
||||
@Body() body: SignupRequest,
|
||||
@ -172,19 +165,11 @@ export class UsersController {
|
||||
} = body;
|
||||
|
||||
const accessToken = retrieveAuthorizationToken(req);
|
||||
// アクセストークンが存在しない場合のエラー
|
||||
if (accessToken == undefined) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000107'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedToken = decode(accessToken, { json: true }) as AccessToken;
|
||||
const payload = jwt.decode(accessToken, { json: true }) as AccessToken;
|
||||
|
||||
//ユーザ作成処理
|
||||
await this.usersService.createUser(
|
||||
decodedToken,
|
||||
payload,
|
||||
name,
|
||||
role,
|
||||
email,
|
||||
@ -276,7 +261,7 @@ export class UsersController {
|
||||
): Promise<PostSortCriteriaResponse> {
|
||||
const { direction, paramName } = body;
|
||||
const accessToken = retrieveAuthorizationToken(req);
|
||||
const decodedToken = decode(accessToken, { json: true }) as AccessToken;
|
||||
const decodedToken = jwt.decode(accessToken, { json: true }) as AccessToken;
|
||||
|
||||
//型チェック
|
||||
if (
|
||||
@ -324,7 +309,7 @@ export class UsersController {
|
||||
): Promise<GetSortCriteriaResponse> {
|
||||
const {} = query;
|
||||
const accessToken = retrieveAuthorizationToken(req);
|
||||
const decodedToken = decode(accessToken, { json: true }) as AccessToken;
|
||||
const decodedToken = jwt.decode(accessToken, { json: true }) as AccessToken;
|
||||
|
||||
const { direction, paramName } = await this.usersService.getSortCriteria(
|
||||
decodedToken,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user