Merged PR 647: ユーザー認証完了のお願い [U-114] の実装

## 概要
[Task3310: ユーザー認証完了のお願い [U-114] の実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3310)

- ユーザー追加後の認証メール送信について正規のメール文面を出すようにしました。

## レビューポイント
- メールに渡す情報の取得内容で不自然な点はないでしょうか?
- メール送信処理に失敗した場合には仮登録したユーザーを削除する処理をそのままにしていますが問題ないでしょうか?

## UIの変更
- なし

## 動作確認状況
- ローカルで確認
This commit is contained in:
makabe.t 2023-12-22 08:23:01 +00:00
parent f4da949bfd
commit 2f2e401ae5
7 changed files with 272 additions and 143 deletions

View File

@ -34,6 +34,7 @@ export const overrideAdB2cService = <TService>(
context: Context, context: Context,
externalIds: string[], externalIds: string[],
) => Promise<AdB2cUser[]>; ) => Promise<AdB2cUser[]>;
getUser?: (context: Context, externalId: string) => Promise<AdB2cUser>;
}, },
): void => { ): void => {
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得 // テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
@ -62,6 +63,12 @@ export const overrideAdB2cService = <TService>(
writable: true, writable: true,
}); });
} }
if (overrides.getUser) {
Object.defineProperty(obj, obj.getUser.name, {
value: overrides.getUser,
writable: true,
});
}
}; };
/** /**

View File

@ -464,19 +464,12 @@ describe('UsersService.createUser', () => {
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( const { account } = await makeTestAccount(
source, source,
{}, {},
{ external_id: adminExternalId }, { external_id: adminExternalId },
); );
const { id: accountId, tier } = account; const { id: accountId } = account;
const { role: adminRole } = admin;
const token: AccessToken = {
userId: adminExternalId,
role: adminRole,
tier: tier,
};
const name = 'test_user1'; const name = 'test_user1';
const role = USER_ROLES.NONE; const role = USER_ROLES.NONE;
@ -500,6 +493,12 @@ describe('UsersService.createUser', () => {
return { sub: externalId }; return { sub: externalId };
}, },
getUser: async () => {
return {
id: adminExternalId,
displayName: 'admin',
};
},
}); });
overrideSendgridService(service, { overrideSendgridService(service, {
sendMail: async () => { sendMail: async () => {
@ -548,19 +547,12 @@ describe('UsersService.createUser', () => {
if (!module) fail(); if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( const { account } = await makeTestAccount(
source, source,
{}, {},
{ external_id: adminExternalId }, { external_id: adminExternalId },
); );
const { id: accountId, tier } = account; const { id: accountId } = account;
const { role: adminRole } = admin;
const token: AccessToken = {
userId: adminExternalId,
role: adminRole,
tier: tier,
};
const name = 'test_user2'; const name = 'test_user2';
const role = USER_ROLES.AUTHOR; const role = USER_ROLES.AUTHOR;
@ -588,6 +580,12 @@ describe('UsersService.createUser', () => {
return { sub: externalId }; return { sub: externalId };
}, },
getUser: async () => {
return {
id: adminExternalId,
displayName: 'admin',
};
},
}); });
overrideSendgridService(service, { overrideSendgridService(service, {
sendMail: async () => { sendMail: async () => {
@ -640,19 +638,12 @@ describe('UsersService.createUser', () => {
if (!module) fail(); if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( const { account } = await makeTestAccount(
source, source,
{}, {},
{ external_id: adminExternalId }, { external_id: adminExternalId },
); );
const { id: accountId, tier } = account; const { id: accountId } = account;
const { role: adminRole } = admin;
const token: AccessToken = {
userId: adminExternalId,
role: adminRole,
tier: tier,
};
const name = 'test_user2'; const name = 'test_user2';
const role = USER_ROLES.AUTHOR; const role = USER_ROLES.AUTHOR;
@ -679,6 +670,12 @@ describe('UsersService.createUser', () => {
return { sub: externalId }; return { sub: externalId };
}, },
getUser: async () => {
return {
id: adminExternalId,
displayName: 'admin',
};
},
}); });
overrideSendgridService(service, { overrideSendgridService(service, {
sendMail: async () => { sendMail: async () => {
@ -731,19 +728,12 @@ describe('UsersService.createUser', () => {
if (!module) fail(); if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( const { account } = await makeTestAccount(
source, source,
{}, {},
{ external_id: adminExternalId }, { external_id: adminExternalId },
); );
const { id: accountId, tier } = account; const { id: accountId } = account;
const { role: adminRole } = admin;
const token: AccessToken = {
userId: adminExternalId,
role: adminRole,
tier: tier,
};
const name = 'test_user3'; const name = 'test_user3';
const role = USER_ROLES.TYPIST; const role = USER_ROLES.TYPIST;
@ -767,6 +757,12 @@ describe('UsersService.createUser', () => {
return { sub: externalId }; return { sub: externalId };
}, },
getUser: async () => {
return {
id: adminExternalId,
displayName: 'admin',
};
},
}); });
overrideSendgridService(service, { overrideSendgridService(service, {
sendMail: async () => { sendMail: async () => {
@ -816,19 +812,7 @@ describe('UsersService.createUser', () => {
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const b2cService = module.get<AdB2cService>(AdB2cService); const b2cService = module.get<AdB2cService>(AdB2cService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( await makeTestAccount(source, {}, { external_id: adminExternalId });
source,
{},
{ external_id: adminExternalId },
);
const { tier } = account;
const { role: adminRole } = admin;
const token: AccessToken = {
userId: adminExternalId,
role: adminRole,
tier: tier,
};
const name = 'test_user1'; const name = 'test_user1';
const role = USER_ROLES.NONE; const role = USER_ROLES.NONE;
@ -853,6 +837,12 @@ describe('UsersService.createUser', () => {
return { sub: externalId }; return { sub: externalId };
}, },
deleteUser: jest.fn(), deleteUser: jest.fn(),
getUser: async () => {
return {
id: adminExternalId,
displayName: 'admin',
};
},
}); });
overrideSendgridService(service, { overrideSendgridService(service, {
sendMail: async () => { sendMail: async () => {
@ -903,19 +893,12 @@ describe('UsersService.createUser', () => {
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const b2cService = module.get<AdB2cService>(AdB2cService); const b2cService = module.get<AdB2cService>(AdB2cService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( const { account } = await makeTestAccount(
source, source,
{}, {},
{ external_id: adminExternalId }, { external_id: adminExternalId },
); );
const { id: accountId, tier } = account; const { id: accountId } = account;
const { role: adminRole } = admin;
const token: AccessToken = {
userId: adminExternalId,
role: adminRole,
tier: tier,
};
const name = 'test_user1'; const name = 'test_user1';
const role = USER_ROLES.NONE; const role = USER_ROLES.NONE;
@ -940,6 +923,12 @@ describe('UsersService.createUser', () => {
return { sub: externalId }; return { sub: externalId };
}, },
deleteUser: jest.fn().mockRejectedValue(new Error('ADB2C error')), deleteUser: jest.fn().mockRejectedValue(new Error('ADB2C error')),
getUser: async () => {
return {
id: adminExternalId,
displayName: 'admin',
};
},
}); });
overrideSendgridService(service, { overrideSendgridService(service, {
sendMail: async () => { sendMail: async () => {
@ -994,19 +983,7 @@ describe('UsersService.createUser', () => {
if (!module) fail(); if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( await makeTestAccount(source, {}, { external_id: adminExternalId });
source,
{},
{ external_id: adminExternalId },
);
const { tier } = account;
const { role: adminRole } = admin;
const token: AccessToken = {
userId: adminExternalId,
role: adminRole,
tier: tier,
};
const name = 'test_user1'; const name = 'test_user1';
const role = USER_ROLES.NONE; const role = USER_ROLES.NONE;
@ -1069,19 +1046,7 @@ describe('UsersService.createUser', () => {
if (!module) fail(); if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( await makeTestAccount(source, {}, { external_id: adminExternalId });
source,
{},
{ external_id: adminExternalId },
);
const { tier } = account;
const { role: adminRole } = admin;
const token: AccessToken = {
userId: adminExternalId,
role: adminRole,
tier: tier,
};
const name = 'test_user1'; const name = 'test_user1';
const role = USER_ROLES.NONE; const role = USER_ROLES.NONE;
@ -1148,19 +1113,7 @@ describe('UsersService.createUser', () => {
if (!module) fail(); if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( await makeTestAccount(source, {}, { external_id: adminExternalId });
source,
{},
{ external_id: adminExternalId },
);
const { tier } = account;
const { role: adminRole } = admin;
const token: AccessToken = {
userId: adminExternalId,
role: adminRole,
tier: tier,
};
const name = 'test_user2'; const name = 'test_user2';
const role = USER_ROLES.AUTHOR; const role = USER_ROLES.AUTHOR;
@ -1188,6 +1141,12 @@ describe('UsersService.createUser', () => {
return { sub: externalId_1 }; return { sub: externalId_1 };
}, },
getUser: async () => {
return {
id: adminExternalId,
displayName: 'admin',
};
},
}); });
overrideSendgridService(service, { overrideSendgridService(service, {
sendMail: async () => { sendMail: async () => {
@ -1277,19 +1236,12 @@ describe('UsersService.createUser', () => {
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const b2cService = module.get<AdB2cService>(AdB2cService); const b2cService = module.get<AdB2cService>(AdB2cService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( const { account } = await makeTestAccount(
source, source,
{}, {},
{ external_id: adminExternalId }, { external_id: adminExternalId },
); );
const { id: accountId, tier } = account; const { id: accountId } = account;
const { role: adminRole } = admin;
const token: AccessToken = {
userId: adminExternalId,
role: adminRole,
tier: tier,
};
const name = 'test_user2'; const name = 'test_user2';
const role = USER_ROLES.AUTHOR; const role = USER_ROLES.AUTHOR;
@ -1377,19 +1329,12 @@ describe('UsersService.createUser', () => {
const b2cService = module.get<AdB2cService>(AdB2cService); const b2cService = module.get<AdB2cService>(AdB2cService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( const { account } = await makeTestAccount(
source, source,
{}, {},
{ external_id: adminExternalId }, { external_id: adminExternalId },
); );
const { id: accountId, tier } = account; const { id: accountId } = account;
const { role: adminRole } = admin;
const token: AccessToken = {
userId: adminExternalId,
role: adminRole,
tier: tier,
};
const name = 'test_user1'; const name = 'test_user1';
const role = USER_ROLES.NONE; const role = USER_ROLES.NONE;
@ -1464,19 +1409,7 @@ describe('UsersService.createUser', () => {
const b2cService = module.get<AdB2cService>(AdB2cService); const b2cService = module.get<AdB2cService>(AdB2cService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( await makeTestAccount(source, {}, { external_id: adminExternalId });
source,
{},
{ external_id: adminExternalId },
);
const { tier } = account;
const { role: adminRole } = admin;
const token: AccessToken = {
userId: adminExternalId,
role: adminRole,
tier: tier,
};
const name = 'test_user1'; const name = 'test_user1';
const role = USER_ROLES.NONE; const role = USER_ROLES.NONE;

View File

@ -51,6 +51,7 @@ import {
import { AccountNotFoundError } from '../../repositories/accounts/errors/types'; import { AccountNotFoundError } from '../../repositories/accounts/errors/types';
import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils'; import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service'; import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service';
import { Account } from '../../repositories/accounts/entity/account.entity';
@Injectable() @Injectable()
export class UsersService { export class UsersService {
@ -187,6 +188,7 @@ export class UsersService {
); );
//DBよりアクセス者の所属するアカウントIDを取得する //DBよりアクセス者の所属するアカウントIDを取得する
let adminUser: EntityUser; let adminUser: EntityUser;
let account: Account | null;
try { try {
adminUser = await this.usersRepository.findUserByExternalId( adminUser = await this.usersRepository.findUserByExternalId(
context, context,
@ -201,6 +203,7 @@ export class UsersService {
} }
const accountId = adminUser.account_id; const accountId = adminUser.account_id;
account = adminUser.account;
//authorIdが重複していないかチェックする //authorIdが重複していないかチェックする
if (authorId) { if (authorId) {
@ -305,24 +308,32 @@ export class UsersService {
//Email送信用のコンテンツを作成する //Email送信用のコンテンツを作成する
try { try {
// メールの内容を構成 if (account === null) {
const { subject, text, html } = throw new Error(`account is null. account_id=${accountId}`);
await this.sendgridService.createMailContentFromEmailConfirmForNormalUser( }
const { primary_admin_user_id: primaryAdminUserId } = account;
if (primaryAdminUserId === null) {
throw new Error(
`primary_admin_user_id is null. account_id=${accountId}`,
);
}
const { external_id: extarnalId } =
await this.usersRepository.findUserById(context, primaryAdminUserId);
const primaryAdmimAdb2cUser = await this.adB2cService.getUser(
context,
extarnalId,
);
const { displayName: primaryAdminUserName } = getUserNameAndMailAddress(
primaryAdmimAdb2cUser,
);
await this.sendgridService.sendMailWithU114(
context, context,
accountId, accountId,
newUser.id, newUser.id,
email, email,
); primaryAdminUserName,
//SendGridAPIを呼び出してメールを送信する
await this.sendgridService.sendMail(
context,
[email],
[],
this.mailFrom,
subject,
text,
html,
); );
} catch (e) { } catch (e) {
this.logger.error(`[${context.getTrackingId()}] error=${e}`); this.logger.error(`[${context.getTrackingId()}] error=${e}`);

View File

@ -18,6 +18,7 @@ import {
USER_EMAIL, USER_EMAIL,
USER_NAME, USER_NAME,
TYPIST_NAME, TYPIST_NAME,
VERIFY_LINK,
} from '../../templates/constants'; } from '../../templates/constants';
@Injectable() @Injectable()
@ -47,6 +48,8 @@ export class SendGridService {
// U-112のテンプレート差分親アカウントがない場合 // U-112のテンプレート差分親アカウントがない場合
private readonly templateU112NoParentHtml: string; private readonly templateU112NoParentHtml: string;
private readonly templateU112NoParentText: string; private readonly templateU112NoParentText: string;
private readonly templateU114Html: string;
private readonly templateU114Text: string;
private readonly templateU117Html: string; private readonly templateU117Html: string;
private readonly templateU117Text: string; private readonly templateU117Text: string;
@ -152,6 +155,15 @@ export class SendGridService {
'utf-8', 'utf-8',
); );
this.templateU114Html = readFileSync(
path.resolve(__dirname, `../../templates/template_U_114.html`),
'utf-8',
);
this.templateU114Text = readFileSync(
path.resolve(__dirname, `../../templates/template_U_114.txt`),
'utf-8',
);
this.templateU117Html = readFileSync( this.templateU117Html = readFileSync(
path.resolve(__dirname, `../../templates/template_U_117.html`), path.resolve(__dirname, `../../templates/template_U_117.html`),
'utf-8', 'utf-8',
@ -693,6 +705,67 @@ export class SendGridService {
} }
} }
/**
* U-114使
* @param context
* @param accountId ID
* @param userId ID
* @param userMail
* @param primaryAdminName (primary)
* @returns mail with u114
*/
async sendMailWithU114(
context: Context,
accountId: number,
userId: number,
userMail: string,
primaryAdminName: string,
): Promise<void> {
this.logger.log(
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU114.name}`,
);
try {
// ユーザー認証用のトークンを作成する
const privateKey = getPrivateKey(this.configService);
const token = sign<{ accountId: number; userId: number; email: string }>(
{
accountId,
userId,
email: userMail,
},
this.emailConfirmLifetime,
privateKey,
);
const path = 'mail-confirm/user/';
const verifyLink = `${this.appDomain}${path}?verify=${token}`;
const subject = 'User Registration Notification [U-114]';
// メールの本文を作成する
const html = this.templateU114Html
.replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName)
.replaceAll(VERIFY_LINK, verifyLink);
const text = this.templateU114Text
.replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName)
.replaceAll(VERIFY_LINK, verifyLink);
// メールを送信する
this.sendMail(
context,
[userMail],
[],
this.mailFrom,
subject,
text,
html,
);
} finally {
this.logger.log(
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU114.name}`,
);
}
}
/** /**
* U-117使 * U-117使
* @param context * @param context

View File

@ -6,6 +6,7 @@ export const TOP_URL = '$TOP_URL$';
export const PRIMARY_ADMIN_NAME = '$PRIMARY_ADMIN_NAME$'; export const PRIMARY_ADMIN_NAME = '$PRIMARY_ADMIN_NAME$';
export const USER_NAME = '$USER_NAME$'; export const USER_NAME = '$USER_NAME$';
export const USER_EMAIL = '$USER_EMAIL$'; export const USER_EMAIL = '$USER_EMAIL$';
export const VERIFY_LINK = '$VERIFY_LINK$';
export const AUTHOR_NAME = '$AUTHOR_NAME$'; export const AUTHOR_NAME = '$AUTHOR_NAME$';
export const FILE_NAME = '$FILE_NAME$'; export const FILE_NAME = '$FILE_NAME$';
export const TYPIST_NAME = '$TYPIST_NAME$'; export const TYPIST_NAME = '$TYPIST_NAME$';

View File

@ -0,0 +1,72 @@
<html>
<head>
<title>User Registration Notification [U-114]</title>
</head>
<body>
<div>
<h3>&lt;English&gt;</h3>
<p>
Your user information has been registered within the ODMS Cloud by your
administrator. To complete your user registration, you must verify your
email address. Please verify by clicking on the link below. Once the
registration is completed, a notification window will appear confirming
that your user information has been verified.
</p>
<p>URL: <a href="$VERIFY_LINK$">$VERIFY_LINK$</a></p>
<p>
If you need support regarding ODMS Cloud, please contact
$PRIMARY_ADMIN_NAME$.
</p>
<p>
If you have received this e-mail in error, please delete this e-mail
from your system.<br />
This is an automatically generated e-mail and this mailbox is not
monitored. Please do not reply.
</p>
</div>
<div>
<h3>&lt;Deutsch&gt;</h3>
<p>
Ihre Benutzerinformationen wurden von Ihrem Administrator in der ODMS
Cloud registriert. Um Ihre Benutzerregistrierung abzuschließen, müssen
Sie Ihre E-Mail-Adresse bestätigen. Bitte überprüfen Sie dies, indem Sie
auf den untenstehenden Link klicken. Sobald die Registrierung
abgeschlossen ist, erscheint ein Benachrichtigungsfenster, das
bestätigt, dass Ihre Benutzerinformationen überprüft wurden.
</p>
<p>URL: <a href="$VERIFY_LINK$">$VERIFY_LINK$</a></p>
<p>
Wenn Sie Unterstützung bezüglich ODMS Cloud benötigen, wenden Sie sich
bitte an $PRIMARY_ADMIN_NAME$.
</p>
<p>
Wenn Sie diese E-Mail fälschlicherweise erhalten haben, löschen Sie
diese E-Mail bitte aus Ihrem System.<br />
Dies ist eine automatisch generierte E-Mail und dieses Postfach wird
nicht überwacht. Bitte nicht antworten.
</p>
</div>
<div>
<h3>&lt;Français&gt;</h3>
<p>
Vos informations utilisateur ont été enregistrées dans le cloud ODMS par
votre administrateur. Pour finaliser votre inscription d'utilisateur,
vous devez vérifier votre adresse e-mail. Veuillez vérifier en cliquant
sur le lien ci-dessous. Une fois l'inscription terminée, une fenêtre de
notification apparaîtra confirmant que vos informations d'utilisateur
ont été vérifiées.
</p>
<p>URL: <a href="$VERIFY_LINK$">$VERIFY_LINK$</a></p>
<p>
Si vous avez besoin d'assistance concernant ODMS Cloud, veuillez
contacter $PRIMARY_ADMIN_NAME$.
</p>
<p>
Si vous avez reçu cet e-mail par erreur, veuillez supprimer cet e-mail
de votre système.<br />
Il s'agit d'un e-mail généré automatiquement et cette boîte aux lettres
n'est pas surveillée. Merci de ne pas répondre.
</p>
</div>
</body>
</html>

View File

@ -0,0 +1,32 @@
<h3>&lt;English&gt;</h3>
Your user information has been registered within the ODMS Cloud by your administrator. To complete your user registration, you must verify your email address. Please verify by clicking on the link below. Once the registration is completed, a notification window will appear confirming that your user information has been verified.
URL: $VERIFY_LINK$
If you need support regarding ODMS Cloud, please contact $PRIMARY_ADMIN_NAME$.
If you have received this e-mail in error, please delete this e-mail from your system.
This is an automatically generated e-mail and this mailbox is not monitored. Please do not reply.
<h3>&lt;Deutsch&gt;</h3>
Ihre Benutzerinformationen wurden von Ihrem Administrator in der ODMS Cloud registriert. Um Ihre Benutzerregistrierung abzuschließen, müssen Sie Ihre E-Mail-Adresse bestätigen. Bitte überprüfen Sie dies, indem Sie auf den untenstehenden Link klicken. Sobald die Registrierung abgeschlossen ist, erscheint ein Benachrichtigungsfenster, das bestätigt, dass Ihre Benutzerinformationen überprüft wurden.
URL: $VERIFY_LINK$
Wenn Sie Unterstützung bezüglich ODMS Cloud benötigen, wenden Sie sich bitte an $PRIMARY_ADMIN_NAME$.
Wenn Sie diese E-Mail fälschlicherweise erhalten haben, löschen Sie diese E-Mail bitte aus Ihrem System.
Dies ist eine automatisch generierte E-Mail und dieses Postfach wird nicht überwacht. Bitte nicht antworten.
<h3>&lt;Français&gt;</h3>
Vos informations utilisateur ont été enregistrées dans le cloud ODMS par votre administrateur. Pour finaliser votre inscription d'utilisateur, vous devez vérifier votre adresse e-mail. Veuillez vérifier en cliquant sur le lien ci-dessous. Une fois l'inscription terminée, une fenêtre de notification apparaîtra confirmant que vos informations d'utilisateur ont été vérifiées.
URL: $VERIFY_LINK$
Si vous avez besoin d'assistance concernant ODMS Cloud, veuillez contacter $PRIMARY_ADMIN_NAME$.
Si vous avez reçu cet e-mail par erreur, veuillez supprimer cet e-mail de votre système.
Il s'agit d'un e-mail généré automatiquement et cette boîte aux lettres n'est pas surveillée. Merci de ne pas répondre.