## 概要 [Task3210: 画面修正(Terms画面)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3210) [Task3211:API修正(バージョン取得API)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/OMDSDictation/_sprints/taskboard/OMDSDictation%20%E3%83%81%E3%83%BC%E3%83%A0/OMDSDictation/%E3%82%B9%E3%83%97%E3%83%AA%E3%83%B3%E3%83%88%2023-1?workitem=3211) [Task3212:API修正(バージョン更新API))](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/OMDSDictation/_sprints/taskboard/OMDSDictation%20%E3%83%81%E3%83%BC%E3%83%A0/OMDSDictation/%E3%82%B9%E3%83%97%E3%83%AA%E3%83%B3%E3%83%88%2023-1?workitem=3212) - このPull Requestでの対象/対象外 Click here to read the terms of use.の文言は多言語対応の対象のため、現在一律同じ文言がでます。 第一~第四階層は 上からEULA,PrivacyNotice,DPAが表示されています 第五階層は、 上から、PrivacyNotice,DPAが表示されています - 影響範囲(他の機能にも影響があるか) ユーザアーカイブテーブルにPrivacyNoticeのバージョンを追加 ## レビューポイント 同意済みプライバシーポリシーはユーザーアーカイブの対象だと認識しているが正しいか。 termsテーブルのdocument_typeの値をPrivacyNoticeにしているが、PRIVACY_NOTICEにしたほうがよいか。 ユニットテストに不足はないか。 ## UIの変更 - Before/Afterのスクショなど - スクショ置き場 https://ndstokyo.sharepoint.com/sites/Piranha/Shared%20Documents/Forms/AllItems.aspx?csf=1&web=1&e=hzPw9b&cid=7737ed1b%2D0eb4%2D4331%2Da238%2D14dd35b27e18&FolderCTID=0x012000C0DCEE65AC2177479C3C761CD137C9C9&id=%2Fsites%2FPiranha%2FShared%20Documents%2FGeneral%2FOMDS%2F%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%2FTask3210&viewid=786a81cf%2Dd15f%2D4dc2%2D9e55%2Dc7a729fbc72f ## 動作確認状況 - ローカルで確認 ## 補足 - 相談、参考資料などがあれば
780 lines
30 KiB
TypeScript
780 lines
30 KiB
TypeScript
import { HttpException, HttpStatus } from '@nestjs/common';
|
||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||
import {
|
||
makeAuthServiceMock,
|
||
makeDefaultAdB2cMockValue,
|
||
makeDefaultConfigValue,
|
||
makeDefaultGetPublicKeyFromJwk,
|
||
} from './test/auth.service.mock';
|
||
import { DataSource } from 'typeorm';
|
||
import { makeContext } from '../../common/log';
|
||
import { makeTestingModule } from '../../common/test/modules';
|
||
import { getAccount, makeTestAccount } from '../../common/test/utility';
|
||
import { AuthService } from './auth.service';
|
||
import {
|
||
createTermInfo,
|
||
deleteAccount,
|
||
updateAccountDelegationPermission,
|
||
} from './test/utility';
|
||
import { v4 as uuidv4 } from 'uuid';
|
||
import { TIERS, USER_ROLES } from '../../constants';
|
||
import { decode, isVerifyError } from '../../common/jwt';
|
||
import { RefreshToken, AccessToken } from '../../common/token';
|
||
|
||
describe('AuthService', () => {
|
||
it('IDトークンの検証とペイロードの取得に成功する', async () => {
|
||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||
const configMockValue = makeDefaultConfigValue();
|
||
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
|
||
//JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替
|
||
service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk;
|
||
const token =
|
||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA';
|
||
|
||
const context = makeContext(`uuidv4`);
|
||
expect(await service.getVerifiedIdToken(context, token)).toEqual(
|
||
idTokenPayload,
|
||
);
|
||
});
|
||
|
||
it('IDトークンの形式が不正な場合、形式不正エラーとなる。', async () => {
|
||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||
const configMockValue = makeDefaultConfigValue();
|
||
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
|
||
const token = 'invalid.id.token';
|
||
|
||
const context = makeContext(`uuidv4`);
|
||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||
new HttpException(makeErrorResponse('E000101'), HttpStatus.UNAUTHORIZED),
|
||
);
|
||
});
|
||
|
||
it('IDトークンの有効期限が切れている場合、有効期限切れエラーとなる。', async () => {
|
||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||
const configMockValue = makeDefaultConfigValue();
|
||
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
|
||
//JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替
|
||
service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk;
|
||
const token =
|
||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjEwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.r9x61Mf1S2qFgU_QDKB6tRFBmTQXyOEtpoacOlL_bQzFz1t3GsxMy6SJIvQQ-LtDgylQ1UCdMFiRuy4V8nyLuME0fR-9IkKsboGvwllHB_Isai3XFoja0jpDHMVby1m0B3Z9xOTb7YsaQGyEH-qs1TtnRm6Ny98h4Po80nK8HGefQZHBOlfQN_B1LiHwI3nLXV18NL-4olKXj2NloNRYtnWM0PaqDQcGvZFaSNvtrSYpo9ddD906QWDGVOQ7WvGSUgdNCoxX8Lb3r2-VSj6n84jpb-Y1Fz-GhLluNglAsBhasnJfUIvCIO3iG5pRyTYjHFAVHmzjr8xMOmhS3s41Jw';
|
||
|
||
const context = makeContext(`uuidv4`);
|
||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||
new HttpException(makeErrorResponse('E000102'), HttpStatus.UNAUTHORIZED),
|
||
);
|
||
});
|
||
|
||
it('IDトークンが開始日より前の場合、開始前エラーとなる。', async () => {
|
||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||
const configMockValue = makeDefaultConfigValue();
|
||
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
|
||
//JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替
|
||
service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk;
|
||
const token =
|
||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6OTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.fX2Gbd7fDPNE3Lw-xbum_5CVqQYqEmMhv_v5u8A-U81pmPD2P5rsJEJx66ns1taFLVaE3j9_OzotxrqjqqQqbACkagGcN5wvA3_ZIxyqmhrKYFJc53ZcO7d0pFWiQlluNBI_pnFNDlSMB2Ut8Th5aiPy2uamBM9wC99bcjo7HkHvTKBf6ljU6rPKoD51qGDWqNxjoH-hdSJ29wprvyxyk_yX6dp-cxXUj5DIgXYQuIZF71rdiPtGlAiyTBns8rS2QlEEXapZVlvYrK4mkpUXVDA7ifD8q6gAC2BStqHeys7CGp2MbV4ZwKCVbAUbMs6Tboh8rADZvQhuTEq7qlhZ-w';
|
||
|
||
const context = makeContext(`uuidv4`);
|
||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||
new HttpException(makeErrorResponse('E000103'), HttpStatus.UNAUTHORIZED),
|
||
);
|
||
});
|
||
|
||
it('IDトークンの署名が不正な場合、署名不正エラーとなる。', async () => {
|
||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||
const configMockValue = makeDefaultConfigValue();
|
||
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
|
||
const token =
|
||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdXNlciIsInN1YiI6InN1YiIsImF1ZCI6ImF1ZCIsIm5vbmNlIjoiZGVmYXVsdE5vbmNlIiwiaWF0IjoxMDAwMDAwMDAwLCJhdXRoX3RpbWUiOjEwMDAwMDAwMDAsImVtYWlscyI6WyJ4eHhAeHguY29tIl0sInRmcCI6InNpZ25pbl91c2VyZmxvdyJ9.sign';
|
||
|
||
const context = makeContext(`uuidv4`);
|
||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||
new HttpException(makeErrorResponse('E000104'), HttpStatus.UNAUTHORIZED),
|
||
);
|
||
});
|
||
|
||
it('IDトークンの発行元が想定と異なる場合、発行元不正エラーとなる。', async () => {
|
||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||
const configMockValue = makeDefaultConfigValue();
|
||
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
|
||
//JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替
|
||
service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk;
|
||
const token =
|
||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaW52bGlkX2lzc3VlciIsInN1YiI6InN1YiIsImF1ZCI6ImF1ZCIsIm5vbmNlIjoiZGVmYXVsdE5vbmNlIiwiaWF0IjoxMDAwMDAwMDAwLCJhdXRoX3RpbWUiOjEwMDAwMDAwMDAsImVtYWlscyI6WyJ4eHhAeHguY29tIl0sInRmcCI6InNpZ25pbl91c2VyZmxvdyJ9.0bp3e1mDG78PX3lo8zgOLXGenIqG_Vi6kw7CbwauAQM-cnUZ_aVCoJ_dAv_QmPElOQKcCkRrAvAZ91FwuHDlBGuuDqx8OwqN0VaD-4NPouoAswj-9HNvBm8gUn-pGaXkvWt_72UdCJavZJjDj_RHur8y8kFt5Qeab3mUP2x-uNcV2Q2x3M_IIfcRiIZkRZm_azKfiVIy7tzoUFLDss97y938aR8imMVxazoSQvj7RWIWylgeRr9yVt7qYl18cnEVL0IGtslFbqhfNsiEmRCMsttm5kXs7E9B0bhhUe_xbJW9VumQ6G7dgMrswevp_jRgbpWJoZsgErtqIRl9Tc9ikA';
|
||
|
||
const context = makeContext(`uuidv4`);
|
||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||
new HttpException(makeErrorResponse('E000105'), HttpStatus.UNAUTHORIZED),
|
||
);
|
||
});
|
||
|
||
it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。(メタデータ)', async () => {
|
||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||
const configMockValue = makeDefaultConfigValue();
|
||
adb2cParam.getMetaData = new Error('failed get metadata');
|
||
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
|
||
const token =
|
||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA';
|
||
|
||
const context = makeContext(`uuidv4`);
|
||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||
new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
),
|
||
);
|
||
});
|
||
it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。(キーセット)', async () => {
|
||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||
const configMockValue = makeDefaultConfigValue();
|
||
adb2cParam.getSignKeySets = new Error('failed get keyset');
|
||
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
|
||
const token =
|
||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA';
|
||
|
||
const context = makeContext(`uuidv4`);
|
||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||
new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
),
|
||
);
|
||
});
|
||
|
||
it('Azure ADB2Cから取得した鍵が一致しない場合、エラーとなる。', async () => {
|
||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||
const configMockValue = makeDefaultConfigValue();
|
||
adb2cParam.getSignKeySets = [
|
||
{ kid: 'invalid', kty: 'RSA', nbf: 0, use: 'sig', e: '', n: '' },
|
||
];
|
||
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
|
||
const token =
|
||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA';
|
||
|
||
const context = makeContext(`uuidv4`);
|
||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||
new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
),
|
||
);
|
||
});
|
||
});
|
||
|
||
describe('checkIsAcceptedLatestVersion', () => {
|
||
let source: DataSource | null = null;
|
||
beforeEach(async () => {
|
||
source = new DataSource({
|
||
type: 'sqlite',
|
||
database: ':memory:',
|
||
logging: false,
|
||
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
|
||
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
|
||
});
|
||
return source.initialize();
|
||
});
|
||
|
||
afterEach(async () => {
|
||
if (!source) return;
|
||
await source.destroy();
|
||
source = null;
|
||
});
|
||
it('同意済み利用規約バージョンが最新のときにチェックが通ること(第五)', async () => {
|
||
if (!source) fail();
|
||
const module = await makeTestingModule(source);
|
||
if (!module) fail();
|
||
const service = module.get<AuthService>(AuthService);
|
||
const { admin } = await makeTestAccount(source, {
|
||
tier: 5,
|
||
});
|
||
const context = makeContext(uuidv4());
|
||
|
||
const idToken = {
|
||
emails: [],
|
||
sub: admin.external_id,
|
||
exp: 0,
|
||
iat: 0,
|
||
};
|
||
|
||
await createTermInfo(source, 'EULA', '1.0');
|
||
await createTermInfo(source, 'PrivacyNotice', '1.0');
|
||
await createTermInfo(source, 'DPA', '1.0');
|
||
const result = await service.isAcceptedLatestVersion(context, idToken);
|
||
expect(result).toBe(true);
|
||
});
|
||
|
||
it('同意済み利用規約バージョンが最新のときにチェックが通ること(第一~第四)', async () => {
|
||
if (!source) fail();
|
||
const module = await makeTestingModule(source);
|
||
if (!module) fail();
|
||
const service = module.get<AuthService>(AuthService);
|
||
const { admin } = await makeTestAccount(source, {
|
||
tier: 4,
|
||
});
|
||
const context = makeContext(uuidv4());
|
||
|
||
const idToken = {
|
||
emails: [],
|
||
sub: admin.external_id,
|
||
exp: 0,
|
||
iat: 0,
|
||
};
|
||
|
||
await createTermInfo(source, 'EULA', '1.0');
|
||
await createTermInfo(source, 'PrivacyNotice', '1.0');
|
||
await createTermInfo(source, 'DPA', '1.0');
|
||
const result = await service.isAcceptedLatestVersion(context, idToken);
|
||
expect(result).toBe(true);
|
||
});
|
||
|
||
it('同意済み利用規約バージョンが最新でないときにチェックが通らないこと(第五)', async () => {
|
||
if (!source) fail();
|
||
const module = await makeTestingModule(source);
|
||
if (!module) fail();
|
||
const service = module.get<AuthService>(AuthService);
|
||
const { admin } = await makeTestAccount(source, {
|
||
tier: 5,
|
||
});
|
||
const context = makeContext(uuidv4());
|
||
|
||
const idToken = {
|
||
emails: [],
|
||
sub: admin.external_id,
|
||
exp: 0,
|
||
iat: 0,
|
||
};
|
||
|
||
await createTermInfo(source, 'EULA', '1.1');
|
||
await createTermInfo(source, 'PrivacyNotice', '1.0');
|
||
await createTermInfo(source, 'DPA', '1.0');
|
||
const result = await service.isAcceptedLatestVersion(context, idToken);
|
||
expect(result).toBe(false);
|
||
});
|
||
|
||
it('同意済み利用規約(EULA)バージョンが最新でないときにチェックが通らないこと(第一~第四)', async () => {
|
||
if (!source) fail();
|
||
const module = await makeTestingModule(source);
|
||
if (!module) fail();
|
||
const service = module.get<AuthService>(AuthService);
|
||
const { admin } = await makeTestAccount(source, {
|
||
tier: 4,
|
||
});
|
||
const context = makeContext(uuidv4());
|
||
|
||
const idToken = {
|
||
emails: [],
|
||
sub: admin.external_id,
|
||
exp: 0,
|
||
iat: 0,
|
||
};
|
||
|
||
await createTermInfo(source, 'EULA', '1.1');
|
||
await createTermInfo(source, 'PrivacyNotice', '1.0');
|
||
await createTermInfo(source, 'DPA', '1.0');
|
||
const result = await service.isAcceptedLatestVersion(context, idToken);
|
||
expect(result).toBe(false);
|
||
});
|
||
|
||
it('同意済み利用規約バージョン(DPA)が最新でないときにチェックが通らないこと(第一~第四)', async () => {
|
||
if (!source) fail();
|
||
const module = await makeTestingModule(source);
|
||
if (!module) fail();
|
||
const service = module.get<AuthService>(AuthService);
|
||
const { admin } = await makeTestAccount(source, {
|
||
tier: 4,
|
||
});
|
||
const context = makeContext(uuidv4());
|
||
|
||
const idToken = {
|
||
emails: [],
|
||
sub: admin.external_id,
|
||
exp: 0,
|
||
iat: 0,
|
||
};
|
||
|
||
await createTermInfo(source, 'EULA', '1.0');
|
||
await createTermInfo(source, 'PrivacyNotice', '1.0');
|
||
await createTermInfo(source, 'DPA', '1.1');
|
||
const result = await service.isAcceptedLatestVersion(context, idToken);
|
||
expect(result).toBe(false);
|
||
});
|
||
|
||
it('同意済みプライバシーポリシーが最新でないときにチェックが通らないこと(第一~第四)', async () => {
|
||
if (!source) fail();
|
||
const module = await makeTestingModule(source);
|
||
if (!module) fail();
|
||
const service = module.get<AuthService>(AuthService);
|
||
const { admin } = await makeTestAccount(source, {
|
||
tier: 4,
|
||
});
|
||
const context = makeContext(uuidv4());
|
||
|
||
const idToken = {
|
||
emails: [],
|
||
sub: admin.external_id,
|
||
exp: 0,
|
||
iat: 0,
|
||
};
|
||
|
||
await createTermInfo(source, 'EULA', '1.0');
|
||
await createTermInfo(source, 'PrivacyNotice', '1.1');
|
||
await createTermInfo(source, 'DPA', '1.0');
|
||
const result = await service.isAcceptedLatestVersion(context, idToken);
|
||
expect(result).toBe(false);
|
||
});
|
||
});
|
||
|
||
describe('generateDelegationRefreshToken', () => {
|
||
let source: DataSource | null = null;
|
||
beforeEach(async () => {
|
||
source = new DataSource({
|
||
type: 'sqlite',
|
||
database: ':memory:',
|
||
logging: false,
|
||
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
|
||
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
|
||
});
|
||
return source.initialize();
|
||
});
|
||
|
||
afterEach(async () => {
|
||
if (!source) return;
|
||
await source.destroy();
|
||
source = null;
|
||
});
|
||
it('代行操作が許可されたパートナーの代行操作用リフレッシュトークンを取得できること', async () => {
|
||
if (!source) fail();
|
||
const module = await makeTestingModule(source);
|
||
if (!module) fail();
|
||
const service = module.get<AuthService>(AuthService);
|
||
const { admin: parentAdmin, account: parentAccount } =
|
||
await makeTestAccount(source, {
|
||
tier: 4,
|
||
});
|
||
const { admin: partnerAdmin, account: partnerAccount } =
|
||
await makeTestAccount(
|
||
source,
|
||
{
|
||
tier: 5,
|
||
parent_account_id: parentAccount.id,
|
||
delegation_permission: true,
|
||
},
|
||
{ role: USER_ROLES.NONE },
|
||
);
|
||
|
||
const context = makeContext(parentAdmin.external_id);
|
||
|
||
const delegationRefreshToken = await service.generateDelegationRefreshToken(
|
||
context,
|
||
parentAdmin.external_id,
|
||
partnerAccount.id,
|
||
);
|
||
|
||
// 取得できた代行操作用リフレッシュトークンをデコード
|
||
const decodeToken = decode<RefreshToken>(delegationRefreshToken);
|
||
if (isVerifyError(decodeToken)) {
|
||
fail();
|
||
}
|
||
|
||
expect(decodeToken.role).toBe('none admin');
|
||
expect(decodeToken.tier).toBe(TIERS.TIER5);
|
||
expect(decodeToken.userId).toBe(partnerAdmin.external_id);
|
||
expect(decodeToken.delegateUserId).toBe(parentAdmin.external_id);
|
||
});
|
||
it('代行操作が許可されていない場合、400エラーとなること', async () => {
|
||
if (!source) fail();
|
||
const module = await makeTestingModule(source);
|
||
if (!module) fail();
|
||
const service = module.get<AuthService>(AuthService);
|
||
const { admin: parentAdmin, account: parentAccount } =
|
||
await makeTestAccount(source, {
|
||
tier: 4,
|
||
});
|
||
const { account: partnerAccount } = await makeTestAccount(
|
||
source,
|
||
{
|
||
tier: 5,
|
||
parent_account_id: parentAccount.id,
|
||
delegation_permission: false,
|
||
},
|
||
{ role: USER_ROLES.NONE },
|
||
);
|
||
|
||
const context = makeContext(parentAdmin.external_id);
|
||
|
||
try {
|
||
await service.generateDelegationRefreshToken(
|
||
context,
|
||
parentAdmin.external_id,
|
||
partnerAccount.id,
|
||
);
|
||
fail();
|
||
} catch (e) {
|
||
if (e instanceof HttpException) {
|
||
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||
expect(e.getResponse()).toEqual(makeErrorResponse('E010503'));
|
||
} else {
|
||
fail();
|
||
}
|
||
}
|
||
});
|
||
|
||
it('代行操作対象が存在しない場合、400エラーとなること', async () => {
|
||
if (!source) fail();
|
||
const module = await makeTestingModule(source);
|
||
if (!module) fail();
|
||
const service = module.get<AuthService>(AuthService);
|
||
const { admin: parentAdmin, account: parentAccount } =
|
||
await makeTestAccount(source, {
|
||
tier: 4,
|
||
});
|
||
await makeTestAccount(
|
||
source,
|
||
{
|
||
tier: 5,
|
||
parent_account_id: parentAccount.id,
|
||
delegation_permission: false,
|
||
},
|
||
{ role: USER_ROLES.NONE },
|
||
);
|
||
|
||
const context = makeContext(parentAdmin.external_id);
|
||
|
||
try {
|
||
await service.generateDelegationRefreshToken(
|
||
context,
|
||
parentAdmin.external_id,
|
||
9999,
|
||
);
|
||
fail();
|
||
} catch (e) {
|
||
if (e instanceof HttpException) {
|
||
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||
expect(e.getResponse()).toEqual(makeErrorResponse('E010501'));
|
||
} else {
|
||
fail();
|
||
}
|
||
}
|
||
});
|
||
});
|
||
|
||
describe('generateDelegationAccessToken', () => {
|
||
let source: DataSource | null = null;
|
||
beforeEach(async () => {
|
||
source = new DataSource({
|
||
type: 'sqlite',
|
||
database: ':memory:',
|
||
logging: false,
|
||
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
|
||
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
|
||
});
|
||
return source.initialize();
|
||
});
|
||
|
||
afterEach(async () => {
|
||
if (!source) return;
|
||
await source.destroy();
|
||
source = null;
|
||
});
|
||
it('代行操作用リフレッシュトークンから代行操作用アクセストークンを取得できること', async () => {
|
||
if (!source) fail();
|
||
const module = await makeTestingModule(source);
|
||
if (!module) fail();
|
||
const service = module.get<AuthService>(AuthService);
|
||
const { admin: parentAdmin, account: parentAccount } =
|
||
await makeTestAccount(source, {
|
||
tier: 4,
|
||
});
|
||
const { admin: partnerAdmin, account: partnerAccount } =
|
||
await makeTestAccount(
|
||
source,
|
||
{
|
||
tier: 5,
|
||
parent_account_id: parentAccount.id,
|
||
delegation_permission: true,
|
||
},
|
||
{ role: USER_ROLES.NONE },
|
||
);
|
||
|
||
const context = makeContext(parentAdmin.external_id);
|
||
|
||
const delegationRefreshToken = await service.generateDelegationRefreshToken(
|
||
context,
|
||
parentAdmin.external_id,
|
||
partnerAccount.id,
|
||
);
|
||
|
||
// 取得できた代行操作用リフレッシュトークンをデコード
|
||
const decodeRefreshToken = decode<RefreshToken>(delegationRefreshToken);
|
||
if (isVerifyError(decodeRefreshToken)) {
|
||
fail();
|
||
}
|
||
|
||
expect(decodeRefreshToken.role).toBe('none admin');
|
||
expect(decodeRefreshToken.tier).toBe(TIERS.TIER5);
|
||
expect(decodeRefreshToken.userId).toBe(partnerAdmin.external_id);
|
||
expect(decodeRefreshToken.delegateUserId).toBe(parentAdmin.external_id);
|
||
|
||
const delegationAccessToken = await service.generateDelegationAccessToken(
|
||
context,
|
||
delegationRefreshToken,
|
||
);
|
||
|
||
// 取得できた代行操作用アクセストークンをデコード
|
||
const decodeAccessToken = decode<AccessToken>(delegationAccessToken);
|
||
if (isVerifyError(decodeAccessToken)) {
|
||
fail();
|
||
}
|
||
|
||
expect(decodeAccessToken.role).toBe('none admin');
|
||
expect(decodeAccessToken.tier).toBe(TIERS.TIER5);
|
||
expect(decodeAccessToken.userId).toBe(partnerAdmin.external_id);
|
||
expect(decodeAccessToken.delegateUserId).toBe(parentAdmin.external_id);
|
||
});
|
||
|
||
it('代行操作用リフレッシュトークンの形式が不正な場合、エラーとなること', async () => {
|
||
if (!source) fail();
|
||
const module = await makeTestingModule(source);
|
||
if (!module) fail();
|
||
const service = module.get<AuthService>(AuthService);
|
||
const { admin: parentAdmin } = await makeTestAccount(source, {
|
||
tier: 4,
|
||
});
|
||
|
||
const context = makeContext(parentAdmin.external_id);
|
||
|
||
try {
|
||
await service.generateDelegationAccessToken(context, 'invalid token');
|
||
fail();
|
||
} catch (e) {
|
||
if (e instanceof HttpException) {
|
||
expect(e.getStatus()).toEqual(HttpStatus.UNAUTHORIZED);
|
||
expect(e.getResponse()).toEqual(makeErrorResponse('E000101'));
|
||
} else {
|
||
fail();
|
||
}
|
||
}
|
||
});
|
||
});
|
||
|
||
describe('updateDelegationAccessToken', () => {
|
||
let source: DataSource | null = null;
|
||
beforeEach(async () => {
|
||
source = new DataSource({
|
||
type: 'sqlite',
|
||
database: ':memory:',
|
||
logging: false,
|
||
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
|
||
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
|
||
});
|
||
return source.initialize();
|
||
});
|
||
|
||
afterEach(async () => {
|
||
if (!source) return;
|
||
await source.destroy();
|
||
source = null;
|
||
});
|
||
|
||
it('代行操作用リフレッシュトークンから代行操作用アクセストークンを更新できること', async () => {
|
||
if (!source) fail();
|
||
const module = await makeTestingModule(source);
|
||
if (!module) fail();
|
||
const service = module.get<AuthService>(AuthService);
|
||
const { admin: parentAdmin, account: parentAccount } =
|
||
await makeTestAccount(source, {
|
||
tier: 4,
|
||
});
|
||
const { admin: partnerAdmin, account: partnerAccount } =
|
||
await makeTestAccount(
|
||
source,
|
||
{
|
||
tier: 5,
|
||
parent_account_id: parentAccount.id,
|
||
delegation_permission: true,
|
||
},
|
||
{ role: USER_ROLES.NONE },
|
||
);
|
||
|
||
const context = makeContext(parentAdmin.external_id);
|
||
|
||
const delegationRefreshToken = await service.generateDelegationRefreshToken(
|
||
context,
|
||
parentAdmin.external_id,
|
||
partnerAccount.id,
|
||
);
|
||
|
||
// 取得できた代行操作用リフレッシュトークンをデコード
|
||
const decodeRefreshToken = decode<RefreshToken>(delegationRefreshToken);
|
||
if (isVerifyError(decodeRefreshToken)) {
|
||
fail();
|
||
}
|
||
|
||
expect(decodeRefreshToken.role).toBe('none admin');
|
||
expect(decodeRefreshToken.tier).toBe(TIERS.TIER5);
|
||
expect(decodeRefreshToken.userId).toBe(partnerAdmin.external_id);
|
||
expect(decodeRefreshToken.delegateUserId).toBe(parentAdmin.external_id);
|
||
|
||
const token = await service.updateDelegationAccessToken(
|
||
context,
|
||
decodeRefreshToken.delegateUserId,
|
||
decodeRefreshToken.userId,
|
||
delegationRefreshToken,
|
||
);
|
||
|
||
// 取得できた代行操作用リフレッシュトークンをデコード
|
||
const decodeAccessToken = decode<RefreshToken>(token);
|
||
if (isVerifyError(decodeAccessToken)) {
|
||
fail();
|
||
}
|
||
|
||
expect(decodeAccessToken.role).toBe('none admin');
|
||
expect(decodeAccessToken.tier).toBe(TIERS.TIER5);
|
||
expect(decodeAccessToken.userId).toBe(partnerAdmin.external_id);
|
||
expect(decodeAccessToken.delegateUserId).toBe(parentAdmin.external_id);
|
||
});
|
||
|
||
it('代行操作対象アカウントの代行操作が許可されていない場合、エラーとなること', async () => {
|
||
if (!source) fail();
|
||
const module = await makeTestingModule(source);
|
||
if (!module) fail();
|
||
const service = module.get<AuthService>(AuthService);
|
||
const { admin: parentAdmin, account: parentAccount } =
|
||
await makeTestAccount(source, {
|
||
tier: 4,
|
||
});
|
||
const { admin: partnerAdmin, account: partnerAccount } =
|
||
await makeTestAccount(
|
||
source,
|
||
{
|
||
tier: 5,
|
||
parent_account_id: parentAccount.id,
|
||
delegation_permission: true,
|
||
},
|
||
{ role: USER_ROLES.NONE },
|
||
);
|
||
|
||
const context = makeContext(parentAdmin.external_id);
|
||
|
||
const delegationRefreshToken = await service.generateDelegationRefreshToken(
|
||
context,
|
||
parentAdmin.external_id,
|
||
partnerAccount.id,
|
||
);
|
||
|
||
// 取得できた代行操作用リフレッシュトークンをデコード
|
||
const decodeRefreshToken = decode<RefreshToken>(delegationRefreshToken);
|
||
if (isVerifyError(decodeRefreshToken)) {
|
||
fail();
|
||
}
|
||
|
||
expect(decodeRefreshToken.role).toBe('none admin');
|
||
expect(decodeRefreshToken.tier).toBe(TIERS.TIER5);
|
||
expect(decodeRefreshToken.userId).toBe(partnerAdmin.external_id);
|
||
expect(decodeRefreshToken.delegateUserId).toBe(parentAdmin.external_id);
|
||
|
||
if (decodeRefreshToken.delegateUserId === undefined) {
|
||
fail();
|
||
}
|
||
|
||
// 代行操作対象アカウントの代行操作を許可しないように変更
|
||
await updateAccountDelegationPermission(source, partnerAccount.id, false);
|
||
const account = await getAccount(source, partnerAccount.id);
|
||
|
||
expect(account?.delegation_permission ?? true).toBeFalsy();
|
||
|
||
try {
|
||
await service.updateDelegationAccessToken(
|
||
context,
|
||
decodeRefreshToken.delegateUserId,
|
||
decodeRefreshToken.userId,
|
||
delegationRefreshToken,
|
||
);
|
||
fail();
|
||
} catch (e) {
|
||
if (e instanceof HttpException) {
|
||
expect(e.getStatus()).toEqual(HttpStatus.UNAUTHORIZED);
|
||
expect(e.getResponse()).toEqual(makeErrorResponse('E010503'));
|
||
} else {
|
||
fail();
|
||
}
|
||
}
|
||
});
|
||
it('代行操作対象アカウントが存在しない場合、エラーとなること', async () => {
|
||
if (!source) fail();
|
||
const module = await makeTestingModule(source);
|
||
if (!module) fail();
|
||
const service = module.get<AuthService>(AuthService);
|
||
const { admin: parentAdmin, account: parentAccount } =
|
||
await makeTestAccount(source, {
|
||
tier: 4,
|
||
});
|
||
const { admin: partnerAdmin, account: partnerAccount } =
|
||
await makeTestAccount(
|
||
source,
|
||
{
|
||
tier: 5,
|
||
parent_account_id: parentAccount.id,
|
||
delegation_permission: true,
|
||
},
|
||
{ role: USER_ROLES.NONE },
|
||
);
|
||
|
||
const context = makeContext(parentAdmin.external_id);
|
||
|
||
const delegationRefreshToken = await service.generateDelegationRefreshToken(
|
||
context,
|
||
parentAdmin.external_id,
|
||
partnerAccount.id,
|
||
);
|
||
|
||
// 取得できた代行操作用リフレッシュトークンをデコード
|
||
const decodeRefreshToken = decode<RefreshToken>(delegationRefreshToken);
|
||
if (isVerifyError(decodeRefreshToken)) {
|
||
fail();
|
||
}
|
||
|
||
expect(decodeRefreshToken.role).toBe('none admin');
|
||
expect(decodeRefreshToken.tier).toBe(TIERS.TIER5);
|
||
expect(decodeRefreshToken.userId).toBe(partnerAdmin.external_id);
|
||
expect(decodeRefreshToken.delegateUserId).toBe(parentAdmin.external_id);
|
||
|
||
if (decodeRefreshToken.delegateUserId === undefined) {
|
||
fail();
|
||
}
|
||
|
||
// 代行操作対象アカウントを削除
|
||
deleteAccount(source, partnerAccount.id);
|
||
|
||
try {
|
||
await service.updateDelegationAccessToken(
|
||
context,
|
||
decodeRefreshToken.delegateUserId,
|
||
partnerAdmin.external_id,
|
||
delegationRefreshToken,
|
||
);
|
||
fail();
|
||
} catch (e) {
|
||
if (e instanceof HttpException) {
|
||
expect(e.getStatus()).toEqual(HttpStatus.UNAUTHORIZED);
|
||
expect(e.getResponse()).toEqual(makeErrorResponse('E010501'));
|
||
} else {
|
||
fail();
|
||
}
|
||
}
|
||
});
|
||
});
|
||
|
||
const idTokenPayload = {
|
||
exp: 9000000000,
|
||
nbf: 1000000000,
|
||
ver: '1.0',
|
||
iss: 'issuer',
|
||
sub: 'sub',
|
||
aud: 'aud',
|
||
nonce: 'defaultNonce',
|
||
iat: 1000000000,
|
||
auth_time: 1000000000,
|
||
emails: ['xxx@xx.com'],
|
||
tfp: 'signin_userflow',
|
||
};
|