Merged PR 71: API実装(ユーザー登録)
## 概要 [Task1593: API実装(ユーザー登録)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1593) https://dev.azure.com/ODMSCloud/ODMS%20Cloud/_git/ODMS%20Cloud users.controllerにアクセストークン取得処理を追加 users.serviceにユーザ追加処理を追加 user.entityにauto_renew、license_alert、notificationを追加 users.repository.serviceにユーザ追加・AuthorId検索処理を追加 ## レビューポイント 処理の記載場所が適切かどうか 期待通りの処理になっているかどうか テストコードの記載方法が正しいかどうか ## UIの変更 なし ## 動作確認状況 ローカルでのビルド・実行を確認 ## 補足 テスト実装について不安要素があります。 ・テストの粒度はこれでよいのでしょうか? ・テスト実行に40分かかってしまうのですが実装方法を間違えている箇所がありそうでしょうか?
This commit is contained in:
parent
d297301212
commit
e9af39bd47
16
dictation_server/src/common/auth/auth.ts
Normal file
16
dictation_server/src/common/auth/auth.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* 権限の過不足を確認する
|
||||
* @param {string[]}
|
||||
* @return {boolean}
|
||||
*/
|
||||
export const confirmPermission = (authority: string): boolean => {
|
||||
console.log(authority);
|
||||
|
||||
return true;
|
||||
// TODO 将来的にscopeの内容に応じた処理を入れることになる
|
||||
// if (authority.startsWith('hogehoge')) {
|
||||
// return true;
|
||||
// } else {
|
||||
// return false;
|
||||
// }
|
||||
};
|
||||
@ -20,7 +20,11 @@ export const ErrorCodes = [
|
||||
'E000104', // トークン署名エラー
|
||||
'E000105', // トークン発行元エラー
|
||||
'E000106', // トークンアルゴリズムエラー
|
||||
'E000107', // トークン不足エラー
|
||||
'E000108', // トークン権限エラー
|
||||
'E010201', // 未認証ユーザエラー
|
||||
'E010202', // 認証済ユーザエラー
|
||||
'E010203', // 管理ユーザ権限エラー
|
||||
'E010301', // メールアドレス登録済みエラー
|
||||
'E010302', // authorId重複エラー
|
||||
] as const;
|
||||
|
||||
@ -9,7 +9,11 @@ export const errors: Errors = {
|
||||
E000104: 'Token invalid signature Error.',
|
||||
E000105: 'Token invalid issuer Error.',
|
||||
E000106: 'Token invalid algorithm Error.',
|
||||
E000107: 'Token is not exist Error.',
|
||||
E000108: 'Token authority failed Error.',
|
||||
E010201: 'Email not verified user Error.',
|
||||
E010202: 'Email already verified user Error.',
|
||||
E010203: 'Administrator Permissions Error.',
|
||||
E010301: 'This email user already created Error',
|
||||
E010302: 'This AuthorId already used Error',
|
||||
};
|
||||
|
||||
17
dictation_server/src/common/http/helper.ts
Normal file
17
dictation_server/src/common/http/helper.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Request } from 'express';
|
||||
|
||||
/**
|
||||
* アクセストークンを取り出す
|
||||
* @param {Request}
|
||||
* @return {string | undefined}
|
||||
*/
|
||||
export const retrieveAccessToken = (req: Request): string | undefined => {
|
||||
const header = req.header('Authorization');
|
||||
|
||||
if (typeof header === 'string') {
|
||||
if (header.startsWith('Bearer ')) {
|
||||
return header.substring('Bearer '.length, header.length);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
@ -2,26 +2,30 @@ import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UsersService } from '../users.service';
|
||||
import { UsersRepositoryService } from '../../../repositories/users/users.repository.service';
|
||||
import { CryptoService } from '../../../gateways/crypto/crypto.service';
|
||||
import { AdB2cService } from '../../../gateways/adb2c/adb2c.service';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import {
|
||||
AdB2cService,
|
||||
ConflictError,
|
||||
} from '../../../gateways/adb2c/adb2c.service';
|
||||
import { SendGridService } from '../../../gateways/sendgrid/sendgrid.service';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { User } from '../../../repositories/users/entity/user.entity';
|
||||
import { JwkSignKey, B2cMetadata } from '../../../common/token';
|
||||
import { User } from 'src/repositories/users/entity/user.entity';
|
||||
|
||||
export type CryptoMockValue = {
|
||||
getPublicKey: string | Error;
|
||||
getPrivateKey: string | Error;
|
||||
};
|
||||
|
||||
export type UsersRepositoryMockValue = {
|
||||
updateUserVerified: undefined | Error;
|
||||
findUserById: User | Error;
|
||||
createNormalUser: User | Error;
|
||||
};
|
||||
|
||||
export type AdB2cMockValue = {
|
||||
getMetaData: B2cMetadata | Error;
|
||||
getSignKeySets: JwkSignKey[] | Error;
|
||||
changePassword: { sub: string } | Error;
|
||||
createUser: string | ConflictError | Error;
|
||||
};
|
||||
|
||||
export type SendGridMockValue = {
|
||||
@ -30,6 +34,9 @@ export type SendGridMockValue = {
|
||||
text: string;
|
||||
html: string;
|
||||
};
|
||||
createMailContentFromEmailConfirmForNormalUser:
|
||||
| { subject: string; text: string; html: string }
|
||||
| Error;
|
||||
sendMail: undefined | Error;
|
||||
};
|
||||
|
||||
@ -51,6 +58,7 @@ export const makeDefaultAdB2cMockValue = (): AdB2cMockValue => {
|
||||
changePassword: {
|
||||
sub: 'TEST9999',
|
||||
},
|
||||
createUser: '001',
|
||||
};
|
||||
};
|
||||
|
||||
@ -73,7 +81,7 @@ export const makeSendGridServiceMock = (value: SendGridMockValue) => {
|
||||
};
|
||||
|
||||
export const makeAdB2cServiceMock = (value: AdB2cMockValue) => {
|
||||
const { getMetaData, getSignKeySets, changePassword } = value;
|
||||
const { getMetaData, getSignKeySets, changePassword, createUser } = value;
|
||||
|
||||
return {
|
||||
getMetaData:
|
||||
@ -92,14 +100,25 @@ export const makeAdB2cServiceMock = (value: AdB2cMockValue) => {
|
||||
: jest
|
||||
.fn<Promise<{ sub: string }>, []>()
|
||||
.mockResolvedValue(changePassword),
|
||||
createUser:
|
||||
createUser instanceof Error
|
||||
? jest.fn<Promise<ConflictError>, []>().mockRejectedValue(createUser)
|
||||
: jest
|
||||
.fn<Promise<string | ConflictError>, []>()
|
||||
.mockResolvedValue(createUser),
|
||||
};
|
||||
};
|
||||
|
||||
export type ConfigMockValue = {
|
||||
get: string | Error;
|
||||
};
|
||||
|
||||
export const makeUsersServiceMock = async (
|
||||
cryptoMockValue: CryptoMockValue,
|
||||
usersRepositoryMockValue: UsersRepositoryMockValue,
|
||||
adB2cMockValue: AdB2cMockValue,
|
||||
sendGridMockValue: SendGridMockValue,
|
||||
configMockValue: ConfigMockValue,
|
||||
): Promise<UsersService> => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [UsersService],
|
||||
@ -118,10 +137,10 @@ export const makeUsersServiceMock = async (
|
||||
return makeUsersRepositoryMock(usersRepositoryMockValue);
|
||||
case AdB2cService:
|
||||
return makeAdB2cServiceMock(adB2cMockValue);
|
||||
case ConfigService:
|
||||
return {};
|
||||
case SendGridService:
|
||||
return makeSendGridServiceMock(sendGridMockValue);
|
||||
return makeSendGridMock(sendGridMockValue);
|
||||
case ConfigService:
|
||||
return makeConfigMock(configMockValue);
|
||||
}
|
||||
})
|
||||
.compile();
|
||||
@ -130,23 +149,26 @@ export const makeUsersServiceMock = async (
|
||||
};
|
||||
|
||||
export const makeCryptoServiceMock = (value: CryptoMockValue) => {
|
||||
const { getPublicKey, getPrivateKey } = value;
|
||||
const { getPublicKey } = value;
|
||||
|
||||
return {
|
||||
getPublicKey:
|
||||
getPublicKey instanceof Error
|
||||
? jest.fn<Promise<void>, []>().mockRejectedValue(getPublicKey)
|
||||
: jest.fn<Promise<string>, []>().mockResolvedValue(getPublicKey),
|
||||
getPrivateKey:
|
||||
getPrivateKey instanceof Error
|
||||
? jest.fn<Promise<void>, []>().mockRejectedValue(getPrivateKey)
|
||||
: jest.fn<Promise<string>, []>().mockResolvedValue(getPrivateKey),
|
||||
};
|
||||
};
|
||||
|
||||
class authorIdError extends Error {
|
||||
constructor(public code: string, e?: string) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
|
||||
export const makeUsersRepositoryMock = (value: UsersRepositoryMockValue) => {
|
||||
const { updateUserVerified } = value;
|
||||
const { findUserById } = value;
|
||||
const { updateUserVerified, findUserById, createNormalUser } = value;
|
||||
|
||||
const aIdError = new authorIdError('ER_DUP_ENTRY');
|
||||
|
||||
return {
|
||||
updateUserVerified:
|
||||
@ -155,11 +177,45 @@ export const makeUsersRepositoryMock = (value: UsersRepositoryMockValue) => {
|
||||
: jest.fn<Promise<void>, []>().mockResolvedValue(updateUserVerified),
|
||||
findUserById:
|
||||
findUserById instanceof Error
|
||||
? jest.fn<Promise<User>, []>().mockRejectedValue(findUserById)
|
||||
? jest.fn<Promise<void>, []>().mockRejectedValue(findUserById)
|
||||
: jest.fn<Promise<User>, []>().mockResolvedValue(findUserById),
|
||||
createNormalUser:
|
||||
createNormalUser instanceof Error
|
||||
? createNormalUser.name == 'ER_DUP_ENTRY'
|
||||
? jest.fn<Promise<void>, []>().mockRejectedValue(aIdError)
|
||||
: jest.fn<Promise<void>, []>().mockRejectedValue(createNormalUser)
|
||||
: jest.fn<Promise<User>, []>().mockResolvedValue(createNormalUser),
|
||||
};
|
||||
};
|
||||
|
||||
export const makeSendGridMock = (value: SendGridMockValue) => {
|
||||
const { sendMail, createMailContentFromEmailConfirmForNormalUser } = value;
|
||||
|
||||
return {
|
||||
sendMail:
|
||||
sendMail instanceof Error
|
||||
? jest.fn<Promise<void>, []>().mockRejectedValue(sendMail)
|
||||
: jest.fn<Promise<void>, []>().mockResolvedValue(sendMail),
|
||||
createMailContentFromEmailConfirmForNormalUser:
|
||||
createMailContentFromEmailConfirmForNormalUser instanceof Error
|
||||
? jest
|
||||
.fn<Promise<void>, []>()
|
||||
.mockRejectedValue(createMailContentFromEmailConfirmForNormalUser)
|
||||
: jest
|
||||
.fn<Promise<{ subject: string; text: string; html: string }>, []>()
|
||||
.mockResolvedValue(createMailContentFromEmailConfirmForNormalUser),
|
||||
};
|
||||
};
|
||||
export const makeConfigMock = (value: ConfigMockValue) => {
|
||||
const { get } = value;
|
||||
|
||||
return {
|
||||
get:
|
||||
get instanceof Error
|
||||
? jest.fn<Promise<void>, []>().mockRejectedValue(get)
|
||||
: jest.fn<Promise<string>, []>().mockResolvedValue(get),
|
||||
};
|
||||
};
|
||||
export const makeDefaultCryptoMockValue = (): CryptoMockValue => {
|
||||
return {
|
||||
getPublicKey: [
|
||||
@ -173,51 +229,34 @@ export const makeDefaultCryptoMockValue = (): CryptoMockValue => {
|
||||
'OQIDAQAB',
|
||||
'-----END PUBLIC KEY-----',
|
||||
].join('\n'),
|
||||
// XXX メール認証API実装時に用意したが使っていないので不要なら削除
|
||||
getPrivateKey: [
|
||||
'-----BEGIN RSA PRIVATE KEY-----',
|
||||
'MIIEowIBAAKCAQEA5IZZNgDew9eGmuFTezwdHYLSaJvUPPIKYoiOeVLD1paWNI51',
|
||||
'7Vkaoh0ngprcKOdv6T1N07V4igK7mOim2zY3yCTR6wcWR3PfFJrl9vh5SOo79koZ',
|
||||
'oJb27YiM4jtxfx2dezzp0T2GoNR5rRolPUbWFJXnDe0DVXYXpJLb4LAlF2XAyYX0',
|
||||
'SYKUVUsJnzm5k4xbXtnwPwVbpm0EdswBE6qSfiL9zWk9dvHoKzSnfSDzDFoFcEoV',
|
||||
'chawzYXf/MM1YR4wo5XyzECc6Q5Ah4z522//mBNNaDHv83Yuw3mGShT73iJ0JQdk',
|
||||
'Tturshv2Ecma38r6ftrIwNYXw4VVatJM8+GOOQIDAQABAoIBADrwp7u097+dK/tw',
|
||||
'WD61n3DIGAqg/lmFt8X4IH8MKLSE/FKr16CS1bqwOEuIM3ZdUtDeXd9Xs7IsyEPE',
|
||||
'5ZwuXK7DSF0M4+Mj8Ip49Q0Aww9aUoLQU9HGfgN/r4599GTrt31clZXA/6Mlighq',
|
||||
'cOZgCcEfdItz8OMu5SQuOIW4CKkCuaWnPOP26UqZocaXNZfpZH0iFLATMMH/TT8x',
|
||||
'ay9ToHTQYE17ijdQ/EOLSwoeDV1CU1CIE3P4YfLJjvpKptly5dTevriHEzBi70Jx',
|
||||
'/KEPUn9Jj2gZafrUxRVhmMbm1zkeYxL3gsqRuTzRjEeeILuZhSJyCkQZyUNARxsg',
|
||||
'QY4DZfECgYEA+YLKUtmYTx60FS6DJ4s31TAsXY8kwhq/lB9E3GBZKDd0DPayXEeK',
|
||||
'4UWRQDTT6MI6fedW69FOZJ5sFLp8HQpcssb4Weq9PCpDhNTx8MCbdH3Um5QR3vfW',
|
||||
'aKq/1XM8MDUnx5XcNYd87Aw3azvJAvOPr69as8IPnj6sKaRR9uQjbYUCgYEA6nfV',
|
||||
'5j0qmn0EJXZJblk4mvvjLLoWSs17j9YlrZJlJxXMDFRYtgnelv73xMxOMvcGoxn5',
|
||||
'ifs7dpaM2x5EmA6jVU5sYaB/beZGEPWqPYGyjIwXPvUGAAv8Gbnvpp+xlSco/Dum',
|
||||
'Iq0w+43ry5/xWh6CjfrvKV0J2bDOiJwPEdu/8iUCgYEAnBBSvL+dpN9vhFAzeOh7',
|
||||
'Y71eAqcmNsLEUcG9MJqTKbSFwhYMOewF0iHRWHeylEPokhfBJn8kqYrtz4lVWFTC',
|
||||
'5o/Nh3BsLNXCpbMMIapXkeWiti1HgE9ErPMgSkJpwz18RDpYIqM8X+jEQS6D7HSr',
|
||||
'yxfDg+w+GJza0rEVE3hfMIECgYBw+KZ2VfhmEWBjEHhXE+QjQMR3s320MwebCUqE',
|
||||
'NCpKx8TWF/naVC0MwfLtvqbbBY0MHyLN6d//xpA9r3rLbRojqzKrY2KiuDYAS+3n',
|
||||
'zssRzxoQOozWju+8EYu30/ADdqfXyIHG6X3VZs87AGiQzGyJLmP3oR1y5y7MQa09',
|
||||
'JI16hQKBgHK5uwJhGa281Oo5/FwQ3uYLymbNwSGrsOJXiEu2XwJEXwVi2ELOKh4/',
|
||||
'03pBk3Kva3fIwEK+vCzDNnxShIQqBE76/2I1K1whOfoUehhYvKHGaXl2j70Zz9Ks',
|
||||
'rkGW1cx7p+yDqATDrwHBHTHFh5bUTTn8dN40n0e0W/llurpbBkJM',
|
||||
'-----END RSA PRIVATE KEY-----',
|
||||
].join('\n'),
|
||||
};
|
||||
};
|
||||
|
||||
export const makeDefaultSendGridlValue = (): SendGridMockValue => {
|
||||
return {
|
||||
sendMail: undefined,
|
||||
createMailContentFromEmailConfirm: { subject: '', text: '', html: '' },
|
||||
createMailContentFromEmailConfirmForNormalUser: {
|
||||
subject: 'test',
|
||||
text: 'test',
|
||||
html: 'test',
|
||||
},
|
||||
};
|
||||
};
|
||||
export const makeDefaultConfigValue = (): ConfigMockValue => {
|
||||
return {
|
||||
get: `test@example.co.jp`,
|
||||
};
|
||||
};
|
||||
|
||||
// 個別のテストケースに対応してそれぞれのMockを用意するのは無駄が多いのでテストケース内で個別の値を設定する
|
||||
export const makeDefaultUsersRepositoryMockValue =
|
||||
(): UsersRepositoryMockValue => {
|
||||
const newUser = new User();
|
||||
newUser.id = 111;
|
||||
return {
|
||||
updateUserVerified: undefined,
|
||||
findUserById: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
export const makeSendGridServiceMockValue = (): SendGridMockValue => {
|
||||
return {
|
||||
createMailContentFromEmailConfirm: { subject: '', text: '', html: '' },
|
||||
sendMail: undefined,
|
||||
findUserById: newUser,
|
||||
createNormalUser: newUser,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,4 +1,12 @@
|
||||
import { Body, Controller, Get, HttpStatus, Post, Req } from '@nestjs/common';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
HttpStatus,
|
||||
Post,
|
||||
Req,
|
||||
HttpException,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiBearerAuth,
|
||||
ApiOperation,
|
||||
@ -16,11 +24,20 @@ import {
|
||||
} from './types/types';
|
||||
import { UsersService } from './users.service';
|
||||
import { Request } from 'express';
|
||||
import { verify, isVerifyError } from '../../common/jwt/jwt';
|
||||
import { CryptoService } from '../../gateways/crypto/crypto.service';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import { AccessToken } from '../../common/token';
|
||||
import { retrieveAccessToken } from '../../common/http/helper';
|
||||
import { confirmPermission } from '../../common/auth/auth';
|
||||
|
||||
@ApiTags('users')
|
||||
@Controller('users')
|
||||
export class UsersController {
|
||||
constructor(private readonly usersService: UsersService) {}
|
||||
constructor(
|
||||
private readonly usersService: UsersService,
|
||||
private readonly cryptoService: CryptoService,
|
||||
) {}
|
||||
|
||||
@ApiResponse({
|
||||
status: HttpStatus.OK,
|
||||
@ -119,8 +136,57 @@ export class UsersController {
|
||||
@Req() req: Request,
|
||||
@Body() body: SignupRequest,
|
||||
): Promise<SignupResponse> {
|
||||
console.log(req.header('Authorization'));
|
||||
console.log(body);
|
||||
const {
|
||||
name,
|
||||
role,
|
||||
email,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
authorId,
|
||||
typistGroupId,
|
||||
} = body;
|
||||
|
||||
// アクセストークンにより権限を確認する
|
||||
const pubKey = await this.cryptoService.getPublicKey();
|
||||
const accessToken = retrieveAccessToken(req);
|
||||
|
||||
//アクセストークンが存在しない場合のエラー
|
||||
if (accessToken == undefined) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000107'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
const payload = verify<AccessToken>(accessToken, pubKey);
|
||||
|
||||
//アクセストークン形式エラー
|
||||
if (isVerifyError(payload)) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000101'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
//アクセストークンの権限不足エラー
|
||||
if (!confirmPermission(payload.scope)) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000108'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
//ユーザ作成処理
|
||||
await this.usersService.createUser(
|
||||
payload,
|
||||
name,
|
||||
role,
|
||||
email,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
authorId,
|
||||
typistGroupId,
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@ -5,21 +5,25 @@ import {
|
||||
makeDefaultUsersRepositoryMockValue,
|
||||
makeUsersServiceMock,
|
||||
makeDefaultAdB2cMockValue,
|
||||
makeSendGridServiceMockValue,
|
||||
makeDefaultSendGridlValue,
|
||||
makeDefaultConfigValue,
|
||||
} from './test/users.service.mock';
|
||||
import { EmailAlreadyVerifiedError } from '../../repositories/users/users.repository.service';
|
||||
import { AccessToken } from 'src/common/token';
|
||||
|
||||
describe('UsersService', () => {
|
||||
it('ユーザの仮登録時に払い出されるトークンにより、未認証のユーザが認証済みになる', async () => {
|
||||
const cryptoMockValue = makeDefaultCryptoMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendGridMockValue = makeSendGridServiceMockValue();
|
||||
const sendGridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
cryptoMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendGridMockValue,
|
||||
configMockValue,
|
||||
);
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||
@ -40,14 +44,19 @@ describe('UsersService', () => {
|
||||
created_at: new Date(),
|
||||
updated_by: 'string;',
|
||||
updated_at: new Date(),
|
||||
auto_renew: true,
|
||||
license_alert: true,
|
||||
notification: true,
|
||||
};
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendGridMockValue = makeSendGridServiceMockValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
const sendGridMockValue = makeDefaultSendGridlValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
cryptoMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendGridMockValue,
|
||||
configMockValue,
|
||||
);
|
||||
|
||||
const token =
|
||||
@ -59,12 +68,14 @@ describe('UsersService', () => {
|
||||
const cryptoMockValue = makeDefaultCryptoMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendGridMockValue = makeSendGridServiceMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
cryptoMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendGridMockValue,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
);
|
||||
const token = 'invalid.id.token';
|
||||
await expect(service.confirmUser(token)).rejects.toEqual(
|
||||
@ -86,14 +97,19 @@ describe('UsersService', () => {
|
||||
created_at: new Date(),
|
||||
updated_by: 'string;',
|
||||
updated_at: new Date(),
|
||||
auto_renew: true,
|
||||
license_alert: true,
|
||||
notification: true,
|
||||
};
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendGridMockValue = makeSendGridServiceMockValue();
|
||||
const sendGridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
cryptoMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendGridMockValue,
|
||||
configMockValue,
|
||||
);
|
||||
const token = 'invalid.id.token';
|
||||
await expect(service.confirmUserAndInitPassword(token)).rejects.toEqual(
|
||||
@ -104,7 +120,9 @@ describe('UsersService', () => {
|
||||
const cryptoMockValue = makeDefaultCryptoMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendGridMockValue = makeSendGridServiceMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
|
||||
usersRepositoryMockValue.updateUserVerified =
|
||||
new EmailAlreadyVerifiedError();
|
||||
|
||||
@ -112,7 +130,8 @@ describe('UsersService', () => {
|
||||
cryptoMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendGridMockValue,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
);
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||
@ -134,9 +153,13 @@ describe('UsersService', () => {
|
||||
created_at: new Date(),
|
||||
updated_by: 'string;',
|
||||
updated_at: new Date(),
|
||||
auto_renew: true,
|
||||
license_alert: true,
|
||||
notification: true,
|
||||
};
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendGridMockValue = makeSendGridServiceMockValue();
|
||||
const sendGridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
usersRepositoryMockValue.updateUserVerified =
|
||||
new EmailAlreadyVerifiedError();
|
||||
|
||||
@ -145,6 +168,7 @@ describe('UsersService', () => {
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendGridMockValue,
|
||||
configMockValue,
|
||||
);
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||
@ -156,14 +180,16 @@ describe('UsersService', () => {
|
||||
const cryptoMockValue = makeDefaultCryptoMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendGridMockValue = makeSendGridServiceMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
usersRepositoryMockValue.updateUserVerified = new Error('DB error');
|
||||
|
||||
const service = await makeUsersServiceMock(
|
||||
cryptoMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendGridMockValue,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
);
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||
@ -188,16 +214,20 @@ describe('UsersService', () => {
|
||||
created_at: new Date(),
|
||||
updated_by: 'string;',
|
||||
updated_at: new Date(),
|
||||
auto_renew: true,
|
||||
license_alert: true,
|
||||
notification: true,
|
||||
};
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendGridMockValue = makeSendGridServiceMockValue();
|
||||
const sendGridMockValue = makeDefaultSendGridlValue();
|
||||
usersRepositoryMockValue.updateUserVerified = new Error('DB error');
|
||||
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
cryptoMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendGridMockValue,
|
||||
configMockValue,
|
||||
);
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||
@ -208,4 +238,262 @@ describe('UsersService', () => {
|
||||
),
|
||||
);
|
||||
});
|
||||
it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:None)', async () => {
|
||||
const cryptoMockValue = makeDefaultCryptoMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
cryptoMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
);
|
||||
const name = 'test_user1';
|
||||
const role = 'None';
|
||||
const email = 'test1@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
const notification = true;
|
||||
const token: AccessToken = { userId: '0001', scope: '' };
|
||||
expect(
|
||||
await service.createUser(
|
||||
token,
|
||||
name,
|
||||
role,
|
||||
email,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('UsersService', () => {
|
||||
it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author)', async () => {
|
||||
const cryptoMockValue = makeDefaultCryptoMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
cryptoMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
);
|
||||
const name = 'test_user2';
|
||||
const role = 'Author';
|
||||
const email = 'test2@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
const notification = true;
|
||||
const authorId = 'testID';
|
||||
const token: AccessToken = { userId: '0001', scope: '' };
|
||||
expect(
|
||||
await service.createUser(
|
||||
token,
|
||||
name,
|
||||
role,
|
||||
email,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
authorId,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('UsersService', () => {
|
||||
it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Transcriptioninst)', async () => {
|
||||
const cryptoMockValue = makeDefaultCryptoMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
cryptoMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
);
|
||||
const name = 'test_user3';
|
||||
const role = 'Transcriptioninst';
|
||||
const email = 'test3@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
const notification = true;
|
||||
const typistGroupId = 111;
|
||||
const token: AccessToken = { userId: '0001', scope: '' };
|
||||
expect(
|
||||
await service.createUser(
|
||||
token,
|
||||
name,
|
||||
role,
|
||||
email,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
undefined,
|
||||
typistGroupId,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
it('DBネットワークエラーとなる場合、エラーとなる。', async () => {
|
||||
const cryptoMockValue = makeDefaultCryptoMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
usersRepositoryMockValue.createNormalUser = new Error('DB error');
|
||||
const service = await makeUsersServiceMock(
|
||||
cryptoMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
);
|
||||
const name = 'test_user5';
|
||||
const role = 'Transcriptioninst';
|
||||
const email = 'test5@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
const notification = true;
|
||||
const token: AccessToken = { userId: '0001', scope: '' };
|
||||
await expect(
|
||||
service.createUser(
|
||||
token,
|
||||
name,
|
||||
role,
|
||||
email,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
),
|
||||
);
|
||||
});
|
||||
it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。', async () => {
|
||||
const cryptoMockValue = makeDefaultCryptoMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
adb2cParam.createUser = new Error();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
cryptoMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
);
|
||||
const name = 'test_user6';
|
||||
const role = 'Transcriptioninst';
|
||||
const email = 'test6@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
const notification = true;
|
||||
const token: AccessToken = { userId: '0001', scope: '' };
|
||||
await expect(
|
||||
service.createUser(
|
||||
token,
|
||||
name,
|
||||
role,
|
||||
email,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
),
|
||||
);
|
||||
});
|
||||
it('メールアドレスが重複している場合、エラーとなる。', async () => {
|
||||
const cryptoMockValue = makeDefaultCryptoMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
adb2cParam.createUser = { reason: 'email', message: 'ObjectConflict' };
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
cryptoMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
);
|
||||
const name = 'test_user7';
|
||||
const role = 'Transcriptioninst';
|
||||
const email = 'test7@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
const notification = true;
|
||||
const token: AccessToken = { userId: '0001', scope: '' };
|
||||
await expect(
|
||||
service.createUser(
|
||||
token,
|
||||
name,
|
||||
role,
|
||||
email,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010301'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
it('AuthorIDが重複している場合、エラーとなる。', async () => {
|
||||
const cryptoMockValue = makeDefaultCryptoMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
usersRepositoryMockValue.createNormalUser = new Error();
|
||||
usersRepositoryMockValue.createNormalUser.name = 'ER_DUP_ENTRY';
|
||||
|
||||
const service = await makeUsersServiceMock(
|
||||
cryptoMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
);
|
||||
const name = 'test_user8';
|
||||
const role = 'Author';
|
||||
const email = 'test8@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
const notification = true;
|
||||
const authorId = 'testID';
|
||||
const token: AccessToken = { userId: '0001', scope: '' };
|
||||
await expect(
|
||||
service.createUser(
|
||||
token,
|
||||
name,
|
||||
role,
|
||||
email,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
authorId,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010302'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
|
||||
@ -6,10 +6,16 @@ import {
|
||||
EmailAlreadyVerifiedError,
|
||||
} from '../../repositories/users/users.repository.service';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import { makePassword } from '../../common/password/password';
|
||||
import { AdB2cService } from '../../gateways/adb2c/adb2c.service';
|
||||
import {
|
||||
AdB2cService,
|
||||
ConflictError,
|
||||
isConflictError,
|
||||
} from '../../gateways/adb2c/adb2c.service';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
|
||||
import { User } from '../../repositories/users/entity/user.entity';
|
||||
import { makePassword } from '../../common/password/password';
|
||||
import { AccessToken } from '../../common/token';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
@ -72,6 +78,127 @@ export class UsersService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ユーザを作成する
|
||||
* @param token
|
||||
* @returns account
|
||||
*/
|
||||
async createUser(
|
||||
accessToken: AccessToken,
|
||||
name: string,
|
||||
role: string,
|
||||
email: string,
|
||||
autoRenew: boolean,
|
||||
licenseAlert: boolean,
|
||||
notification: boolean,
|
||||
authorId?: string | undefined,
|
||||
groupID?: number | undefined,
|
||||
): Promise<void> {
|
||||
//アクセストークンからユーザーIDを取得する
|
||||
// TODO アクセストークンの中身が具体的に確定したら、型変換を取り払う必要があるかも
|
||||
this.logger.log(`[IN] ${this.createUser.name}`);
|
||||
const userId = Number(accessToken.userId);
|
||||
|
||||
//DBよりアクセス者の所属するアカウントIDを取得する
|
||||
let adminUser: User;
|
||||
try {
|
||||
adminUser = await this.usersRepository.findUserById(userId);
|
||||
} catch (e) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const accountId = adminUser.account_id;
|
||||
|
||||
// ランダムなパスワードを生成する
|
||||
const ramdomPassword = makePassword();
|
||||
|
||||
//Azure AD B2Cにユーザーを新規登録する
|
||||
let externalUser: { sub: string } | ConflictError;
|
||||
try {
|
||||
// idpにユーザーを作成
|
||||
externalUser = await this.adB2cService.createUser(
|
||||
email,
|
||||
ramdomPassword,
|
||||
name,
|
||||
);
|
||||
} catch (e) {
|
||||
console.log('create externalUser failed');
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
// メールアドレス重複エラー
|
||||
if (isConflictError(externalUser)) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010301'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
//Azure AD B2Cに登録したユーザー情報のID(sub)と受け取った情報を使ってDBにユーザーを登録する
|
||||
let newUser: User;
|
||||
// TODO 本来はNULLだが、テーブル定義に誤ってNOTNULLが付いているため、一時的に適当な値を設定
|
||||
const accepted_terms_version = 'xxx';
|
||||
try {
|
||||
// ユーザ作成
|
||||
newUser = await this.usersRepository.createNormalUser(
|
||||
accountId,
|
||||
externalUser.sub,
|
||||
role,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
authorId,
|
||||
accepted_terms_version,
|
||||
);
|
||||
} catch (e) {
|
||||
console.log('create user failed');
|
||||
switch (e.code) {
|
||||
case 'ER_DUP_ENTRY':
|
||||
//AuthorID重複エラー
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010302'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
default:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//Email送信用のコンテンツを作成する
|
||||
try {
|
||||
// メールの送信元を取得
|
||||
const from = this.configService.get<string>('MAIL_FROM') ?? '';
|
||||
|
||||
// メールの内容を構成
|
||||
const { subject, text, html } =
|
||||
await this.sendgridService.createMailContentFromEmailConfirmForNormalUser(
|
||||
accountId,
|
||||
newUser.id,
|
||||
email,
|
||||
);
|
||||
|
||||
//SendGridAPIを呼び出してメールを送信する
|
||||
await this.sendgridService.sendMail(email, from, subject, text, html);
|
||||
} catch (e) {
|
||||
console.log('create user failed');
|
||||
console.log(`[NOT IMPLEMENT] [RECOVER] delete user: ${newUser.id}`);
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
this.logger.log(`[OUT] ${this.createUser.name}`);
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* confirm User And Init Password
|
||||
* @param token ユーザ仮登録時に払いだされるトークン
|
||||
|
||||
@ -47,6 +47,43 @@ export class SendGridService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Email認証用のメールコンテンツを作成する(一般ユーザ向け)
|
||||
* @param accountId 認証対象のユーザーが所属するアカウントのID
|
||||
* @param userId 認証対象のユーザーのID
|
||||
* @param email 認証対象のユーザーのメールアドレス
|
||||
* @returns メールのサブジェクトとコンテンツ
|
||||
*/
|
||||
//TODO 中身が管理ユーザ向けのままなので、修正の必要あり
|
||||
async createMailContentFromEmailConfirmForNormalUser(
|
||||
accountId: number,
|
||||
userId: number,
|
||||
email: string,
|
||||
): Promise<{ subject: string; text: string; html: string }> {
|
||||
const lifetime =
|
||||
this.configService.get<number>('EMAIL_CONFIRM_LIFETIME') ?? 0;
|
||||
const privateKey =
|
||||
this.configService.get<string>('JWT_PRIVATE_KEY')?.replace('\\n', '\n') ??
|
||||
'';
|
||||
const token = sign<{ accountId: number; userId: number; email: string }>(
|
||||
{
|
||||
accountId,
|
||||
userId,
|
||||
email,
|
||||
},
|
||||
lifetime,
|
||||
privateKey,
|
||||
);
|
||||
const domains = this.configService.get<string>('APP_DOMAIN');
|
||||
const path = 'mail-confirm/';
|
||||
|
||||
return {
|
||||
subject: 'Verify your new account',
|
||||
text: `The verification URL. ${domains}${path}?verify=${token}`,
|
||||
html: `<p>The verification URL.<p><a href="${domains}${path}?verify=${token}">${domains}${path}?verify=${token}"</a>`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* メールを送信する
|
||||
* @param accountId アカウントID
|
||||
|
||||
@ -29,6 +29,15 @@ export class User {
|
||||
@Column()
|
||||
email_verified: boolean;
|
||||
|
||||
@Column()
|
||||
auto_renew: boolean;
|
||||
|
||||
@Column()
|
||||
license_alert: boolean;
|
||||
|
||||
@Column()
|
||||
notification: boolean;
|
||||
|
||||
@Column('timestamp', { nullable: true })
|
||||
deleted_at?: Date;
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import { User } from './entity/user.entity';
|
||||
// UsersRepositoryServiceで発生するエラーを定義
|
||||
export class EmailAlreadyVerifiedError extends Error {}
|
||||
export class UserNotFoundError extends Error {}
|
||||
export class AuthorIdAlreadyExistError extends Error {}
|
||||
|
||||
@Injectable()
|
||||
export class UsersRepositoryService {
|
||||
@ -35,6 +36,49 @@ export class UsersRepositoryService {
|
||||
return createdEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 一般ユーザを作成する
|
||||
* @param account_id
|
||||
* @param external_id
|
||||
* @param role
|
||||
* @param autoRenew
|
||||
* @param licenseAlert
|
||||
* @param notification
|
||||
* @param authorId
|
||||
* @returns User
|
||||
*/
|
||||
async createNormalUser(
|
||||
accountId: number,
|
||||
externalUserId: string,
|
||||
role: string,
|
||||
auto_renew: boolean,
|
||||
license_alert: boolean,
|
||||
notification: boolean,
|
||||
author_id: string,
|
||||
accepted_terms_version: string,
|
||||
): Promise<User> {
|
||||
const user = new User();
|
||||
|
||||
user.role = role;
|
||||
user.account_id = accountId;
|
||||
user.external_id = externalUserId;
|
||||
user.auto_renew = auto_renew;
|
||||
user.license_alert = license_alert;
|
||||
user.notification = notification;
|
||||
user.author_id = author_id;
|
||||
user.accepted_terms_version = accepted_terms_version;
|
||||
|
||||
const createdEntity = await this.dataSource.transaction(
|
||||
async (entityManager) => {
|
||||
const repo = entityManager.getRepository(User);
|
||||
const newUser = repo.create(user);
|
||||
const persisted = await repo.save(newUser);
|
||||
return persisted;
|
||||
},
|
||||
);
|
||||
return createdEntity;
|
||||
}
|
||||
|
||||
async findVerifiedUser(sub: string): Promise<User | undefined> {
|
||||
const user = await this.dataSource.getRepository(User).findOne({
|
||||
where: {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user