saito.k 0e68f26c57 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を使うテストに置き換え
    - 結果は変えずに通ることを確認
  - テストケースを追加し、新たな観点でテストを作成

## 補足
- 相談、参考資料などがあれば
2024-05-30 00:18:59 +00:00

305 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Context } from '../log';
import {
AdB2cService,
ConflictError,
} from '../../gateways/adb2c/adb2c.service';
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
import { User, newUser } from '../../repositories/users/entity/user.entity';
import { UsersRepositoryService } from '../../repositories/users/users.repository.service';
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service';
import { Account } from '../../repositories/accounts/entity/account.entity';
import { AdB2cUser } from '../../gateways/adb2c/types/types';
// ### ユニットテスト用コード以外では絶対に使用してはいけないダーティな手段を使用しているが、他の箇所では使用しないこと ###
/**
* adB2cServiceのモックを作成して、TServiceが依存するサービス(adB2cService)の参照を上書きする
* ※ serviceに指定するオブジェクトは`adB2cService: AdB2cService`メンバ変数を持つ必要がある
* @param service 上書きしたいTService
* @param overrides adB2cServiceの各種メソッドのモックが返す値省略した場合は既定のダミーの値
*/
export const overrideAdB2cService = <TService>(
service: TService,
overrides: {
createUser?: (
context: Context,
email: string,
password: string,
username: string,
) => Promise<{ sub: string } | ConflictError>;
deleteUser?: (externalId: string, context: Context) => Promise<void>;
deleteUsers?: (externalIds: string[], context: Context) => Promise<void>;
getUsers?: (
context: Context,
externalIds: string[],
) => Promise<AdB2cUser[]>;
getUser?: (context: Context, externalId: string) => Promise<AdB2cUser>;
changePassword?: (
context: Context,
externalId: string,
password: string,
) => Promise<void>;
},
): void => {
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
const obj = (service as any).adB2cService as AdB2cService;
if (overrides.createUser) {
Object.defineProperty(obj, obj.createUser.name, {
value: overrides.createUser,
writable: true,
});
}
if (overrides.deleteUser) {
Object.defineProperty(obj, obj.deleteUser.name, {
value: overrides.deleteUser,
writable: true,
});
}
if (overrides.deleteUsers) {
Object.defineProperty(obj, obj.deleteUsers.name, {
value: overrides.deleteUsers,
writable: true,
});
}
if (overrides.getUsers) {
Object.defineProperty(obj, obj.getUsers.name, {
value: overrides.getUsers,
writable: true,
});
}
if (overrides.getUser) {
Object.defineProperty(obj, obj.getUser.name, {
value: overrides.getUser,
writable: true,
});
}
if (overrides.changePassword) {
Object.defineProperty(obj, obj.changePassword.name, {
value: overrides.changePassword,
writable: true,
});
}
};
/**
* sendgridServiceのモックを作成して、TServiceが依存するサービス(sendgridService)の参照を上書きする
* ※ serviceに指定するオブジェクトは`sendgridService: SendgridService`メンバ変数を持つ必要がある
* @param service 上書きしたいTService
* @param overrides sendgridServiceの各種メソッドのモックが返す値省略した場合は既定のダミーの値
*/
export const overrideSendgridService = <TService>(
service: TService,
overrides: {
sendMail?: (
context: Context,
to: string[],
cc: string[],
from: string,
subject: string,
text: string,
html: string,
) => Promise<void>;
},
): void => {
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
const obj = (service as any).sendgridService as SendGridService;
if (overrides.sendMail) {
Object.defineProperty(obj, obj.sendMail.name, {
value: overrides.sendMail,
writable: true,
});
} else {
// [重要]
// sendMailだけは外部に対する送信を行ってしまう & 失敗によりメールアドレス自体の信頼度が変動してしまうため、
// overrideした場合には"必ず"偽物の呼び出しになるようにしておく
Object.defineProperty(obj, obj.sendMail.name, {
value: async () => {
return;
},
writable: true,
});
}
};
/**
* usersRepositoryのモックを作成して、TServiceが依存するサービス(usersRepositoryService)の参照を上書きする
* ※ serviceに指定するオブジェクトは`usersRepository: UsersRepositoryService`メンバ変数を持つ必要がある
* @param service 上書きしたいTService
* @param overrides usersRepositoryの各種メソッドのモックが返す値省略した場合は既定のダミーの値
*/
export const overrideUsersRepositoryService = <TService>(
service: TService,
overrides: {
createNormalUser?: (user: newUser) => Promise<User>;
deleteNormalUser?: (userId: number) => Promise<void>;
updateUserVerified?: (context: Context, userId: number) => Promise<void>;
updateUserUnverified?: (context: Context, userId: number) => Promise<void>;
},
): void => {
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
const obj = (service as any).usersRepository as UsersRepositoryService;
if (overrides.createNormalUser) {
Object.defineProperty(obj, obj.createNormalUser.name, {
value: overrides.createNormalUser,
writable: true,
});
}
if (overrides.deleteNormalUser) {
Object.defineProperty(obj, obj.deleteNormalUser.name, {
value: overrides.deleteNormalUser,
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,
});
}
};
/**
* blobStorageServiceのモックを作成して、TServiceが依存するサービス(blobStorageService)の参照を上書きする
* ※ serviceに指定するオブジェクトは`blobstorageService: blobStorageService`メンバ変数を持つ必要がある
* @param service 上書きしたいTService
* @param overrides blobStorageServiceの各種メソッドのモックが返す値省略した場合は既定のダミーの値
*/
export const overrideBlobstorageService = <TService>(
service: TService,
overrides: {
createContainer?: (
context: Context,
accountId: number,
country: string,
) => Promise<void>;
deleteContainer?: (
context: Context,
accountId: number,
country: string,
) => Promise<void>;
deleteFile?: (
context: Context,
accountId: number,
country: string,
fileName: string,
) => Promise<void>;
containerExists?: (
context: Context,
accountId: number,
country: string,
) => Promise<boolean>;
publishUploadSas?: (
context: Context,
accountId: number,
country: string,
) => Promise<string>;
publishTemplateUploadSas?: (
context: Context,
accountId: number,
country: string,
) => Promise<string>;
uploadImportsBlob?: (
context: Context,
fileName: string,
content: string,
) => Promise<void>;
},
): void => {
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
const obj = (service as any).blobStorageService as BlobstorageService;
if (overrides.createContainer) {
Object.defineProperty(obj, obj.createContainer.name, {
value: overrides.createContainer,
writable: true,
});
}
if (overrides.deleteContainer) {
Object.defineProperty(obj, obj.deleteContainer.name, {
value: overrides.deleteContainer,
writable: true,
});
}
if (overrides.deleteFile) {
Object.defineProperty(obj, obj.deleteFile.name, {
value: overrides.deleteFile,
writable: true,
});
}
if (overrides.containerExists) {
Object.defineProperty(obj, obj.containerExists.name, {
value: overrides.containerExists,
writable: true,
});
}
if (overrides.publishUploadSas) {
Object.defineProperty(obj, obj.publishUploadSas.name, {
value: overrides.publishUploadSas,
writable: true,
});
}
if (overrides.publishTemplateUploadSas) {
Object.defineProperty(obj, obj.publishTemplateUploadSas.name, {
value: overrides.publishTemplateUploadSas,
writable: true,
});
}
if (overrides.uploadImportsBlob) {
Object.defineProperty(obj, obj.uploadImportsBlob.name, {
value: overrides.uploadImportsBlob,
writable: true,
});
}
};
/**
* accountsRepositoryのモックを作成して、TServiceが依存するサービス(AccountsRepositoryService)の参照を上書きする
* ※ serviceに指定するオブジェクトは`accountsRepository: AccountsRepositoryService`メンバ変数を持つ必要がある
* @param service 上書きしたいTService
* @param overrides accountsRepositoryの各種メソッドのモックが返す値省略した場合は本物のメソッドが呼ばれる
*/
export const overrideAccountsRepositoryService = <TService>(
service: TService,
overrides: {
createAccount?: (
companyName: string,
country: string,
dealerAccountId: number | undefined,
tier: number,
adminExternalUserId: string,
adminUserRole: string,
adminUserAcceptedEulaVersion: string,
adminUserAcceptedDpaVersion: string,
) => Promise<{ newAccount: Account; adminUser: User }>;
deleteAccount?: (accountId: number, userId: number) => Promise<void>;
deleteAccountAndInsertArchives?: (accountId: number) => Promise<User[]>;
},
): void => {
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
const obj = (service as any).accountRepository as AccountsRepositoryService;
if (overrides.deleteAccount) {
Object.defineProperty(obj, obj.deleteAccount.name, {
value: overrides.deleteAccount,
writable: true,
});
}
if (overrides.createAccount) {
Object.defineProperty(obj, obj.createAccount.name, {
value: overrides.createAccount,
writable: true,
});
}
if (overrides.deleteAccountAndInsertArchives) {
Object.defineProperty(obj, obj.deleteAccountAndInsertArchives.name, {
value: overrides.deleteAccountAndInsertArchives,
writable: true,
});
}
};