Merged PR 906: ユーザー認証API修正
## 概要 [Task4182: ユーザー認証API修正](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/4182) - 認証済みチェックをパスワード変更より先に行うように修正 - パスワード変更に失敗したら、認証済みフラグをfalseにするリカバリ処理追加 - リカバリに失敗したら手動復旧ログを出力 - メール送信に失敗したらエラーを返すように修正 - メール送信に失敗したらリカバリ処理を行うように修正 - リカバリに失敗したら手動復旧ログを出力 - テスト修正 - リカバリ処理を考慮したケースを追加 ## レビューポイント - リカバリ処理の記述 - メール送信でエラーが起きたときにエラーを握りつぶさないようにしたが問題ないか - メール送信で失敗したときにエラーを握りつぶすと、ユーザーは届かないメールを待つしかなくなる - 失敗を伝えて、リカバリをしてあげると再実行してもらうことができる。 ## クエリの変更 - クエリの変更はなし ## 動作確認状況 - ローカルで確認 - 行った修正がデグレを発生させていないことを確認できるか - 既存のテストケースをDBを使うテストに置き換え - 結果は変えずに通ることを確認 - テストケースを追加し、新たな観点でテストを作成 ## 補足 - 相談、参考資料などがあれば
This commit is contained in:
parent
9736d23653
commit
0e68f26c57
@ -15,11 +15,8 @@ const UserVerifyPage: React.FC = (): JSX.Element => {
|
|||||||
const jwt = query.get("verify") ?? "";
|
const jwt = query.get("verify") ?? "";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!jwt) {
|
|
||||||
navigate("/mail-confirm/failed");
|
|
||||||
}
|
|
||||||
dispatch(userVerifyAsync({ jwt }));
|
dispatch(userVerifyAsync({ jwt }));
|
||||||
}, [navigate, dispatch, jwt]);
|
}, [dispatch, jwt]);
|
||||||
|
|
||||||
const verifyState = useSelector(VerifyStateSelector);
|
const verifyState = useSelector(VerifyStateSelector);
|
||||||
|
|
||||||
|
|||||||
@ -35,6 +35,11 @@ export const overrideAdB2cService = <TService>(
|
|||||||
externalIds: string[],
|
externalIds: string[],
|
||||||
) => Promise<AdB2cUser[]>;
|
) => Promise<AdB2cUser[]>;
|
||||||
getUser?: (context: Context, externalId: string) => Promise<AdB2cUser>;
|
getUser?: (context: Context, externalId: string) => Promise<AdB2cUser>;
|
||||||
|
changePassword?: (
|
||||||
|
context: Context,
|
||||||
|
externalId: string,
|
||||||
|
password: string,
|
||||||
|
) => Promise<void>;
|
||||||
},
|
},
|
||||||
): void => {
|
): void => {
|
||||||
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
|
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
|
||||||
@ -69,6 +74,12 @@ export const overrideAdB2cService = <TService>(
|
|||||||
writable: true,
|
writable: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (overrides.changePassword) {
|
||||||
|
Object.defineProperty(obj, obj.changePassword.name, {
|
||||||
|
value: overrides.changePassword,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,6 +133,8 @@ export const overrideUsersRepositoryService = <TService>(
|
|||||||
overrides: {
|
overrides: {
|
||||||
createNormalUser?: (user: newUser) => Promise<User>;
|
createNormalUser?: (user: newUser) => Promise<User>;
|
||||||
deleteNormalUser?: (userId: number) => Promise<void>;
|
deleteNormalUser?: (userId: number) => Promise<void>;
|
||||||
|
updateUserVerified?: (context: Context, userId: number) => Promise<void>;
|
||||||
|
updateUserUnverified?: (context: Context, userId: number) => Promise<void>;
|
||||||
},
|
},
|
||||||
): void => {
|
): void => {
|
||||||
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
|
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
|
||||||
@ -138,6 +151,18 @@ export const overrideUsersRepositoryService = <TService>(
|
|||||||
writable: true,
|
writable: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (overrides.updateUserVerified) {
|
||||||
|
Object.defineProperty(obj, obj.updateUserVerified.name, {
|
||||||
|
value: overrides.updateUserVerified,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (overrides.updateUserUnverified) {
|
||||||
|
Object.defineProperty(obj, obj.updateUserUnverified.name, {
|
||||||
|
value: overrides.updateUserUnverified,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import {
|
|||||||
makeDefaultUsersRepositoryMockValue,
|
makeDefaultUsersRepositoryMockValue,
|
||||||
makeUsersServiceMock,
|
makeUsersServiceMock,
|
||||||
} from './test/users.service.mock';
|
} from './test/users.service.mock';
|
||||||
import { EmailAlreadyVerifiedError } from '../../repositories/users/errors/types';
|
|
||||||
import {
|
import {
|
||||||
createLicense,
|
createLicense,
|
||||||
createUserGroup,
|
createUserGroup,
|
||||||
@ -22,6 +21,7 @@ import {
|
|||||||
LICENSE_ALLOCATED_STATUS,
|
LICENSE_ALLOCATED_STATUS,
|
||||||
LICENSE_EXPIRATION_THRESHOLD_DAYS,
|
LICENSE_EXPIRATION_THRESHOLD_DAYS,
|
||||||
LICENSE_TYPE,
|
LICENSE_TYPE,
|
||||||
|
MANUAL_RECOVERY_REQUIRED,
|
||||||
TASK_STATUS,
|
TASK_STATUS,
|
||||||
USER_AUDIO_FORMAT,
|
USER_AUDIO_FORMAT,
|
||||||
USER_LICENSE_EXPIRY_STATUS,
|
USER_LICENSE_EXPIRY_STATUS,
|
||||||
@ -59,6 +59,7 @@ import { createTask } from '../files/test/utility';
|
|||||||
import { createCheckoutPermissions } from '../tasks/test/utility';
|
import { createCheckoutPermissions } from '../tasks/test/utility';
|
||||||
import { MultipleImportErrors } from './types/types';
|
import { MultipleImportErrors } from './types/types';
|
||||||
import { TestLogger } from '../../common/test/logger';
|
import { TestLogger } from '../../common/test/logger';
|
||||||
|
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
|
||||||
|
|
||||||
describe('UsersService.confirmUser', () => {
|
describe('UsersService.confirmUser', () => {
|
||||||
let source: DataSource | null = null;
|
let source: DataSource | null = null;
|
||||||
@ -254,216 +255,564 @@ describe('UsersService.confirmUser', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('UsersService.confirmUserAndInitPassword', () => {
|
describe('UsersService.confirmUserAndInitPassword', () => {
|
||||||
|
let source: DataSource | null = null;
|
||||||
|
beforeAll(async () => {
|
||||||
|
if (source == null) {
|
||||||
|
source = await (async () => {
|
||||||
|
const s = new DataSource({
|
||||||
|
type: 'mysql',
|
||||||
|
host: 'test_mysql_db',
|
||||||
|
port: 3306,
|
||||||
|
username: 'user',
|
||||||
|
password: 'password',
|
||||||
|
database: 'odms',
|
||||||
|
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
|
||||||
|
synchronize: false, // trueにすると自動的にmigrationが行われるため注意
|
||||||
|
logger: new TestLogger('none'),
|
||||||
|
logging: true,
|
||||||
|
});
|
||||||
|
return await s.initialize();
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
if (source) {
|
||||||
|
await truncateAllTable(source);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await source?.destroy();
|
||||||
|
source = null;
|
||||||
|
});
|
||||||
it('ユーザーが発行されたパスワードでログインできるようにする', async () => {
|
it('ユーザーが発行されたパスワードでログインできるようにする', async () => {
|
||||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
if (!source) fail();
|
||||||
usersRepositoryMockValue.findUserById = {
|
const module = await makeTestingModule(source);
|
||||||
id: 1,
|
if (!module) fail();
|
||||||
external_id: 'TEST9999',
|
const service = module.get<UsersService>(UsersService);
|
||||||
account_id: 1,
|
const adminExternalId = 'ADMIN0001';
|
||||||
role: 'None',
|
const { account } = await makeTestAccount(
|
||||||
accepted_eula_version: 'string',
|
source,
|
||||||
accepted_privacy_notice_version: 'string',
|
{},
|
||||||
accepted_dpa_version: 'string',
|
{ external_id: adminExternalId },
|
||||||
email_verified: false,
|
|
||||||
created_by: 'string;',
|
|
||||||
created_at: new Date(),
|
|
||||||
updated_by: 'string;',
|
|
||||||
updated_at: new Date(),
|
|
||||||
auto_renew: true,
|
|
||||||
notification: true,
|
|
||||||
encryption: false,
|
|
||||||
prompt: false,
|
|
||||||
account: null,
|
|
||||||
author_id: null,
|
|
||||||
deleted_at: null,
|
|
||||||
encryption_password: null,
|
|
||||||
license: null,
|
|
||||||
userGroupMembers: null,
|
|
||||||
};
|
|
||||||
const licensesRepositoryMockValue = null;
|
|
||||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
|
||||||
const configMockValue = makeDefaultConfigValue();
|
|
||||||
const sortCriteriaRepositoryMockValue =
|
|
||||||
makeDefaultSortCriteriaRepositoryMockValue();
|
|
||||||
const sendGridMockValue = makeDefaultSendGridlValue();
|
|
||||||
const service = await makeUsersServiceMock(
|
|
||||||
usersRepositoryMockValue,
|
|
||||||
licensesRepositoryMockValue,
|
|
||||||
adb2cParam,
|
|
||||||
sendGridMockValue,
|
|
||||||
configMockValue,
|
|
||||||
sortCriteriaRepositoryMockValue,
|
|
||||||
);
|
);
|
||||||
|
const { id: accountId } = account;
|
||||||
|
// ユーザー作成
|
||||||
|
await makeTestUser(source, {
|
||||||
|
account_id: accountId,
|
||||||
|
external_id: 'externalId_user1',
|
||||||
|
role: USER_ROLES.NONE,
|
||||||
|
author_id: undefined,
|
||||||
|
auto_renew: true,
|
||||||
|
encryption: false,
|
||||||
|
encryption_password: undefined,
|
||||||
|
prompt: false,
|
||||||
|
email_verified: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
overrideAdB2cService(service, {
|
||||||
|
changePassword: jest.fn(),
|
||||||
|
getUser: async () => {
|
||||||
|
return {
|
||||||
|
id: adminExternalId,
|
||||||
|
displayName: 'admin',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let _subject: string = '';
|
||||||
|
overrideSendgridService(service, {
|
||||||
|
sendMail: async (
|
||||||
|
context: Context,
|
||||||
|
to: string[],
|
||||||
|
cc: string[],
|
||||||
|
from: string,
|
||||||
|
subject: string,
|
||||||
|
text: string,
|
||||||
|
html: string,
|
||||||
|
) => {
|
||||||
|
_subject = subject;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const token =
|
const token =
|
||||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||||
expect(
|
await service.confirmUserAndInitPassword(
|
||||||
|
makeContext('trackingId', 'requestId'),
|
||||||
|
token,
|
||||||
|
);
|
||||||
|
expect(_subject).toBe('Temporary password [U-113]');
|
||||||
|
const user = await getUserFromExternalId(source, 'externalId_user1');
|
||||||
|
expect(user?.email_verified).toBe(true);
|
||||||
|
});
|
||||||
|
it('トークンの形式が不正な場合、形式不正エラーとなる。(メール認証API)', async () => {
|
||||||
|
if (!source) fail();
|
||||||
|
const module = await makeTestingModule(source);
|
||||||
|
if (!module) fail();
|
||||||
|
const service = module.get<UsersService>(UsersService);
|
||||||
|
const sendgridService = module.get<SendGridService>(SendGridService);
|
||||||
|
const adB2cService = module.get<AdB2cService>(AdB2cService);
|
||||||
|
const adminExternalId = 'ADMIN0001';
|
||||||
|
const { account } = await makeTestAccount(
|
||||||
|
source,
|
||||||
|
{},
|
||||||
|
{ external_id: adminExternalId },
|
||||||
|
);
|
||||||
|
const { id: accountId } = account;
|
||||||
|
// ユーザー作成
|
||||||
|
await makeTestUser(source, {
|
||||||
|
account_id: accountId,
|
||||||
|
external_id: 'externalId_user1',
|
||||||
|
role: USER_ROLES.NONE,
|
||||||
|
author_id: undefined,
|
||||||
|
auto_renew: true,
|
||||||
|
encryption: false,
|
||||||
|
encryption_password: undefined,
|
||||||
|
prompt: false,
|
||||||
|
email_verified: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
overrideAdB2cService(service, {
|
||||||
|
changePassword: jest.fn(),
|
||||||
|
getUser: async () => {
|
||||||
|
return {
|
||||||
|
id: adminExternalId,
|
||||||
|
displayName: 'admin',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
overrideSendgridService(service, {
|
||||||
|
sendMail: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const token = 'invalid.id.token';
|
||||||
|
|
||||||
|
try {
|
||||||
await service.confirmUserAndInitPassword(
|
await service.confirmUserAndInitPassword(
|
||||||
makeContext('trackingId', 'requestId'),
|
makeContext('trackingId', 'requestId'),
|
||||||
token,
|
token,
|
||||||
),
|
);
|
||||||
).toEqual(undefined);
|
} catch (e) {
|
||||||
});
|
if (e instanceof HttpException) {
|
||||||
|
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||||
it('トークンの形式が不正な場合、形式不正エラーとなる。(メール認証API)', async () => {
|
expect(e.getResponse()).toEqual(makeErrorResponse('E000101'));
|
||||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
} else {
|
||||||
usersRepositoryMockValue.findUserById = {
|
fail();
|
||||||
id: 1,
|
}
|
||||||
external_id: 'TEST9999',
|
}
|
||||||
account_id: 1,
|
const user = await getUserFromExternalId(source, 'externalId_user1');
|
||||||
role: 'None',
|
// ユーザーが認証されていないことを確認
|
||||||
accepted_eula_version: 'string',
|
expect(user?.email_verified).toBe(false);
|
||||||
accepted_privacy_notice_version: 'string',
|
// メールが送信されていないことを確認
|
||||||
accepted_dpa_version: 'string',
|
expect(sendgridService.sendMail).toBeCalledTimes(0);
|
||||||
email_verified: false,
|
// パスワードが変更されていないことを確認
|
||||||
created_by: 'string;',
|
expect(adB2cService.changePassword).toBeCalledTimes(0);
|
||||||
created_at: new Date(),
|
|
||||||
updated_by: 'string;',
|
|
||||||
updated_at: new Date(),
|
|
||||||
auto_renew: true,
|
|
||||||
notification: true,
|
|
||||||
encryption: false,
|
|
||||||
prompt: false,
|
|
||||||
account: null,
|
|
||||||
author_id: null,
|
|
||||||
deleted_at: null,
|
|
||||||
encryption_password: null,
|
|
||||||
license: null,
|
|
||||||
userGroupMembers: null,
|
|
||||||
};
|
|
||||||
const licensesRepositoryMockValue = null;
|
|
||||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
|
||||||
const sendGridMockValue = makeDefaultSendGridlValue();
|
|
||||||
const configMockValue = makeDefaultConfigValue();
|
|
||||||
const sortCriteriaRepositoryMockValue =
|
|
||||||
makeDefaultSortCriteriaRepositoryMockValue();
|
|
||||||
const service = await makeUsersServiceMock(
|
|
||||||
usersRepositoryMockValue,
|
|
||||||
licensesRepositoryMockValue,
|
|
||||||
adb2cParam,
|
|
||||||
sendGridMockValue,
|
|
||||||
configMockValue,
|
|
||||||
sortCriteriaRepositoryMockValue,
|
|
||||||
);
|
|
||||||
const token = 'invalid.id.token';
|
|
||||||
await expect(
|
|
||||||
service.confirmUserAndInitPassword(
|
|
||||||
makeContext('trackingId', 'requestId'),
|
|
||||||
token,
|
|
||||||
),
|
|
||||||
).rejects.toEqual(
|
|
||||||
new HttpException(makeErrorResponse('E000101'), HttpStatus.BAD_REQUEST),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
it('ユーザが既に認証済みだった場合、認証済みユーザエラーとなる。(メール認証API)', async () => {
|
it('ユーザが既に認証済みだった場合、認証済みユーザエラーとなる。(メール認証API)', async () => {
|
||||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
if (!source) fail();
|
||||||
usersRepositoryMockValue.findUserById = {
|
const module = await makeTestingModule(source);
|
||||||
id: 1,
|
if (!module) fail();
|
||||||
external_id: 'TEST9999',
|
const service = module.get<UsersService>(UsersService);
|
||||||
account_id: 1,
|
const sendgridService = module.get<SendGridService>(SendGridService);
|
||||||
role: 'None',
|
const adB2cService = module.get<AdB2cService>(AdB2cService);
|
||||||
accepted_eula_version: 'string',
|
const adminExternalId = 'ADMIN0001';
|
||||||
accepted_privacy_notice_version: 'string',
|
const { account } = await makeTestAccount(
|
||||||
accepted_dpa_version: 'string',
|
source,
|
||||||
email_verified: true,
|
{},
|
||||||
created_by: 'string;',
|
{ external_id: adminExternalId },
|
||||||
created_at: new Date(),
|
);
|
||||||
updated_by: 'string;',
|
const { id: accountId } = account;
|
||||||
updated_at: new Date(),
|
// ユーザー作成
|
||||||
|
await makeTestUser(source, {
|
||||||
|
account_id: accountId,
|
||||||
|
external_id: 'externalId_user1',
|
||||||
|
role: USER_ROLES.NONE,
|
||||||
|
author_id: undefined,
|
||||||
auto_renew: true,
|
auto_renew: true,
|
||||||
notification: true,
|
|
||||||
encryption: false,
|
encryption: false,
|
||||||
|
encryption_password: undefined,
|
||||||
prompt: false,
|
prompt: false,
|
||||||
account: null,
|
email_verified: true, // emailを認証済みにする
|
||||||
author_id: null,
|
});
|
||||||
deleted_at: null,
|
|
||||||
encryption_password: null,
|
overrideAdB2cService(service, {
|
||||||
license: null,
|
changePassword: jest.fn(),
|
||||||
userGroupMembers: null,
|
getUser: async () => {
|
||||||
};
|
return {
|
||||||
const licensesRepositoryMockValue = null;
|
id: adminExternalId,
|
||||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
displayName: 'admin',
|
||||||
const sendGridMockValue = makeDefaultSendGridlValue();
|
};
|
||||||
const configMockValue = makeDefaultConfigValue();
|
},
|
||||||
const sortCriteriaRepositoryMockValue =
|
});
|
||||||
makeDefaultSortCriteriaRepositoryMockValue();
|
overrideSendgridService(service, {
|
||||||
usersRepositoryMockValue.updateUserVerified = new EmailAlreadyVerifiedError(
|
sendMail: jest.fn(),
|
||||||
`Email already verified user`,
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const service = await makeUsersServiceMock(
|
|
||||||
usersRepositoryMockValue,
|
|
||||||
licensesRepositoryMockValue,
|
|
||||||
adb2cParam,
|
|
||||||
sendGridMockValue,
|
|
||||||
configMockValue,
|
|
||||||
sortCriteriaRepositoryMockValue,
|
|
||||||
);
|
|
||||||
const token =
|
const token =
|
||||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||||
await expect(
|
|
||||||
service.confirmUserAndInitPassword(
|
try {
|
||||||
|
await service.confirmUserAndInitPassword(
|
||||||
makeContext('trackingId', 'requestId'),
|
makeContext('trackingId', 'requestId'),
|
||||||
token,
|
token,
|
||||||
),
|
);
|
||||||
).rejects.toEqual(
|
} catch (e) {
|
||||||
new HttpException(makeErrorResponse('E010202'), HttpStatus.BAD_REQUEST),
|
if (e instanceof HttpException) {
|
||||||
|
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||||
|
expect(e.getResponse()).toEqual(makeErrorResponse('E010202'));
|
||||||
|
} else {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const user = await getUserFromExternalId(source, 'externalId_user1');
|
||||||
|
// ユーザーが認証されていることを確認
|
||||||
|
expect(user?.email_verified).toBe(true);
|
||||||
|
// メールが送信されていないことを確認
|
||||||
|
expect(sendgridService.sendMail).toBeCalledTimes(0);
|
||||||
|
// パスワードが変更されていないことを確認
|
||||||
|
expect(adB2cService.changePassword).toBeCalledTimes(0);
|
||||||
|
});
|
||||||
|
it('ADB2Cユーザーのパスワード更新に失敗した場合、リカバリ処理を行い、メールを未認証のままにする。(メール認証API)', async () => {
|
||||||
|
if (!source) fail();
|
||||||
|
const module = await makeTestingModule(source);
|
||||||
|
if (!module) fail();
|
||||||
|
const service = module.get<UsersService>(UsersService);
|
||||||
|
const sendgridService = module.get<SendGridService>(SendGridService);
|
||||||
|
const adminExternalId = 'ADMIN0001';
|
||||||
|
const { account } = await makeTestAccount(
|
||||||
|
source,
|
||||||
|
{},
|
||||||
|
{ external_id: adminExternalId },
|
||||||
);
|
);
|
||||||
|
const { id: accountId } = account;
|
||||||
|
// ユーザー作成
|
||||||
|
await makeTestUser(source, {
|
||||||
|
account_id: accountId,
|
||||||
|
external_id: 'externalId_user1',
|
||||||
|
role: USER_ROLES.NONE,
|
||||||
|
author_id: undefined,
|
||||||
|
auto_renew: true,
|
||||||
|
encryption: false,
|
||||||
|
encryption_password: undefined,
|
||||||
|
prompt: false,
|
||||||
|
email_verified: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
overrideAdB2cService(service, {
|
||||||
|
changePassword: async () => {
|
||||||
|
throw new Error('ADB2C Error');
|
||||||
|
},
|
||||||
|
getUser: async () => {
|
||||||
|
return {
|
||||||
|
id: adminExternalId,
|
||||||
|
displayName: 'admin',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
overrideSendgridService(service, {
|
||||||
|
sendMail: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const token =
|
||||||
|
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||||
|
try {
|
||||||
|
await service.confirmUserAndInitPassword(
|
||||||
|
makeContext('trackingId', 'requestId'),
|
||||||
|
token,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof HttpException) {
|
||||||
|
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
|
||||||
|
} else {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const user = await getUserFromExternalId(source, 'externalId_user1');
|
||||||
|
// ユーザーが認証されていないことを確認
|
||||||
|
expect(user?.email_verified).toBe(false);
|
||||||
|
// メールが送信されていないことを確認
|
||||||
|
expect(sendgridService.sendMail).toBeCalledTimes(0);
|
||||||
|
});
|
||||||
|
it('ADB2Cユーザーのパスワード更新に失敗した場合、リカバリ処理を行うが、リカバリ処理に失敗すると認証のままになる(メール認証API)', async () => {
|
||||||
|
if (!source) fail();
|
||||||
|
const module = await makeTestingModule(source);
|
||||||
|
if (!module) fail();
|
||||||
|
const service = module.get<UsersService>(UsersService);
|
||||||
|
const sendgridService = module.get<SendGridService>(SendGridService);
|
||||||
|
const adminExternalId = 'ADMIN0001';
|
||||||
|
const { account } = await makeTestAccount(
|
||||||
|
source,
|
||||||
|
{},
|
||||||
|
{ external_id: adminExternalId },
|
||||||
|
);
|
||||||
|
const { id: accountId } = account;
|
||||||
|
// ユーザー作成
|
||||||
|
await makeTestUser(source, {
|
||||||
|
account_id: accountId,
|
||||||
|
external_id: 'externalId_user1',
|
||||||
|
role: USER_ROLES.NONE,
|
||||||
|
author_id: undefined,
|
||||||
|
auto_renew: true,
|
||||||
|
encryption: false,
|
||||||
|
encryption_password: undefined,
|
||||||
|
prompt: false,
|
||||||
|
email_verified: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const loggerSpy = jest
|
||||||
|
.spyOn(service['logger'], 'error')
|
||||||
|
.mockImplementation();
|
||||||
|
|
||||||
|
overrideAdB2cService(service, {
|
||||||
|
changePassword: async () => {
|
||||||
|
throw new Error('ADB2C Error');
|
||||||
|
},
|
||||||
|
getUser: async () => {
|
||||||
|
return {
|
||||||
|
id: adminExternalId,
|
||||||
|
displayName: 'admin',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
overrideUsersRepositoryService(service, {
|
||||||
|
updateUserUnverified: async () => {
|
||||||
|
throw new Error('DB Error');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
overrideSendgridService(service, {
|
||||||
|
sendMail: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const token =
|
||||||
|
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||||
|
try {
|
||||||
|
await service.confirmUserAndInitPassword(
|
||||||
|
makeContext('trackingId', 'requestId'),
|
||||||
|
token,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof HttpException) {
|
||||||
|
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
|
||||||
|
} else {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const user = await getUserFromExternalId(source, 'externalId_user1');
|
||||||
|
// ユーザーが認証されたままであることを確認
|
||||||
|
expect(user?.email_verified).toBe(true);
|
||||||
|
// メールが送信されていないことを確認
|
||||||
|
expect(sendgridService.sendMail).toBeCalledTimes(0);
|
||||||
|
// loggerSpyがスパイしているlogger.logメソッドが出力したログを確認(目視確認用)
|
||||||
|
const logs = loggerSpy.mock.calls.map((call) => call[0]);
|
||||||
|
// 手動復旧が必要なエラーログが出力されていること
|
||||||
|
expect(logs.some((x) => x.startsWith(MANUAL_RECOVERY_REQUIRED))).toBe(true);
|
||||||
});
|
});
|
||||||
it('DBネットワークエラーとなる場合、エラーとなる。(メール認証API)', async () => {
|
it('DBネットワークエラーとなる場合、エラーとなる。(メール認証API)', async () => {
|
||||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
if (!source) fail();
|
||||||
usersRepositoryMockValue.findUserById = {
|
const module = await makeTestingModule(source);
|
||||||
id: 1,
|
if (!module) fail();
|
||||||
external_id: 'TEST9999',
|
const service = module.get<UsersService>(UsersService);
|
||||||
account_id: 1,
|
const sendgridService = module.get<SendGridService>(SendGridService);
|
||||||
role: 'None',
|
const adB2cService = module.get<AdB2cService>(AdB2cService);
|
||||||
accepted_eula_version: 'string',
|
const adminExternalId = 'ADMIN0001';
|
||||||
accepted_privacy_notice_version: 'string',
|
const { account } = await makeTestAccount(
|
||||||
accepted_dpa_version: 'string',
|
source,
|
||||||
email_verified: false,
|
{},
|
||||||
created_by: 'string;',
|
{ external_id: adminExternalId },
|
||||||
created_at: new Date(),
|
|
||||||
updated_by: 'string;',
|
|
||||||
updated_at: new Date(),
|
|
||||||
auto_renew: true,
|
|
||||||
notification: true,
|
|
||||||
encryption: false,
|
|
||||||
prompt: false,
|
|
||||||
account: null,
|
|
||||||
author_id: null,
|
|
||||||
deleted_at: null,
|
|
||||||
encryption_password: null,
|
|
||||||
license: null,
|
|
||||||
userGroupMembers: null,
|
|
||||||
};
|
|
||||||
const licensesRepositoryMockValue = null;
|
|
||||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
|
||||||
const sendGridMockValue = makeDefaultSendGridlValue();
|
|
||||||
usersRepositoryMockValue.updateUserVerified = new Error('DB error');
|
|
||||||
const configMockValue = makeDefaultConfigValue();
|
|
||||||
const sortCriteriaRepositoryMockValue =
|
|
||||||
makeDefaultSortCriteriaRepositoryMockValue();
|
|
||||||
const service = await makeUsersServiceMock(
|
|
||||||
usersRepositoryMockValue,
|
|
||||||
licensesRepositoryMockValue,
|
|
||||||
adb2cParam,
|
|
||||||
sendGridMockValue,
|
|
||||||
configMockValue,
|
|
||||||
sortCriteriaRepositoryMockValue,
|
|
||||||
);
|
);
|
||||||
|
const { id: accountId } = account;
|
||||||
|
// ユーザー作成
|
||||||
|
await makeTestUser(source, {
|
||||||
|
account_id: accountId,
|
||||||
|
external_id: 'externalId_user1',
|
||||||
|
role: USER_ROLES.NONE,
|
||||||
|
author_id: undefined,
|
||||||
|
auto_renew: true,
|
||||||
|
encryption: false,
|
||||||
|
encryption_password: undefined,
|
||||||
|
prompt: false,
|
||||||
|
email_verified: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
overrideAdB2cService(service, {
|
||||||
|
changePassword: jest.fn(),
|
||||||
|
getUser: async () => {
|
||||||
|
return {
|
||||||
|
id: adminExternalId,
|
||||||
|
displayName: 'admin',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
overrideSendgridService(service, {
|
||||||
|
sendMail: jest.fn(),
|
||||||
|
});
|
||||||
|
// DBエラーを発生させる
|
||||||
|
overrideUsersRepositoryService(service, {
|
||||||
|
updateUserVerified: async () => {
|
||||||
|
throw new Error('DB Error');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const token =
|
const token =
|
||||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||||
await expect(
|
try {
|
||||||
service.confirmUserAndInitPassword(
|
await service.confirmUserAndInitPassword(
|
||||||
makeContext('trackingId', 'requestId'),
|
makeContext('trackingId', 'requestId'),
|
||||||
token,
|
token,
|
||||||
),
|
);
|
||||||
).rejects.toEqual(
|
} catch (e) {
|
||||||
new HttpException(
|
if (e instanceof HttpException) {
|
||||||
makeErrorResponse('E009999'),
|
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
|
||||||
),
|
} else {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const user = await getUserFromExternalId(source, 'externalId_user1');
|
||||||
|
// ユーザーが認証されていないことを確認
|
||||||
|
expect(user?.email_verified).toBe(false);
|
||||||
|
// メールが送信されていないことを確認
|
||||||
|
expect(sendgridService.sendMail).toBeCalledTimes(0);
|
||||||
|
// パスワードが変更されていないことを確認
|
||||||
|
expect(adB2cService.changePassword).toBeCalledTimes(0);
|
||||||
|
});
|
||||||
|
it('メール送信に失敗した場合、リカバリ処理を行い、メールを未認証の状態にする。(メール認証API)', async () => {
|
||||||
|
if (!source) fail();
|
||||||
|
const module = await makeTestingModule(source);
|
||||||
|
if (!module) fail();
|
||||||
|
const service = module.get<UsersService>(UsersService);
|
||||||
|
const adb2cService = module.get<AdB2cService>(AdB2cService);
|
||||||
|
const adminExternalId = 'ADMIN0001';
|
||||||
|
const { account } = await makeTestAccount(
|
||||||
|
source,
|
||||||
|
{},
|
||||||
|
{ external_id: adminExternalId },
|
||||||
);
|
);
|
||||||
|
const { id: accountId } = account;
|
||||||
|
// ユーザー作成
|
||||||
|
await makeTestUser(source, {
|
||||||
|
account_id: accountId,
|
||||||
|
external_id: 'externalId_user1',
|
||||||
|
role: USER_ROLES.NONE,
|
||||||
|
author_id: undefined,
|
||||||
|
auto_renew: true,
|
||||||
|
encryption: false,
|
||||||
|
encryption_password: undefined,
|
||||||
|
prompt: false,
|
||||||
|
email_verified: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
overrideAdB2cService(service, {
|
||||||
|
changePassword: jest.fn(),
|
||||||
|
getUser: async () => {
|
||||||
|
return {
|
||||||
|
id: adminExternalId,
|
||||||
|
displayName: 'admin',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
overrideSendgridService(service, {
|
||||||
|
sendMail: async () => {
|
||||||
|
throw new Error('SendGrid Error');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const token =
|
||||||
|
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||||
|
try {
|
||||||
|
await service.confirmUserAndInitPassword(
|
||||||
|
makeContext('trackingId', 'requestId'),
|
||||||
|
token,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof HttpException) {
|
||||||
|
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
|
||||||
|
} else {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const user = await getUserFromExternalId(source, 'externalId_user1');
|
||||||
|
// ユーザーが認証されていないことを確認
|
||||||
|
expect(user?.email_verified).toBe(false);
|
||||||
|
// ADB2Cのパスワードが変更されていることを確認(パスワードは変更されても、ユーザーにメールが届いていないので問題ない)
|
||||||
|
expect(adb2cService.changePassword).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
it('メール送信に失敗した場合、リカバリ処理を行うが、リカバリ処理に失敗するとADB2Cのパスワードが変更され、DB上も認証された状態になる(メール認証API)', async () => {
|
||||||
|
if (!source) fail();
|
||||||
|
const module = await makeTestingModule(source);
|
||||||
|
if (!module) fail();
|
||||||
|
const service = module.get<UsersService>(UsersService);
|
||||||
|
const adB2cService = module.get<AdB2cService>(AdB2cService);
|
||||||
|
const adminExternalId = 'ADMIN0001';
|
||||||
|
const { account } = await makeTestAccount(
|
||||||
|
source,
|
||||||
|
{},
|
||||||
|
{ external_id: adminExternalId },
|
||||||
|
);
|
||||||
|
const { id: accountId } = account;
|
||||||
|
// ユーザー作成
|
||||||
|
await makeTestUser(source, {
|
||||||
|
account_id: accountId,
|
||||||
|
external_id: 'externalId_user1',
|
||||||
|
role: USER_ROLES.NONE,
|
||||||
|
author_id: undefined,
|
||||||
|
auto_renew: true,
|
||||||
|
encryption: false,
|
||||||
|
encryption_password: undefined,
|
||||||
|
prompt: false,
|
||||||
|
email_verified: false,
|
||||||
|
});
|
||||||
|
const loggerSpy = jest
|
||||||
|
.spyOn(service['logger'], 'error')
|
||||||
|
.mockImplementation();
|
||||||
|
|
||||||
|
overrideAdB2cService(service, {
|
||||||
|
changePassword: jest.fn(),
|
||||||
|
getUser: async () => {
|
||||||
|
return {
|
||||||
|
id: adminExternalId,
|
||||||
|
displayName: 'admin',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
overrideUsersRepositoryService(service, {
|
||||||
|
updateUserUnverified: async () => {
|
||||||
|
throw new Error('DB Error');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
overrideSendgridService(service, {
|
||||||
|
sendMail: async () => {
|
||||||
|
throw new Error('SendGrid Error');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const token =
|
||||||
|
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||||
|
try {
|
||||||
|
await service.confirmUserAndInitPassword(
|
||||||
|
makeContext('trackingId', 'requestId'),
|
||||||
|
token,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof HttpException) {
|
||||||
|
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
|
||||||
|
} else {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const user = await getUserFromExternalId(source, 'externalId_user1');
|
||||||
|
// ユーザーが認証されたままであることを確認
|
||||||
|
expect(user?.email_verified).toBe(true);
|
||||||
|
// ADB2Cのパスワードが変更されていることを確認
|
||||||
|
expect(adB2cService.changePassword).toBeCalledTimes(1);
|
||||||
|
// loggerSpyがスパイしているlogger.logメソッドが出力したログを確認(目視確認用)
|
||||||
|
const logs = loggerSpy.mock.calls.map((call) => call[0]);
|
||||||
|
// 手動復旧が必要なエラーログが出力されていること
|
||||||
|
expect(logs.some((x) => x.startsWith(MANUAL_RECOVERY_REQUIRED))).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -516,46 +516,10 @@ export class UsersService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ランダムなパスワードを生成する
|
|
||||||
const ramdomPassword = makePassword();
|
|
||||||
const { accountId, userId, email } = decodedToken;
|
const { accountId, userId, email } = decodedToken;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// ユーザー情報からAzure AD B2CのIDを特定する
|
|
||||||
const user = await this.usersRepository.findUserById(context, userId);
|
|
||||||
const extarnalId = user.external_id;
|
|
||||||
// パスワードを変更する
|
|
||||||
await this.adB2cService.changePassword(
|
|
||||||
context,
|
|
||||||
extarnalId,
|
|
||||||
ramdomPassword,
|
|
||||||
);
|
|
||||||
// ユーザを認証済みにする
|
// ユーザを認証済みにする
|
||||||
await this.usersRepository.updateUserVerified(context, userId);
|
await this.usersRepository.updateUserVerified(context, userId);
|
||||||
|
|
||||||
// メール送信処理
|
|
||||||
try {
|
|
||||||
const { external_id: primaryAdminUserExternalId } =
|
|
||||||
await this.getPrimaryAdminUser(context, accountId);
|
|
||||||
|
|
||||||
const adb2cUser = await this.adB2cService.getUser(
|
|
||||||
context,
|
|
||||||
primaryAdminUserExternalId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { displayName: primaryAdminName } =
|
|
||||||
getUserNameAndMailAddress(adb2cUser);
|
|
||||||
|
|
||||||
await this.sendgridService.sendMailWithU113(
|
|
||||||
context,
|
|
||||||
email,
|
|
||||||
primaryAdminName,
|
|
||||||
ramdomPassword,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
|
||||||
// メール送信に関する例外はログだけ出して握りつぶす
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
@ -572,6 +536,62 @@ export class UsersService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ランダムなパスワードを生成する
|
||||||
|
const ramdomPassword = makePassword();
|
||||||
|
try {
|
||||||
|
// ユーザー情報からAzure AD B2CのIDを特定する
|
||||||
|
const user = await this.usersRepository.findUserById(context, userId);
|
||||||
|
const extarnalId = user.external_id;
|
||||||
|
// パスワードを変更する
|
||||||
|
await this.adB2cService.changePassword(
|
||||||
|
context,
|
||||||
|
extarnalId,
|
||||||
|
ramdomPassword,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||||
|
this.logger.error(
|
||||||
|
`[${context.getTrackingId()}] change password failed. userId=${userId}`,
|
||||||
|
);
|
||||||
|
// リカバリー処理
|
||||||
|
// ユーザを未認証に戻す
|
||||||
|
await this.updateUserUnverified(context, userId);
|
||||||
|
|
||||||
|
throw new HttpException(
|
||||||
|
makeErrorResponse('E009999'),
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// メール送信処理
|
||||||
|
try {
|
||||||
|
const { external_id: primaryAdminUserExternalId } =
|
||||||
|
await this.getPrimaryAdminUser(context, accountId);
|
||||||
|
|
||||||
|
const adb2cUser = await this.adB2cService.getUser(
|
||||||
|
context,
|
||||||
|
primaryAdminUserExternalId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { displayName: primaryAdminName } =
|
||||||
|
getUserNameAndMailAddress(adb2cUser);
|
||||||
|
|
||||||
|
await this.sendgridService.sendMailWithU113(
|
||||||
|
context,
|
||||||
|
email,
|
||||||
|
primaryAdminName,
|
||||||
|
ramdomPassword,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
// リカバリー処理
|
||||||
|
// ユーザーを未認証に戻す
|
||||||
|
await this.updateUserUnverified(context, userId);
|
||||||
|
throw new HttpException(
|
||||||
|
makeErrorResponse('E009999'),
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
);
|
||||||
} finally {
|
} finally {
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
`[OUT] [${context.getTrackingId()}] ${
|
`[OUT] [${context.getTrackingId()}] ${
|
||||||
@ -1791,4 +1811,36 @@ export class UsersService {
|
|||||||
|
|
||||||
return primaryAdmin;
|
return primaryAdmin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザーを未認証にする
|
||||||
|
* @param context
|
||||||
|
* @param userId
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
private async updateUserUnverified(
|
||||||
|
context: Context,
|
||||||
|
userId: number,
|
||||||
|
): Promise<void> {
|
||||||
|
this.logger.log(
|
||||||
|
`[IN] [${context.getTrackingId()}] ${
|
||||||
|
this.updateUserUnverified.name
|
||||||
|
} | params: { userId: ${userId} };`,
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
await this.usersRepository.updateUserUnverified(context, userId);
|
||||||
|
this.logger.log(
|
||||||
|
`[${context.getTrackingId()}] update user unverified: ${userId}`,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`[${context.getTrackingId()}] error=${error}`);
|
||||||
|
this.logger.error(
|
||||||
|
`${MANUAL_RECOVERY_REQUIRED} [${context.getTrackingId()}] Failed to update user unverified: ${userId}`,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
this.logger.log(
|
||||||
|
`[OUT] [${context.getTrackingId()}] ${this.updateUserUnverified.name}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -399,7 +399,7 @@ export class UsersRepositoryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 管理ユーザーがメール認証済みなら認証情報を更新する
|
* ユーザーがメール認証済みなら認証情報を更新する
|
||||||
* @param user
|
* @param user
|
||||||
* @returns update
|
* @returns update
|
||||||
*/
|
*/
|
||||||
@ -437,6 +437,40 @@ export class UsersRepositoryService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザーをメール未認証にする
|
||||||
|
* @param user
|
||||||
|
* @param context
|
||||||
|
* @param id
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
async updateUserUnverified(context: Context, id: number): Promise<void> {
|
||||||
|
await this.dataSource.transaction(async (entityManager) => {
|
||||||
|
const userRepo = entityManager.getRepository(User);
|
||||||
|
const targetUser = await userRepo.findOne({
|
||||||
|
where: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 運用上ユーザがいないことはあり得ないが、プログラム上発生しうるのでエラーとして処理
|
||||||
|
if (!targetUser) {
|
||||||
|
throw new UserNotFoundError(`User not Found.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
targetUser.email_verified = false;
|
||||||
|
|
||||||
|
await updateEntity(
|
||||||
|
userRepo,
|
||||||
|
{ id: targetUser.id },
|
||||||
|
targetUser,
|
||||||
|
this.isCommentOut,
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emailを認証済みにして、トライアルライセンスを作成する
|
* Emailを認証済みにして、トライアルライセンスを作成する
|
||||||
* @param id
|
* @param id
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user