Merged PR 538: Azure AD B2Cの結果をCacheManagerにキャッシュするよう修正
## 概要 [Task2967: Azure AD B2Cの結果をCacheManagerにキャッシュするよう修正](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2967) - キャッシュを扱うRedisServiceを追加 - AdB2cServiceでRedisServiceを使って結果をキャッシュする実装を追加 - ADB2Cの呼び出しと、キャッシュからの取得が行われた時にログを出す実装を追加 - Azure Monitorで呼び出しコストでアラート出したくなった時のための予防的追加 - 開発環境でローカルのredisを操作する用途のredis-cliをインストールする設定を追加&スクリプトを追加 - `getUser` と `getUsers` の返り値の方を統一 & 使用されなくなった方の型を削除 - AdB2Cの`ttl` に設定する用の値を環境変数に追加 - 今後実装予定のトークンのキャッシュとはTTLを別にしたかったため - 複数ユーザー削除処理内でのindex処理が不適切と思われる箇所があったので修正 ## レビューポイント - **Redisへのget/set/delが失敗した際に、エラーログだけ出して成功 or 取得対象なしと同様の動作をするように作成したが、問題なさそうか** - これは速度向上用のキャッシュが死んでいても業務は動くべきではないか、という考えによるもの - 通信できない=障害中であると想定されるので、失敗しても良いような気もするので相談 - **AdB2cService内でキャッシュを扱う箇所のコードの可読性に問題はないか** - 更にWrapしてキャッシュの具体的な動きを隠蔽することも考えたが、詳細なエラーの制御をしづらくなりそうだったので具体的な引数の変換等以上のことはしない形で実装 - AdB2cServiceが十分に末端の処理なので詳細な処理を生で書いていても認知負荷はそう変わらない可能性がある - **キャッシュする値の性質によってTTLを変えられる仕組みを前提に設計・実装したが、懸念点はないか** - **TTLに設定する値は妥当そうか** - **`Aadb2cUser` を削除したが問題ないか** - **`deleteUsers` 内のログ処理の変更は適切か** - to 岩田さん ## 動作確認状況 - ローカルで確認 - npm run testが通過することを確認
This commit is contained in:
parent
01d92b2408
commit
83add51148
@ -83,6 +83,10 @@ jobs:
|
|||||||
REFRESH_TOKEN_LIFETIME_WEB: 0
|
REFRESH_TOKEN_LIFETIME_WEB: 0
|
||||||
REFRESH_TOKEN_LIFETIME_DEFAULT: 0
|
REFRESH_TOKEN_LIFETIME_DEFAULT: 0
|
||||||
ACCESS_TOKEN_LIFETIME_WEB: 0
|
ACCESS_TOKEN_LIFETIME_WEB: 0
|
||||||
|
REDIS_HOST: xxxxxxxxxxxx
|
||||||
|
REDIS_PORT: 0
|
||||||
|
REDIS_PASSWORD: xxxxxxxxxxxx
|
||||||
|
ADB2C_CACHE_TTL: 0
|
||||||
- task: Docker@0
|
- task: Docker@0
|
||||||
displayName: build
|
displayName: build
|
||||||
inputs:
|
inputs:
|
||||||
|
|||||||
@ -17,6 +17,11 @@ RUN bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "$
|
|||||||
&& apt-get install default-jre -y \
|
&& apt-get install default-jre -y \
|
||||||
&& apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts
|
&& apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts
|
||||||
|
|
||||||
|
# Install redis-cli
|
||||||
|
RUN curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
|
||||||
|
RUN sudo apt-get update -y
|
||||||
|
RUN sudo apt-get install redis -y
|
||||||
|
|
||||||
# COPY --from=golang:1.18-buster /usr/local/go/ /usr/local/go/
|
# COPY --from=golang:1.18-buster /usr/local/go/ /usr/local/go/
|
||||||
ENV GO111MODULE=auto
|
ENV GO111MODULE=auto
|
||||||
COPY library-scripts/go-debian.sh /tmp/library-scripts/
|
COPY library-scripts/go-debian.sh /tmp/library-scripts/
|
||||||
|
|||||||
@ -30,3 +30,7 @@ ACCESS_TOKEN_LIFETIME_WEB=7200000
|
|||||||
REFRESH_TOKEN_LIFETIME_WEB=86400000
|
REFRESH_TOKEN_LIFETIME_WEB=86400000
|
||||||
REFRESH_TOKEN_LIFETIME_DEFAULT=2592000000
|
REFRESH_TOKEN_LIFETIME_DEFAULT=2592000000
|
||||||
EMAIL_CONFIRM_LIFETIME=86400000
|
EMAIL_CONFIRM_LIFETIME=86400000
|
||||||
|
REDIS_HOST=redis-cache
|
||||||
|
REDIS_PORT=6379
|
||||||
|
REDIS_PASSWORD=omdsredispass
|
||||||
|
ADB2C_CACHE_TTL=86400
|
||||||
223
dictation_server/package-lock.json
generated
223
dictation_server/package-lock.json
generated
@ -26,7 +26,7 @@
|
|||||||
"@types/jsonwebtoken": "^9.0.1",
|
"@types/jsonwebtoken": "^9.0.1",
|
||||||
"@types/uuid": "^8.3.4",
|
"@types/uuid": "^8.3.4",
|
||||||
"axios": "^1.3.4",
|
"axios": "^1.3.4",
|
||||||
"cache-manager": "^5.2.0",
|
"cache-manager": "^5.2.4",
|
||||||
"cache-manager-redis-store": "^2.0.0",
|
"cache-manager-redis-store": "^2.0.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
@ -49,7 +49,8 @@
|
|||||||
"@nestjs/schematics": "^8.0.0",
|
"@nestjs/schematics": "^8.0.0",
|
||||||
"@nestjs/swagger": "^6.3.0",
|
"@nestjs/swagger": "^6.3.0",
|
||||||
"@nestjs/testing": "^9.3.12",
|
"@nestjs/testing": "^9.3.12",
|
||||||
"@types/cache-manager-redis-store": "^2.0.1",
|
"@types/cache-manager": "^4.0.4",
|
||||||
|
"@types/cache-manager-redis-store": "^2.0.3",
|
||||||
"@types/cookie-parser": "^1.4.3",
|
"@types/cookie-parser": "^1.4.3",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@types/express-session": "^1.17.5",
|
"@types/express-session": "^1.17.5",
|
||||||
@ -677,17 +678,89 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
"version": "7.18.6",
|
"version": "7.22.13",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
|
||||||
"integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
|
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/highlight": "^7.18.6"
|
"@babel/highlight": "^7.22.13",
|
||||||
|
"chalk": "^2.4.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/code-frame/node_modules/ansi-styles": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^1.9.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame/node_modules/chalk": {
|
||||||
|
"version": "2.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||||
|
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^3.2.1",
|
||||||
|
"escape-string-regexp": "^1.0.5",
|
||||||
|
"supports-color": "^5.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame/node_modules/color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame/node_modules/color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame/node_modules/escape-string-regexp": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame/node_modules/has-flag": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/code-frame/node_modules/supports-color": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
|
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"has-flag": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/compat-data": {
|
"node_modules/@babel/compat-data": {
|
||||||
"version": "7.21.0",
|
"version": "7.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz",
|
||||||
@ -737,12 +810,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/generator": {
|
"node_modules/@babel/generator": {
|
||||||
"version": "7.21.3",
|
"version": "7.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
|
||||||
"integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==",
|
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.21.3",
|
"@babel/types": "^7.23.0",
|
||||||
"@jridgewell/gen-mapping": "^0.3.2",
|
"@jridgewell/gen-mapping": "^0.3.2",
|
||||||
"@jridgewell/trace-mapping": "^0.3.17",
|
"@jridgewell/trace-mapping": "^0.3.17",
|
||||||
"jsesc": "^2.5.1"
|
"jsesc": "^2.5.1"
|
||||||
@ -794,34 +867,34 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-environment-visitor": {
|
"node_modules/@babel/helper-environment-visitor": {
|
||||||
"version": "7.18.9",
|
"version": "7.22.20",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||||
"integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
|
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-function-name": {
|
"node_modules/@babel/helper-function-name": {
|
||||||
"version": "7.21.0",
|
"version": "7.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
||||||
"integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
|
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/template": "^7.20.7",
|
"@babel/template": "^7.22.15",
|
||||||
"@babel/types": "^7.21.0"
|
"@babel/types": "^7.23.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-hoist-variables": {
|
"node_modules/@babel/helper-hoist-variables": {
|
||||||
"version": "7.18.6",
|
"version": "7.22.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
|
||||||
"integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
|
"integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.18.6"
|
"@babel/types": "^7.22.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@ -880,30 +953,30 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-split-export-declaration": {
|
"node_modules/@babel/helper-split-export-declaration": {
|
||||||
"version": "7.18.6",
|
"version": "7.22.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
|
||||||
"integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
|
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.18.6"
|
"@babel/types": "^7.22.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-string-parser": {
|
"node_modules/@babel/helper-string-parser": {
|
||||||
"version": "7.19.4",
|
"version": "7.22.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
|
||||||
"integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
|
"integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-validator-identifier": {
|
"node_modules/@babel/helper-validator-identifier": {
|
||||||
"version": "7.19.1",
|
"version": "7.22.20",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||||
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
|
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@ -933,13 +1006,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/highlight": {
|
"node_modules/@babel/highlight": {
|
||||||
"version": "7.18.6",
|
"version": "7.22.20",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
|
||||||
"integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
|
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-validator-identifier": "^7.18.6",
|
"@babel/helper-validator-identifier": "^7.22.20",
|
||||||
"chalk": "^2.0.0",
|
"chalk": "^2.4.2",
|
||||||
"js-tokens": "^4.0.0"
|
"js-tokens": "^4.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1018,9 +1091,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.21.3",
|
"version": "7.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
|
||||||
"integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==",
|
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
@ -1203,33 +1276,33 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/template": {
|
"node_modules/@babel/template": {
|
||||||
"version": "7.20.7",
|
"version": "7.22.15",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||||
"integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
|
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.18.6",
|
"@babel/code-frame": "^7.22.13",
|
||||||
"@babel/parser": "^7.20.7",
|
"@babel/parser": "^7.22.15",
|
||||||
"@babel/types": "^7.20.7"
|
"@babel/types": "^7.22.15"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/traverse": {
|
"node_modules/@babel/traverse": {
|
||||||
"version": "7.21.3",
|
"version": "7.23.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
|
||||||
"integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==",
|
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.18.6",
|
"@babel/code-frame": "^7.22.13",
|
||||||
"@babel/generator": "^7.21.3",
|
"@babel/generator": "^7.23.0",
|
||||||
"@babel/helper-environment-visitor": "^7.18.9",
|
"@babel/helper-environment-visitor": "^7.22.20",
|
||||||
"@babel/helper-function-name": "^7.21.0",
|
"@babel/helper-function-name": "^7.23.0",
|
||||||
"@babel/helper-hoist-variables": "^7.18.6",
|
"@babel/helper-hoist-variables": "^7.22.5",
|
||||||
"@babel/helper-split-export-declaration": "^7.18.6",
|
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||||
"@babel/parser": "^7.21.3",
|
"@babel/parser": "^7.23.0",
|
||||||
"@babel/types": "^7.21.3",
|
"@babel/types": "^7.23.0",
|
||||||
"debug": "^4.1.0",
|
"debug": "^4.1.0",
|
||||||
"globals": "^11.1.0"
|
"globals": "^11.1.0"
|
||||||
},
|
},
|
||||||
@ -1247,13 +1320,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/types": {
|
"node_modules/@babel/types": {
|
||||||
"version": "7.21.3",
|
"version": "7.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
|
||||||
"integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==",
|
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-string-parser": "^7.19.4",
|
"@babel/helper-string-parser": "^7.22.5",
|
||||||
"@babel/helper-validator-identifier": "^7.19.1",
|
"@babel/helper-validator-identifier": "^7.22.20",
|
||||||
"to-fast-properties": "^2.0.0"
|
"to-fast-properties": "^2.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -2863,15 +2936,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/cache-manager": {
|
"node_modules/@types/cache-manager": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/cache-manager/-/cache-manager-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/cache-manager/-/cache-manager-4.0.4.tgz",
|
||||||
"integrity": "sha512-fT5FMdzsiSX0AbgnS5gDvHl2Nco0h5zYyjwDQy4yPC7Ww6DeGMVKPRqIZtg9HOXDV2kkc18SL1B0N8f0BecrCA==",
|
"integrity": "sha512-Kyk9uF54w5/JQWLDKr5378euWUPvebknZut6UpsKhO3R7vE5a5o71QxTR2uev1niBgVAoXAR+BCNMU1lipjxWQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/cache-manager-redis-store": {
|
"node_modules/@types/cache-manager-redis-store": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/cache-manager-redis-store/-/cache-manager-redis-store-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/cache-manager-redis-store/-/cache-manager-redis-store-2.0.3.tgz",
|
||||||
"integrity": "sha512-8QuccvcPieh1xM/5kReE76SfdcIdEB0ePc+54ah/NBuK2eG+6O50SX4WKoJX81UxGdW3sh/WlDaDNqjnqxWNsA==",
|
"integrity": "sha512-6OpmRgz0KaTlh6zvqslxEKipCJWmTlI8HTtTzHkrYfPTpsISppaD2tRHaq6U+0jUCf1KvxMpm8RwCp2bnmFlZQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/cache-manager": "*",
|
"@types/cache-manager": "*",
|
||||||
@ -4264,12 +4337,12 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/cache-manager": {
|
"node_modules/cache-manager": {
|
||||||
"version": "5.2.3",
|
"version": "5.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-5.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-5.2.4.tgz",
|
||||||
"integrity": "sha512-9OErI8fksFkxAMJ8Mco0aiZSdphyd90HcKiOMJQncSlU1yq/9lHHxrT8PDayxrmr9IIIZPOAEfXuGSD7g29uog==",
|
"integrity": "sha512-gkuCjug16NdGvKm/sydxGVx17uffrSWcEe2xraBtwRCgdYcFxwJAla4OYpASAZT2yhSoxgDiWL9XH6IAChcZJA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"lru-cache": "^9.1.2"
|
"lru-cache": "^10.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cache-manager-redis-store": {
|
"node_modules/cache-manager-redis-store": {
|
||||||
@ -4310,9 +4383,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cache-manager/node_modules/lru-cache": {
|
"node_modules/cache-manager/node_modules/lru-cache": {
|
||||||
"version": "9.1.2",
|
"version": "10.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz",
|
||||||
"integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==",
|
"integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "14 || >=16.14"
|
"node": "14 || >=16.14"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,7 +46,7 @@
|
|||||||
"@types/jsonwebtoken": "^9.0.1",
|
"@types/jsonwebtoken": "^9.0.1",
|
||||||
"@types/uuid": "^8.3.4",
|
"@types/uuid": "^8.3.4",
|
||||||
"axios": "^1.3.4",
|
"axios": "^1.3.4",
|
||||||
"cache-manager": "^5.2.0",
|
"cache-manager": "^5.2.4",
|
||||||
"cache-manager-redis-store": "^2.0.0",
|
"cache-manager-redis-store": "^2.0.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
@ -69,7 +69,8 @@
|
|||||||
"@nestjs/schematics": "^8.0.0",
|
"@nestjs/schematics": "^8.0.0",
|
||||||
"@nestjs/swagger": "^6.3.0",
|
"@nestjs/swagger": "^6.3.0",
|
||||||
"@nestjs/testing": "^9.3.12",
|
"@nestjs/testing": "^9.3.12",
|
||||||
"@types/cache-manager-redis-store": "^2.0.1",
|
"@types/cache-manager": "^4.0.4",
|
||||||
|
"@types/cache-manager-redis-store": "^2.0.3",
|
||||||
"@types/cookie-parser": "^1.4.3",
|
"@types/cookie-parser": "^1.4.3",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@types/express-session": "^1.17.5",
|
"@types/express-session": "^1.17.5",
|
||||||
|
|||||||
3
dictation_server/redis.sh
Normal file
3
dictation_server/redis.sh
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# source redis.sh で実行することでログインできる
|
||||||
|
source .env.local
|
||||||
|
redis-cli -h $REDIS_HOST -a $REDIS_PASSWORD
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareConsumer, Module } from '@nestjs/common';
|
import { CacheModule, MiddlewareConsumer, Module } from '@nestjs/common';
|
||||||
import { HealthController } from './health.controller';
|
import { HealthController } from './health.controller';
|
||||||
import { ServeStaticModule } from '@nestjs/serve-static';
|
import { ServeStaticModule } from '@nestjs/serve-static';
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
@ -50,7 +50,8 @@ import { WorkflowsService } from './features/workflows/workflows.service';
|
|||||||
import { validate } from './common/validators/env.validator';
|
import { validate } from './common/validators/env.validator';
|
||||||
import { WorkflowsRepositoryModule } from './repositories/workflows/workflows.repository.module';
|
import { WorkflowsRepositoryModule } from './repositories/workflows/workflows.repository.module';
|
||||||
import { TermsModule } from './features/terms/terms.module';
|
import { TermsModule } from './features/terms/terms.module';
|
||||||
|
import { RedisModule } from './gateways/redis/redis.module';
|
||||||
|
import * as redisStore from 'cache-manager-redis-store';
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
ServeStaticModule.forRootAsync({
|
ServeStaticModule.forRootAsync({
|
||||||
@ -103,6 +104,31 @@ import { TermsModule } from './features/terms/terms.module';
|
|||||||
}),
|
}),
|
||||||
inject: [ConfigService],
|
inject: [ConfigService],
|
||||||
}),
|
}),
|
||||||
|
CacheModule.registerAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
useFactory: async (configService: ConfigService) => {
|
||||||
|
const host = configService.getOrThrow<string>('REDIS_HOST');
|
||||||
|
const port = configService.getOrThrow<number>('REDIS_PORT');
|
||||||
|
const password = configService.getOrThrow<string>('REDIS_PASSWORD');
|
||||||
|
if (process.env.STAGE === 'local') {
|
||||||
|
return {
|
||||||
|
store: redisStore,
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
password: password,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
store: redisStore,
|
||||||
|
url: `rediss://${host}:${port}`,
|
||||||
|
password: password,
|
||||||
|
tls: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
inject: [ConfigService],
|
||||||
|
isGlobal: true,
|
||||||
|
}),
|
||||||
NotificationModule,
|
NotificationModule,
|
||||||
NotificationhubModule,
|
NotificationhubModule,
|
||||||
BlobstorageModule,
|
BlobstorageModule,
|
||||||
@ -110,6 +136,7 @@ import { TermsModule } from './features/terms/terms.module';
|
|||||||
SortCriteriaRepositoryModule,
|
SortCriteriaRepositoryModule,
|
||||||
WorktypesRepositoryModule,
|
WorktypesRepositoryModule,
|
||||||
TermsModule,
|
TermsModule,
|
||||||
|
RedisModule,
|
||||||
],
|
],
|
||||||
controllers: [
|
controllers: [
|
||||||
HealthController,
|
HealthController,
|
||||||
|
|||||||
1
dictation_server/src/common/cache/constants.ts
vendored
Normal file
1
dictation_server/src/common/cache/constants.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const ADB2C_PREFIX = "adb2c-external-id:"
|
||||||
19
dictation_server/src/common/cache/index.ts
vendored
Normal file
19
dictation_server/src/common/cache/index.ts
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { ADB2C_PREFIX } from './constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ADB2Cのユーザー格納用のキーを生成する
|
||||||
|
* @param externalId 外部ユーザーID
|
||||||
|
* @returns キャッシュのキー
|
||||||
|
*/
|
||||||
|
export const makeADB2CKey = (externalId: string): string => {
|
||||||
|
return `${ADB2C_PREFIX}${externalId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ADB2Cのユーザー格納用のキーから外部ユーザーIDを取得する
|
||||||
|
* @param key キャッシュのキー
|
||||||
|
* @returns 外部ユーザーID
|
||||||
|
*/
|
||||||
|
export const restoreAdB2cID = (key: string): string => {
|
||||||
|
return key.replace(ADB2C_PREFIX, '');
|
||||||
|
}
|
||||||
@ -37,6 +37,7 @@ import { WorkflowsModule } from '../../features/workflows/workflows.module';
|
|||||||
import { TermsService } from '../../features/terms/terms.service';
|
import { TermsService } from '../../features/terms/terms.service';
|
||||||
import { TermsRepositoryModule } from '../../repositories/terms/terms.repository.module';
|
import { TermsRepositoryModule } from '../../repositories/terms/terms.repository.module';
|
||||||
import { TermsModule } from '../../features/terms/terms.module';
|
import { TermsModule } from '../../features/terms/terms.module';
|
||||||
|
import { CacheModule } from '@nestjs/common';
|
||||||
|
|
||||||
export const makeTestingModule = async (
|
export const makeTestingModule = async (
|
||||||
datasource: DataSource,
|
datasource: DataSource,
|
||||||
@ -76,6 +77,7 @@ export const makeTestingModule = async (
|
|||||||
SortCriteriaRepositoryModule,
|
SortCriteriaRepositoryModule,
|
||||||
WorktypesRepositoryModule,
|
WorktypesRepositoryModule,
|
||||||
TermsRepositoryModule,
|
TermsRepositoryModule,
|
||||||
|
CacheModule.register({ isGlobal: true }),
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
AuthService,
|
AuthService,
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import type {
|
|||||||
IDToken,
|
IDToken,
|
||||||
JwkSignKey,
|
JwkSignKey,
|
||||||
RefreshToken,
|
RefreshToken,
|
||||||
Aadb2cUser,
|
|
||||||
} from './types';
|
} from './types';
|
||||||
import { isIDToken } from './typeguard';
|
import { isIDToken } from './typeguard';
|
||||||
|
|
||||||
@ -14,6 +13,5 @@ export type {
|
|||||||
IDToken,
|
IDToken,
|
||||||
JwkSignKey,
|
JwkSignKey,
|
||||||
RefreshToken,
|
RefreshToken,
|
||||||
Aadb2cUser,
|
|
||||||
};
|
};
|
||||||
export { isIDToken };
|
export { isIDToken };
|
||||||
|
|||||||
@ -56,8 +56,3 @@ export type JwkSignKey = {
|
|||||||
e: string;
|
e: string;
|
||||||
n: string;
|
n: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Aadb2cUser = {
|
|
||||||
displayName: string;
|
|
||||||
mail: string;
|
|
||||||
};
|
|
||||||
|
|||||||
@ -156,6 +156,22 @@ export class EnvValidator {
|
|||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
EMAIL_CONFIRM_LIFETIME: number;
|
EMAIL_CONFIRM_LIFETIME: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
|
REDIS_HOST: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsNumber()
|
||||||
|
REDIS_PORT: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
|
REDIS_PASSWORD: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsNumber()
|
||||||
|
ADB2C_CACHE_TTL: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validate(config: Record<string, unknown>) {
|
export function validate(config: Record<string, unknown>) {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { Aadb2cUser, B2cMetadata, JwkSignKey } from '../../../common/token';
|
import { B2cMetadata, JwkSignKey } from '../../../common/token';
|
||||||
import {
|
import {
|
||||||
AdB2cService,
|
AdB2cService,
|
||||||
ConflictError,
|
ConflictError,
|
||||||
@ -42,7 +42,7 @@ export type AdB2cMockValue = {
|
|||||||
getSignKeySets: JwkSignKey[] | Error;
|
getSignKeySets: JwkSignKey[] | Error;
|
||||||
changePassword: { sub: string } | Error;
|
changePassword: { sub: string } | Error;
|
||||||
createUser: string | ConflictError | Error;
|
createUser: string | ConflictError | Error;
|
||||||
getUser: Aadb2cUser | Error;
|
getUser: AdB2cUser | Error;
|
||||||
getUsers: AdB2cUser[] | Error;
|
getUsers: AdB2cUser[] | Error;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -184,10 +184,10 @@ export const makeAdB2cServiceMock = (value: AdB2cMockValue) => {
|
|||||||
getUser:
|
getUser:
|
||||||
getUser instanceof Error
|
getUser instanceof Error
|
||||||
? jest
|
? jest
|
||||||
.fn<Promise<Aadb2cUser | undefined>, []>()
|
.fn<Promise<AdB2cUser | undefined>, []>()
|
||||||
.mockRejectedValue(getUser)
|
.mockRejectedValue(getUser)
|
||||||
: jest
|
: jest
|
||||||
.fn<Promise<Aadb2cUser | undefined>, []>()
|
.fn<Promise<AdB2cUser | undefined>, []>()
|
||||||
.mockResolvedValue(getUser),
|
.mockResolvedValue(getUser),
|
||||||
getUsers:
|
getUsers:
|
||||||
getUsers instanceof Error
|
getUsers instanceof Error
|
||||||
@ -337,8 +337,8 @@ export const makeDefaultAdB2cMockValue = (): AdB2cMockValue => {
|
|||||||
},
|
},
|
||||||
createUser: '001',
|
createUser: '001',
|
||||||
getUser: {
|
getUser: {
|
||||||
|
id: "xxxx-xxxxx-xxxxx-xxxx",
|
||||||
displayName: 'Hanako Sato',
|
displayName: 'Hanako Sato',
|
||||||
mail: 'hanako@sample.com',
|
|
||||||
},
|
},
|
||||||
getUsers: AdB2cMockUsers,
|
getUsers: AdB2cMockUsers,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import {
|
|||||||
IsPasswordvalid,
|
IsPasswordvalid,
|
||||||
} from '../../../common/validators/encryptionPassword.validator';
|
} from '../../../common/validators/encryptionPassword.validator';
|
||||||
import { IsRoleAuthorDataValid } from '../../../common/validators/roleAuthor.validator';
|
import { IsRoleAuthorDataValid } from '../../../common/validators/roleAuthor.validator';
|
||||||
import { Aadb2cUser } from '../../../common/token';
|
|
||||||
|
|
||||||
export class ConfirmRequest {
|
export class ConfirmRequest {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { AdB2cService } from './adb2c.service';
|
import { AdB2cService } from './adb2c.service';
|
||||||
|
import { RedisModule } from '../redis/redis.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule],
|
imports: [ConfigModule, RedisModule],
|
||||||
exports: [AdB2cService],
|
exports: [AdB2cService],
|
||||||
providers: [AdB2cService],
|
providers: [AdB2cService],
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,14 +1,16 @@
|
|||||||
import { ClientSecretCredential } from '@azure/identity';
|
import { ClientSecretCredential } from '@azure/identity';
|
||||||
import { Client } from '@microsoft/microsoft-graph-client';
|
import { Client } from '@microsoft/microsoft-graph-client';
|
||||||
import { TokenCredentialAuthenticationProvider } from '@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials';
|
import { TokenCredentialAuthenticationProvider } from '@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials';
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { CACHE_MANAGER, Inject, Injectable, Logger } from '@nestjs/common';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Aadb2cUser, B2cMetadata, JwkSignKey } from '../../common/token';
|
import { B2cMetadata, JwkSignKey } from '../../common/token';
|
||||||
import { AdB2cResponse, AdB2cUser } from './types/types';
|
import { AdB2cResponse, AdB2cUser } from './types/types';
|
||||||
import { isPromiseRejectedResult } from './utils/utils';
|
import { isPromiseRejectedResult } from './utils/utils';
|
||||||
import { Context } from '../../common/log';
|
import { Context } from '../../common/log';
|
||||||
import { ADB2C_SIGN_IN_TYPE, MANUAL_RECOVERY_REQUIRED } from '../../constants';
|
import { ADB2C_SIGN_IN_TYPE, MANUAL_RECOVERY_REQUIRED } from '../../constants';
|
||||||
|
import { makeADB2CKey, restoreAdB2cID } from '../../common/cache';
|
||||||
|
import { RedisService } from '../redis/redis.service';
|
||||||
|
|
||||||
export type ConflictError = {
|
export type ConflictError = {
|
||||||
reason: 'email';
|
reason: 'email';
|
||||||
@ -35,9 +37,14 @@ export class AdB2cService {
|
|||||||
this.configService.getOrThrow<string>('TENANT_NAME');
|
this.configService.getOrThrow<string>('TENANT_NAME');
|
||||||
private readonly flowName =
|
private readonly flowName =
|
||||||
this.configService.getOrThrow<string>('SIGNIN_FLOW_NAME');
|
this.configService.getOrThrow<string>('SIGNIN_FLOW_NAME');
|
||||||
|
private readonly ttl =
|
||||||
|
this.configService.getOrThrow<number>('ADB2C_CACHE_TTL');
|
||||||
private graphClient: Client;
|
private graphClient: Client;
|
||||||
|
|
||||||
constructor(private readonly configService: ConfigService) {
|
constructor(
|
||||||
|
private readonly configService: ConfigService,
|
||||||
|
private readonly redisService: RedisService,
|
||||||
|
) {
|
||||||
// ADB2Cへの認証情報
|
// ADB2Cへの認証情報
|
||||||
const credential = new ClientSecretCredential(
|
const credential = new ClientSecretCredential(
|
||||||
this.configService.getOrThrow<string>('ADB2C_TENANT_ID'),
|
this.configService.getOrThrow<string>('ADB2C_TENANT_ID'),
|
||||||
@ -176,11 +183,25 @@ export class AdB2cService {
|
|||||||
* @param externalId 外部ユーザーID
|
* @param externalId 外部ユーザーID
|
||||||
* @returns ユーザ情報
|
* @returns ユーザ情報
|
||||||
*/
|
*/
|
||||||
async getUser(externalId: string): Promise<Aadb2cUser> {
|
async getUser(externalId: string): Promise<AdB2cUser> {
|
||||||
this.logger.log(`[IN] ${this.getUser.name}`);
|
this.logger.log(`[IN] ${this.getUser.name}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await this.graphClient.api(`users/${externalId}`).get();
|
const key = makeADB2CKey(externalId);
|
||||||
|
|
||||||
|
// キャッシュ上に存在していれば、キャッシュから取得する
|
||||||
|
const cachedUser = await this.redisService.get<AdB2cUser>(key);
|
||||||
|
if (cachedUser) {
|
||||||
|
this.logger.log(`[CACHE HIT] id: ${externalId}`);
|
||||||
|
return cachedUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
// キャッシュ上に存在していなければ、ADB2Cから取得してキャッシュに保存する
|
||||||
|
const user = await this.graphClient.api(`users/${externalId}`).get();
|
||||||
|
await this.redisService.set(key, user, this.ttl);
|
||||||
|
this.logger.log(`[ADB2C GET] externalId: ${externalId}`);
|
||||||
|
|
||||||
|
return user;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error(e);
|
this.logger.error(e);
|
||||||
throw e;
|
throw e;
|
||||||
@ -203,12 +224,23 @@ export class AdB2cService {
|
|||||||
} | params: { externalIds:[${externalIds.join(',')}] };`,
|
} | params: { externalIds:[${externalIds.join(',')}] };`,
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
const keys = externalIds.map((externalId) => makeADB2CKey(externalId));
|
||||||
TODO [Task2002] 現状の実装だと1リクエストで最大15パラメータまでしか設定できないため、
|
const cache = await this.redisService.mget<AdB2cUser>(keys);
|
||||||
別タスクでアカウント単位の検索用パラメータを用いて取得するように修正する。
|
|
||||||
タスク 2002: B2Cからの名前取得をより低コストで行えるように修正する
|
// キャッシュ上に存在していれば、キャッシュから取得する
|
||||||
*/
|
const cachedUsers = cache.flatMap((x) => (x.value ? [x.value] : []));
|
||||||
const chunkExternalIds = splitArrayInChunksOfFifteen(externalIds);
|
if (cachedUsers.length > 0) {
|
||||||
|
this.logger.log(
|
||||||
|
`[CACHE HIT] ids: ${cachedUsers.map((x) => x.id).join(',')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// キャッシュ上に存在していなければ、ADB2Cから取得する
|
||||||
|
const queryExternalIds = cache
|
||||||
|
.filter((x) => x.value === null)
|
||||||
|
.map((x) => restoreAdB2cID(x.key));
|
||||||
|
|
||||||
|
const chunkExternalIds = splitArrayInChunksOfFifteen(queryExternalIds);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const b2cUsers: AdB2cUser[] = [];
|
const b2cUsers: AdB2cUser[] = [];
|
||||||
@ -221,9 +253,22 @@ export class AdB2cService {
|
|||||||
.get();
|
.get();
|
||||||
|
|
||||||
b2cUsers.push(...res.value);
|
b2cUsers.push(...res.value);
|
||||||
|
|
||||||
|
// 取得したユーザーをキャッシュに保存する
|
||||||
|
const users = res.value.map((user) => {
|
||||||
|
const key = makeADB2CKey(user.id);
|
||||||
|
return {
|
||||||
|
key: key,
|
||||||
|
value: user,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
await this.redisService.mset(users, this.ttl);
|
||||||
|
this.logger.log(
|
||||||
|
`[ADB2C GET] externalIds: ${res.value?.map((x) => x.id).join(',')}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return b2cUsers;
|
return [...cachedUsers, ...b2cUsers];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error(e);
|
this.logger.error(e);
|
||||||
const { statusCode } = e;
|
const { statusCode } = e;
|
||||||
@ -249,6 +294,15 @@ export class AdB2cService {
|
|||||||
try {
|
try {
|
||||||
// https://learn.microsoft.com/en-us/graph/api/user-delete?view=graph-rest-1.0&tabs=javascript#example
|
// https://learn.microsoft.com/en-us/graph/api/user-delete?view=graph-rest-1.0&tabs=javascript#example
|
||||||
await this.graphClient.api(`users/${externalId}`).delete();
|
await this.graphClient.api(`users/${externalId}`).delete();
|
||||||
|
this.logger.log(`[ADB2C DELETE] externalId: ${externalId}`);
|
||||||
|
|
||||||
|
// キャッシュからも削除する
|
||||||
|
try {
|
||||||
|
await this.redisService.del(makeADB2CKey(externalId));
|
||||||
|
} catch (e) {
|
||||||
|
// キャッシュからの削除に失敗しても、ADB2Cからの削除は成功しているため例外はスローしない
|
||||||
|
this.logger.error(`error=${e}`);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error(`error=${e}`);
|
this.logger.error(`error=${e}`);
|
||||||
throw e;
|
throw e;
|
||||||
@ -270,18 +324,27 @@ export class AdB2cService {
|
|||||||
try {
|
try {
|
||||||
// 複数ユーザーを一括削除する方法がないため、1人ずつで削除を行う(rate limitに大きな影響がないこと確認済)
|
// 複数ユーザーを一括削除する方法がないため、1人ずつで削除を行う(rate limitに大きな影響がないこと確認済)
|
||||||
const results = await Promise.allSettled(
|
const results = await Promise.allSettled(
|
||||||
externalIds.map(
|
externalIds.map(async (externalId) => {
|
||||||
async (x) => await this.graphClient.api(`users/${x}`).delete(),
|
await this.graphClient.api(`users/${externalId}`).delete();
|
||||||
),
|
this.logger.log(`[ADB2C DELETE] externalId: ${externalId}`);
|
||||||
);
|
|
||||||
|
|
||||||
// 失敗したプロミスを抽出
|
// キャッシュからも削除する
|
||||||
const failedPromises = results.filter(
|
try {
|
||||||
(result) => result.status === 'rejected',
|
await this.redisService.del(makeADB2CKey(externalId));
|
||||||
|
} catch (e) {
|
||||||
|
// キャッシュからの削除に失敗しても、ADB2Cからの削除は成功しているため例外はスローしない
|
||||||
|
this.logger.error(`error=${e}`);
|
||||||
|
}
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 失敗したプロミスのエラーをログに記録
|
// 失敗したプロミスのエラーをログに記録
|
||||||
failedPromises.forEach((result, index) => {
|
results.forEach((result, index) => {
|
||||||
|
// statusがrejectedでない場合は、エラーが発生していないためログに記録しない
|
||||||
|
if (result.status !== 'rejected') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const failedId = externalIds[index];
|
const failedId = externalIds[index];
|
||||||
if (isPromiseRejectedResult(result)) {
|
if (isPromiseRejectedResult(result)) {
|
||||||
const error = result.reason.toString();
|
const error = result.reason.toString();
|
||||||
@ -304,7 +367,8 @@ export class AdB2cService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO [Task2002] 文字列の配列を15要素ずつ区切る(この処理も別タスクで削除予定)
|
// ID指定で検索できる最大数は15のため、文字列の配列を15要素ずつ区切る。
|
||||||
|
// PJ状況的にAzure AD B2C側のユーザーパラメータを増やして一括Queryできるようにする事が難しいので個別にQueryする。[2023/10/29]
|
||||||
const splitArrayInChunksOfFifteen = (arr: string[]): string[][] => {
|
const splitArrayInChunksOfFifteen = (arr: string[]): string[][] => {
|
||||||
const result: string[][] = [];
|
const result: string[][] = [];
|
||||||
const chunkSize = 15; // SDKの制限数
|
const chunkSize = 15; // SDKの制限数
|
||||||
|
|||||||
8
dictation_server/src/gateways/redis/redis.module.ts
Normal file
8
dictation_server/src/gateways/redis/redis.module.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { CacheModule, Module } from '@nestjs/common';
|
||||||
|
import { RedisService } from './redis.service';
|
||||||
|
@Module({
|
||||||
|
imports: [],
|
||||||
|
providers: [RedisService],
|
||||||
|
exports: [RedisService],
|
||||||
|
})
|
||||||
|
export class RedisModule {}
|
||||||
110
dictation_server/src/gateways/redis/redis.service.ts
Normal file
110
dictation_server/src/gateways/redis/redis.service.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import {
|
||||||
|
CACHE_MANAGER,
|
||||||
|
Inject,
|
||||||
|
Injectable,
|
||||||
|
InternalServerErrorException,
|
||||||
|
Logger,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { Cache } from 'cache-manager';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RedisService {
|
||||||
|
private readonly logger = new Logger(RedisService.name);
|
||||||
|
|
||||||
|
constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* キーに対応する値を設定する。
|
||||||
|
* @param key キー
|
||||||
|
* @param value キーに対応する値
|
||||||
|
* @param ttl 有効期限(秒)
|
||||||
|
*/
|
||||||
|
async set(
|
||||||
|
key: string,
|
||||||
|
value: unknown,
|
||||||
|
ttl?: number | undefined,
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
// cache-manager-redis-store がcache-managerのset形式と不一致な値の渡し方を採用しているため、
|
||||||
|
// @types/cache-managerのset形式を使用すると、redisに値が保存されない。
|
||||||
|
// そのため、{ttl : ttl} をany型として渡すことで、強引にcache-manager-redis-storeのsetを使用する。
|
||||||
|
// https://www.npmjs.com/package/cache-manager
|
||||||
|
await this.cacheManager.set(key, value, { ttl: ttl } as any);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 複数のキーとそのキーに対応する値を設定する。
|
||||||
|
* @template T
|
||||||
|
* @param records キーとそのキーに対応する値のペアの配列
|
||||||
|
* @param ttl 有効期限(秒)
|
||||||
|
*/
|
||||||
|
async mset<T>(
|
||||||
|
records: { key: string; value: T }[],
|
||||||
|
ttl?: number | undefined,
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
// cache-manager-redis-store のmsetが壊れており、利用できないため、
|
||||||
|
// 一つずつsetする。
|
||||||
|
for await (const record of records) {
|
||||||
|
await this.set(record.key, record.value, ttl);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* キーに対応する値を取得する。
|
||||||
|
* @template T
|
||||||
|
* @param key キー
|
||||||
|
* @returns キーに対応する値
|
||||||
|
*/
|
||||||
|
async get<T>(key: string): Promise<T | undefined> {
|
||||||
|
try {
|
||||||
|
const value = await this.cacheManager.get<T>(key);
|
||||||
|
return value;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 複数のキーに対して、対応する値を取得する。
|
||||||
|
* キーに対応する値がなかった場合、valueにはnullがセットされる。
|
||||||
|
* @param keys キーの配列
|
||||||
|
* @returns キーとそのキーに対応する値のペアの配列
|
||||||
|
*/
|
||||||
|
async mget<T>(keys: string[]): Promise<{ key: string; value: T | null }[]> {
|
||||||
|
if (keys.length === 0) return []; // mget操作は0件の時エラーとなるため、0件は特別扱いする
|
||||||
|
|
||||||
|
try {
|
||||||
|
const records = await this.cacheManager.store.mget(...keys);
|
||||||
|
// getで取得した順序とKeysの順序は一致するはずなので、indexを利用してペアになるよう加工する
|
||||||
|
return records.map((record, index) => {
|
||||||
|
return {
|
||||||
|
key: keys[index],
|
||||||
|
value: record ? (record as T) : null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* キーに対応する値を削除する。
|
||||||
|
* @param key キー
|
||||||
|
*/
|
||||||
|
async del(key: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
await this.cacheManager.del(key);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user