Merge branch 'develop' into release-ccb
This commit is contained in:
commit
97620881d3
@ -6,8 +6,50 @@ import { getTranslationID } from "translation";
|
||||
const Footer: React.FC = () => {
|
||||
const [t] = useTranslation();
|
||||
return (
|
||||
<footer className={`${styles.footer}`}>
|
||||
<div>{t(getTranslationID("common.label.copyRight"))}</div>
|
||||
<footer
|
||||
className={`${styles.footer}`}
|
||||
style={{
|
||||
padding: "0.5rem",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
marginBottom: "0.5rem",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{t(getTranslationID("common.label.copyRight"))}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: "1rem",
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="https://download.omsystem.com/pages/odms_download/odms_cloud_eula/eula/en/"
|
||||
target="_blank"
|
||||
className={styles.linkTx}
|
||||
style={{ color: "#999999" }}
|
||||
data-tag="open-eula"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{t(getTranslationID("common.label.eula"))}
|
||||
</a>
|
||||
<a
|
||||
href="https://download.omsystem.com/pages/odms_download/odms_cloud_eula/privacy_notice/en/"
|
||||
target="_blank"
|
||||
className={styles.linkTx}
|
||||
style={{ color: "#999999" }}
|
||||
data-tag="open-privacy-notice"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{t(getTranslationID("common.label.privacyNotice"))}
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -173,7 +173,7 @@ const AccountPage: React.FC = (): JSX.Element => {
|
||||
changeDealer({
|
||||
parentAccountId:
|
||||
dealers.find(
|
||||
(x) => x.name === event.target.value
|
||||
(x) => x.id === Number(event.target.value)
|
||||
)?.id || undefined,
|
||||
})
|
||||
);
|
||||
@ -190,7 +190,7 @@ const AccountPage: React.FC = (): JSX.Element => {
|
||||
)} --`}
|
||||
</option>
|
||||
{dealers.map((x) => (
|
||||
<option key={x.name} value={x.name}>
|
||||
<option key={x.id} value={x.id}>
|
||||
{x.name}
|
||||
</option>
|
||||
))}
|
||||
|
||||
@ -4,6 +4,7 @@ import { UpdateTokenTimer } from "components/auth/updateTokenTimer";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getTranslationID } from "translation";
|
||||
import styles from "styles/app.module.scss";
|
||||
import Footer from "components/footer";
|
||||
|
||||
const SupportPage: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
@ -45,6 +46,7 @@ const SupportPage: React.FC = () => {
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -15,11 +15,8 @@ const UserVerifyPage: React.FC = (): JSX.Element => {
|
||||
const jwt = query.get("verify") ?? "";
|
||||
|
||||
useEffect(() => {
|
||||
if (!jwt) {
|
||||
navigate("/mail-confirm/failed");
|
||||
}
|
||||
dispatch(userVerifyAsync({ jwt }));
|
||||
}, [navigate, dispatch, jwt]);
|
||||
}, [dispatch, jwt]);
|
||||
|
||||
const verifyState = useSelector(VerifyStateSelector);
|
||||
|
||||
|
||||
@ -31,7 +31,9 @@
|
||||
"tier4": "Händler",
|
||||
"tier5": "Kunde",
|
||||
"notSelected": "Keine",
|
||||
"signOutButton": "Abmelden"
|
||||
"signOutButton": "Abmelden",
|
||||
"eula": "End User License Agreement",
|
||||
"privacyNotice": "Privacy Notice"
|
||||
}
|
||||
},
|
||||
"topPage": {
|
||||
@ -51,7 +53,7 @@
|
||||
"logoAlt": "OM Dictation Management System in the Cloud"
|
||||
},
|
||||
"text": {
|
||||
"maintenanceNotificationTitle": "(de)サービス停止のお知らせ",
|
||||
"maintenanceNotificationTitle": "Hinweis auf geplante Wartungsarbeiten",
|
||||
"maintenanceNotification": "Aufgrund von Systemwartungsarbeiten wird ODMS Cloud ab dem 12. Juni, 6:00 Uhr UTC-Zeit, etwa eine Stunde lang nicht verfügbar sein. Wir entschuldigen uns für etwaige Unannehmlichkeiten, die während der Wartung entstanden sind."
|
||||
}
|
||||
},
|
||||
|
||||
@ -31,7 +31,9 @@
|
||||
"tier4": "Dealer",
|
||||
"tier5": "Customer",
|
||||
"notSelected": "None",
|
||||
"signOutButton": "Sign out"
|
||||
"signOutButton": "Sign out",
|
||||
"eula": "End User License Agreement",
|
||||
"privacyNotice": "Privacy Notice"
|
||||
}
|
||||
},
|
||||
"topPage": {
|
||||
@ -51,7 +53,7 @@
|
||||
"logoAlt": "OM Dictation Management System in the Cloud"
|
||||
},
|
||||
"text": {
|
||||
"maintenanceNotificationTitle": "サービス停止のお知らせ",
|
||||
"maintenanceNotificationTitle": "Notice of scheduled maintenance",
|
||||
"maintenanceNotification": "Due to system maintenance, ODMS Cloud will be unavailable for approximately one hour starting from June 12th, 6:00AM UTC time. We apologize for any inconvenience caused during the maintenance."
|
||||
}
|
||||
},
|
||||
|
||||
@ -31,7 +31,9 @@
|
||||
"tier4": "Distribuidor",
|
||||
"tier5": "Cliente",
|
||||
"notSelected": "Ninguno",
|
||||
"signOutButton": "cerrar sesión"
|
||||
"signOutButton": "cerrar sesión",
|
||||
"eula": "End User License Agreement",
|
||||
"privacyNotice": "Privacy Notice"
|
||||
}
|
||||
},
|
||||
"topPage": {
|
||||
@ -51,7 +53,7 @@
|
||||
"logoAlt": "OM Dictation Management System in the Cloud"
|
||||
},
|
||||
"text": {
|
||||
"maintenanceNotificationTitle": "(es)サービス停止のお知らせ",
|
||||
"maintenanceNotificationTitle": "Aviso de mantenimiento programado",
|
||||
"maintenanceNotification": "Debido al mantenimiento del sistema, ODMS Cloud no estará disponible durante aproximadamente una hora a partir del 12 de junio a las 6:00 am, hora UTC. Pedimos disculpas por cualquier inconveniente causado durante el mantenimiento."
|
||||
}
|
||||
},
|
||||
|
||||
@ -31,7 +31,9 @@
|
||||
"tier4": "Revendeur",
|
||||
"tier5": "Client",
|
||||
"notSelected": "Aucune",
|
||||
"signOutButton": "se déconnecter"
|
||||
"signOutButton": "se déconnecter",
|
||||
"eula": "End User License Agreement",
|
||||
"privacyNotice": "Privacy Notice"
|
||||
}
|
||||
},
|
||||
"topPage": {
|
||||
@ -51,7 +53,7 @@
|
||||
"logoAlt": "OM Dictation Management System in the Cloud"
|
||||
},
|
||||
"text": {
|
||||
"maintenanceNotificationTitle": "(fr)サービス停止のお知らせ",
|
||||
"maintenanceNotificationTitle": "Avis de maintenance programmée",
|
||||
"maintenanceNotification": "En raison de la maintenance du système, ODMS Cloud sera indisponible pendant environ une heure à partir du 12 juin à 6h00, heure UTC. Nous nous excusons pour tout inconvénient causé lors de la maintenance."
|
||||
}
|
||||
},
|
||||
|
||||
@ -35,6 +35,11 @@ export const overrideAdB2cService = <TService>(
|
||||
externalIds: string[],
|
||||
) => Promise<AdB2cUser[]>;
|
||||
getUser?: (context: Context, externalId: string) => Promise<AdB2cUser>;
|
||||
changePassword?: (
|
||||
context: Context,
|
||||
externalId: string,
|
||||
password: string,
|
||||
) => Promise<void>;
|
||||
},
|
||||
): void => {
|
||||
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
|
||||
@ -69,6 +74,12 @@ export const overrideAdB2cService = <TService>(
|
||||
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: {
|
||||
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メンバ変数の参照を取得
|
||||
@ -138,6 +151,18 @@ export const overrideUsersRepositoryService = <TService>(
|
||||
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,6 +8,7 @@ import {
|
||||
DeletePartnerAccountRequest,
|
||||
GetPartnerUsersRequest,
|
||||
UpdatePartnerInfoRequest,
|
||||
CreateWorktypesRequest,
|
||||
} from './types/types';
|
||||
import { plainToClass } from 'class-transformer';
|
||||
import { validate } from 'class-validator';
|
||||
@ -41,6 +42,88 @@ describe('AccountsController', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
|
||||
describe('valdation CreateWorktypesRequest', () => {
|
||||
it('最低限の有効なリクエストが成功する', async () => {
|
||||
const request = new CreateWorktypesRequest();
|
||||
request.worktypeId = 'TEST';
|
||||
|
||||
const valdationObject = plainToClass(CreateWorktypesRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(0);
|
||||
});
|
||||
it('worktypeIdが指定されていない場合、リクエストが失敗する', async () => {
|
||||
const request = new CreateWorktypesRequest();
|
||||
|
||||
const valdationObject = plainToClass(CreateWorktypesRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(1);
|
||||
});
|
||||
it('worktypeIdが空文字の場合、リクエストが失敗する', async () => {
|
||||
const request = new CreateWorktypesRequest();
|
||||
request.worktypeId = '';
|
||||
|
||||
const valdationObject = plainToClass(CreateWorktypesRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(1);
|
||||
});
|
||||
it('worktypeIdが16文字を超える場合、リクエストが失敗する', async () => {
|
||||
const request = new CreateWorktypesRequest();
|
||||
request.worktypeId = '123456789A1234567';
|
||||
|
||||
const valdationObject = plainToClass(CreateWorktypesRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(1);
|
||||
});
|
||||
it('worktypeIdが16文字の場合、リクエストが成功する', async () => {
|
||||
const request = new CreateWorktypesRequest();
|
||||
request.worktypeId = '123456789A123456';
|
||||
|
||||
const valdationObject = plainToClass(CreateWorktypesRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(0);
|
||||
});
|
||||
it('worktypeIdに使用不可文字が含まれる場合、リクエストが失敗する', async () => {
|
||||
const request = new CreateWorktypesRequest();
|
||||
request.worktypeId = 'test.test';
|
||||
|
||||
const valdationObject = plainToClass(CreateWorktypesRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(1);
|
||||
});
|
||||
it('descriptionが255文字を超える場合、リクエストが失敗する', async () => {
|
||||
const request = new CreateWorktypesRequest();
|
||||
request.worktypeId = 'TEST';
|
||||
request.description =
|
||||
'1234567A0A1234567A1A1234567A2A1234567A3A1234567A4A1234567A5A1234567A6A1234567A7A1234567A8A1234567A9A' +
|
||||
'1234567B0B1234567B1B1234567B2B1234567B3B1234567B4B1234567B5B1234567B6B1234567B7B1234567B8B1234567B9B' +
|
||||
'1234567A0A1234567A1A1234567A2A1234567A3A1234567A4A123456';
|
||||
|
||||
const valdationObject = plainToClass(CreateWorktypesRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(1);
|
||||
});
|
||||
it('descriptionが255文字の場合、リクエストが成功する', async () => {
|
||||
const request = new CreateWorktypesRequest();
|
||||
request.worktypeId = 'TEST';
|
||||
request.description =
|
||||
'1234567A0A1234567A1A1234567A2A1234567A3A1234567A4A1234567A5A1234567A6A1234567A7A1234567A8A1234567A9A' +
|
||||
'1234567B0B1234567B1B1234567B2B1234567B3B1234567B4B1234567B5B1234567B6B1234567B7B1234567B8B1234567B9B' +
|
||||
'1234567A0A1234567A1A1234567A2A1234567A3A1234567A4A12345';
|
||||
|
||||
const valdationObject = plainToClass(CreateWorktypesRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('valdation switchParentRequest', () => {
|
||||
it('最低限の有効なリクエストが成功する', async () => {
|
||||
const request = new SwitchParentRequest();
|
||||
|
||||
@ -2,6 +2,9 @@ import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { NotificationController } from './notification.controller';
|
||||
import { NotificationService } from './notification.service';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { RegisterRequest } from './types/types';
|
||||
import { plainToClass } from 'class-transformer';
|
||||
import { validate } from 'class-validator';
|
||||
|
||||
describe('NotificationController', () => {
|
||||
let controller: NotificationController;
|
||||
@ -28,3 +31,57 @@ describe('NotificationController', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('valdation register', () => {
|
||||
it('有効なリクエストが成功する(wns)', async () => {
|
||||
const request = new RegisterRequest();
|
||||
request.pns = 'wns';
|
||||
request.handler = 'test';
|
||||
|
||||
const valdationObject = plainToClass(RegisterRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(0);
|
||||
});
|
||||
it('有効なリクエストが成功する(apn)', async () => {
|
||||
const request = new RegisterRequest();
|
||||
request.pns = 'apns';
|
||||
request.handler = 'test';
|
||||
|
||||
const valdationObject = plainToClass(RegisterRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(0);
|
||||
});
|
||||
it('pnsが不正(wns or apns以外)な場合、リクエストが失敗する', async () => {
|
||||
const request = new RegisterRequest();
|
||||
request.pns = 'invalid';
|
||||
request.handler = 'test';
|
||||
|
||||
const valdationObject = plainToClass(RegisterRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(1);
|
||||
});
|
||||
it('handlerが空文字の場合、リクエストが失敗する', async () => {
|
||||
const request = new RegisterRequest();
|
||||
request.pns = 'apns';
|
||||
request.handler = '';
|
||||
|
||||
const valdationObject = plainToClass(RegisterRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(1);
|
||||
});
|
||||
it('handlerが文字列でない場合、リクエストが失敗する', async () => {
|
||||
const request = {
|
||||
pns: 'apns',
|
||||
handler: 123,
|
||||
};
|
||||
|
||||
const valdationObject = plainToClass(RegisterRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
@ -8,7 +8,6 @@ import {
|
||||
makeDefaultUsersRepositoryMockValue,
|
||||
makeUsersServiceMock,
|
||||
} from './test/users.service.mock';
|
||||
import { EmailAlreadyVerifiedError } from '../../repositories/users/errors/types';
|
||||
import {
|
||||
createLicense,
|
||||
createUserGroup,
|
||||
@ -22,6 +21,7 @@ import {
|
||||
LICENSE_ALLOCATED_STATUS,
|
||||
LICENSE_EXPIRATION_THRESHOLD_DAYS,
|
||||
LICENSE_TYPE,
|
||||
MANUAL_RECOVERY_REQUIRED,
|
||||
TASK_STATUS,
|
||||
USER_AUDIO_FORMAT,
|
||||
USER_LICENSE_EXPIRY_STATUS,
|
||||
@ -59,6 +59,7 @@ import { createTask } from '../files/test/utility';
|
||||
import { createCheckoutPermissions } from '../tasks/test/utility';
|
||||
import { MultipleImportErrors } from './types/types';
|
||||
import { TestLogger } from '../../common/test/logger';
|
||||
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
|
||||
|
||||
describe('UsersService.confirmUser', () => {
|
||||
let source: DataSource | null = null;
|
||||
@ -254,216 +255,564 @@ describe('UsersService.confirmUser', () => {
|
||||
});
|
||||
|
||||
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 () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
usersRepositoryMockValue.findUserById = {
|
||||
id: 1,
|
||||
external_id: 'TEST9999',
|
||||
account_id: 1,
|
||||
role: 'None',
|
||||
accepted_eula_version: 'string',
|
||||
accepted_privacy_notice_version: 'string',
|
||||
accepted_dpa_version: 'string',
|
||||
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,
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
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',
|
||||
};
|
||||
},
|
||||
});
|
||||
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 =
|
||||
'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(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
token,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('トークンの形式が不正な場合、形式不正エラーとなる。(メール認証API)', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
usersRepositoryMockValue.findUserById = {
|
||||
id: 1,
|
||||
external_id: 'TEST9999',
|
||||
account_id: 1,
|
||||
role: 'None',
|
||||
accepted_eula_version: 'string',
|
||||
accepted_privacy_notice_version: 'string',
|
||||
accepted_dpa_version: 'string',
|
||||
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 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),
|
||||
);
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E000101'));
|
||||
} 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 () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
usersRepositoryMockValue.findUserById = {
|
||||
id: 1,
|
||||
external_id: 'TEST9999',
|
||||
account_id: 1,
|
||||
role: 'None',
|
||||
accepted_eula_version: 'string',
|
||||
accepted_privacy_notice_version: 'string',
|
||||
accepted_dpa_version: 'string',
|
||||
email_verified: true,
|
||||
created_by: 'string;',
|
||||
created_at: new Date(),
|
||||
updated_by: 'string;',
|
||||
updated_at: new Date(),
|
||||
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,
|
||||
notification: true,
|
||||
encryption: false,
|
||||
encryption_password: undefined,
|
||||
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();
|
||||
usersRepositoryMockValue.updateUserVerified = new EmailAlreadyVerifiedError(
|
||||
`Email already verified user`,
|
||||
);
|
||||
email_verified: true, // emailを認証済みにする
|
||||
});
|
||||
|
||||
overrideAdB2cService(service, {
|
||||
changePassword: jest.fn(),
|
||||
getUser: async () => {
|
||||
return {
|
||||
id: adminExternalId,
|
||||
displayName: 'admin',
|
||||
};
|
||||
},
|
||||
});
|
||||
overrideSendgridService(service, {
|
||||
sendMail: jest.fn(),
|
||||
});
|
||||
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendGridMockValue,
|
||||
configMockValue,
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||
await expect(
|
||||
service.confirmUserAndInitPassword(
|
||||
|
||||
try {
|
||||
await service.confirmUserAndInitPassword(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
token,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010202'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
} catch (e) {
|
||||
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 () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
usersRepositoryMockValue.findUserById = {
|
||||
id: 1,
|
||||
external_id: 'TEST9999',
|
||||
account_id: 1,
|
||||
role: 'None',
|
||||
accepted_eula_version: 'string',
|
||||
accepted_privacy_notice_version: 'string',
|
||||
accepted_dpa_version: 'string',
|
||||
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 sendGridMockValue = makeDefaultSendGridlValue();
|
||||
usersRepositoryMockValue.updateUserVerified = new Error('DB error');
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
const sortCriteriaRepositoryMockValue =
|
||||
makeDefaultSortCriteriaRepositoryMockValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendGridMockValue,
|
||||
configMockValue,
|
||||
sortCriteriaRepositoryMockValue,
|
||||
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(),
|
||||
});
|
||||
// DBエラーを発生させる
|
||||
overrideUsersRepositoryService(service, {
|
||||
updateUserVerified: async () => {
|
||||
throw new Error('DB Error');
|
||||
},
|
||||
});
|
||||
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||
await expect(
|
||||
service.confirmUserAndInitPassword(
|
||||
try {
|
||||
await service.confirmUserAndInitPassword(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
token,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
),
|
||||
);
|
||||
} 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);
|
||||
// パスワードが変更されていないことを確認
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
// メール送信処理
|
||||
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) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
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 {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${
|
||||
@ -1791,4 +1811,36 @@ export class UsersService {
|
||||
|
||||
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
|
||||
* @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を認証済みにして、トライアルライセンスを作成する
|
||||
* @param id
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user