diff --git a/dictation_server/src/app.module.ts b/dictation_server/src/app.module.ts index d18759c..1e1169d 100644 --- a/dictation_server/src/app.module.ts +++ b/dictation_server/src/app.module.ts @@ -7,7 +7,6 @@ import { LoggerMiddleware } from './common/loggerMiddleware'; import { AuthModule } from './features/auth/auth.module'; import { AuthController } from './features/auth/auth.controller'; import { AuthService } from './features/auth/auth.service'; -import { CryptoModule } from './gateways/crypto/crypto.module'; import { AdB2cModule } from './gateways/adb2c/adb2c.module'; import { AccountsController } from './features/accounts/accounts.controller'; import { AccountsService } from './features/accounts/accounts.service'; @@ -49,7 +48,6 @@ import { SortCriteriaRepositoryModule } from './repositories/sort_criteria/sort_ isGlobal: true, }), AuthModule, - CryptoModule, AdB2cModule, AccountsModule, UsersModule, diff --git a/dictation_server/src/common/guards/auth/authguards.module.ts b/dictation_server/src/common/guards/auth/authguards.module.ts index b87a961..9a5f871 100644 --- a/dictation_server/src/common/guards/auth/authguards.module.ts +++ b/dictation_server/src/common/guards/auth/authguards.module.ts @@ -1,8 +1,9 @@ import { Module } from '@nestjs/common'; import { AuthGuard } from './authguards'; +import { ConfigModule } from '@nestjs/config'; @Module({ - imports: [], + imports: [ConfigModule], controllers: [], providers: [AuthGuard], }) diff --git a/dictation_server/src/common/guards/auth/authguards.ts b/dictation_server/src/common/guards/auth/authguards.ts index 355ac66..f0a5ceb 100644 --- a/dictation_server/src/common/guards/auth/authguards.ts +++ b/dictation_server/src/common/guards/auth/authguards.ts @@ -1,25 +1,23 @@ import { - Injectable, CanActivate, ExecutionContext, HttpException, HttpStatus, + Injectable, } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { Request } from 'express'; +import { getPublicKey } from '../../../common/jwt/jwt'; +import { makeErrorResponse } from '../../error/makeErrorResponse'; +import { retrieveAuthorizationToken } from '../../http/helper'; import { isVerifyError, verify } from '../../jwt'; import { AccessToken } from '../../token'; -import { retrieveAuthorizationToken } from '../../http/helper'; -import { makeErrorResponse } from '../../error/makeErrorResponse'; @Injectable() export class AuthGuard implements CanActivate { constructor(private readonly configService: ConfigService) {} - canActivate(context: ExecutionContext): boolean | Promise { - const pubkey = this.configService - .getOrThrow('JWT_PUBLIC_KEY') - .replace('\\n', '\n'); + const pubkey = getPublicKey(this.configService); const req = context.switchToHttp().getRequest(); const token = retrieveAuthorizationToken(req); diff --git a/dictation_server/src/common/jwt/jwt.ts b/dictation_server/src/common/jwt/jwt.ts index 72df919..3059ddb 100644 --- a/dictation_server/src/common/jwt/jwt.ts +++ b/dictation_server/src/common/jwt/jwt.ts @@ -1,3 +1,4 @@ +import { ConfigService } from '@nestjs/config'; import * as jwt from 'jsonwebtoken'; // XXX: decodeがうまく使えないことがあるので応急対応 バージョン9以降だとなる? import { decode as jwtDecode } from 'jsonwebtoken'; @@ -126,3 +127,19 @@ export const decode = (token: string): T | VerifyError => { } } }; + +export const getPrivateKey = (configService: ConfigService): string => { + return ( + // 開発環境用に改行コードを置換する + // 本番環境では\\nが含まれないため、置換が行われない想定 + configService.get('JWT_PRIVATE_KEY')?.replace('\\n', '\n') ?? '' + ); +}; + +export const getPublicKey = (configService: ConfigService): string => { + return ( + // 開発環境用に改行コードを置換する + // 本番環境では\\nが含まれないため、置換が行われない想定 + configService.get('JWT_PUBLIC_KEY')?.replace('\\n', '\n') ?? '' + ); +}; diff --git a/dictation_server/src/features/auth/auth.controller.ts b/dictation_server/src/features/auth/auth.controller.ts index 2882b83..2fcd54c 100644 --- a/dictation_server/src/features/auth/auth.controller.ts +++ b/dictation_server/src/features/auth/auth.controller.ts @@ -64,6 +64,7 @@ export class AuthController { idToken, body.type, ); + const accessToken = await this.authService.generateAccessToken( refreshToken, ); diff --git a/dictation_server/src/features/auth/auth.module.ts b/dictation_server/src/features/auth/auth.module.ts index 25d0e08..e502c2f 100644 --- a/dictation_server/src/features/auth/auth.module.ts +++ b/dictation_server/src/features/auth/auth.module.ts @@ -1,12 +1,12 @@ import { Module } from '@nestjs/common'; -import { UsersRepositoryModule } from '../../repositories/users/users.repository.module'; +import { ConfigModule } from '@nestjs/config'; import { AdB2cModule } from '../../gateways/adb2c/adb2c.module'; -import { CryptoModule } from '../../gateways/crypto/crypto.module'; +import { UsersRepositoryModule } from '../../repositories/users/users.repository.module'; import { AuthController } from './auth.controller'; import { AuthService } from './auth.service'; @Module({ - imports: [CryptoModule, AdB2cModule, UsersRepositoryModule], + imports: [ConfigModule, AdB2cModule, UsersRepositoryModule], controllers: [AuthController], providers: [AuthService], }) diff --git a/dictation_server/src/features/auth/auth.service.spec.ts b/dictation_server/src/features/auth/auth.service.spec.ts index bfe6431..a14268a 100644 --- a/dictation_server/src/features/auth/auth.service.spec.ts +++ b/dictation_server/src/features/auth/auth.service.spec.ts @@ -3,14 +3,15 @@ import { makeErrorResponse } from '../../common/error/makeErrorResponse'; import { makeAuthServiceMock, makeDefaultAdB2cMockValue, - makeDefaultCryptoMockValue, + makeDefaultGetPublicKeyFromJwk, } from './test/auth.service.mock'; describe('AuthService', () => { it('IDトークンの検証とペイロードの取得に成功する', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); - const cryptoParam = makeDefaultCryptoMockValue(); - const service = await makeAuthServiceMock(adb2cParam, cryptoParam); + const service = await makeAuthServiceMock(adb2cParam); + //JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替 + service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk; const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA'; @@ -19,8 +20,7 @@ describe('AuthService', () => { it('IDトークンの形式が不正な場合、形式不正エラーとなる。', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); - const cryptoParam = makeDefaultCryptoMockValue(); - const service = await makeAuthServiceMock(adb2cParam, cryptoParam); + const service = await makeAuthServiceMock(adb2cParam); const token = 'invalid.id.token'; await expect(service.getVerifiedIdToken(token)).rejects.toEqual( @@ -30,8 +30,9 @@ describe('AuthService', () => { it('IDトークンの有効期限が切れている場合、有効期限切れエラーとなる。', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); - const cryptoParam = makeDefaultCryptoMockValue(); - const service = await makeAuthServiceMock(adb2cParam, cryptoParam); + const service = await makeAuthServiceMock(adb2cParam); + //JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替 + service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk; const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjEwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.r9x61Mf1S2qFgU_QDKB6tRFBmTQXyOEtpoacOlL_bQzFz1t3GsxMy6SJIvQQ-LtDgylQ1UCdMFiRuy4V8nyLuME0fR-9IkKsboGvwllHB_Isai3XFoja0jpDHMVby1m0B3Z9xOTb7YsaQGyEH-qs1TtnRm6Ny98h4Po80nK8HGefQZHBOlfQN_B1LiHwI3nLXV18NL-4olKXj2NloNRYtnWM0PaqDQcGvZFaSNvtrSYpo9ddD906QWDGVOQ7WvGSUgdNCoxX8Lb3r2-VSj6n84jpb-Y1Fz-GhLluNglAsBhasnJfUIvCIO3iG5pRyTYjHFAVHmzjr8xMOmhS3s41Jw'; @@ -42,8 +43,9 @@ describe('AuthService', () => { it('IDトークンが開始日より前の場合、開始前エラーとなる。', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); - const cryptoParam = makeDefaultCryptoMockValue(); - const service = await makeAuthServiceMock(adb2cParam, cryptoParam); + const service = await makeAuthServiceMock(adb2cParam); + //JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替 + service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk; const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6OTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.fX2Gbd7fDPNE3Lw-xbum_5CVqQYqEmMhv_v5u8A-U81pmPD2P5rsJEJx66ns1taFLVaE3j9_OzotxrqjqqQqbACkagGcN5wvA3_ZIxyqmhrKYFJc53ZcO7d0pFWiQlluNBI_pnFNDlSMB2Ut8Th5aiPy2uamBM9wC99bcjo7HkHvTKBf6ljU6rPKoD51qGDWqNxjoH-hdSJ29wprvyxyk_yX6dp-cxXUj5DIgXYQuIZF71rdiPtGlAiyTBns8rS2QlEEXapZVlvYrK4mkpUXVDA7ifD8q6gAC2BStqHeys7CGp2MbV4ZwKCVbAUbMs6Tboh8rADZvQhuTEq7qlhZ-w'; @@ -54,8 +56,7 @@ describe('AuthService', () => { it('IDトークンの署名が不正な場合、署名不正エラーとなる。', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); - const cryptoParam = makeDefaultCryptoMockValue(); - const service = await makeAuthServiceMock(adb2cParam, cryptoParam); + const service = await makeAuthServiceMock(adb2cParam); const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdXNlciIsInN1YiI6InN1YiIsImF1ZCI6ImF1ZCIsIm5vbmNlIjoiZGVmYXVsdE5vbmNlIiwiaWF0IjoxMDAwMDAwMDAwLCJhdXRoX3RpbWUiOjEwMDAwMDAwMDAsImVtYWlscyI6WyJ4eHhAeHguY29tIl0sInRmcCI6InNpZ25pbl91c2VyZmxvdyJ9.sign'; @@ -66,8 +67,9 @@ describe('AuthService', () => { it('IDトークンの発行元が想定と異なる場合、発行元不正エラーとなる。', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); - const cryptoParam = makeDefaultCryptoMockValue(); - const service = await makeAuthServiceMock(adb2cParam, cryptoParam); + const service = await makeAuthServiceMock(adb2cParam); + //JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替 + service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk; const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaW52bGlkX2lzc3VlciIsInN1YiI6InN1YiIsImF1ZCI6ImF1ZCIsIm5vbmNlIjoiZGVmYXVsdE5vbmNlIiwiaWF0IjoxMDAwMDAwMDAwLCJhdXRoX3RpbWUiOjEwMDAwMDAwMDAsImVtYWlscyI6WyJ4eHhAeHguY29tIl0sInRmcCI6InNpZ25pbl91c2VyZmxvdyJ9.0bp3e1mDG78PX3lo8zgOLXGenIqG_Vi6kw7CbwauAQM-cnUZ_aVCoJ_dAv_QmPElOQKcCkRrAvAZ91FwuHDlBGuuDqx8OwqN0VaD-4NPouoAswj-9HNvBm8gUn-pGaXkvWt_72UdCJavZJjDj_RHur8y8kFt5Qeab3mUP2x-uNcV2Q2x3M_IIfcRiIZkRZm_azKfiVIy7tzoUFLDss97y938aR8imMVxazoSQvj7RWIWylgeRr9yVt7qYl18cnEVL0IGtslFbqhfNsiEmRCMsttm5kXs7E9B0bhhUe_xbJW9VumQ6G7dgMrswevp_jRgbpWJoZsgErtqIRl9Tc9ikA'; @@ -79,8 +81,7 @@ describe('AuthService', () => { it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。(メタデータ)', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); adb2cParam.getMetaData = new Error('failed get metadata'); - const cryptoParam = makeDefaultCryptoMockValue(); - const service = await makeAuthServiceMock(adb2cParam, cryptoParam); + const service = await makeAuthServiceMock(adb2cParam); const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA'; @@ -94,8 +95,7 @@ describe('AuthService', () => { it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。(キーセット)', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); adb2cParam.getSignKeySets = new Error('failed get keyset'); - const cryptoParam = makeDefaultCryptoMockValue(); - const service = await makeAuthServiceMock(adb2cParam, cryptoParam); + const service = await makeAuthServiceMock(adb2cParam); const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA'; @@ -112,8 +112,7 @@ describe('AuthService', () => { adb2cParam.getSignKeySets = [ { kid: 'invalid', kty: 'RSA', nbf: 0, use: 'sig', e: '', n: '' }, ]; - const cryptoParam = makeDefaultCryptoMockValue(); - const service = await makeAuthServiceMock(adb2cParam, cryptoParam); + const service = await makeAuthServiceMock(adb2cParam); const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA'; diff --git a/dictation_server/src/features/auth/auth.service.ts b/dictation_server/src/features/auth/auth.service.ts index d59c653..8852ff6 100644 --- a/dictation_server/src/features/auth/auth.service.ts +++ b/dictation_server/src/features/auth/auth.service.ts @@ -1,24 +1,30 @@ import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import jwt from 'jsonwebtoken'; -import { UsersRepositoryService } from '../../repositories/users/users.repository.service'; +import jwkToPem from 'jwk-to-pem'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; -import { isVerifyError, sign, verify } from '../../common/jwt'; +import { sign } from '../../common/jwt'; +import { + getPrivateKey, + getPublicKey, + isVerifyError, + verify, +} from '../../common/jwt/jwt'; import { AccessToken, IDToken, - isIDToken, + JwkSignKey, RefreshToken, + isIDToken, } from '../../common/token'; -import { AdB2cService } from '../../gateways/adb2c/adb2c.service'; -import { CryptoService } from '../../gateways/crypto/crypto.service'; -import { User } from '../../repositories/users/entity/user.entity'; import { ADMIN_ROLES, USER_ROLES } from '../../constants'; +import { AdB2cService } from '../../gateways/adb2c/adb2c.service'; +import { User } from '../../repositories/users/entity/user.entity'; +import { UsersRepositoryService } from '../../repositories/users/users.repository.service'; @Injectable() export class AuthService { constructor( - private readonly cryptoService: CryptoService, private readonly adB2cService: AdB2cService, private readonly configService: ConfigService, private readonly usersRepository: UsersRepositoryService, @@ -87,8 +93,7 @@ export class AuthService { } // 要求された環境用トークンの寿命を決定 const refreshTokenLifetime = type === 'web' ? lifetimeWeb : lifetimeDefault; - - const privateKey = await this.cryptoService.getPrivateKey(); + const privateKey = getPrivateKey(this.configService); // ユーザーのロールを設定 // 万一不正なRoleが登録されていた場合、そのままDBの値を使用すると不正なロールのリフレッシュトークンが発行されるため、 @@ -135,8 +140,8 @@ export class AuthService { async generateAccessToken(refreshToken: string): Promise { const lifetime = this.configService.get('ACCESS_TOKEN_LIFETIME_WEB'); - const privateKey = await this.cryptoService.getPrivateKey(); - const pubkey = await this.cryptoService.getPublicKey(); + const privateKey = getPrivateKey(this.configService); + const pubkey = getPublicKey(this.configService); const token = verify(refreshToken, pubkey); if (isVerifyError(token)) { @@ -189,7 +194,7 @@ export class AuthService { throw new Error('Public Key Not Found.'); } - const publicKey = await this.cryptoService.getPublicKeyFromJwk(jwkKey); + const publicKey = this.getPublicKeyFromJwk(jwkKey); const verifiedToken = jwt.verify(token, publicKey, { algorithms: ['RS256'], @@ -237,6 +242,24 @@ export class AuthService { } } + getPublicKeyFromJwk(jwkKey: JwkSignKey): string { + try { + // JWK形式のJSONなのでJWTの公開鍵として使えるようにPEM形式に変換 + const publicKey = jwkToPem({ + kty: 'RSA', + n: jwkKey.n, + e: jwkKey.e, + }); + + return publicKey; + } catch (e) { + this.logger.error(`error=${e}`); + throw e; + } finally { + this.logger.log(`[OUT] ${this.getPublicKeyFromJwk.name}`); + } + } + /** * JWT検証時のError、JsonWebTokenErrorをメッセージごとに仕分けてHTTPエラーを生成 */ diff --git a/dictation_server/src/features/auth/test/auth.service.mock.ts b/dictation_server/src/features/auth/test/auth.service.mock.ts index 30abd07..08a7f32 100644 --- a/dictation_server/src/features/auth/test/auth.service.mock.ts +++ b/dictation_server/src/features/auth/test/auth.service.mock.ts @@ -1,6 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; import { AdB2cService } from '../../../gateways/adb2c/adb2c.service'; -import { CryptoService } from '../../../gateways/crypto/crypto.service'; import { JwkSignKey, B2cMetadata } from '../../../common/token'; import { AuthService } from '../auth.service'; import { ConfigService } from '@nestjs/config'; @@ -11,13 +10,8 @@ export type AdB2cMockValue = { getSignKeySets: JwkSignKey[] | Error; }; -export type CryptoMockValue = { - getPublicKeyFromJwk: string | Error; -}; - export const makeAuthServiceMock = async ( adB2cMockValue: AdB2cMockValue, - cryptoMockValue: CryptoMockValue, ): Promise => { const module: TestingModule = await Test.createTestingModule({ providers: [AuthService], @@ -26,8 +20,6 @@ export const makeAuthServiceMock = async ( switch (token) { case AdB2cService: return makeAdB2cServiceMock(adB2cMockValue); - case CryptoService: - return makeCryptoServiceMock(cryptoMockValue); case ConfigService: return {}; case UsersRepositoryService: @@ -74,33 +66,17 @@ export const makeAdB2cServiceMock = (value: AdB2cMockValue) => { }; }; -export const makeDefaultCryptoMockValue = (): CryptoMockValue => { - return { - getPublicKeyFromJwk: [ - '-----BEGIN PUBLIC KEY-----', - 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5IZZNgDew9eGmuFTezwd', - 'HYLSaJvUPPIKYoiOeVLD1paWNI517Vkaoh0ngprcKOdv6T1N07V4igK7mOim2zY3', - 'yCTR6wcWR3PfFJrl9vh5SOo79koZoJb27YiM4jtxfx2dezzp0T2GoNR5rRolPUbW', - 'FJXnDe0DVXYXpJLb4LAlF2XAyYX0SYKUVUsJnzm5k4xbXtnwPwVbpm0EdswBE6qS', - 'fiL9zWk9dvHoKzSnfSDzDFoFcEoVchawzYXf/MM1YR4wo5XyzECc6Q5Ah4z522//', - 'mBNNaDHv83Yuw3mGShT73iJ0JQdkTturshv2Ecma38r6ftrIwNYXw4VVatJM8+GO', - 'OQIDAQAB', - '-----END PUBLIC KEY-----', - ].join('\n'), - }; -}; - -export const makeCryptoServiceMock = (value: CryptoMockValue) => { - const { getPublicKeyFromJwk } = value; - - return { - getPublicKeyFromJwk: - getPublicKeyFromJwk instanceof Error - ? jest - .fn, [JwkSignKey]>() - .mockRejectedValue(getPublicKeyFromJwk) - : jest - .fn, [JwkSignKey]>() - .mockResolvedValue(getPublicKeyFromJwk), - }; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const makeDefaultGetPublicKeyFromJwk = (jwkKey: JwkSignKey): string => { + return [ + '-----BEGIN PUBLIC KEY-----', + 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5IZZNgDew9eGmuFTezwd', + 'HYLSaJvUPPIKYoiOeVLD1paWNI517Vkaoh0ngprcKOdv6T1N07V4igK7mOim2zY3', + 'yCTR6wcWR3PfFJrl9vh5SOo79koZoJb27YiM4jtxfx2dezzp0T2GoNR5rRolPUbW', + 'FJXnDe0DVXYXpJLb4LAlF2XAyYX0SYKUVUsJnzm5k4xbXtnwPwVbpm0EdswBE6qS', + 'fiL9zWk9dvHoKzSnfSDzDFoFcEoVchawzYXf/MM1YR4wo5XyzECc6Q5Ah4z522//', + 'mBNNaDHv83Yuw3mGShT73iJ0JQdkTturshv2Ecma38r6ftrIwNYXw4VVatJM8+GO', + 'OQIDAQAB', + '-----END PUBLIC KEY-----', + ].join('\n'); }; diff --git a/dictation_server/src/features/files/files.service.spec.ts b/dictation_server/src/features/files/files.service.spec.ts index 2b55cbf..b536394 100644 --- a/dictation_server/src/features/files/files.service.spec.ts +++ b/dictation_server/src/features/files/files.service.spec.ts @@ -22,6 +22,7 @@ describe('FilesService', () => { await service.publishUploadSas({ userId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx', role: 'Author', + tier: 5, }), ).toEqual('https://blob-storage?sas-token'); }); @@ -43,6 +44,7 @@ describe('FilesService', () => { await service.publishUploadSas({ userId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx', role: 'Author', + tier: 5, }), ).toEqual('https://blob-storage?sas-token'); }); @@ -63,6 +65,7 @@ describe('FilesService', () => { service.publishUploadSas({ userId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx', role: 'Author', + tier: 5, }), ).rejects.toEqual( new HttpException(makeErrorResponse('E009999'), HttpStatus.UNAUTHORIZED), @@ -86,6 +89,7 @@ describe('FilesService', () => { service.publishUploadSas({ userId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx', role: 'Author', + tier: 5, }), ).rejects.toEqual( new HttpException(makeErrorResponse('E009999'), HttpStatus.UNAUTHORIZED), diff --git a/dictation_server/src/features/licenses/licenses.controller.spec.ts b/dictation_server/src/features/licenses/licenses.controller.spec.ts index 94cfefd..70daf6c 100644 --- a/dictation_server/src/features/licenses/licenses.controller.spec.ts +++ b/dictation_server/src/features/licenses/licenses.controller.spec.ts @@ -1,7 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { LicensesController } from './licenses.controller'; import { LicensesService } from './licenses.service'; -import { CryptoService } from '../../gateways/crypto/crypto.service'; import { ConfigModule } from '@nestjs/config'; describe('LicensesController', () => { @@ -17,7 +16,7 @@ describe('LicensesController', () => { }), ], controllers: [LicensesController], - providers: [LicensesService, CryptoService], + providers: [LicensesService], }) .overrideProvider(LicensesService) .useValue(mockLicensesService) diff --git a/dictation_server/src/features/licenses/licenses.controller.ts b/dictation_server/src/features/licenses/licenses.controller.ts index 5e78c15..9c3eeb2 100644 --- a/dictation_server/src/features/licenses/licenses.controller.ts +++ b/dictation_server/src/features/licenses/licenses.controller.ts @@ -1,38 +1,33 @@ import { Body, Controller, + HttpException, HttpStatus, Post, Req, UseGuards, - HttpException, } from '@nestjs/common'; import { + ApiBearerAuth, + ApiOperation, ApiResponse, ApiTags, - ApiOperation, - ApiBearerAuth, } from '@nestjs/swagger'; -import { ErrorResponse } from '../../common/error/types/types'; -import { LicensesService } from './licenses.service'; -import { CreateOrdersResponse, CreateOrdersRequest } from './types/types'; import { Request } from 'express'; -import { CryptoService } from '../../gateways/crypto/crypto.service'; -import { retrieveAuthorizationToken } from '../../common/http/helper'; -import { confirmPermission } from '../../common/auth/auth'; +import { decode } from 'jsonwebtoken'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; -import { isVerifyError, verify } from '../../common/jwt'; -import { AccessToken } from '../../common/token'; +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 { LicensesService } from './licenses.service'; +import { CreateOrdersRequest, CreateOrdersResponse } from './types/types'; @ApiTags('licenses') @Controller('licenses') export class LicensesController { - constructor( - private readonly licensesService: LicensesService, - private readonly cryptoService: CryptoService, - ) {} - + constructor(private readonly licensesService: LicensesService) {} @ApiResponse({ status: HttpStatus.OK, type: CreateOrdersResponse, @@ -55,8 +50,8 @@ export class LicensesController { }) @ApiOperation({ operationId: 'createOrders' }) @ApiBearerAuth() - // @UseGuards(AuthGuard) - // @UseGuards(RoleGuard.requireds({ roles: ['admin', 'author'] })) + @UseGuards(AuthGuard) + @UseGuards(RoleGuard.requireds({ roles: ['admin', 'author'] })) @Post('/orders') async createOrders( @Req() req: Request, @@ -65,8 +60,6 @@ export class LicensesController { console.log(req.header('Authorization')); console.log(body); - // アクセストークンにより権限を確認する - const pubKey = await this.cryptoService.getPublicKey(); const accessToken = retrieveAuthorizationToken(req); //アクセストークンが存在しない場合のエラー @@ -76,26 +69,13 @@ export class LicensesController { HttpStatus.UNAUTHORIZED, ); } - const payload = verify(accessToken, pubKey); - - //アクセストークン形式エラー - if (isVerifyError(payload)) { - throw new HttpException( - makeErrorResponse('E000101'), - HttpStatus.UNAUTHORIZED, - ); - } - //アクセストークンの権限不足エラー - if (!confirmPermission(payload.role)) { - throw new HttpException( - makeErrorResponse('E000108'), - HttpStatus.UNAUTHORIZED, - ); - } + const decodedToken = decode(accessToken, { + json: true, + }) as AccessToken; // ライセンス注文処理 await this.licensesService.licenseOrders( - payload, + decodedToken, body.poNumber, body.orderCount, ); diff --git a/dictation_server/src/features/licenses/licenses.module.ts b/dictation_server/src/features/licenses/licenses.module.ts index e1d7c8b..d3a69a4 100644 --- a/dictation_server/src/features/licenses/licenses.module.ts +++ b/dictation_server/src/features/licenses/licenses.module.ts @@ -1,14 +1,12 @@ import { Module } from '@nestjs/common'; import { LicensesController } from './licenses.controller'; import { LicensesService } from './licenses.service'; -import { CryptoModule } from '../../gateways/crypto/crypto.module'; import { UsersRepositoryModule } from '../../repositories/users/users.repository.module'; import { AccountsRepositoryModule } from '../../repositories/accounts/accounts.repository.module'; import { LicensesRepositoryModule } from '../../repositories/licenses/licenses.repository.module'; @Module({ imports: [ - CryptoModule, UsersRepositoryModule, AccountsRepositoryModule, LicensesRepositoryModule, diff --git a/dictation_server/src/features/licenses/licenses.service.spec.ts b/dictation_server/src/features/licenses/licenses.service.spec.ts index 632ada0..f2ccd25 100644 --- a/dictation_server/src/features/licenses/licenses.service.spec.ts +++ b/dictation_server/src/features/licenses/licenses.service.spec.ts @@ -23,7 +23,7 @@ describe('LicensesService', () => { accountsRepositoryMockValue, ); const body = new CreateOrdersRequest(); - const token: AccessToken = { userId: '0001', role: '' }; + const token: AccessToken = { userId: '0001', role: '', tier: 5 }; body.orderCount = 1000; body.poNumber = '1'; expect( @@ -45,7 +45,7 @@ describe('LicensesService', () => { accountsRepositoryMockValue, ); const body = new CreateOrdersRequest(); - const token: AccessToken = { userId: '', role: '' }; + const token: AccessToken = { userId: '', role: '', tier: 5 }; body.orderCount = 1000; body.poNumber = '1'; await expect( @@ -72,7 +72,7 @@ describe('LicensesService', () => { accountsRepositoryMockValue, ); const body = new CreateOrdersRequest(); - const token: AccessToken = { userId: '0001', role: '' }; + const token: AccessToken = { userId: '0001', role: '', tier: 5 }; body.orderCount = 1000; body.poNumber = '1'; await expect( @@ -97,7 +97,7 @@ describe('LicensesService', () => { accountsRepositoryMockValue, ); const body = new CreateOrdersRequest(); - const token: AccessToken = { userId: '0001', role: '' }; + const token: AccessToken = { userId: '0001', role: '', tier: 5 }; body.orderCount = 1000; body.poNumber = '1'; await expect( diff --git a/dictation_server/src/features/users/test/users.service.mock.ts b/dictation_server/src/features/users/test/users.service.mock.ts index 815c252..3c34b68 100644 --- a/dictation_server/src/features/users/test/users.service.mock.ts +++ b/dictation_server/src/features/users/test/users.service.mock.ts @@ -5,7 +5,6 @@ import { AdB2cService, ConflictError, } from '../../../gateways/adb2c/adb2c.service'; -import { CryptoService } from '../../../gateways/crypto/crypto.service'; import { SendGridService } from '../../../gateways/sendgrid/sendgrid.service'; import { User } from '../../../repositories/users/entity/user.entity'; import { UsersRepositoryService } from '../../../repositories/users/users.repository.service'; @@ -56,7 +55,6 @@ export type SendGridMockValue = { }; export const makeUsersServiceMock = async ( - cryptoMockValue: CryptoMockValue, usersRepositoryMockValue: UsersRepositoryMockValue, adB2cMockValue: AdB2cMockValue, sendGridMockValue: SendGridMockValue, @@ -67,15 +65,12 @@ export const makeUsersServiceMock = async ( providers: [UsersService], imports: [ ConfigModule.forRoot({ - ignoreEnvFile: true, - ignoreEnvVars: true, + envFilePath: ['.env.local', '.env'], }), ], }) .useMocker((token) => { switch (token) { - case CryptoService: - return makeCryptoServiceMock(cryptoMockValue); case UsersRepositoryService: return makeUsersRepositoryMock(usersRepositoryMockValue); case AdB2cService: @@ -277,22 +272,6 @@ export const makeConfigMock = (value: ConfigMockValue) => { }; }; -export const makeDefaultCryptoMockValue = (): CryptoMockValue => { - return { - getPublicKey: [ - '-----BEGIN PUBLIC KEY-----', - 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5IZZNgDew9eGmuFTezwd', - 'HYLSaJvUPPIKYoiOeVLD1paWNI517Vkaoh0ngprcKOdv6T1N07V4igK7mOim2zY3', - 'yCTR6wcWR3PfFJrl9vh5SOo79koZoJb27YiM4jtxfx2dezzp0T2GoNR5rRolPUbW', - 'FJXnDe0DVXYXpJLb4LAlF2XAyYX0SYKUVUsJnzm5k4xbXtnwPwVbpm0EdswBE6qS', - 'fiL9zWk9dvHoKzSnfSDzDFoFcEoVchawzYXf/MM1YR4wo5XyzECc6Q5Ah4z522//', - 'mBNNaDHv83Yuw3mGShT73iJ0JQdkTturshv2Ecma38r6ftrIwNYXw4VVatJM8+GO', - 'OQIDAQAB', - '-----END PUBLIC KEY-----', - ].join('\n'), - }; -}; - export const makeDefaultSendGridlValue = (): SendGridMockValue => { return { sendMail: undefined, diff --git a/dictation_server/src/features/users/users.controller.spec.ts b/dictation_server/src/features/users/users.controller.spec.ts index 851ceb0..6193160 100644 --- a/dictation_server/src/features/users/users.controller.spec.ts +++ b/dictation_server/src/features/users/users.controller.spec.ts @@ -1,13 +1,11 @@ import { Test, TestingModule } from '@nestjs/testing'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; -import { CryptoService } from '../../gateways/crypto/crypto.service'; import { ConfigModule } from '@nestjs/config'; describe('UsersController', () => { let controller: UsersController; const mockUserService = {}; - const mockCryptoService = {}; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -18,12 +16,10 @@ describe('UsersController', () => { }), ], controllers: [UsersController], - providers: [UsersService, CryptoService], + providers: [UsersService], }) .overrideProvider(UsersService) .useValue(mockUserService) - .overrideProvider(CryptoService) - .useValue(mockCryptoService) .compile(); controller = module.get(UsersController); diff --git a/dictation_server/src/features/users/users.controller.ts b/dictation_server/src/features/users/users.controller.ts index a20bd1f..80bd654 100644 --- a/dictation_server/src/features/users/users.controller.ts +++ b/dictation_server/src/features/users/users.controller.ts @@ -2,12 +2,12 @@ import { Body, Controller, Get, + HttpException, HttpStatus, Post, - Req, - HttpException, - UseGuards, Query, + Req, + UseGuards, } from '@nestjs/common'; import { ApiBearerAuth, @@ -16,39 +16,35 @@ import { ApiTags, } from '@nestjs/swagger'; import { Request } from 'express'; -import { confirmPermission } from '../../common/auth/auth'; +import { decode } from 'jsonwebtoken'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; import { ErrorResponse } from '../../common/error/types/types'; -import { retrieveAuthorizationToken } from '../../common/http/helper'; -import { isVerifyError, verify } from '../../common/jwt/jwt'; -import { AccessToken } from '../../common/token'; -import { CryptoService } from '../../gateways/crypto/crypto.service'; -import { - ConfirmRequest, - ConfirmResponse, - GetRelationsResponse, - GetUsersResponse, - 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 { 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, +} from './types/types'; +import { UsersService } from './users.service'; + @ApiTags('users') @Controller('users') export class UsersController { - constructor( - private readonly usersService: UsersService, - private readonly cryptoService: CryptoService, - ) {} + constructor(private readonly usersService: UsersService) {} @ApiResponse({ status: HttpStatus.OK, @@ -114,14 +110,12 @@ export class UsersController { }) @ApiOperation({ operationId: 'getUsers' }) @ApiBearerAuth() - // @UseGuards(AuthGuard) - // @UseGuards(RoleGuard.requireds({ roles: ['admin', 'author'] })) + @UseGuards(AuthGuard) + @UseGuards(RoleGuard.requireds({ roles: ['admin', 'author'] })) @Get() async getUsers(@Req() req: Request): Promise { console.log(req.header('Authorization')); - // アクセストークンにより権限を確認する - const pubKey = await this.cryptoService.getPublicKey(); const accessToken = retrieveAuthorizationToken(req); // アクセストークンが存在しない場合のエラー @@ -131,25 +125,9 @@ export class UsersController { HttpStatus.UNAUTHORIZED, ); } - const payload = verify(accessToken, pubKey); + const decodedToken = decode(accessToken, { json: true }) as AccessToken; - // アクセストークン形式エラー - if (isVerifyError(payload)) { - throw new HttpException( - makeErrorResponse('E000101'), - HttpStatus.UNAUTHORIZED, - ); - } - - // アクセストークンの権限不足エラー - if (!confirmPermission(payload.role)) { - throw new HttpException( - makeErrorResponse('E000108'), - HttpStatus.UNAUTHORIZED, - ); - } - - const users = await this.usersService.getUsers(accessToken); + const users = await this.usersService.getUsers(decodedToken); return { users }; } @@ -175,6 +153,8 @@ export class UsersController { }) @ApiOperation({ operationId: 'signup' }) @ApiBearerAuth() + @UseGuards(AuthGuard) + @UseGuards(RoleGuard.requireds({ roles: ['admin', 'author'] })) @Post('/signup') async signup( @Req() req: Request, @@ -191,37 +171,20 @@ export class UsersController { typistGroupId, } = body; - // アクセストークンにより権限を確認する - const pubKey = await this.cryptoService.getPublicKey(); const accessToken = retrieveAuthorizationToken(req); - - //アクセストークンが存在しない場合のエラー + // アクセストークンが存在しない場合のエラー if (accessToken == undefined) { throw new HttpException( makeErrorResponse('E000107'), HttpStatus.UNAUTHORIZED, ); } - const payload = verify(accessToken, pubKey); - //アクセストークン形式エラー - if (isVerifyError(payload)) { - throw new HttpException( - makeErrorResponse('E000101'), - HttpStatus.UNAUTHORIZED, - ); - } - //アクセストークンの権限不足エラー - if (!confirmPermission(payload.role)) { - throw new HttpException( - makeErrorResponse('E000108'), - HttpStatus.UNAUTHORIZED, - ); - } + const decodedToken = decode(accessToken, { json: true }) as AccessToken; //ユーザ作成処理 await this.usersService.createUser( - payload, + decodedToken, name, role, email, @@ -313,7 +276,7 @@ export class UsersController { ): Promise { const { direction, paramName } = body; const accessToken = retrieveAuthorizationToken(req); - const decodedToken = jwt.decode(accessToken, { json: true }) as AccessToken; + const decodedToken = decode(accessToken, { json: true }) as AccessToken; //型チェック if ( @@ -361,7 +324,7 @@ export class UsersController { ): Promise { const {} = query; const accessToken = retrieveAuthorizationToken(req); - const decodedToken = jwt.decode(accessToken, { json: true }) as AccessToken; + const decodedToken = decode(accessToken, { json: true }) as AccessToken; const { direction, paramName } = await this.usersService.getSortCriteria( decodedToken, diff --git a/dictation_server/src/features/users/users.module.ts b/dictation_server/src/features/users/users.module.ts index 0149c9a..febd59f 100644 --- a/dictation_server/src/features/users/users.module.ts +++ b/dictation_server/src/features/users/users.module.ts @@ -1,16 +1,14 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { AdB2cModule } from '../../gateways/adb2c/adb2c.module'; -import { CryptoModule } from '../../gateways/crypto/crypto.module'; import { SendGridModule } from '../../gateways/sendgrid/sendgrid.module'; +import { SortCriteriaRepositoryModule } from '../../repositories/sort_criteria/sort_criteria.repository.module'; import { UsersRepositoryModule } from '../../repositories/users/users.repository.module'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; -import { SortCriteriaRepositoryModule } from '../../repositories/sort_criteria/sort_criteria.repository.module'; @Module({ imports: [ - CryptoModule, UsersRepositoryModule, SortCriteriaRepositoryModule, AdB2cModule, diff --git a/dictation_server/src/features/users/users.service.spec.ts b/dictation_server/src/features/users/users.service.spec.ts index 68090b0..4580ff5 100644 --- a/dictation_server/src/features/users/users.service.spec.ts +++ b/dictation_server/src/features/users/users.service.spec.ts @@ -6,7 +6,6 @@ import { EmailAlreadyVerifiedError } from '../../repositories/users/users.reposi import { makeDefaultAdB2cMockValue, makeDefaultConfigValue, - makeDefaultCryptoMockValue, makeDefaultSendGridlValue, makeDefaultSortCriteriaRepositoryMockValue, makeDefaultUsersRepositoryMockValue, @@ -16,7 +15,6 @@ import { User } from './types/types'; describe('UsersService', () => { it('ユーザの仮登録時に払い出されるトークンにより、未認証のユーザが認証済みになる', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendGridMockValue = makeDefaultSendGridlValue(); @@ -24,7 +22,6 @@ describe('UsersService', () => { const sortCriteriaRepositoryMockValue = makeDefaultSortCriteriaRepositoryMockValue(); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendGridMockValue, @@ -37,7 +34,6 @@ describe('UsersService', () => { }); it('ユーザーが発行されたパスワードでログインできるようにする', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); usersRepositoryMockValue.findUserById = { id: 1, @@ -60,7 +56,6 @@ describe('UsersService', () => { makeDefaultSortCriteriaRepositoryMockValue(); const sendGridMockValue = makeDefaultSendGridlValue(); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendGridMockValue, @@ -74,7 +69,6 @@ describe('UsersService', () => { }); it('トークンの形式が不正な場合、形式不正エラーとなる。', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -82,7 +76,6 @@ describe('UsersService', () => { const sortCriteriaRepositoryMockValue = makeDefaultSortCriteriaRepositoryMockValue(); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -96,7 +89,6 @@ describe('UsersService', () => { }); it('トークンの形式が不正な場合、形式不正エラーとなる。(メール認証API)', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); usersRepositoryMockValue.findUserById = { id: 1, @@ -119,7 +111,6 @@ describe('UsersService', () => { const sortCriteriaRepositoryMockValue = makeDefaultSortCriteriaRepositoryMockValue(); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendGridMockValue, @@ -132,7 +123,6 @@ describe('UsersService', () => { ); }); it('ユーザが既に認証済みだった場合、認証済みユーザエラーとなる。', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -144,7 +134,6 @@ describe('UsersService', () => { new EmailAlreadyVerifiedError(); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -158,7 +147,6 @@ describe('UsersService', () => { ); }); it('ユーザが既に認証済みだった場合、認証済みユーザエラーとなる。(メール認証API)', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); usersRepositoryMockValue.findUserById = { id: 1, @@ -184,7 +172,6 @@ describe('UsersService', () => { new EmailAlreadyVerifiedError(); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendGridMockValue, @@ -198,7 +185,6 @@ describe('UsersService', () => { ); }); it('DBネットワークエラーとなる場合、エラーとなる。', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -208,7 +194,6 @@ describe('UsersService', () => { usersRepositoryMockValue.updateUserVerified = new Error('DB error'); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -225,7 +210,6 @@ describe('UsersService', () => { ); }); it('DBネットワークエラーとなる場合、エラーとなる。(メール認証API)', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); usersRepositoryMockValue.findUserById = { id: 1, @@ -249,7 +233,6 @@ describe('UsersService', () => { const sortCriteriaRepositoryMockValue = makeDefaultSortCriteriaRepositoryMockValue(); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendGridMockValue, @@ -266,7 +249,6 @@ describe('UsersService', () => { ); }); it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:None)', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -274,7 +256,6 @@ describe('UsersService', () => { const sortCriteriaRepositoryMockValue = makeDefaultSortCriteriaRepositoryMockValue(); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -287,7 +268,7 @@ describe('UsersService', () => { const autoRenew = true; const licenseAlert = true; const notification = true; - const token: AccessToken = { userId: '0001', role: '' }; + const token: AccessToken = { userId: '0001', role: '', tier: 5 }; expect( await service.createUser( token, @@ -304,7 +285,6 @@ describe('UsersService', () => { describe('UsersService', () => { it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author)', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -312,7 +292,6 @@ describe('UsersService', () => { const sortCriteriaRepositoryMockValue = makeDefaultSortCriteriaRepositoryMockValue(); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -326,7 +305,7 @@ describe('UsersService', () => { const licenseAlert = true; const notification = true; const authorId = 'testID'; - const token: AccessToken = { userId: '0001', role: '' }; + const token: AccessToken = { userId: '0001', role: '', tier: 5 }; expect( await service.createUser( token, @@ -344,7 +323,6 @@ describe('UsersService', () => { describe('UsersService', () => { it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Transcriptioninst)', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -352,7 +330,6 @@ describe('UsersService', () => { const sortCriteriaRepositoryMockValue = makeDefaultSortCriteriaRepositoryMockValue(); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -366,7 +343,7 @@ describe('UsersService', () => { const licenseAlert = true; const notification = true; const typistGroupId = 111; - const token: AccessToken = { userId: '0001', role: '' }; + const token: AccessToken = { userId: '0001', role: '', tier: 5 }; expect( await service.createUser( token, @@ -384,7 +361,6 @@ describe('UsersService', () => { }); it('DBネットワークエラーとなる場合、エラーとなる。', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -393,7 +369,6 @@ it('DBネットワークエラーとなる場合、エラーとなる。', async makeDefaultSortCriteriaRepositoryMockValue(); usersRepositoryMockValue.createNormalUser = new Error('DB error'); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -406,7 +381,7 @@ it('DBネットワークエラーとなる場合、エラーとなる。', async const autoRenew = true; const licenseAlert = true; const notification = true; - const token: AccessToken = { userId: '0001', role: '' }; + const token: AccessToken = { userId: '0001', role: '', tier: 5 }; await expect( service.createUser( token, @@ -425,7 +400,6 @@ it('DBネットワークエラーとなる場合、エラーとなる。', async ); }); it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); adb2cParam.createUser = new Error(); @@ -434,7 +408,6 @@ it('Azure ADB2Cでネットワークエラーとなる場合、エラーとな const sortCriteriaRepositoryMockValue = makeDefaultSortCriteriaRepositoryMockValue(); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -447,7 +420,7 @@ it('Azure ADB2Cでネットワークエラーとなる場合、エラーとな const autoRenew = true; const licenseAlert = true; const notification = true; - const token: AccessToken = { userId: '0001', role: '' }; + const token: AccessToken = { userId: '0001', role: '', tier: 5 }; await expect( service.createUser( token, @@ -466,7 +439,6 @@ it('Azure ADB2Cでネットワークエラーとなる場合、エラーとな ); }); it('メールアドレスが重複している場合、エラーとなる。', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); adb2cParam.createUser = { reason: 'email', message: 'ObjectConflict' }; @@ -475,7 +447,6 @@ it('メールアドレスが重複している場合、エラーとなる。', a const sortCriteriaRepositoryMockValue = makeDefaultSortCriteriaRepositoryMockValue(); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -488,7 +459,7 @@ it('メールアドレスが重複している場合、エラーとなる。', a const autoRenew = true; const licenseAlert = true; const notification = true; - const token: AccessToken = { userId: '0001', role: '' }; + const token: AccessToken = { userId: '0001', role: '', tier: 5 }; await expect( service.createUser( token, @@ -504,7 +475,6 @@ it('メールアドレスが重複している場合、エラーとなる。', a ); }); it('AuthorIDが重複している場合、エラーとなる。(AuthorID重複チェックでエラー)', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -515,7 +485,6 @@ it('AuthorIDが重複している場合、エラーとなる。(AuthorID重複 usersRepositoryMockValue.existsAuthorId = true; const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -529,7 +498,7 @@ it('AuthorIDが重複している場合、エラーとなる。(AuthorID重複 const licenseAlert = true; const notification = true; const authorId = 'testID'; - const token: AccessToken = { userId: '0001', role: '' }; + const token: AccessToken = { userId: '0001', role: '', tier: 5 }; await expect( service.createUser( token, @@ -546,7 +515,6 @@ it('AuthorIDが重複している場合、エラーとなる。(AuthorID重複 ); }); it('AuthorIDが重複している場合、エラーとなる。(insert失敗)', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -557,7 +525,6 @@ it('AuthorIDが重複している場合、エラーとなる。(insert失敗)', usersRepositoryMockValue.createNormalUser.name = 'ER_DUP_ENTRY'; const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -571,7 +538,7 @@ it('AuthorIDが重複している場合、エラーとなる。(insert失敗)', const licenseAlert = true; const notification = true; const authorId = 'testID'; - const token: AccessToken = { userId: '0001', role: '' }; + const token: AccessToken = { userId: '0001', role: '', tier: 5 }; await expect( service.createUser( token, @@ -589,7 +556,6 @@ it('AuthorIDが重複している場合、エラーとなる。(insert失敗)', }); it('ユーザの一覧を取得する', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -597,17 +563,16 @@ it('ユーザの一覧を取得する', async () => { const sortCriteriaRepositoryMockValue = makeDefaultSortCriteriaRepositoryMockValue(); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, configMockValue, sortCriteriaRepositoryMockValue, ); - const token = - 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw'; - expect(await service.getUsers(token)).toEqual(expectedUsers); + expect( + await service.getUsers({ role: 'Admin', userId: 'XXXXXX', tier: 5 }), + ).toEqual(expectedUsers); }); const expectedUsers = [ @@ -636,7 +601,6 @@ const expectedUsers = [ ]; it('ユーザの一覧を取得に失敗する', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -648,7 +612,6 @@ it('ユーザの一覧を取得に失敗する', async () => { ); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -656,16 +619,14 @@ it('ユーザの一覧を取得に失敗する', async () => { sortCriteriaRepositoryMockValue, ); - const token = - 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw'; - - await expect(service.getUsers(token)).rejects.toEqual( + await expect( + service.getUsers({ role: 'Admin', userId: 'XXXXXX', tier: 5 }), + ).rejects.toEqual( new HttpException(makeErrorResponse('E009999'), HttpStatus.NOT_FOUND), ); }); it('ユーザの一覧を0件取得する', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -678,7 +639,6 @@ it('ユーザの一覧を0件取得する', async () => { usersRepositoryMockValue.findSameAccountUsers = noDbUsers; const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -686,15 +646,13 @@ it('ユーザの一覧を0件取得する', async () => { sortCriteriaRepositoryMockValue, ); - const token = - 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw'; - const emptyMergedUsers: User[] = []; - expect(await service.getUsers(token)).toEqual(emptyMergedUsers); + expect( + await service.getUsers({ role: 'Admin', userId: 'XXXXXX', tier: 5 }), + ).toEqual(emptyMergedUsers); }); it('ソート条件を変更できる', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -702,7 +660,6 @@ it('ソート条件を変更できる', async () => { const sortCriteriaRepositoryMockValue = makeDefaultSortCriteriaRepositoryMockValue(); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -714,12 +671,12 @@ it('ソート条件を変更できる', async () => { await service.updateSortCriteria('AUTHOR_ID', 'ASC', { role: 'none admin', userId: 'xxxxxxxxxxxx', + tier: 5, }), ).toEqual(undefined); }); it('ユーザー情報が存在せず、ソート条件を変更できない', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -730,7 +687,6 @@ it('ユーザー情報が存在せず、ソート条件を変更できない', a usersRepositoryMockValue.findUserByExternalId = new Error('user not found'); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -742,6 +698,7 @@ it('ユーザー情報が存在せず、ソート条件を変更できない', a service.updateSortCriteria('AUTHOR_ID', 'ASC', { role: 'none admin', userId: 'xxxxxxxxxxxx', + tier: 5, }), ).rejects.toEqual( new HttpException( @@ -752,7 +709,6 @@ it('ユーザー情報が存在せず、ソート条件を変更できない', a }); it('ソート条件が存在せず、ソート条件を変更できない', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -764,7 +720,6 @@ it('ソート条件が存在せず、ソート条件を変更できない', asyn ); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -776,6 +731,7 @@ it('ソート条件が存在せず、ソート条件を変更できない', asyn service.updateSortCriteria('AUTHOR_ID', 'ASC', { role: 'none admin', userId: 'xxxxxxxxxxxx', + tier: 5, }), ).rejects.toEqual( new HttpException( @@ -786,7 +742,6 @@ it('ソート条件が存在せず、ソート条件を変更できない', asyn }); it('ソート条件を取得できる', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -794,7 +749,6 @@ it('ソート条件を取得できる', async () => { const sortCriteriaRepositoryMockValue = makeDefaultSortCriteriaRepositoryMockValue(); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -806,12 +760,12 @@ it('ソート条件を取得できる', async () => { await service.getSortCriteria({ role: 'none admin', userId: 'xxxxxxxxxxxx', + tier: 5, }), ).toEqual({ direction: 'ASC', paramName: 'JOB_NUMBER' }); }); it('ソート条件が存在せず、ソート条件を取得できない', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -824,7 +778,6 @@ it('ソート条件が存在せず、ソート条件を取得できない', asyn ); const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -836,6 +789,7 @@ it('ソート条件が存在せず、ソート条件を取得できない', asyn service.getSortCriteria({ role: 'none admin', userId: 'xxxxxxxxxxxx', + tier: 5, }), ).rejects.toEqual( new HttpException( @@ -846,7 +800,6 @@ it('ソート条件が存在せず、ソート条件を取得できない', asyn }); it('DBから取得した値が不正だった場合、エラーとなる', async () => { - const cryptoMockValue = makeDefaultCryptoMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const sendgridMockValue = makeDefaultSendGridlValue(); @@ -861,7 +814,6 @@ it('DBから取得した値が不正だった場合、エラーとなる', async }; const service = await makeUsersServiceMock( - cryptoMockValue, usersRepositoryMockValue, adb2cParam, sendgridMockValue, @@ -873,6 +825,7 @@ it('DBから取得した値が不正だった場合、エラーとなる', async service.getSortCriteria({ role: 'none admin', userId: 'xxxxxxxxxxxx', + tier: 5, }), ).rejects.toEqual( new HttpException( diff --git a/dictation_server/src/features/users/users.service.ts b/dictation_server/src/features/users/users.service.ts index ad5c6fd..81a5f4c 100644 --- a/dictation_server/src/features/users/users.service.ts +++ b/dictation_server/src/features/users/users.service.ts @@ -2,20 +2,20 @@ import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; import { isVerifyError, verify } from '../../common/jwt'; +import { getPublicKey } from '../../common/jwt/jwt'; import { makePassword } from '../../common/password/password'; import { AccessToken } from '../../common/token'; import { - TaskListSortableAttribute, SortDirection, - isTaskListSortableAttribute, + TaskListSortableAttribute, isSortDirection, + isTaskListSortableAttribute, } from '../../common/types/sort'; import { AdB2cService, ConflictError, isConflictError, } from '../../gateways/adb2c/adb2c.service'; -import { CryptoService } from '../../gateways/crypto/crypto.service'; import { SendGridService } from '../../gateways/sendgrid/sendgrid.service'; import { SortCriteriaRepositoryService } from '../../repositories/sort_criteria/sort_criteria.repository.service'; import { User as EntityUser } from '../../repositories/users/entity/user.entity'; @@ -28,7 +28,6 @@ import { User } from './types/types'; @Injectable() export class UsersService { constructor( - private readonly cryptoService: CryptoService, private readonly usersRepository: UsersRepositoryService, private readonly sortCriteriaRepository: SortCriteriaRepositoryService, private readonly adB2cService: AdB2cService, @@ -43,8 +42,7 @@ export class UsersService { */ async confirmUser(token: string): Promise { this.logger.log(`[IN] ${this.confirmUser.name}`); - - const pubKey = await this.cryptoService.getPublicKey(); + const pubKey = getPublicKey(this.configService); const decodedToken = verify<{ accountId: number; @@ -236,7 +234,8 @@ export class UsersService { async confirmUserAndInitPassword(token: string): Promise { this.logger.log(`[IN] ${this.confirmUserAndInitPassword.name}`); - const pubKey = await this.cryptoService.getPublicKey(); + const pubKey = getPublicKey(this.configService); + const decodedToken = verify<{ accountId: number; userId: number; @@ -295,20 +294,13 @@ export class UsersService { * @param accessToken * @returns users */ - async getUsers(accessToken: string): Promise { + async getUsers(accessToken: AccessToken): Promise { this.logger.log(`[IN] ${this.getUsers.name}`); try { - // DBよりアクセス者の所属するアカウントを取得する - const pubKey = await this.cryptoService.getPublicKey(); - const payload = verify(accessToken, pubKey); - if (isVerifyError(payload)) { - throw new Error(`${payload.reason} | ${payload.message}`); - } - // DBから同一アカウントのユーザ一覧を取得する const dbUsers = await this.usersRepository.findSameAccountUsers( - payload.userId, + accessToken.userId, ); // 値をマージして定義されたレスポンス通りに返す diff --git a/dictation_server/src/gateways/crypto/crypto.module.ts b/dictation_server/src/gateways/crypto/crypto.module.ts deleted file mode 100644 index 2d98065..0000000 --- a/dictation_server/src/gateways/crypto/crypto.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; -import { CryptoService } from './crypto.service'; - -@Module({ - imports: [ConfigModule], - exports: [CryptoService], - providers: [CryptoService], -}) -export class CryptoModule {} diff --git a/dictation_server/src/gateways/crypto/crypto.service.ts b/dictation_server/src/gateways/crypto/crypto.service.ts deleted file mode 100644 index 8fe3727..0000000 --- a/dictation_server/src/gateways/crypto/crypto.service.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { DefaultAzureCredential } from '@azure/identity'; -import { Injectable, Logger } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import jwkToPem from 'jwk-to-pem'; -import { JwkSignKey } from '../../common/token'; - -@Injectable() -export class CryptoService { - private readonly logger = new Logger(CryptoService.name); - private readonly credential: DefaultAzureCredential; - private readonly url: string; - - constructor(private readonly configService: ConfigService) {} - - /** - * Gets private key - * @returns private key(PEM) - */ - async getPrivateKey(): Promise { - try { - const key = this.configService.get('JWT_PRIVATE_KEY'); - if (key) { - // 開発環境用に改行コードを置換する - // 本番環境では\\nが含まれないため、置換が行われない想定 - return key.replace('\\n', '\n'); - } - throw new Error(`JWT_PRIVATE_KEY not found.`); - } catch (e) { - this.logger.error(`error=${e}`); - throw e; - } finally { - this.logger.log(`[OUT] ${this.getPrivateKey.name}`); - } - } - - /** - * Gets public key - * @returns public key(PEM) - */ - async getPublicKey(): Promise { - try { - const key = this.configService.get('JWT_PUBLIC_KEY'); - if (key) { - // 開発環境用に改行コードを置換する - // 本番環境では\\nが含まれないため、置換が行われない想定 - return key.replace('\\n', '\n'); - } - throw new Error(`JWT_PUBLIC_KEY not found.`); - } catch (e) { - this.logger.error(`error=${e}`); - throw e; - } finally { - this.logger.log(`[OUT] ${this.getPublicKey.name}`); - } - } - - async getPublicKeyFromJwk(jwkKey: JwkSignKey): Promise { - try { - // JWK形式のJSONなのでJWTの公開鍵として使えるようにPEM形式に変換 - const publicKey = jwkToPem({ - kty: 'RSA', - n: jwkKey.n, - e: jwkKey.e, - }); - - return publicKey; - } catch (e) { - this.logger.error(`error=${e}`); - throw e; - } finally { - this.logger.log(`[OUT] ${this.getPublicKey.name}`); - } - } -} diff --git a/dictation_server/src/gateways/sendgrid/sendgrid.service.ts b/dictation_server/src/gateways/sendgrid/sendgrid.service.ts index b3eff66..b64e42b 100644 --- a/dictation_server/src/gateways/sendgrid/sendgrid.service.ts +++ b/dictation_server/src/gateways/sendgrid/sendgrid.service.ts @@ -2,6 +2,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { sign } from '../../common/jwt'; import sendgrid from '@sendgrid/mail'; +import { getPrivateKey } from '../../common/jwt/jwt'; @Injectable() export class SendGridService { @@ -25,9 +26,7 @@ export class SendGridService { ): Promise<{ subject: string; text: string; html: string }> { const lifetime = this.configService.get('EMAIL_CONFIRM_LIFETIME') ?? 0; - const privateKey = - this.configService.get('JWT_PRIVATE_KEY')?.replace('\\n', '\n') ?? - ''; + const privateKey = getPrivateKey(this.configService); const token = sign<{ accountId: number; userId: number; email: string }>( { accountId, @@ -62,9 +61,9 @@ export class SendGridService { ): Promise<{ subject: string; text: string; html: string }> { const lifetime = this.configService.get('EMAIL_CONFIRM_LIFETIME') ?? 0; - const privateKey = - this.configService.get('JWT_PRIVATE_KEY')?.replace('\\n', '\n') ?? - ''; + + const privateKey = getPrivateKey(this.configService); + const token = sign<{ accountId: number; userId: number; email: string }>( { accountId,