From f03a502d26fd2e870675cfab16f12f7c221bc3b6 Mon Sep 17 00:00:00 2001 From: Pauline Date: Wed, 10 Dec 2025 23:44:47 +0900 Subject: [PATCH 01/25] Update yarn.lock file --- yarn.lock | 528 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 416 insertions(+), 112 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8b4eeb3ae..605ea0131 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2117,9 +2117,9 @@ __metadata: languageName: node linkType: hard -"@cypress/request@npm:^2.88.5": - version: 2.88.6 - resolution: "@cypress/request@npm:2.88.6" +"@cypress/request@npm:^3.0.9": + version: 3.0.9 + resolution: "@cypress/request@npm:3.0.9" dependencies: aws-sign2: ~0.7.0 aws4: ^1.8.0 @@ -2127,20 +2127,19 @@ __metadata: combined-stream: ~1.0.6 extend: ~3.0.2 forever-agent: ~0.6.1 - form-data: ~2.3.2 - har-validator: ~5.1.3 - http-signature: ~1.2.0 + form-data: ~4.0.4 + http-signature: ~1.4.0 is-typedarray: ~1.0.0 isstream: ~0.1.2 json-stringify-safe: ~5.0.1 mime-types: ~2.1.19 performance-now: ^2.1.0 - qs: ~6.5.2 + qs: 6.14.0 safe-buffer: ^5.1.2 - tough-cookie: ~2.5.0 + tough-cookie: ^5.0.0 tunnel-agent: ^0.6.0 uuid: ^8.3.2 - checksum: 31d4586e212e20955091cdf67a0de746c2587f97f7ff0e2b8e84e772d98a73a1c6051c9090dd938ecdf595c37aec9193b6211faf754b5fcf43c7a151b4d274b7 + checksum: d22327b6fc52c8c6bbfa82b3a5b40f890f869da615c0dd494b3bf2c016eb3b1875082b451571d6b109a84c7e73a5869e9a997cdb71716e46900f607002e6afd0 languageName: node linkType: hard @@ -2589,13 +2588,6 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^14.14.31": - version: 14.17.12 - resolution: "@types/node@npm:14.17.12" - checksum: 7efbce3781a0ea5d7a39bca3c5ed9c4e4d99fed3483fb2a89670aeb049cd9d25f1ddecd8fb58420fd92de371278721ddd6e3ad95c4f55992592f1ac5d1dedf98 - languageName: node - linkType: hard - "@types/parse-json@npm:^4.0.0": version: 4.0.0 resolution: "@types/parse-json@npm:4.0.0" @@ -2603,10 +2595,10 @@ __metadata: languageName: node linkType: hard -"@types/sinonjs__fake-timers@npm:^6.0.2": - version: 6.0.3 - resolution: "@types/sinonjs__fake-timers@npm:6.0.3" - checksum: 6def7829e18b6f23506f7c4d6843e9ab80ae2ae983e2321c10fd816667297af519d96819aa436f30ae1a9a35b75bfbe4489f310cee1bf0983eede1ba3248b44c +"@types/sinonjs__fake-timers@npm:8.1.1": + version: 8.1.1 + resolution: "@types/sinonjs__fake-timers@npm:8.1.1" + checksum: ca09d54d47091d87020824a73f026300fa06b17cd9f2f9b9387f28b549364b141ef194ee28db762f6588de71d8febcd17f753163cb7ea116b8387c18e80ebd5c languageName: node linkType: hard @@ -2624,6 +2616,13 @@ __metadata: languageName: node linkType: hard +"@types/tmp@npm:^0.2.3": + version: 0.2.6 + resolution: "@types/tmp@npm:0.2.6" + checksum: 0b24bb6040cc289440a609e10ec99a704978c890a5828ff151576489090b2257ce2e2570b0f320ace9c8099c3642ea6221fbdf6d8f2e22b7cd1f4fbf6e989e3e + languageName: node + linkType: hard + "@types/webpack-sources@npm:^0.1.5": version: 0.1.9 resolution: "@types/webpack-sources@npm:0.1.9" @@ -3225,6 +3224,20 @@ __metadata: languageName: node linkType: hard +"async-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-function@npm:1.0.0" + checksum: 9102e246d1ed9b37ac36f57f0a6ca55226876553251a31fc80677e71471f463a54c872dc78d5d7f80740c8ba624395cccbe8b60f7b690c4418f487d8e9fd1106 + languageName: node + linkType: hard + +"async-generator-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-generator-function@npm:1.0.0" + checksum: 74a71a4a2dd7afd06ebb612f6d612c7f4766a351bedffde466023bf6dae629e46b0d2cd38786239e0fbf245de0c7df76035465e16d1213774a0efb22fec0d713 + languageName: node + linkType: hard + "async-limiter@npm:~1.0.0": version: 1.0.1 resolution: "async-limiter@npm:1.0.1" @@ -3241,13 +3254,6 @@ __metadata: languageName: node linkType: hard -"async@npm:^3.2.0": - version: 3.2.1 - resolution: "async@npm:3.2.1" - checksum: fcd6dc73d36bdb0e5bf978ea27832136cc8116cef15e0a2dc9a5c63f362c5f82363996a349bf3046d590587b7f30614ac340a4f3db6d48780a07f40f81941e0c - languageName: node - linkType: hard - "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -3616,7 +3622,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.4.3, buffer@npm:^5.5.0": +"buffer@npm:^5.4.3, buffer@npm:^5.5.0, buffer@npm:^5.7.1": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -3712,6 +3718,16 @@ __metadata: languageName: node linkType: hard +"call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind-apply-helpers@npm:1.0.2" + dependencies: + es-errors: ^1.3.0 + function-bind: ^1.1.2 + checksum: b2863d74fcf2a6948221f65d95b91b4b2d90cfe8927650b506141e669f7d5de65cea191bf788838bc40d13846b7886c5bc5c84ab96c3adbcf88ad69a72fcdc6b + languageName: node + linkType: hard + "call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": version: 1.0.2 resolution: "call-bind@npm:1.0.2" @@ -3735,6 +3751,16 @@ __metadata: languageName: node linkType: hard +"call-bound@npm:^1.0.2": + version: 1.0.4 + resolution: "call-bound@npm:1.0.4" + dependencies: + call-bind-apply-helpers: ^1.0.2 + get-intrinsic: ^1.3.0 + checksum: 2f6399488d1c272f56306ca60ff696575e2b7f31daf23bc11574798c84d9f2759dceb0cb1f471a85b77f28962a7ac6411f51d283ea2e45319009a19b6ccab3b2 + languageName: node + linkType: hard + "callsites@npm:^3.0.0": version: 3.1.0 resolution: "callsites@npm:3.1.0" @@ -3817,13 +3843,6 @@ __metadata: languageName: node linkType: hard -"check-more-types@npm:^2.24.0": - version: 2.24.0 - resolution: "check-more-types@npm:2.24.0" - checksum: b09080ec3404d20a4b0ead828994b2e5913236ef44ed3033a27062af0004cf7d2091fbde4b396bf13b7ce02fb018bc9960b48305e6ab2304cd82d73ed7a51ef4 - languageName: node - linkType: hard - "chokidar@npm:>=3.0.0 <4.0.0, chokidar@npm:^3.0.0, chokidar@npm:^3.5.1": version: 3.5.2 resolution: "chokidar@npm:3.5.2" @@ -3880,10 +3899,10 @@ __metadata: languageName: node linkType: hard -"ci-info@npm:^3.1.1": - version: 3.2.0 - resolution: "ci-info@npm:3.2.0" - checksum: c68995a94e95ce3f233ff845e62dfc56f2e8ff1e3f5c1361bcdd520cbbc9726d8a54cbc1a685cb9ee19c3c5e71a1dade6dda23eb364b59b8e6c32508a9b761bc +"ci-info@npm:^4.1.0": + version: 4.3.1 + resolution: "ci-info@npm:4.3.1" + checksum: 66c159d92648e8a07acab0a3a0681bff6ccc39aa44916263208c4d97bbbeedbbc886d7611fd30c21df1aa624ce3c6fcdfde982e74689e3e014e064e1d0805f94 languageName: node linkType: hard @@ -3919,17 +3938,16 @@ __metadata: languageName: node linkType: hard -"cli-table3@npm:~0.6.0": - version: 0.6.0 - resolution: "cli-table3@npm:0.6.0" +"cli-table3@npm:0.6.1": + version: 0.6.1 + resolution: "cli-table3@npm:0.6.1" dependencies: - colors: ^1.1.2 - object-assign: ^4.1.0 + colors: 1.4.0 string-width: ^4.2.0 dependenciesMeta: colors: optional: true - checksum: 98682a2d3eef5ad07d34a08f90398d0640004e28ecf8eb59006436f11ed7b4d453db09f46c2ea880618fbd61fee66321b3b3ee1b20276bc708b6baf6f9663d75 + checksum: 956e175f8eb019c26465b9f1e51121c08d8978e2aab04be7f8520ea8a4e67906fcbd8516dfb77e386ae3730ef0281aa21a65613dffbfa3d62969263252bd25a9 languageName: node linkType: hard @@ -4054,14 +4072,14 @@ __metadata: languageName: node linkType: hard -"colors@npm:^1.1.2": +"colors@npm:1.4.0": version: 1.4.0 resolution: "colors@npm:1.4.0" checksum: 98aa2c2418ad87dedf25d781be69dc5fc5908e279d9d30c34d8b702e586a0474605b3a189511482b9d5ed0d20c867515d22749537f7bc546256c6014f3ebdcec languageName: node linkType: hard -"combined-stream@npm:^1.0.6, combined-stream@npm:~1.0.6": +"combined-stream@npm:^1.0.6, combined-stream@npm:^1.0.8, combined-stream@npm:~1.0.6": version: 1.0.8 resolution: "combined-stream@npm:1.0.8" dependencies: @@ -4077,10 +4095,10 @@ __metadata: languageName: node linkType: hard -"commander@npm:^5.1.0": - version: 5.1.0 - resolution: "commander@npm:5.1.0" - checksum: 0b7fec1712fbcc6230fcb161d8d73b4730fa91a21dc089515489402ad78810547683f058e2a9835929c212fead1d6a6ade70db28bbb03edbc2829a9ab7d69447 +"commander@npm:^6.2.1": + version: 6.2.1 + resolution: "commander@npm:6.2.1" + checksum: d7090410c0de6bc5c67d3ca41c41760d6d268f3c799e530aafb73b7437d1826bbf0d2a3edac33f8b57cc9887b4a986dce307fa5557e109be40eadb7c43b21742 languageName: node linkType: hard @@ -4610,54 +4628,55 @@ __metadata: languageName: node linkType: hard -"cypress@npm:8.3.0": - version: 8.3.0 - resolution: "cypress@npm:8.3.0" +"cypress@npm:^15.7.0": + version: 15.7.1 + resolution: "cypress@npm:15.7.1" dependencies: - "@cypress/request": ^2.88.5 + "@cypress/request": ^3.0.9 "@cypress/xvfb": ^1.2.4 - "@types/node": ^14.14.31 - "@types/sinonjs__fake-timers": ^6.0.2 + "@types/sinonjs__fake-timers": 8.1.1 "@types/sizzle": ^2.3.2 + "@types/tmp": ^0.2.3 arch: ^2.2.0 blob-util: ^2.0.2 bluebird: ^3.7.2 + buffer: ^5.7.1 cachedir: ^2.3.0 chalk: ^4.1.0 - check-more-types: ^2.24.0 + ci-info: ^4.1.0 cli-cursor: ^3.1.0 - cli-table3: ~0.6.0 - commander: ^5.1.0 + cli-table3: 0.6.1 + commander: ^6.2.1 common-tags: ^1.8.0 dayjs: ^1.10.4 - debug: ^4.3.2 + debug: ^4.3.4 enquirer: ^2.3.6 - eventemitter2: ^6.4.3 + eventemitter2: 6.4.7 execa: 4.1.0 executable: ^4.1.1 extract-zip: 2.0.1 figures: ^3.2.0 fs-extra: ^9.1.0 - getos: ^3.2.1 - is-ci: ^3.0.0 + hasha: 5.2.2 is-installed-globally: ~0.4.0 - lazy-ass: ^1.6.0 listr2: ^3.8.3 lodash: ^4.17.21 log-symbols: ^4.0.0 - minimist: ^1.2.5 + minimist: ^1.2.8 ospath: ^1.2.2 pretty-bytes: ^5.6.0 - ramda: ~0.27.1 + process: ^0.11.10 + proxy-from-env: 1.0.0 request-progress: ^3.0.0 supports-color: ^8.1.1 - tmp: ~0.2.1 + systeminformation: 5.27.7 + tmp: ~0.2.4 + tree-kill: 1.2.2 untildify: ^4.0.0 - url: ^0.11.0 yauzl: ^2.10.0 bin: cypress: bin/cypress - checksum: acffc5ee3a347c3dc0989b43509f1d56ef2089aab6d8a2a8087f91dd93fd81d86939fa71a6df4a6690014385650e24599777bc5513c66ef7acfe34e6733dc4d6 + checksum: 3c542667ebdb7ce7514137de5a6a0fbf081ec7f58c438ad99adb4a6514692d3b1a448aed449781f161ae3109b98a9ceb5e1fb19b5f71e56647bfc98523e0e9b1 languageName: node linkType: hard @@ -4693,7 +4712,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.3.2, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:~4.3.1": +"debug@npm:4, debug@npm:4.3.2, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:~4.3.1": version: 4.3.2 resolution: "debug@npm:4.3.2" dependencies: @@ -4979,6 +4998,17 @@ __metadata: languageName: node linkType: hard +"dunder-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" + dependencies: + call-bind-apply-helpers: ^1.0.1 + es-errors: ^1.3.0 + gopd: ^1.2.0 + checksum: 149207e36f07bd4941921b0ca929e3a28f1da7bd6b6ff8ff7f4e2f2e460675af4576eeba359c635723dc189b64cdd4787e0255897d5b135ccc5d15cb8685fc90 + languageName: node + linkType: hard + "ecc-jsbn@npm:~0.1.1": version: 0.1.2 resolution: "ecc-jsbn@npm:0.1.2" @@ -5157,6 +5187,13 @@ __metadata: languageName: node linkType: hard +"es-define-property@npm:^1.0.1": + version: 1.0.1 + resolution: "es-define-property@npm:1.0.1" + checksum: 0512f4e5d564021c9e3a644437b0155af2679d10d80f21adaf868e64d30efdfbd321631956f20f42d655fedb2e3a027da479fad3fa6048f768eb453a80a5f80a + languageName: node + linkType: hard + "es-errors@npm:^1.3.0": version: 1.3.0 resolution: "es-errors@npm:1.3.0" @@ -5171,6 +5208,27 @@ __metadata: languageName: node linkType: hard +"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": + version: 1.1.1 + resolution: "es-object-atoms@npm:1.1.1" + dependencies: + es-errors: ^1.3.0 + checksum: 214d3767287b12f36d3d7267ef342bbbe1e89f899cfd67040309fc65032372a8e60201410a99a1645f2f90c1912c8c49c8668066f6bdd954bcd614dda2e3da97 + languageName: node + linkType: hard + +"es-set-tostringtag@npm:^2.1.0": + version: 2.1.0 + resolution: "es-set-tostringtag@npm:2.1.0" + dependencies: + es-errors: ^1.3.0 + get-intrinsic: ^1.2.6 + has-tostringtag: ^1.0.2 + hasown: ^2.0.2 + checksum: 789f35de4be3dc8d11fdcb91bc26af4ae3e6d602caa93299a8c45cf05d36cc5081454ae2a6d3afa09cceca214b76c046e4f8151e092e6fc7feeb5efb9e794fc6 + languageName: node + linkType: hard + "esbuild-android-arm64@npm:0.13.8": version: 0.13.8 resolution: "esbuild-android-arm64@npm:0.13.8" @@ -5445,10 +5503,10 @@ __metadata: languageName: node linkType: hard -"eventemitter2@npm:^6.4.3": - version: 6.4.4 - resolution: "eventemitter2@npm:6.4.4" - checksum: b5e707039973d5a770bc4c64255604df66df3a1f63389dccb7118af163b9f790ca7596463d7868426339301ad9de5ef1c3f4a9c7ac3b93874c5ca792916dede1 +"eventemitter2@npm:6.4.7": + version: 6.4.7 + resolution: "eventemitter2@npm:6.4.7" + checksum: 1b36a77e139d6965ebf3a36c01fa00c089ae6b80faa1911e52888f40b3a7057b36a2cc45dcd1ad87cda3798fe7b97a0aabcbb8175a8b96092a23bb7d0f039e66 languageName: node linkType: hard @@ -5827,6 +5885,19 @@ __metadata: languageName: node linkType: hard +"form-data@npm:~4.0.4": + version: 4.0.5 + resolution: "form-data@npm:4.0.5" + dependencies: + asynckit: ^0.4.0 + combined-stream: ^1.0.8 + es-set-tostringtag: ^2.1.0 + hasown: ^2.0.2 + mime-types: ^2.1.12 + checksum: af8328413c16d0cded5fccc975a44d227c5120fd46a9e81de8acf619d43ed838414cc6d7792195b30b248f76a65246949a129a4dadd148721948f90cd6d4fb69 + languageName: node + linkType: hard + "forwarded@npm:0.2.0": version: 0.2.0 resolution: "forwarded@npm:0.2.0" @@ -5957,6 +6028,13 @@ __metadata: languageName: node linkType: hard +"generator-function@npm:^2.0.0": + version: 2.0.1 + resolution: "generator-function@npm:2.0.1" + checksum: 3bf87f7b0230de5d74529677e6c3ceb3b7b5d9618b5a22d92b45ce3876defbaf5a77791b25a61b0fa7d13f95675b5ff67a7769f3b9af33f096e34653519e873d + languageName: node + linkType: hard + "gensync@npm:^1.0.0-beta.2": version: 1.0.0-beta.2 resolution: "gensync@npm:1.0.0-beta.2" @@ -5995,6 +6073,37 @@ __metadata: languageName: node linkType: hard +"get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.3.0": + version: 1.3.1 + resolution: "get-intrinsic@npm:1.3.1" + dependencies: + async-function: ^1.0.0 + async-generator-function: ^1.0.0 + call-bind-apply-helpers: ^1.0.2 + es-define-property: ^1.0.1 + es-errors: ^1.3.0 + es-object-atoms: ^1.1.1 + function-bind: ^1.1.2 + generator-function: ^2.0.0 + get-proto: ^1.0.1 + gopd: ^1.2.0 + has-symbols: ^1.1.0 + hasown: ^2.0.2 + math-intrinsics: ^1.1.0 + checksum: c02b3b6a445f9cd53e14896303794ac60f9751f58a69099127248abdb0251957174c6524245fc68579dc8e6a35161d3d94c93e665f808274716f4248b269436a + languageName: node + linkType: hard + +"get-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "get-proto@npm:1.0.1" + dependencies: + dunder-proto: ^1.0.1 + es-object-atoms: ^1.0.0 + checksum: 4fc96afdb58ced9a67558698b91433e6b037aaa6f1493af77498d7c85b141382cf223c0e5946f334fb328ee85dfe6edd06d218eaf09556f4bc4ec6005d7f5f7b + languageName: node + linkType: hard + "get-stream@npm:^4.0.0": version: 4.1.0 resolution: "get-stream@npm:4.1.0" @@ -6013,15 +6122,6 @@ __metadata: languageName: node linkType: hard -"getos@npm:^3.2.1": - version: 3.2.1 - resolution: "getos@npm:3.2.1" - dependencies: - async: ^3.2.0 - checksum: 42fd78a66d47cebd3e09de5566cc0044e034b08f4a000a310dbd89a77b02c65d8f4002554bfa495ea5bdc4fa9d515f5ac785a7cc474ba45383cc697f865eeaf1 - languageName: node - linkType: hard - "getpass@npm:^0.1.1": version: 0.1.7 resolution: "getpass@npm:0.1.7" @@ -6132,6 +6232,13 @@ __metadata: languageName: node linkType: hard +"gopd@npm:^1.2.0": + version: 1.2.0 + resolution: "gopd@npm:1.2.0" + checksum: cc6d8e655e360955bdccaca51a12a474268f95bb793fc3e1f2bdadb075f28bfd1fd988dab872daf77a61d78cbaf13744bc8727a17cfb1d150d76047d805375f3 + languageName: node + linkType: hard + "graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.3, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": version: 4.2.8 resolution: "graceful-fs@npm:4.2.8" @@ -6214,6 +6321,13 @@ __metadata: languageName: node linkType: hard +"has-symbols@npm:^1.1.0": + version: 1.1.0 + resolution: "has-symbols@npm:1.1.0" + checksum: b2316c7302a0e8ba3aaba215f834e96c22c86f192e7310bdf689dd0e6999510c89b00fbc5742571507cebf25764d68c988b3a0da217369a73596191ac0ce694b + languageName: node + linkType: hard + "has-tostringtag@npm:^1.0.0": version: 1.0.0 resolution: "has-tostringtag@npm:1.0.0" @@ -6223,6 +6337,15 @@ __metadata: languageName: node linkType: hard +"has-tostringtag@npm:^1.0.2": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" + dependencies: + has-symbols: ^1.0.3 + checksum: 999d60bb753ad714356b2c6c87b7fb74f32463b8426e159397da4bde5bca7e598ab1073f4d8d4deafac297f2eb311484cd177af242776bf05f0d11565680468d + languageName: node + linkType: hard + "has-unicode@npm:^2.0.0": version: 2.0.1 resolution: "has-unicode@npm:2.0.1" @@ -6239,6 +6362,16 @@ __metadata: languageName: node linkType: hard +"hasha@npm:5.2.2": + version: 5.2.2 + resolution: "hasha@npm:5.2.2" + dependencies: + is-stream: ^2.0.0 + type-fest: ^0.8.0 + checksum: 06cc474bed246761ff61c19d629977eb5f53fa817be4313a255a64ae0f433e831a29e83acb6555e3f4592b348497596f1d1653751008dda4f21c9c21ca60ac5a + languageName: node + linkType: hard + "hasown@npm:^2.0.0": version: 2.0.1 resolution: "hasown@npm:2.0.1" @@ -6248,6 +6381,15 @@ __metadata: languageName: node linkType: hard +"hasown@npm:^2.0.2": + version: 2.0.2 + resolution: "hasown@npm:2.0.2" + dependencies: + function-bind: ^1.1.2 + checksum: e8516f776a15149ca6c6ed2ae3110c417a00b62260e222590e54aa367cbcd6ed99122020b37b7fbdf05748df57b265e70095d7bf35a47660587619b15ffb93db + languageName: node + linkType: hard + "hdr-histogram-js@npm:^2.0.1": version: 2.0.1 resolution: "hdr-histogram-js@npm:2.0.1" @@ -6405,6 +6547,17 @@ __metadata: languageName: node linkType: hard +"http-signature@npm:~1.4.0": + version: 1.4.0 + resolution: "http-signature@npm:1.4.0" + dependencies: + assert-plus: ^1.0.0 + jsprim: ^2.0.2 + sshpk: ^1.18.0 + checksum: f07f4cc0481e4461c68b9b7d1a25bf2ec4cef8e0061812b989c1e64f504b4b11f75f88022102aea05d25d47a87789599f1a310b1f8a56945a50c93e54c7ee076 + languageName: node + linkType: hard + "https-proxy-agent@npm:5.0.0, https-proxy-agent@npm:^5.0.0": version: 5.0.0 resolution: "https-proxy-agent@npm:5.0.0" @@ -6666,17 +6819,6 @@ __metadata: languageName: node linkType: hard -"is-ci@npm:^3.0.0": - version: 3.0.0 - resolution: "is-ci@npm:3.0.0" - dependencies: - ci-info: ^3.1.1 - bin: - is-ci: bin.js - checksum: 4b45aef32dd42dcb1f6fb3cd4b3a7ee7e18ea47516d2129005f46c3f36983506bb471382bac890973cf48a2f60d926a24461674ca2d9dc10744d82d4a876c26b - languageName: node - linkType: hard - "is-core-module@npm:^2.2.0": version: 2.6.0 resolution: "is-core-module@npm:2.6.0" @@ -7084,6 +7226,13 @@ __metadata: languageName: node linkType: hard +"json-schema@npm:0.4.0": + version: 0.4.0 + resolution: "json-schema@npm:0.4.0" + checksum: 66389434c3469e698da0df2e7ac5a3281bcff75e797a5c127db7c5b56270e01ae13d9afa3c03344f76e32e81678337a8c912bdbb75101c62e487dc3778461d72 + languageName: node + linkType: hard + "json-stringify-safe@npm:~5.0.1": version: 5.0.1 resolution: "json-stringify-safe@npm:5.0.1" @@ -7180,6 +7329,18 @@ __metadata: languageName: node linkType: hard +"jsprim@npm:^2.0.2": + version: 2.0.2 + resolution: "jsprim@npm:2.0.2" + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.4.0 + verror: 1.10.0 + checksum: d175f6b1991e160cb0aa39bc857da780e035611986b5492f32395411879fdaf4e513d98677f08f7352dac93a16b66b8361c674b86a3fa406e2e7af6b26321838 + languageName: node + linkType: hard + "karma-chrome-launcher@npm:^3.1.0": version: 3.1.0 resolution: "karma-chrome-launcher@npm:3.1.0" @@ -7300,13 +7461,6 @@ __metadata: languageName: node linkType: hard -"lazy-ass@npm:^1.6.0": - version: 1.6.0 - resolution: "lazy-ass@npm:1.6.0" - checksum: 5a3ebb17915b03452320804466345382a6c25ac782ec4874fecdb2385793896cd459be2f187dc7def8899180c32ee0ab9a1aa7fe52193ac3ff3fe29bb0591729 - languageName: node - linkType: hard - "less-loader@npm:10.0.1": version: 10.0.1 resolution: "less-loader@npm:10.0.1" @@ -7631,7 +7785,7 @@ __metadata: "@types/jasmine": ~3.8.0 "@types/node": ^12.11.1 bootstrap: ^4.6.1 - cypress: 8.3.0 + cypress: ^15.7.0 font-awesome: 4.7.0 jasmine-core: ~5.1.2 jquery: 3.5.0 @@ -7672,6 +7826,13 @@ __metadata: languageName: node linkType: hard +"math-intrinsics@npm:^1.1.0": + version: 1.1.0 + resolution: "math-intrinsics@npm:1.1.0" + checksum: 0e513b29d120f478c85a70f49da0b8b19bc638975eca466f2eeae0071f3ad00454c621bf66e16dd435896c208e719fc91ad79bbfba4e400fe0b372e7c1c9c9a2 + languageName: node + linkType: hard + "mdn-data@npm:2.0.14": version: 2.0.14 resolution: "mdn-data@npm:2.0.14" @@ -7876,7 +8037,7 @@ __metadata: languageName: node linkType: hard -"minimist@npm:^1.2.0, minimist@npm:^1.2.5": +"minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.8": version: 1.2.8 resolution: "minimist@npm:1.2.8" checksum: 75a6d645fb122dad29c06a7597bddea977258957ed88d7a6df59b5cd3fe4a527e253e9bbf2e783e4b73657f9098b96a5fe96ab8a113655d4109108577ecf85b0 @@ -8430,6 +8591,13 @@ __metadata: languageName: node linkType: hard +"object-inspect@npm:^1.13.3": + version: 1.13.4 + resolution: "object-inspect@npm:1.13.4" + checksum: 582810c6a8d2ef988ea0a39e69e115a138dad8f42dd445383b394877e5816eb4268489f316a6f74ee9c4e0a984b3eab1028e3e79d62b1ed67c726661d55c7a8b + languageName: node + linkType: hard + "object-is@npm:^1.0.1": version: 1.1.5 resolution: "object-is@npm:1.1.5" @@ -9733,6 +9901,13 @@ __metadata: languageName: node linkType: hard +"process@npm:^0.11.10": + version: 0.11.10 + resolution: "process@npm:0.11.10" + checksum: bfcce49814f7d172a6e6a14d5fa3ac92cc3d0c3b9feb1279774708a719e19acd673995226351a082a9ae99978254e320ccda4240ddc474ba31a76c79491ca7c3 + languageName: node + linkType: hard + "promise-inflight@npm:^1.0.1": version: 1.0.1 resolution: "promise-inflight@npm:1.0.1" @@ -9760,6 +9935,13 @@ __metadata: languageName: node linkType: hard +"proxy-from-env@npm:1.0.0": + version: 1.0.0 + resolution: "proxy-from-env@npm:1.0.0" + checksum: 292e28d1de0c315958d71d8315eb546dd3cd8c8cbc2dab7c54eeb9f5c17f421771964ad0b5e1f77011bab2305bdae42e1757ce33bdb1ccc3e87732322a8efcf1 + languageName: node + linkType: hard + "prr@npm:~1.0.1": version: 1.0.1 resolution: "prr@npm:1.0.1" @@ -9831,6 +10013,15 @@ __metadata: languageName: node linkType: hard +"qs@npm:6.14.0": + version: 6.14.0 + resolution: "qs@npm:6.14.0" + dependencies: + side-channel: ^1.1.0 + checksum: 189b52ad4e9a0da1a16aff4c58b2a554a8dad9bd7e287c7da7446059b49ca2e33a49e570480e8be406b87fccebf134f51c373cbce36c8c83859efa0c9b71d635 + languageName: node + linkType: hard + "qs@npm:6.7.0": version: 6.7.0 resolution: "qs@npm:6.7.0" @@ -9866,13 +10057,6 @@ __metadata: languageName: node linkType: hard -"ramda@npm:~0.27.1": - version: 0.27.1 - resolution: "ramda@npm:0.27.1" - checksum: 31a0c0ef739b2525d7615f84cbb5d3cb89ee0c795469b711f729ea1d8df0dccc3cd75d3717a1e9742d42315ce86435680b7c87743eb7618111c60c144a5b8059 - languageName: node - linkType: hard - "randombytes@npm:^2.1.0": version: 2.1.0 resolution: "randombytes@npm:2.1.0" @@ -10623,6 +10807,41 @@ __metadata: languageName: node linkType: hard +"side-channel-list@npm:^1.0.0": + version: 1.0.0 + resolution: "side-channel-list@npm:1.0.0" + dependencies: + es-errors: ^1.3.0 + object-inspect: ^1.13.3 + checksum: 603b928997abd21c5a5f02ae6b9cc36b72e3176ad6827fab0417ead74580cc4fb4d5c7d0a8a2ff4ead34d0f9e35701ed7a41853dac8a6d1a664fcce1a044f86f + languageName: node + linkType: hard + +"side-channel-map@npm:^1.0.1": + version: 1.0.1 + resolution: "side-channel-map@npm:1.0.1" + dependencies: + call-bound: ^1.0.2 + es-errors: ^1.3.0 + get-intrinsic: ^1.2.5 + object-inspect: ^1.13.3 + checksum: 42501371cdf71f4ccbbc9c9e2eb00aaaab80a4c1c429d5e8da713fd4d39ef3b8d4a4b37ed4f275798a65260a551a7131fd87fe67e922dba4ac18586d6aab8b06 + languageName: node + linkType: hard + +"side-channel-weakmap@npm:^1.0.2": + version: 1.0.2 + resolution: "side-channel-weakmap@npm:1.0.2" + dependencies: + call-bound: ^1.0.2 + es-errors: ^1.3.0 + get-intrinsic: ^1.2.5 + object-inspect: ^1.13.3 + side-channel-map: ^1.0.1 + checksum: a815c89bc78c5723c714ea1a77c938377ea710af20d4fb886d362b0d1f8ac73a17816a5f6640f354017d7e292a43da9c5e876c22145bac00b76cfb3468001736 + languageName: node + linkType: hard + "side-channel@npm:^1.0.4": version: 1.0.5 resolution: "side-channel@npm:1.0.5" @@ -10635,6 +10854,19 @@ __metadata: languageName: node linkType: hard +"side-channel@npm:^1.1.0": + version: 1.1.0 + resolution: "side-channel@npm:1.1.0" + dependencies: + es-errors: ^1.3.0 + object-inspect: ^1.13.3 + side-channel-list: ^1.0.0 + side-channel-map: ^1.0.1 + side-channel-weakmap: ^1.0.2 + checksum: bf73d6d6682034603eb8e99c63b50155017ed78a522d27c2acec0388a792c3ede3238b878b953a08157093b85d05797217d270b7666ba1f111345fbe933380ff + languageName: node + linkType: hard + "signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2": version: 3.0.3 resolution: "signal-exit@npm:3.0.3" @@ -10881,6 +11113,27 @@ __metadata: languageName: node linkType: hard +"sshpk@npm:^1.18.0": + version: 1.18.0 + resolution: "sshpk@npm:1.18.0" + dependencies: + asn1: ~0.2.3 + assert-plus: ^1.0.0 + bcrypt-pbkdf: ^1.0.0 + dashdash: ^1.12.0 + ecc-jsbn: ~0.1.1 + getpass: ^0.1.1 + jsbn: ~0.1.0 + safer-buffer: ^2.0.2 + tweetnacl: ~0.14.0 + bin: + sshpk-conv: bin/sshpk-conv + sshpk-sign: bin/sshpk-sign + sshpk-verify: bin/sshpk-verify + checksum: 01d43374eee3a7e37b3b82fdbecd5518cbb2e47ccbed27d2ae30f9753f22bd6ffad31225cb8ef013bc3fb7785e686cea619203ee1439a228f965558c367c3cfa + languageName: node + linkType: hard + "sshpk@npm:^1.7.0": version: 1.16.1 resolution: "sshpk@npm:1.16.1" @@ -11194,6 +11447,16 @@ __metadata: languageName: node linkType: hard +"systeminformation@npm:5.27.7": + version: 5.27.7 + resolution: "systeminformation@npm:5.27.7" + bin: + systeminformation: lib/cli.js + checksum: 3d800de2e4ac9ab2c1f3f0a29901756c3ca08ed25f541b75452b545e6eb2c8c8ef4a82f8fbf0fe25c4b0865ef9bc7f8efd7ded813acc9f22c287d4daf6141a49 + conditions: (os=darwin | os=linux | os=win32 | os=freebsd | os=openbsd | os=netbsd | os=sunos | os=android) + languageName: node + linkType: hard + "tapable@npm:^2.1.1, tapable@npm:^2.2.0": version: 2.2.0 resolution: "tapable@npm:2.2.0" @@ -11292,6 +11555,24 @@ __metadata: languageName: node linkType: hard +"tldts-core@npm:^6.1.86": + version: 6.1.86 + resolution: "tldts-core@npm:6.1.86" + checksum: 0a715457e03101deff9b34cf45dcd91b81985ef32d35b8e9c4764dcf76369bf75394304997584080bb7b8897e94e20f35f3e8240a1ec87d6faba3cc34dc5a954 + languageName: node + linkType: hard + +"tldts@npm:^6.1.32": + version: 6.1.86 + resolution: "tldts@npm:6.1.86" + dependencies: + tldts-core: ^6.1.86 + bin: + tldts: bin/cli.js + checksum: e5c57664f73663c6c8f7770db02c0c03d6f877fe837854c72037be8092826f95b8e568962358441ef18431b80b7e40ed88391c70873ee7ec0d4344999a12e3de + languageName: node + linkType: hard + "tmp@npm:^0.0.33": version: 0.0.33 resolution: "tmp@npm:0.0.33" @@ -11301,7 +11582,7 @@ __metadata: languageName: node linkType: hard -"tmp@npm:^0.2.1, tmp@npm:~0.2.1": +"tmp@npm:^0.2.1": version: 0.2.1 resolution: "tmp@npm:0.2.1" dependencies: @@ -11310,6 +11591,13 @@ __metadata: languageName: node linkType: hard +"tmp@npm:~0.2.4": + version: 0.2.5 + resolution: "tmp@npm:0.2.5" + checksum: 9d18e58060114154939930457b9e198b34f9495bcc05a343bc0a0a29aa546d2c1c2b343dae05b87b17c8fde0af93ab7d8fe8574a8f6dc2cd8fd3f2ca1ad0d8e1 + languageName: node + linkType: hard + "to-fast-properties@npm:^2.0.0": version: 2.0.0 resolution: "to-fast-properties@npm:2.0.0" @@ -11340,6 +11628,15 @@ __metadata: languageName: node linkType: hard +"tough-cookie@npm:^5.0.0": + version: 5.1.2 + resolution: "tough-cookie@npm:5.1.2" + dependencies: + tldts: ^6.1.32 + checksum: 31c626a77ac247b881665851035773afe7eeac283b91ed8da3c297ed55480ea1dd1ba3f5bb1f94b653ac2d5b184f17ce4bf1cf6ca7c58ee7c321b4323c4f8024 + languageName: node + linkType: hard + "tough-cookie@npm:~2.5.0": version: 2.5.0 resolution: "tough-cookie@npm:2.5.0" @@ -11458,6 +11755,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^0.8.0": + version: 0.8.1 + resolution: "type-fest@npm:0.8.1" + checksum: d61c4b2eba24009033ae4500d7d818a94fd6d1b481a8111612ee141400d5f1db46f199c014766b9fa9b31a6a7374d96fc748c6d688a78a3ce5a33123839becb7 + languageName: node + linkType: hard + "type-is@npm:~1.6.17, type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" From f6fea0e3fa038ab40be795dc6111d15a36b4ca1f Mon Sep 17 00:00:00 2001 From: Pauline Date: Thu, 11 Dec 2025 00:08:00 +0900 Subject: [PATCH 02/25] Update token validator in LoginEndpoint --- .../radarbase/management/config/SecurityConfiguration.kt | 3 +++ .../java/org/radarbase/management/web/rest/LoginEndpoint.kt | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt b/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt index 9eeb8239d..1cdaa2258 100644 --- a/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt +++ b/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt @@ -88,6 +88,9 @@ class SecurityConfiguration @Bean fun http401UnauthorizedEntryPoint(): Http401UnauthorizedEntryPoint = Http401UnauthorizedEntryPoint() + @Bean + fun tokenValidatorBean(): TokenValidator = tokenValidator + @Bean fun jwtAuthenticationFilter(): JwtAuthenticationFilter { val useInternalAuth = managementPortalProperties.authServer.internal diff --git a/src/main/java/org/radarbase/management/web/rest/LoginEndpoint.kt b/src/main/java/org/radarbase/management/web/rest/LoginEndpoint.kt index 496674182..276de4a39 100644 --- a/src/main/java/org/radarbase/management/web/rest/LoginEndpoint.kt +++ b/src/main/java/org/radarbase/management/web/rest/LoginEndpoint.kt @@ -3,9 +3,9 @@ package org.radarbase.management.web.rest import org.radarbase.auth.exception.IdpException import org.radarbase.auth.exception.TokenValidationException import org.radarbase.auth.token.DataRadarToken +import org.radarbase.auth.authentication.TokenValidator import org.radarbase.management.config.ManagementPortalProperties import org.radarbase.management.security.JwtAuthenticationFilter.Companion.radarToken -import org.radarbase.management.security.jwt.ManagementPortalOauthKeyStoreHandler import org.radarbase.management.service.LoginService import org.springframework.beans.factory.annotation.Autowired import org.springframework.web.bind.annotation.GetMapping @@ -20,7 +20,7 @@ import javax.servlet.http.HttpSession class LoginEndpoint @Autowired constructor( private val loginService: LoginService, private val managementPortalProperties: ManagementPortalProperties, - private val keyStoreHandler: ManagementPortalOauthKeyStoreHandler, + private val tokenValidator: TokenValidator, ) { /** * OAuth2 login redirect endpoint. @@ -46,7 +46,7 @@ class LoginEndpoint @Autowired constructor( // Validate the token and store it in the HTTP session so that subsequent // frontend requests can authenticate using the session cookie, without // ever exposing the raw token in the browser URL. - val radarToken = keyStoreHandler.tokenValidator.validateBlocking(accessToken) + val radarToken = tokenValidator.validateBlocking(accessToken) session.radarToken = DataRadarToken(radarToken) val baseUrl = managementPortalProperties.common.managementPortalBaseUrl From aa5d712253ca1aca9ae395e0f7e92dc6e047d79d Mon Sep 17 00:00:00 2001 From: Pauline Date: Fri, 12 Dec 2025 22:57:05 +0900 Subject: [PATCH 03/25] Remove unused OAuthLogin methods and unused SessionResource --- .../config/OAuth2LoginUiWebConfig.kt | 16 ---- .../config/OAuth2ServerConfiguration.kt | 1 - .../config/SecurityConfiguration.kt | 2 - .../management/web/rest/SessionResource.kt | 85 ------------------- 4 files changed, 104 deletions(-) delete mode 100644 src/main/java/org/radarbase/management/web/rest/SessionResource.kt diff --git a/src/main/java/org/radarbase/management/config/OAuth2LoginUiWebConfig.kt b/src/main/java/org/radarbase/management/config/OAuth2LoginUiWebConfig.kt index 256e1d7d7..58c1f2588 100644 --- a/src/main/java/org/radarbase/management/config/OAuth2LoginUiWebConfig.kt +++ b/src/main/java/org/radarbase/management/config/OAuth2LoginUiWebConfig.kt @@ -51,22 +51,6 @@ class OAuth2LoginUiWebConfig( @Autowired private val clientDetailsService: ClientDetailsService? = null - @RequestMapping("/oauth2/authorize") - fun redirect_authorize(request: HttpServletRequest): String { - val returnString = URLEncoder.encode(request.requestURL.toString().replace("oauth2", "oauth") + "?" + request.parameterMap.map{ param -> param.key + "=" + param.value.first()}.joinToString("&"), "UTF-8") - val mpUrl = managementPortalProperties.common.baseUrl - return "redirect:$mpUrl/kratos-ui/login?return_to=$returnString" - } - - @PostMapping( - "/oauth2/token", - consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], - produces = [MediaType.APPLICATION_FORM_URLENCODED_VALUE] - ) - fun redirect_token(@RequestParam parameters: Map, request: HttpServletRequest, response: HttpServletResponse) { - var dispatcher: RequestDispatcher = request.servletContext.getRequestDispatcher("/oauth/token/") - dispatcher.forward(request, response) - } @PostMapping(value = ["/oauth/token"], consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE] diff --git a/src/main/java/org/radarbase/management/config/OAuth2ServerConfiguration.kt b/src/main/java/org/radarbase/management/config/OAuth2ServerConfiguration.kt index f81ff7bfa..9de3a2e9b 100644 --- a/src/main/java/org/radarbase/management/config/OAuth2ServerConfiguration.kt +++ b/src/main/java/org/radarbase/management/config/OAuth2ServerConfiguration.kt @@ -152,7 +152,6 @@ class OAuth2ServerConfiguration( .antMatchers("/api/profile-info").permitAll() .antMatchers("/api/sitesettings").permitAll() .antMatchers("/api/public/projects").permitAll() - .antMatchers("/api/logout-url").permitAll() .antMatchers("/api/**").authenticated() .antMatchers("/management/health").permitAll() .antMatchers("/management/**").hasAuthority(RoleAuthority.SYS_ADMIN_AUTHORITY) diff --git a/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt b/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt index 1cdaa2258..82b9b1d55 100644 --- a/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt +++ b/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt @@ -116,7 +116,6 @@ class SecurityConfiguration .skipUrlPattern(HttpMethod.GET, "/api/sitesettings") .skipUrlPattern(HttpMethod.GET, "/api/redirect/**") .skipUrlPattern(HttpMethod.GET, "/api/profile-info") - .skipUrlPattern(HttpMethod.GET, "/api/logout-url") .skipUrlPattern(HttpMethod.POST, "/api/webhook/**") .skipUrlPattern(HttpMethod.GET, "/oauth2/authorize") .skipUrlPattern(HttpMethod.GET, "/images/**") @@ -140,7 +139,6 @@ class SecurityConfiguration .antMatchers("/api-docs/**") .antMatchers("/swagger-ui.html") .antMatchers("/api-docs{,.json,.yml}") - .antMatchers("/api/logout-url") .antMatchers("/api/profile-info") .antMatchers("/api/activate") .antMatchers("/api/sitesettings") diff --git a/src/main/java/org/radarbase/management/web/rest/SessionResource.kt b/src/main/java/org/radarbase/management/web/rest/SessionResource.kt deleted file mode 100644 index 7b64b634a..000000000 --- a/src/main/java/org/radarbase/management/web/rest/SessionResource.kt +++ /dev/null @@ -1,85 +0,0 @@ -package org.radarbase.management.web.rest - -import io.ktor.client.* -import io.ktor.client.call.* -import io.ktor.client.engine.cio.* -import io.ktor.client.plugins.* -import io.ktor.client.plugins.contentnegotiation.* -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.serialization.kotlinx.json.* -import io.micrometer.core.annotation.Timed -import kotlinx.serialization.json.Json -import org.radarbase.auth.exception.IdpException -import org.radarbase.auth.kratos.SessionService -import org.radarbase.management.config.ManagementPortalProperties -import org.radarbase.management.web.rest.util.HeaderUtil -import org.slf4j.LoggerFactory -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.http.HttpHeaders -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RestController -import java.time.Duration -import javax.servlet.http.HttpServletRequest - - -/** - * REST controller for managing Sessions. - */ -@RestController -@RequestMapping("/api") -class SessionResource(managementPortalProperties: ManagementPortalProperties) { - private lateinit var sessionService: SessionService - - init { - sessionService = SessionService(managementPortalProperties.identityServer.serverUrl) - } - - private val httpClient = HttpClient(CIO).config { - install(HttpTimeout) { - connectTimeoutMillis = Duration.ofSeconds(10).toMillis() - socketTimeoutMillis = Duration.ofSeconds(10).toMillis() - requestTimeoutMillis = Duration.ofSeconds(300).toMillis() - } - install(ContentNegotiation) { - json(Json { - ignoreUnknownKeys = true - coerceInputValues = true - }) - } - } - - /** - * GET /logout-url : Gets a kratos logout url for the current session. - * - * @return the ResponseEntity with status 200 (Ok) and with body [String]. - */ - @GetMapping("/logout-url") - @Timed - @Throws(IdpException::class) - suspend fun getLogoutUrl(httpRequest: HttpServletRequest): ResponseEntity { - val sessionToken = HeaderUtil.parseCookies(httpRequest.getHeader(HttpHeaders.COOKIE)).find { it.name == "ory_kratos_session" } - ?.value - - return try { - sessionToken ?: throw IdpException("no ory_kratos_session could be parsed from the headers") - - ResponseEntity - .ok() - .body(sessionService.getLogoutUrl(sessionToken)) - } catch (e: Throwable) { - ResponseEntity.badRequest() - .headers(e.message?.let { - HeaderUtil.createFailureAlert("NoSession", - it, "could not create logout url") - }) - .body("") - } - } - - companion object { - private val log = LoggerFactory.getLogger(SessionResource::class.java) - } -} From b2e5f5080c7eb218abc0a516dd8d202932f47839 Mon Sep 17 00:00:00 2001 From: Pauline Date: Fri, 12 Dec 2025 22:57:15 +0900 Subject: [PATCH 04/25] Restore commented out integration tests --- .../management/service/UserServiceIntTest.kt | 109 +++++++++--------- .../web/rest/UserResourceIntTest.kt | 62 +++++----- 2 files changed, 84 insertions(+), 87 deletions(-) diff --git a/src/test/java/org/radarbase/management/service/UserServiceIntTest.kt b/src/test/java/org/radarbase/management/service/UserServiceIntTest.kt index 4e593df18..aec5765d3 100644 --- a/src/test/java/org/radarbase/management/service/UserServiceIntTest.kt +++ b/src/test/java/org/radarbase/management/service/UserServiceIntTest.kt @@ -73,19 +73,18 @@ class UserServiceIntTest( Assertions.assertThat(maybeUser?.resetKey).isNotNull() } - //TODO this test should fail, remove? -// @Test -// @Throws(NotAuthorizedException::class) -// fun assertThatOnlyActivatedUserCanRequestPasswordReset() { -// runBlocking { -// val user = userService.createUser(userDto) -// val maybeUser = userService.requestPasswordReset( -// userDto.email!! -// ) -// Assertions.assertThat(maybeUser).isNull() -// userRepository.delete(user) -// } -// } + @Test + @Throws(NotAuthorizedException::class) + fun assertThatOnlyActivatedUserCanRequestPasswordReset() { + runBlocking { + val user = userService.createUser(userDto) + val maybeUser = userService.requestPasswordReset( + userDto.email!! + ) + Assertions.assertThat(maybeUser).isNull() + userRepository.delete(user) + } + } @Test @Throws(NotAuthorizedException::class) @@ -107,50 +106,48 @@ class UserServiceIntTest( } } - //TODO this functionality will be removed, remove test -// @Test -// @Throws(NotAuthorizedException::class) -// fun assertThatResetKeyMustBeValid() { -// runBlocking { -// val user = userService.createUser(userDto) -// val daysAgo = ZonedDateTime.now().minusHours(25) -// user.activated = true -// user.resetDate = daysAgo -// user.resetKey = "1234" -// userRepository.save(user) -// val maybeUser = userService.completePasswordReset( -// "johndoe2", -// user.resetKey!! -// ) -// Assertions.assertThat(maybeUser).isNull() -// userService.deleteUser(user.login!!) -// } -// } + @Test + @Throws(NotAuthorizedException::class) + fun assertThatResetKeyMustBeValid() { + runBlocking { + val user = userService.createUser(userDto) + val daysAgo = ZonedDateTime.now().minusHours(25) + user.activated = true + user.resetDate = daysAgo + user.resetKey = "1234" + userRepository.save(user) + val maybeUser = userService.completePasswordReset( + "johndoe2", + user.resetKey!! + ) + Assertions.assertThat(maybeUser).isNull() + userService.deleteUser(user.login!!) + } + } - //TODO this functionality will be removed, remove test -// @Test -// @Throws(NotAuthorizedException::class) -// fun assertThatUserCanResetPassword() { -// runBlocking { -// val user = userService.createUser(userDto) -// val oldPassword = user.password -// val daysAgo = ZonedDateTime.now().minusHours(2) -// val resetKey = passwordService.generateResetKey() -// user.activated = true -// user.resetDate = daysAgo -// user.resetKey = resetKey -// userService.updateUser(userMapper.userToUserDTO(user)!!) -// val maybeUser = userService.completePasswordReset( -// "johndoe2", -// user.resetKey!! -// ) -// Assertions.assertThat(maybeUser).isNotNull() -// Assertions.assertThat(maybeUser?.resetDate).isNull() -// Assertions.assertThat(maybeUser?.resetKey).isNull() -// Assertions.assertThat(maybeUser?.password).isNotEqualTo(oldPassword) -// userService.deleteUser(user.login!!) -// } -// } + @Test + @Throws(NotAuthorizedException::class) + fun assertThatUserCanResetPassword() { + runBlocking { + val user = userService.createUser(userDto) + val oldPassword = user.password + val daysAgo = ZonedDateTime.now().minusHours(2) + val resetKey = passwordService.generateResetKey() + user.activated = true + user.resetDate = daysAgo + user.resetKey = resetKey + userService.updateUser(userMapper.userToUserDTO(user)!!) + val maybeUser = userService.completePasswordReset( + "johndoe2", + user.resetKey!! + ) + Assertions.assertThat(maybeUser).isNotNull() + Assertions.assertThat(maybeUser?.resetDate).isNull() + Assertions.assertThat(maybeUser?.resetKey).isNull() + Assertions.assertThat(maybeUser?.password).isNotEqualTo(oldPassword) + userService.deleteUser(user.login!!) + } + } @Test fun testFindNotActivatedUsersByCreationDateBefore() { diff --git a/src/test/java/org/radarbase/management/web/rest/UserResourceIntTest.kt b/src/test/java/org/radarbase/management/web/rest/UserResourceIntTest.kt index 716096214..93bdfd3e0 100644 --- a/src/test/java/org/radarbase/management/web/rest/UserResourceIntTest.kt +++ b/src/test/java/org/radarbase/management/web/rest/UserResourceIntTest.kt @@ -119,37 +119,37 @@ internal class UserResourceIntTest( } } -// @Test -// @Transactional -// @Throws(Exception::class) -// fun createUser() { -// val databaseSizeBeforeCreate = userRepository.findAll().size -// -// // Create the User -// val roles: MutableSet = HashSet() -// val role = RoleDTO() -// role.authorityName = RoleAuthority.SYS_ADMIN_AUTHORITY -// roles.add(role) -// val managedUserVm = createDefaultUser(roles) -// val result = restUserMockMvc.perform( -// MockMvcRequestBuilders.post("/api/users") -// .contentType(TestUtil.APPLICATION_JSON_UTF8) -// .content(TestUtil.convertObjectToJsonBytes(managedUserVm)) -// ).andReturn() -// -// restUserMockMvc.perform(asyncDispatch(result)) -// .andExpect(MockMvcResultMatchers.status().isCreated()) -// -// // Validate the User in the database -// val userList = userRepository.findAll() -// Assertions.assertThat(userList).hasSize(databaseSizeBeforeCreate + 1) -// val testUser = userList[userList.size - 1] -// Assertions.assertThat(testUser.login).isEqualTo(UserServiceIntTest.DEFAULT_LOGIN) -// Assertions.assertThat(testUser.firstName).isEqualTo(UserServiceIntTest.DEFAULT_FIRSTNAME) -// Assertions.assertThat(testUser.lastName).isEqualTo(UserServiceIntTest.DEFAULT_LASTNAME) -// Assertions.assertThat(testUser.email).isEqualTo(UserServiceIntTest.DEFAULT_EMAIL) -// Assertions.assertThat(testUser.langKey).isEqualTo(UserServiceIntTest.DEFAULT_LANGKEY) -// } + @Test + @Transactional + @Throws(Exception::class) + fun createUser() { + val databaseSizeBeforeCreate = userRepository.findAll().size + + // Create the User + val roles: MutableSet = HashSet() + val role = RoleDTO() + role.authorityName = RoleAuthority.SYS_ADMIN_AUTHORITY + roles.add(role) + val managedUserVm = createDefaultUser(roles) + val result = restUserMockMvc.perform( + MockMvcRequestBuilders.post("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVm)) + ).andReturn() + + restUserMockMvc.perform(asyncDispatch(result)) + .andExpect(MockMvcResultMatchers.status().isCreated()) + + // Validate the User in the database + val userList = userRepository.findAll() + Assertions.assertThat(userList).hasSize(databaseSizeBeforeCreate + 1) + val testUser = userList[userList.size - 1] + Assertions.assertThat(testUser.login).isEqualTo(UserServiceIntTest.DEFAULT_LOGIN) + Assertions.assertThat(testUser.firstName).isEqualTo(UserServiceIntTest.DEFAULT_FIRSTNAME) + Assertions.assertThat(testUser.lastName).isEqualTo(UserServiceIntTest.DEFAULT_LASTNAME) + Assertions.assertThat(testUser.email).isEqualTo(UserServiceIntTest.DEFAULT_EMAIL) + Assertions.assertThat(testUser.langKey).isEqualTo(UserServiceIntTest.DEFAULT_LANGKEY) + } @Test @Transactional From dbf4e0ce0fc7920b00f1a1885d1bf63c0c3fbe8f Mon Sep 17 00:00:00 2001 From: Pauline Date: Fri, 12 Dec 2025 22:59:11 +0900 Subject: [PATCH 05/25] Remove unused HeaderUtil method --- .../management/web/rest/util/HeaderUtil.kt | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/src/main/java/org/radarbase/management/web/rest/util/HeaderUtil.kt b/src/main/java/org/radarbase/management/web/rest/util/HeaderUtil.kt index f37849c23..e185c659f 100644 --- a/src/main/java/org/radarbase/management/web/rest/util/HeaderUtil.kt +++ b/src/main/java/org/radarbase/management/web/rest/util/HeaderUtil.kt @@ -100,26 +100,4 @@ object HeaderUtil { } .reduce { a: String, b: String -> java.lang.String.join("/", a, b) } } - - - - /** - * Custom cookie parser as the httprequest.cookies method cuts off '='. - */ - fun parseCookies(cookieHeader: String?): List { - val result: List = listOf() - if (cookieHeader != null) { - val cookiesRaw = cookieHeader.split("; ".toRegex()).dropLastWhile { it.isEmpty() } - .toTypedArray() - return cookiesRaw.map{ - val parts = it.split("=".toRegex(), limit = 2).toTypedArray() - var value = if (parts.size > 1) parts[1] else "" - if (value.length >= 2 && value.startsWith("\"") && value.endsWith("\"")) { - value = value.substring(1, value.length - 1) - } - Cookie(name = parts[0], value = parts[1]) - }.toList() - } - return result - } } From f530b3bbfaf5a632d853a73fe63305598514364f Mon Sep 17 00:00:00 2001 From: Pauline Date: Thu, 18 Dec 2025 23:49:11 +0800 Subject: [PATCH 06/25] Update projectMapperDecorator --- .../mapper/decorator/ProjectMapperDecorator.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/radarbase/management/service/mapper/decorator/ProjectMapperDecorator.kt b/src/main/java/org/radarbase/management/service/mapper/decorator/ProjectMapperDecorator.kt index 3547cdfd2..0a2975627 100644 --- a/src/main/java/org/radarbase/management/service/mapper/decorator/ProjectMapperDecorator.kt +++ b/src/main/java/org/radarbase/management/service/mapper/decorator/ProjectMapperDecorator.kt @@ -26,16 +26,14 @@ abstract class ProjectMapperDecorator : ProjectMapper { @Autowired(required = false) lateinit var metaTokenService: MetaTokenService override fun projectToProjectDTO(project: Project?): ProjectDTO? { - val dto = delegate.projectToProjectDTO(project) - dto?.humanReadableProjectName = project?.attributes?.get(ProjectDTO.HUMAN_READABLE_PROJECT_NAME) - if (metaTokenService != null) { - try { - dto?.persistentTokenTimeout = metaTokenService.getMetaTokenTimeout(true, project).toMillis() + return delegate.projectToProjectDTO(project)?.apply { + this.humanReadableProjectName = project?.attributes?.get(ProjectDTO.HUMAN_READABLE_PROJECT_NAME) + this.persistentTokenTimeout = try { + metaTokenService?.getMetaTokenTimeout(true, project)?.toMillis() } catch (ex: BadRequestException) { - dto?.persistentTokenTimeout = null + null } } - return dto } override fun projectToProjectDTOReduced(project: Project?): ProjectDTO? { From 26e11281865d7b3d56f29773cd49bfeac9ec525a Mon Sep 17 00:00:00 2001 From: Pauline Date: Thu, 18 Dec 2025 23:50:55 +0800 Subject: [PATCH 07/25] Remove unused ConditionalOnProperty import --- .../radarbase/management/service/DefaultOAuthClientService.kt | 1 - src/main/java/org/radarbase/management/service/MailService.kt | 1 - .../java/org/radarbase/management/service/MetaTokenService.kt | 1 - .../java/org/radarbase/management/web/rest/MetaTokenResource.kt | 1 - .../org/radarbase/management/web/rest/OAuthClientsResource.kt | 1 - .../java/org/radarbase/management/web/rest/WebhookResource.kt | 1 - 6 files changed, 6 deletions(-) diff --git a/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt b/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt index f0c10ecc2..0c03f51ed 100644 --- a/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt +++ b/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt @@ -7,7 +7,6 @@ import org.radarbase.management.service.mapper.ClientDetailsMapper import org.radarbase.management.web.rest.errors.* import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.security.authentication.UsernamePasswordAuthenticationToken import org.springframework.security.core.Authentication import org.springframework.security.core.authority.SimpleGrantedAuthority diff --git a/src/main/java/org/radarbase/management/service/MailService.kt b/src/main/java/org/radarbase/management/service/MailService.kt index 488ddcfc2..9b1e11971 100644 --- a/src/main/java/org/radarbase/management/service/MailService.kt +++ b/src/main/java/org/radarbase/management/service/MailService.kt @@ -4,7 +4,6 @@ import org.radarbase.management.config.ManagementPortalProperties import org.radarbase.management.domain.User import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.context.MessageSource import org.springframework.mail.javamail.JavaMailSender import org.springframework.mail.javamail.MimeMessageHelper diff --git a/src/main/java/org/radarbase/management/service/MetaTokenService.kt b/src/main/java/org/radarbase/management/service/MetaTokenService.kt index 4a3414f19..fa269ea67 100644 --- a/src/main/java/org/radarbase/management/service/MetaTokenService.kt +++ b/src/main/java/org/radarbase/management/service/MetaTokenService.kt @@ -12,7 +12,6 @@ import org.radarbase.management.web.rest.MetaTokenResource import org.radarbase.management.web.rest.errors.* import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional diff --git a/src/main/java/org/radarbase/management/web/rest/MetaTokenResource.kt b/src/main/java/org/radarbase/management/web/rest/MetaTokenResource.kt index 7a30f0774..332070e34 100644 --- a/src/main/java/org/radarbase/management/web/rest/MetaTokenResource.kt +++ b/src/main/java/org/radarbase/management/web/rest/MetaTokenResource.kt @@ -10,7 +10,6 @@ import org.radarbase.management.service.MetaTokenService import org.radarbase.management.service.dto.TokenDTO import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* import java.net.MalformedURLException diff --git a/src/main/java/org/radarbase/management/web/rest/OAuthClientsResource.kt b/src/main/java/org/radarbase/management/web/rest/OAuthClientsResource.kt index ac0de4f70..5f10f8adc 100644 --- a/src/main/java/org/radarbase/management/web/rest/OAuthClientsResource.kt +++ b/src/main/java/org/radarbase/management/web/rest/OAuthClientsResource.kt @@ -16,7 +16,6 @@ import org.radarbase.management.web.rest.errors.NotFoundException import org.radarbase.management.web.rest.util.HeaderUtil import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.boot.actuate.audit.AuditEvent import org.springframework.boot.actuate.audit.AuditEventRepository import org.springframework.http.HttpStatus diff --git a/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt b/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt index 52fefcda5..fe669528b 100644 --- a/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt +++ b/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt @@ -13,7 +13,6 @@ import org.radarbase.management.web.rest.errors.NotFoundException import org.radarbase.management.web.rest.util.HeaderUtil import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* import java.net.URISyntaxException From 8f9080a5c6b2d34e0e02cb81e8eecf4015b22c09 Mon Sep 17 00:00:00 2001 From: Pauline Date: Fri, 19 Dec 2025 00:08:03 +0800 Subject: [PATCH 08/25] Improve readability --- .../auth/authentication/TokenValidator.kt | 10 +++---- .../ManagementPortalSecurityConfigLoader.kt | 4 +-- .../config/OAuth2LoginUiWebConfig.kt | 6 ++--- .../config/OAuth2ServerConfiguration.kt | 7 ++--- .../management/service/OAuthClientUtils.kt | 5 ++-- .../management/service/SubjectService.kt | 26 +++++++++---------- 6 files changed, 26 insertions(+), 32 deletions(-) diff --git a/radar-auth/src/main/java/org/radarbase/auth/authentication/TokenValidator.kt b/radar-auth/src/main/java/org/radarbase/auth/authentication/TokenValidator.kt index 17d210682..58886edb9 100644 --- a/radar-auth/src/main/java/org/radarbase/auth/authentication/TokenValidator.kt +++ b/radar-auth/src/main/java/org/radarbase/auth/authentication/TokenValidator.kt @@ -28,7 +28,7 @@ constructor( /** Maximum time that the token verifier does not need to be fetched. */ maxAge: Duration = Duration.ofDays(1), ) { - private val algorithmLoaders: List? + private val algorithmLoaders: List init { val config = CacheConfig( @@ -38,7 +38,7 @@ constructor( ) algorithmLoaders = verifierLoaders?.map { loader -> CachedValue(config, supplier = loader::fetch) - } + } ?: emptyList() } /** @@ -77,13 +77,13 @@ constructor( suspend fun validate(token: String): RadarToken { val result: Result = consumeFirst { emit -> val causes = algorithmLoaders - ?.forkJoin { cache -> + .forkJoin { cache -> val result = cache.verify(token) // short-circuit to return the first successful result if (result.isSuccess) emit(result) result } - ?.flatMap { + .flatMap { it.exceptionOrNull() ?.suppressedExceptions ?: emptyList() @@ -103,7 +103,7 @@ constructor( /** Refresh the token verifiers from cache on the next validation. */ fun refresh() { - algorithmLoaders?.forEach { it.clear() } + algorithmLoaders.forEach { it.clear() } } companion object { diff --git a/src/main/java/org/radarbase/management/config/ManagementPortalSecurityConfigLoader.kt b/src/main/java/org/radarbase/management/config/ManagementPortalSecurityConfigLoader.kt index be4065e93..a11683b03 100644 --- a/src/main/java/org/radarbase/management/config/ManagementPortalSecurityConfigLoader.kt +++ b/src/main/java/org/radarbase/management/config/ManagementPortalSecurityConfigLoader.kt @@ -139,8 +139,8 @@ class ManagementPortalSecurityConfigLoader( return } logger.info("Loading additional ManagementPortal clients...") - val path = managementPortalProperties.oauth?.clientsFile - if (Objects.isNull(path) || path == "") { + val path = managementPortalProperties.oauth.clientsFile + if (path.isNullOrBlank()) { logger.info("No OAuth clients file specified, additional clients loading aborted.") return } diff --git a/src/main/java/org/radarbase/management/config/OAuth2LoginUiWebConfig.kt b/src/main/java/org/radarbase/management/config/OAuth2LoginUiWebConfig.kt index 58c1f2588..c850bdc11 100644 --- a/src/main/java/org/radarbase/management/config/OAuth2LoginUiWebConfig.kt +++ b/src/main/java/org/radarbase/management/config/OAuth2LoginUiWebConfig.kt @@ -72,11 +72,11 @@ class OAuth2LoginUiWebConfig( val clientId: String = parameters.get("client_id") ?: principal.name var radarPrincipal = RadarPrincipal(clientId, principal) - val token2 = this.tokenEndPoint.postAccessToken(radarPrincipal, parameters) - return getResponse(token2.body) + val tokenResponse = this.tokenEndPoint.postAccessToken(radarPrincipal, parameters) + return buildTokenResponse(tokenResponse.body) } - fun getResponse(accessToken: OAuth2AccessToken): ResponseEntity { + fun buildTokenResponse(accessToken: OAuth2AccessToken): ResponseEntity { val headers = HttpHeaders() headers["Cache-Control"] = "no-store" headers["Pragma"] = "no-cache" diff --git a/src/main/java/org/radarbase/management/config/OAuth2ServerConfiguration.kt b/src/main/java/org/radarbase/management/config/OAuth2ServerConfiguration.kt index 9de3a2e9b..9b1d4f1b3 100644 --- a/src/main/java/org/radarbase/management/config/OAuth2ServerConfiguration.kt +++ b/src/main/java/org/radarbase/management/config/OAuth2ServerConfiguration.kt @@ -62,11 +62,8 @@ class OAuth2ServerConfiguration( .successHandler { request, response, authentication -> request.session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext()) val savedRequest = request.session.getAttribute("SPRING_SECURITY_SAVED_REQUEST") as? SavedRequest - val redirectUrl = if (savedRequest != null) { - savedRequest.redirectUrl - } else { - request.requestURI + if (request.queryString != null) "?${request.queryString}" else "" - } + val queryString = request.queryString?.let { "?$it" } ?: "" + val redirectUrl = savedRequest?.redirectUrl ?: request.requestURI + queryString val safeRedirectUrl = try { val uri = URI(redirectUrl) diff --git a/src/main/java/org/radarbase/management/service/OAuthClientUtils.kt b/src/main/java/org/radarbase/management/service/OAuthClientUtils.kt index 456035c4d..216853b6b 100644 --- a/src/main/java/org/radarbase/management/service/OAuthClientUtils.kt +++ b/src/main/java/org/radarbase/management/service/OAuthClientUtils.kt @@ -20,9 +20,8 @@ object OAuthClientUtils { */ fun checkProtected(details: ClientDetails) { val info = details.additionalInformation - if (Objects.nonNull(info) && info.containsKey(PROTECTED_KEY) && info[PROTECTED_KEY] - .toString().equals("true", ignoreCase = true) - ) { + val isProtectedClient = info?.get(PROTECTED_KEY)?.toString()?.equals("true", ignoreCase = true) == true + if (isProtectedClient) { throw InvalidRequestException( "Cannot modify protected client", EntityName.Companion.OAUTH_CLIENT, ErrorConstants.ERR_OAUTH_CLIENT_PROTECTED, diff --git a/src/main/java/org/radarbase/management/service/SubjectService.kt b/src/main/java/org/radarbase/management/service/SubjectService.kt index ea3c36808..5ec93cf02 100644 --- a/src/main/java/org/radarbase/management/service/SubjectService.kt +++ b/src/main/java/org/radarbase/management/service/SubjectService.kt @@ -75,7 +75,7 @@ class SubjectService( @Transactional suspend fun createSubject( subjectDto: SubjectDTO, - activated: Boolean? = true, + activated: Boolean = true, ): SubjectDTO? { val subject = subjectMapper.subjectDTOToSubject(subjectDto) ?: throw NullPointerException() // assign roles @@ -83,7 +83,7 @@ class SubjectService( val project = projectMapper.projectDTOToProject(subjectDto.project) val projectParticipantRole = getProjectParticipantRole(project, RoleAuthority.PARTICIPANT) val roles = user!!.roles - roles?.add(projectParticipantRole) + roles.add(projectParticipantRole) // Set group subject.group = getSubjectGroup(project, subjectDto.group) @@ -95,18 +95,16 @@ class SubjectService( user.langKey = "en" user.resetDate = ZonedDateTime.now() // default subject is activated. - user.activated = activated!! + user.activated = activated // default identity is externalId user.identity = subject.externalId // set if any devices are set as assigned - if (subject.sources.isNotEmpty()) { - subject.sources.forEach( - Consumer { s: Source -> - s.assigned = true - s.subject(subject) - }, - ) - } + subject.sources.forEach( + Consumer { s: Source -> + s.assigned = true + s.subject(subject) + }, + ) if (subject.enrollmentDate == null) { subject.enrollmentDate = ZonedDateTime.now() } @@ -161,9 +159,9 @@ class SubjectService( project: Project?, authority: RoleAuthority, ): Role { - val ans: Role? = + val existingRole: Role? = roleRepository.findOneByProjectIdAndAuthorityName(project?.id, authority.authority) - return if (ans == null) { + return if (existingRole == null) { val subjectRole = Role() val auth: Authority = authorityRepository.findByAuthorityName(authority.authority) @@ -174,7 +172,7 @@ class SubjectService( roleRepository.save(subjectRole) subjectRole } else { - ans + existingRole } } From b4e0335321f548d068e40424cce75f9e25711b47 Mon Sep 17 00:00:00 2001 From: Pauline Date: Fri, 19 Dec 2025 00:21:06 +0800 Subject: [PATCH 09/25] Add separate annotations for identity and authserver configs --- .../config/annotations/AuthServerDisabled.kt | 15 +++++++++++++++ .../config/{ => annotations}/AuthServerEnabled.kt | 5 +++-- .../config/annotations/IdentityServerDisabled.kt | 15 +++++++++++++++ .../config/annotations/IdentityServerEnabled.kt | 15 +++++++++++++++ 4 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/radarbase/management/config/annotations/AuthServerDisabled.kt rename src/main/java/org/radarbase/management/config/{ => annotations}/AuthServerEnabled.kt (80%) create mode 100644 src/main/java/org/radarbase/management/config/annotations/IdentityServerDisabled.kt create mode 100644 src/main/java/org/radarbase/management/config/annotations/IdentityServerEnabled.kt diff --git a/src/main/java/org/radarbase/management/config/annotations/AuthServerDisabled.kt b/src/main/java/org/radarbase/management/config/annotations/AuthServerDisabled.kt new file mode 100644 index 000000000..d6ff1457e --- /dev/null +++ b/src/main/java/org/radarbase/management/config/annotations/AuthServerDisabled.kt @@ -0,0 +1,15 @@ +package org.radarbase.management.config.annotations + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.context.annotation.Configuration + +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +@Configuration +@ConditionalOnProperty( + name = ["managementportal.authServer.internal"], + havingValue = "false", + matchIfMissing = false +) +annotation class AuthServerDisabled + diff --git a/src/main/java/org/radarbase/management/config/AuthServerEnabled.kt b/src/main/java/org/radarbase/management/config/annotations/AuthServerEnabled.kt similarity index 80% rename from src/main/java/org/radarbase/management/config/AuthServerEnabled.kt rename to src/main/java/org/radarbase/management/config/annotations/AuthServerEnabled.kt index ed2632add..14450ccf7 100644 --- a/src/main/java/org/radarbase/management/config/AuthServerEnabled.kt +++ b/src/main/java/org/radarbase/management/config/annotations/AuthServerEnabled.kt @@ -1,4 +1,4 @@ -package org.radarbase.management.config +package org.radarbase.management.config.annotations import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.context.annotation.Configuration @@ -11,4 +11,5 @@ import org.springframework.context.annotation.Configuration havingValue = "true", matchIfMissing = true ) -annotation class AuthServerEnabled \ No newline at end of file +annotation class AuthServerEnabled + diff --git a/src/main/java/org/radarbase/management/config/annotations/IdentityServerDisabled.kt b/src/main/java/org/radarbase/management/config/annotations/IdentityServerDisabled.kt new file mode 100644 index 000000000..05f8f508e --- /dev/null +++ b/src/main/java/org/radarbase/management/config/annotations/IdentityServerDisabled.kt @@ -0,0 +1,15 @@ +package org.radarbase.management.config.annotations + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.context.annotation.Configuration + +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +@Configuration +@ConditionalOnProperty( + name = ["managementportal.identityServer.internal"], + havingValue = "false", + matchIfMissing = false +) +annotation class IdentityServerDisabled + diff --git a/src/main/java/org/radarbase/management/config/annotations/IdentityServerEnabled.kt b/src/main/java/org/radarbase/management/config/annotations/IdentityServerEnabled.kt new file mode 100644 index 000000000..19a4483d4 --- /dev/null +++ b/src/main/java/org/radarbase/management/config/annotations/IdentityServerEnabled.kt @@ -0,0 +1,15 @@ +package org.radarbase.management.config.annotations + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.context.annotation.Configuration + +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +@Configuration +@ConditionalOnProperty( + name = ["managementportal.identityServer.internal"], + havingValue = "true", + matchIfMissing = true +) +annotation class IdentityServerEnabled + From 46cb57eb5d9a5848918c53a0510b08018c9aa776 Mon Sep 17 00:00:00 2001 From: Pauline Date: Fri, 19 Dec 2025 00:24:36 +0800 Subject: [PATCH 10/25] Improve readability by OAuthclient and UserService beans conditionally and update annotation usage --- .../config/OAuth2LoginUiWebConfig.kt | 1 + .../config/OAuth2ServerConfiguration.kt | 1 + .../ManagementPortalOauthKeyStoreHandler.kt | 2 +- .../service/DefaultOAuthClientService.kt | 2 +- .../config/OAuthClientServiceConfig.kt | 46 +++++++------ .../service/config/UserServiceConfig.kt | 68 +++++++++++++------ .../web/rest/OAuthClientsResource.kt | 2 +- .../management/web/rest/TokenKeyEndpoint.kt | 2 +- .../management/web/rest/WebhookResource.kt | 2 + 9 files changed, 82 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/radarbase/management/config/OAuth2LoginUiWebConfig.kt b/src/main/java/org/radarbase/management/config/OAuth2LoginUiWebConfig.kt index c850bdc11..13b749530 100644 --- a/src/main/java/org/radarbase/management/config/OAuth2LoginUiWebConfig.kt +++ b/src/main/java/org/radarbase/management/config/OAuth2LoginUiWebConfig.kt @@ -1,5 +1,6 @@ package org.radarbase.management.config +import org.radarbase.management.config.annotations.AuthServerEnabled import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.HttpHeaders diff --git a/src/main/java/org/radarbase/management/config/OAuth2ServerConfiguration.kt b/src/main/java/org/radarbase/management/config/OAuth2ServerConfiguration.kt index 9b1d4f1b3..b51767554 100644 --- a/src/main/java/org/radarbase/management/config/OAuth2ServerConfiguration.kt +++ b/src/main/java/org/radarbase/management/config/OAuth2ServerConfiguration.kt @@ -2,6 +2,7 @@ package org.radarbase.management.config import javax.sql.DataSource import org.radarbase.auth.authorization.RoleAuthority +import org.radarbase.management.config.annotations.AuthServerEnabled import org.radarbase.management.repository.UserRepository import org.radarbase.management.security.* import org.radarbase.management.security.jwt.* diff --git a/src/main/java/org/radarbase/management/security/jwt/ManagementPortalOauthKeyStoreHandler.kt b/src/main/java/org/radarbase/management/security/jwt/ManagementPortalOauthKeyStoreHandler.kt index c9c6a8faa..3fca00a84 100644 --- a/src/main/java/org/radarbase/management/security/jwt/ManagementPortalOauthKeyStoreHandler.kt +++ b/src/main/java/org/radarbase/management/security/jwt/ManagementPortalOauthKeyStoreHandler.kt @@ -7,7 +7,7 @@ import org.radarbase.auth.authentication.TokenValidator import org.radarbase.auth.jwks.JsonWebKeySet import org.radarbase.auth.jwks.JwkAlgorithmParser import org.radarbase.auth.jwks.JwksTokenVerifierLoader -import org.radarbase.management.config.AuthServerEnabled +import org.radarbase.management.config.annotations.AuthServerEnabled import org.radarbase.management.config.ManagementPortalProperties import org.radarbase.management.config.ManagementPortalProperties.Oauth import org.radarbase.management.security.jwt.algorithm.EcdsaJwtAlgorithm diff --git a/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt b/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt index 0c03f51ed..f3a6cb648 100644 --- a/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt +++ b/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt @@ -1,6 +1,6 @@ package org.radarbase.management.service -import org.radarbase.management.config.AuthServerEnabled +import org.radarbase.management.config.annotations.AuthServerEnabled import org.radarbase.management.domain.User import org.radarbase.management.service.dto.ClientDetailsDTO import org.radarbase.management.service.mapper.ClientDetailsMapper diff --git a/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt b/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt index 5e8ec14c8..2cbd3c371 100644 --- a/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt +++ b/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt @@ -1,5 +1,7 @@ package org.radarbase.management.service.config +import org.radarbase.management.config.annotations.AuthServerEnabled +import org.radarbase.management.config.annotations.AuthServerDisabled import org.radarbase.management.config.ManagementPortalProperties import org.radarbase.management.service.DefaultOAuthClientService import org.radarbase.management.service.OAuthClientService @@ -15,31 +17,33 @@ import org.springframework.security.oauth2.provider.client.JdbcClientDetailsServ @Configuration class OAuthClientServiceConfiguration { - @Bean - @Primary - fun oAuthClientService( - managementPortalProperties: ManagementPortalProperties, - jdbcClientDetailsService: JdbcClientDetailsService?, - clientDetailsMapper: ClientDetailsMapper?, - authorizationServerEndpointsConfiguration: AuthorizationServerEndpointsConfiguration? - ): OAuthClientService { - return if (managementPortalProperties.authServer.internal) { + @AuthServerEnabled + @Configuration + class DefaultOAuthClientServiceConfiguration { + @Bean + @Primary + fun defaultOAuthClientService( + jdbcClientDetailsService: JdbcClientDetailsService, + clientDetailsMapper: ClientDetailsMapper, + authorizationServerEndpointsConfiguration: AuthorizationServerEndpointsConfiguration + ): OAuthClientService { log.info("Using internal OAuth client management") - require(jdbcClientDetailsService != null) { - "JdbcClientDetailsService must be available when using internal OAuth server" - } - require(clientDetailsMapper != null) { - "ClientDetailsMapper must be available when using internal OAuth server" - } - require(authorizationServerEndpointsConfiguration != null) { - "AuthorizationServerEndpointsConfiguration must be available when using internal OAuth server" - } - DefaultOAuthClientService( + return DefaultOAuthClientService( jdbcClientDetailsService, clientDetailsMapper, authorizationServerEndpointsConfiguration ) - } else { + } + } + + @AuthServerDisabled + @Configuration + class HydraOAuthClientServiceConfiguration { + @Bean + @Primary + fun hydraOAuthClientService( + managementPortalProperties: ManagementPortalProperties + ): OAuthClientService { log.info("Using Hydra external OAuth client management") // Validate Hydra configuration require(managementPortalProperties.authServer.serverUrl.isNotBlank()) { @@ -48,7 +52,7 @@ class OAuthClientServiceConfiguration { require(managementPortalProperties.authServer.serverAdminUrl.isNotBlank()) { "Hydra admin server URL must be configured when using external OAuth management" } - HydraOAuthClientService(managementPortalProperties) + return HydraOAuthClientService(managementPortalProperties) } } diff --git a/src/main/java/org/radarbase/management/service/config/UserServiceConfig.kt b/src/main/java/org/radarbase/management/service/config/UserServiceConfig.kt index 81dcb5f8f..d025a900f 100644 --- a/src/main/java/org/radarbase/management/service/config/UserServiceConfig.kt +++ b/src/main/java/org/radarbase/management/service/config/UserServiceConfig.kt @@ -1,5 +1,7 @@ package org.radarbase.management.service.config +import org.radarbase.management.config.annotations.IdentityServerEnabled +import org.radarbase.management.config.annotations.IdentityServerDisabled import org.radarbase.management.config.ManagementPortalProperties import org.radarbase.management.service.DefaultUserService import org.radarbase.management.service.KratosUserService @@ -7,7 +9,6 @@ import org.radarbase.management.service.UserService import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Primary -import org.springframework.beans.factory.annotation.Autowired import org.radarbase.management.repository.UserRepository import org.radarbase.management.service.PasswordService import org.radarbase.management.service.mapper.UserMapper @@ -19,29 +20,58 @@ import org.radarbase.management.service.MailService @Configuration class UserServiceConfiguration { - @Bean - @Primary - fun userService( - userRepository: UserRepository, - passwordService: PasswordService, - userMapper: UserMapper, - revisionService: RevisionService, - managementPortalProperties: ManagementPortalProperties, - authService: AuthService, - mailService: MailService - ): UserService { - return if (managementPortalProperties.identityServer.internal) { + @IdentityServerEnabled + @Configuration + class DefaultUserServiceConfiguration { + @Bean + @Primary + fun defaultUserService( + userRepository: UserRepository, + passwordService: PasswordService, + userMapper: UserMapper, + revisionService: RevisionService, + managementPortalProperties: ManagementPortalProperties, + authService: AuthService, + mailService: MailService + ): UserService { log.info("Using internal user management") - DefaultUserService(userRepository, passwordService, userMapper, - revisionService, managementPortalProperties, authService, mailService) - } else { + return DefaultUserService( + userRepository, + passwordService, + userMapper, + revisionService, + managementPortalProperties, + authService, + mailService + ) + } + } + + @IdentityServerDisabled + @Configuration + class KratosUserServiceConfiguration { + @Bean + @Primary + fun kratosUserService( + userRepository: UserRepository, + passwordService: PasswordService, + userMapper: UserMapper, + revisionService: RevisionService, + managementPortalProperties: ManagementPortalProperties, + authService: AuthService + ): UserService { log.info("Using Kratos external user management") - // Could add validation here require(managementPortalProperties.identityServer.serverUrl.isNotBlank()) { "Kratos server URL must be configured when using external identity management" } - KratosUserService(userRepository, passwordService, userMapper, - revisionService, managementPortalProperties, authService) + return KratosUserService( + userRepository, + passwordService, + userMapper, + revisionService, + managementPortalProperties, + authService + ) } } diff --git a/src/main/java/org/radarbase/management/web/rest/OAuthClientsResource.kt b/src/main/java/org/radarbase/management/web/rest/OAuthClientsResource.kt index 5f10f8adc..8d70d0bce 100644 --- a/src/main/java/org/radarbase/management/web/rest/OAuthClientsResource.kt +++ b/src/main/java/org/radarbase/management/web/rest/OAuthClientsResource.kt @@ -3,7 +3,7 @@ package org.radarbase.management.web.rest import io.micrometer.core.annotation.Timed import org.radarbase.auth.authorization.EntityDetails import org.radarbase.auth.authorization.Permission -import org.radarbase.management.config.AuthServerEnabled +import org.radarbase.management.config.annotations.AuthServerEnabled import org.radarbase.management.security.Constants import org.radarbase.management.security.NotAuthorizedException import org.radarbase.management.service.* diff --git a/src/main/java/org/radarbase/management/web/rest/TokenKeyEndpoint.kt b/src/main/java/org/radarbase/management/web/rest/TokenKeyEndpoint.kt index a44ac3b09..5e9146ea5 100644 --- a/src/main/java/org/radarbase/management/web/rest/TokenKeyEndpoint.kt +++ b/src/main/java/org/radarbase/management/web/rest/TokenKeyEndpoint.kt @@ -2,7 +2,7 @@ package org.radarbase.management.web.rest import io.micrometer.core.annotation.Timed import org.radarbase.auth.jwks.JsonWebKeySet -import org.radarbase.management.config.AuthServerEnabled +import org.radarbase.management.config.annotations.AuthServerEnabled import org.radarbase.management.security.jwt.ManagementPortalOauthKeyStoreHandler import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired diff --git a/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt b/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt index fe669528b..1d708262c 100644 --- a/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt +++ b/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt @@ -16,7 +16,9 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* import java.net.URISyntaxException +import org.radarbase.management.config.annotations.IdentityServerDisabled +@IdentityServerDisabled @RestController @RequestMapping("/api/webhook/kratos") private class WebhookResource From e29f83992e567f4ea7ab6eeb7f6e94a408257e81 Mon Sep 17 00:00:00 2001 From: Pauline Date: Fri, 19 Dec 2025 00:27:59 +0800 Subject: [PATCH 11/25] Make hydra jwt key path configurable --- .../radarbase/management/config/ManagementPortalProperties.kt | 1 + .../org/radarbase/management/config/SecurityConfiguration.kt | 2 +- src/main/resources/config/application-dev.yml | 1 + src/main/resources/config/application-prod.yml | 1 + src/test/resources/config/application.yml | 1 + 5 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/radarbase/management/config/ManagementPortalProperties.kt b/src/main/java/org/radarbase/management/config/ManagementPortalProperties.kt index 6a02b3c55..7782a3e2e 100644 --- a/src/main/java/org/radarbase/management/config/ManagementPortalProperties.kt +++ b/src/main/java/org/radarbase/management/config/ManagementPortalProperties.kt @@ -29,6 +29,7 @@ data class ManagementPortalProperties @ConstructorBinding constructor( val serverUrl: String, val serverAdminUrl: String, val loginUrl: String, + val keysPath: String = "/admin/keys/hydra.jwt.access-token", ) data class Mail @ConstructorBinding constructor( diff --git a/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt b/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt index 82b9b1d55..568682e7b 100644 --- a/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt +++ b/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt @@ -49,7 +49,7 @@ class SecurityConfiguration val loaderList = mutableListOf( JwksTokenVerifierLoader( managementPortalProperties.authServer.serverAdminUrl + - "/admin/keys/hydra.jwt.access-token", + managementPortalProperties.authServer.keysPath, RES_MANAGEMENT_PORTAL, JwkAlgorithmParser(), ) diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml index 136f4984a..13b6ef1ac 100644 --- a/src/main/resources/config/application-dev.yml +++ b/src/main/resources/config/application-dev.yml @@ -175,6 +175,7 @@ managementportal: serverUrl: http://localhost:4444 serverAdminUrl: http://localhost:4445 loginUrl: http://localhost:4444 + keysPath: /admin/keys/hydra.jwt.access-token # =================================================================== diff --git a/src/main/resources/config/application-prod.yml b/src/main/resources/config/application-prod.yml index d79555a20..3be1dfae2 100644 --- a/src/main/resources/config/application-prod.yml +++ b/src/main/resources/config/application-prod.yml @@ -160,6 +160,7 @@ managementportal: serverUrl: http://hydra:4444 serverAdminUrl: http://hydra:4445 loginUrl: http://localhost:4444 + keysPath: /admin/keys/hydra.jwt.access-token # =================================================================== # JHipster specific properties diff --git a/src/test/resources/config/application.yml b/src/test/resources/config/application.yml index 2fd14d682..307e25e50 100644 --- a/src/test/resources/config/application.yml +++ b/src/test/resources/config/application.yml @@ -140,5 +140,6 @@ managementportal: serverUrl: http://hydra:4444 serverAdminUrl: http://hydra:4445 loginUrl: http://localhost:4444 + keysPath: /admin/keys/hydra.jwt.access-token siteSettings: hiddenSubjectFields: [] From 63f7351d6ed65121c7bc694f59ae2e812477f4b1 Mon Sep 17 00:00:00 2001 From: Pauline Date: Fri, 19 Dec 2025 19:33:40 +0800 Subject: [PATCH 12/25] Fix UserService tests --- .../management/service/UserServiceIntTest.kt | 154 +++++++++++------- 1 file changed, 98 insertions(+), 56 deletions(-) diff --git a/src/test/java/org/radarbase/management/service/UserServiceIntTest.kt b/src/test/java/org/radarbase/management/service/UserServiceIntTest.kt index aec5765d3..55999b897 100644 --- a/src/test/java/org/radarbase/management/service/UserServiceIntTest.kt +++ b/src/test/java/org/radarbase/management/service/UserServiceIntTest.kt @@ -1,6 +1,5 @@ package org.radarbase.management.service -import kotlinx.coroutines.runBlocking import org.assertj.core.api.Assertions import org.hibernate.envers.AuditReaderFactory import org.hibernate.envers.query.AuditEntity @@ -52,6 +51,7 @@ class UserServiceIntTest( private lateinit var entityManager: EntityManager private lateinit var userDto: UserDTO + @BeforeEach fun setUp() { entityManager = entityManagerFactory.createEntityManager( @@ -59,7 +59,18 @@ class UserServiceIntTest( ) userDto = userMapper.userToUserDTO(createEntity(passwordService))!! - userRepository.findOneByLogin(userDto.login)?.let { userRepository.delete(it)} + // Clean up any existing user with the same login/email before each test + userRepository.findOneByLogin(userDto.login)?.let { user -> + userRepository.delete(user) + userRepository.flush() + } + // Also check by email in case login differs + userDto.email?.let { email -> + userRepository.findOneByEmail(email)?.let { user -> + userRepository.delete(user) + userRepository.flush() + } + } } @Test @@ -76,77 +87,108 @@ class UserServiceIntTest( @Test @Throws(NotAuthorizedException::class) fun assertThatOnlyActivatedUserCanRequestPasswordReset() { - runBlocking { - val user = userService.createUser(userDto) - val maybeUser = userService.requestPasswordReset( - userDto.email!! - ) - Assertions.assertThat(maybeUser).isNull() - userRepository.delete(user) + // Ensure user doesn't exist first + userRepository.findOneByLogin(userDto.login)?.let { + userRepository.delete(it) + userRepository.flush() } + + // Create user manually using repository + val user = createEntity(passwordService) + userRepository.save(user) + userRepository.flush() + + // Deactivate the user to test that only activated users can request password reset + user.activated = false + userRepository.save(user) + userRepository.flush() + + val maybeUser = userService.requestPasswordReset( + userDto.email!! + ) + Assertions.assertThat(maybeUser).isNull() + userRepository.delete(user) + userRepository.flush() } @Test @Throws(NotAuthorizedException::class) fun assertThatResetKeyMustNotBeOlderThan24Hours() { - runBlocking { - val user = userService.createUser(userDto) - val daysAgo = ZonedDateTime.now().minusHours(25) - val resetKey = passwordService.generateResetKey() - user.activated = true - user.resetDate = daysAgo - user.resetKey = resetKey - userService.updateUser(userMapper.userToUserDTO(user)!!) - val maybeUser = userService.completePasswordReset( - "johndoe2", - user.resetKey!! - ) - Assertions.assertThat(maybeUser).isNull() - userService.deleteUser(user.login!!) - } + val user = createEntity(passwordService) + userRepository.save(user) + userRepository.flush() + + val daysAgo = ZonedDateTime.now().minusHours(25) + val resetKey = passwordService.generateResetKey() + user.activated = true + user.resetDate = daysAgo + user.resetKey = resetKey + userRepository.save(user) + userRepository.flush() + + val maybeUser = userService.completePasswordReset( + "johndoe2", + user.resetKey!! + ) + Assertions.assertThat(maybeUser).isNull() + userRepository.delete(user) + userRepository.flush() } @Test @Throws(NotAuthorizedException::class) fun assertThatResetKeyMustBeValid() { - runBlocking { - val user = userService.createUser(userDto) - val daysAgo = ZonedDateTime.now().minusHours(25) - user.activated = true - user.resetDate = daysAgo - user.resetKey = "1234" - userRepository.save(user) - val maybeUser = userService.completePasswordReset( - "johndoe2", - user.resetKey!! - ) - Assertions.assertThat(maybeUser).isNull() - userService.deleteUser(user.login!!) + // Ensure user doesn't exist first + userRepository.findOneByLogin(userDto.login)?.let { + userRepository.delete(it) + userRepository.flush() } + + val user = createEntity(passwordService) + userRepository.save(user) + userRepository.flush() + + val daysAgo = ZonedDateTime.now().minusHours(25) + user.activated = true + user.resetDate = daysAgo + user.resetKey = "1234" + userRepository.save(user) + userRepository.flush() // Ensure changes are persisted + val maybeUser = userService.completePasswordReset( + "johndoe2", + user.resetKey!! + ) + Assertions.assertThat(maybeUser).isNull() + userRepository.delete(user) + userRepository.flush() } @Test @Throws(NotAuthorizedException::class) fun assertThatUserCanResetPassword() { - runBlocking { - val user = userService.createUser(userDto) - val oldPassword = user.password - val daysAgo = ZonedDateTime.now().minusHours(2) - val resetKey = passwordService.generateResetKey() - user.activated = true - user.resetDate = daysAgo - user.resetKey = resetKey - userService.updateUser(userMapper.userToUserDTO(user)!!) - val maybeUser = userService.completePasswordReset( - "johndoe2", - user.resetKey!! - ) - Assertions.assertThat(maybeUser).isNotNull() - Assertions.assertThat(maybeUser?.resetDate).isNull() - Assertions.assertThat(maybeUser?.resetKey).isNull() - Assertions.assertThat(maybeUser?.password).isNotEqualTo(oldPassword) - userService.deleteUser(user.login!!) - } + val user = createEntity(passwordService) + userRepository.save(user) + userRepository.flush() + + val oldPassword = user.password + val daysAgo = ZonedDateTime.now().minusHours(2) + val resetKey = passwordService.generateResetKey() + user.activated = true + user.resetDate = daysAgo + user.resetKey = resetKey + userRepository.save(user) + userRepository.flush() + + val maybeUser = userService.completePasswordReset( + "johndoe2", + user.resetKey!! + ) + Assertions.assertThat(maybeUser).isNotNull() + Assertions.assertThat(maybeUser?.resetDate).isNull() + Assertions.assertThat(maybeUser?.resetKey).isNull() + Assertions.assertThat(maybeUser?.password).isNotEqualTo(oldPassword) + userRepository.delete(user) + userRepository.flush() } @Test From b54cc4455a0731b37a25ae9886467d073410b4d1 Mon Sep 17 00:00:00 2001 From: Pauline Date: Fri, 19 Dec 2025 19:48:20 +0800 Subject: [PATCH 13/25] Create separate TokenValidator bean --- .../config/SecurityConfiguration.kt | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt b/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt index 568682e7b..5b31e1fa4 100644 --- a/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt +++ b/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt @@ -43,30 +43,32 @@ class SecurityConfiguration private val managementPortalProperties: ManagementPortalProperties, private val userRepository: UserRepository ) : WebSecurityConfigurerAdapter() { - val tokenValidator: TokenValidator - /** Get the default token validator. */ - get() { - val loaderList = mutableListOf( + /** + * Create the token validator instance. + * This is a private method to avoid creating multiple instances. + */ + private fun createTokenValidator(): TokenValidator { + val loaderList = mutableListOf( + JwksTokenVerifierLoader( + managementPortalProperties.authServer.serverAdminUrl + + managementPortalProperties.authServer.keysPath, + RES_MANAGEMENT_PORTAL, + JwkAlgorithmParser(), + ) + ) + + // Only load ManagementPortal's own token_key endpoint if the internal auth server is enabled. + if (managementPortalProperties.authServer.internal) { + loaderList.add( JwksTokenVerifierLoader( - managementPortalProperties.authServer.serverAdminUrl + - managementPortalProperties.authServer.keysPath, + managementPortalProperties.common.managementPortalBaseUrl + "/oauth/token_key", RES_MANAGEMENT_PORTAL, - JwkAlgorithmParser(), + JwkAlgorithmParser() ) ) - - // Only load ManagementPortal's own token_key endpoint if the internal auth server is enabled. - if (managementPortalProperties.authServer.internal) { - loaderList.add( - JwksTokenVerifierLoader( - managementPortalProperties.common.managementPortalBaseUrl + "/oauth/token_key", - RES_MANAGEMENT_PORTAL, - JwkAlgorithmParser() - ) - ) - } - return TokenValidator(loaderList) } + return TokenValidator(loaderList) + } @PostConstruct fun init() { @@ -89,10 +91,10 @@ class SecurityConfiguration fun http401UnauthorizedEntryPoint(): Http401UnauthorizedEntryPoint = Http401UnauthorizedEntryPoint() @Bean - fun tokenValidatorBean(): TokenValidator = tokenValidator + fun tokenValidatorBean(): TokenValidator = createTokenValidator() @Bean - fun jwtAuthenticationFilter(): JwtAuthenticationFilter { + fun jwtAuthenticationFilter(tokenValidator: TokenValidator): JwtAuthenticationFilter { val useInternalAuth = managementPortalProperties.authServer.internal return JwtAuthenticationFilter( @@ -164,7 +166,7 @@ class SecurityConfiguration .authenticationEntryPoint(http401UnauthorizedEntryPoint()) .and() .addFilterBefore( - jwtAuthenticationFilter(), + jwtAuthenticationFilter(tokenValidatorBean()), UsernamePasswordAuthenticationFilter::class.java, ) .authorizeRequests() From 740729fe55948714449ed06f4e5d7892232edbb1 Mon Sep 17 00:00:00 2001 From: Pauline Date: Wed, 21 Jan 2026 22:50:46 +0800 Subject: [PATCH 14/25] Remove unnecessary Primary annotation from OauthClientServiceConfig --- .../management/service/config/OAuthClientServiceConfig.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt b/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt index 2cbd3c371..2937a0f47 100644 --- a/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt +++ b/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt @@ -10,7 +10,6 @@ import org.radarbase.management.service.mapper.ClientDetailsMapper import org.slf4j.LoggerFactory import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.context.annotation.Primary import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService @@ -21,7 +20,6 @@ class OAuthClientServiceConfiguration { @Configuration class DefaultOAuthClientServiceConfiguration { @Bean - @Primary fun defaultOAuthClientService( jdbcClientDetailsService: JdbcClientDetailsService, clientDetailsMapper: ClientDetailsMapper, @@ -40,7 +38,6 @@ class OAuthClientServiceConfiguration { @Configuration class HydraOAuthClientServiceConfiguration { @Bean - @Primary fun hydraOAuthClientService( managementPortalProperties: ManagementPortalProperties ): OAuthClientService { From c61368186c3064e9f8a7273373ac489df8b0042f Mon Sep 17 00:00:00 2001 From: Pauline Date: Wed, 21 Jan 2026 23:01:43 +0800 Subject: [PATCH 15/25] Separate out internal and external tokenvalidators --- .../config/SecurityConfiguration.kt | 99 ++++++++++++------- 1 file changed, 66 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt b/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt index 5b31e1fa4..c83e81698 100644 --- a/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt +++ b/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt @@ -3,6 +3,8 @@ package org.radarbase.management.config import org.radarbase.auth.authentication.TokenValidator import org.radarbase.auth.jwks.JwkAlgorithmParser import org.radarbase.auth.jwks.JwksTokenVerifierLoader +import org.radarbase.management.config.annotations.AuthServerDisabled +import org.radarbase.management.config.annotations.AuthServerEnabled import org.radarbase.management.repository.UserRepository import org.radarbase.management.security.Http401UnauthorizedEntryPoint import org.radarbase.management.security.JwtAuthenticationFilter @@ -41,35 +43,9 @@ class SecurityConfiguration private val applicationEventPublisher: ApplicationEventPublisher, private val passwordEncoder: PasswordEncoder, private val managementPortalProperties: ManagementPortalProperties, - private val userRepository: UserRepository + private val userRepository: UserRepository, + private val tokenValidator: TokenValidator, ) : WebSecurityConfigurerAdapter() { - /** - * Create the token validator instance. - * This is a private method to avoid creating multiple instances. - */ - private fun createTokenValidator(): TokenValidator { - val loaderList = mutableListOf( - JwksTokenVerifierLoader( - managementPortalProperties.authServer.serverAdminUrl + - managementPortalProperties.authServer.keysPath, - RES_MANAGEMENT_PORTAL, - JwkAlgorithmParser(), - ) - ) - - // Only load ManagementPortal's own token_key endpoint if the internal auth server is enabled. - if (managementPortalProperties.authServer.internal) { - loaderList.add( - JwksTokenVerifierLoader( - managementPortalProperties.common.managementPortalBaseUrl + "/oauth/token_key", - RES_MANAGEMENT_PORTAL, - JwkAlgorithmParser() - ) - ) - } - return TokenValidator(loaderList) - } - @PostConstruct fun init() { try { @@ -91,10 +67,7 @@ class SecurityConfiguration fun http401UnauthorizedEntryPoint(): Http401UnauthorizedEntryPoint = Http401UnauthorizedEntryPoint() @Bean - fun tokenValidatorBean(): TokenValidator = createTokenValidator() - - @Bean - fun jwtAuthenticationFilter(tokenValidator: TokenValidator): JwtAuthenticationFilter { + fun jwtAuthenticationFilter(): JwtAuthenticationFilter { val useInternalAuth = managementPortalProperties.authServer.internal return JwtAuthenticationFilter( @@ -166,7 +139,7 @@ class SecurityConfiguration .authenticationEntryPoint(http401UnauthorizedEntryPoint()) .and() .addFilterBefore( - jwtAuthenticationFilter(tokenValidatorBean()), + jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter::class.java, ) .authorizeRequests() @@ -191,3 +164,63 @@ class SecurityConfiguration const val RES_MANAGEMENT_PORTAL = "res_ManagementPortal" } } + +/** + * TokenValidator configuration for internal auth server. + * Active when managementportal.authServer.internal=true (or missing). + */ +@AuthServerEnabled +@Configuration +class InternalTokenValidatorConfiguration( + private val managementPortalProperties: ManagementPortalProperties, +) { + + @Bean + fun internalTokenValidator(): TokenValidator { + val loaderList = mutableListOf( + JwksTokenVerifierLoader( + managementPortalProperties.authServer.serverAdminUrl + + managementPortalProperties.authServer.keysPath, + SecurityConfiguration.RES_MANAGEMENT_PORTAL, + JwkAlgorithmParser(), + ) + ) + + // Also load ManagementPortal's own token_key endpoint for internal auth. + loaderList.add( + JwksTokenVerifierLoader( + managementPortalProperties.common.managementPortalBaseUrl + "/oauth/token_key", + SecurityConfiguration.RES_MANAGEMENT_PORTAL, + JwkAlgorithmParser() + ) + ) + + return TokenValidator(loaderList) + } +} + +/** + * TokenValidator configuration for external auth server (e.g. Hydra). + * Active when managementportal.authServer.internal=false. + */ +@AuthServerDisabled +@Configuration +class ExternalTokenValidatorConfiguration( + private val managementPortalProperties: ManagementPortalProperties, +) { + + @Bean + fun externalTokenValidator(): TokenValidator { + val loaderList = listOf( + JwksTokenVerifierLoader( + managementPortalProperties.authServer.serverAdminUrl + + managementPortalProperties.authServer.keysPath, + SecurityConfiguration.RES_MANAGEMENT_PORTAL, + JwkAlgorithmParser(), + ) + ) + + return TokenValidator(loaderList) + } +} + From 0a75855c080961fee4c16367b2a6c687a4df1c00 Mon Sep 17 00:00:00 2001 From: Pauline Date: Wed, 21 Jan 2026 23:47:07 +0800 Subject: [PATCH 16/25] Fix OAuthClientService conditional beans --- .../radarbase/management/service/DefaultOAuthClientService.kt | 3 --- .../management/service/config/OAuthClientServiceConfig.kt | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt b/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt index f3a6cb648..0e22fa0c9 100644 --- a/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt +++ b/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt @@ -18,15 +18,12 @@ import org.springframework.security.oauth2.provider.NoSuchClientException import org.springframework.security.oauth2.provider.OAuth2Authentication import org.springframework.security.oauth2.provider.OAuth2Request import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService -import org.springframework.stereotype.Service import java.util.* /** * Default implementation of OAuthClientService using Spring OAuth2 and JDBC. * This service handles OAuth client and token related functions using the internal OAuth server. */ -@AuthServerEnabled -@Service class DefaultOAuthClientService( @Autowired private val clientDetailsService: JdbcClientDetailsService, @Autowired private val clientDetailsMapper: ClientDetailsMapper, diff --git a/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt b/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt index 2937a0f47..a3961914c 100644 --- a/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt +++ b/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt @@ -10,6 +10,7 @@ import org.radarbase.management.service.mapper.ClientDetailsMapper import org.slf4j.LoggerFactory import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService @@ -20,6 +21,7 @@ class OAuthClientServiceConfiguration { @Configuration class DefaultOAuthClientServiceConfiguration { @Bean + @ConditionalOnMissingBean(OAuthClientService::class) fun defaultOAuthClientService( jdbcClientDetailsService: JdbcClientDetailsService, clientDetailsMapper: ClientDetailsMapper, @@ -38,6 +40,7 @@ class OAuthClientServiceConfiguration { @Configuration class HydraOAuthClientServiceConfiguration { @Bean + @ConditionalOnMissingBean(OAuthClientService::class) fun hydraOAuthClientService( managementPortalProperties: ManagementPortalProperties ): OAuthClientService { From 61ac443f6dd4ba4885b7845d85b47b85b4a9768b Mon Sep 17 00:00:00 2001 From: Pauline Date: Thu, 22 Jan 2026 00:29:45 +0800 Subject: [PATCH 17/25] Fix Service annottion in DefaultOauthClientService --- .../radarbase/management/service/DefaultOAuthClientService.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt b/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt index 0e22fa0c9..20595973e 100644 --- a/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt +++ b/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt @@ -24,6 +24,7 @@ import java.util.* * Default implementation of OAuthClientService using Spring OAuth2 and JDBC. * This service handles OAuth client and token related functions using the internal OAuth server. */ +@Service class DefaultOAuthClientService( @Autowired private val clientDetailsService: JdbcClientDetailsService, @Autowired private val clientDetailsMapper: ClientDetailsMapper, From 5353eb8f91161ed8359edbfec7d1400a23cb64b0 Mon Sep 17 00:00:00 2001 From: Pauline Date: Tue, 10 Feb 2026 04:44:47 +0800 Subject: [PATCH 18/25] Minor fixes --- .github/workflows/main.yml | 3 +-- .../management/service/DefaultOAuthClientService.kt | 3 +-- .../radarbase/management/service/HydraOAuthClientService.kt | 2 +- .../management/service/config/OAuthClientServiceConfig.kt | 6 +++--- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5e3f4fce0..ac16c456b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,10 +35,9 @@ jobs: id: yarn-cache-dir-path run: echo "dir=.yarn/cache" >> $GITHUB_OUTPUT - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: temurin - java-version: 17 - name: Setup Gradle uses: gradle/gradle-build-action@v2 diff --git a/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt b/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt index 20595973e..55dab7660 100644 --- a/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt +++ b/src/main/java/org/radarbase/management/service/DefaultOAuthClientService.kt @@ -24,8 +24,7 @@ import java.util.* * Default implementation of OAuthClientService using Spring OAuth2 and JDBC. * This service handles OAuth client and token related functions using the internal OAuth server. */ -@Service -class DefaultOAuthClientService( +open class DefaultOAuthClientService( @Autowired private val clientDetailsService: JdbcClientDetailsService, @Autowired private val clientDetailsMapper: ClientDetailsMapper, @Autowired private val authorizationServerEndpointsConfiguration: AuthorizationServerEndpointsConfiguration diff --git a/src/main/java/org/radarbase/management/service/HydraOAuthClientService.kt b/src/main/java/org/radarbase/management/service/HydraOAuthClientService.kt index 595586f0e..6b6ad8e40 100644 --- a/src/main/java/org/radarbase/management/service/HydraOAuthClientService.kt +++ b/src/main/java/org/radarbase/management/service/HydraOAuthClientService.kt @@ -29,7 +29,7 @@ import java.time.Duration * This service handles OAuth client operations through Ory Hydra's admin API. */ @Service -class HydraOAuthClientService( +open class HydraOAuthClientService( @Autowired private val managementPortalProperties: ManagementPortalProperties ) : OAuthClientService { diff --git a/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt b/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt index a3961914c..2cbd3c371 100644 --- a/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt +++ b/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt @@ -10,7 +10,7 @@ import org.radarbase.management.service.mapper.ClientDetailsMapper import org.slf4j.LoggerFactory import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.context.annotation.Primary import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService @@ -21,7 +21,7 @@ class OAuthClientServiceConfiguration { @Configuration class DefaultOAuthClientServiceConfiguration { @Bean - @ConditionalOnMissingBean(OAuthClientService::class) + @Primary fun defaultOAuthClientService( jdbcClientDetailsService: JdbcClientDetailsService, clientDetailsMapper: ClientDetailsMapper, @@ -40,7 +40,7 @@ class OAuthClientServiceConfiguration { @Configuration class HydraOAuthClientServiceConfiguration { @Bean - @ConditionalOnMissingBean(OAuthClientService::class) + @Primary fun hydraOAuthClientService( managementPortalProperties: ManagementPortalProperties ): OAuthClientService { From b2c266dce0e74f700c6246691743f6e6e85eda4b Mon Sep 17 00:00:00 2001 From: Pauline Date: Tue, 10 Feb 2026 04:45:53 +0800 Subject: [PATCH 19/25] Fix: GA --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ac16c456b..f86a1b742 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,6 +38,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: temurin + java-version: '17' - name: Setup Gradle uses: gradle/gradle-build-action@v2 From 13a5c995263b76e4f602f4e458e75606c76abae8 Mon Sep 17 00:00:00 2001 From: Pauline Date: Tue, 10 Feb 2026 04:02:12 +0800 Subject: [PATCH 20/25] Improve authServer and identityServer var naming --- README.md | 26 ++++++++++--------- src/main/docker/app.yml | 14 +++++----- src/main/docker/managementportal.yml | 13 +++++----- .../config/ManagementPortalProperties.kt | 13 +++++----- .../service/HydraOAuthClientService.kt | 3 +-- .../management/service/KratosUserService.kt | 4 +-- .../management/service/LoginService.kt | 4 +-- .../config/OAuthClientServiceConfig.kt | 8 +++--- .../service/config/UserServiceConfig.kt | 2 +- .../management/web/rest/WebhookResource.kt | 2 +- src/main/resources/config/application-dev.yml | 12 ++++----- .../config/application-e2e-prod-test.yml | 4 +-- .../resources/config/application-prod.yml | 12 ++++----- src/test/resources/config/application.yml | 14 +++++----- 14 files changed, 67 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 4821d5c6f..875169459 100644 --- a/README.md +++ b/README.md @@ -147,18 +147,19 @@ for other options on overriding the default configuration. | `MANAGEMENTPORTAL_OAUTH_KEY_STORE_PASSWORD` | `radarbase` | Password for the JWT keystore | | `MANAGEMENTPORTAL_OAUTH_SIGNING_KEY_ALIAS` | `radarbase-managementportal-ec` | Alias in the keystore of the keypair to use for signing | | `MANAGEMENTPORTAL_OAUTH_ENABLE_PUBLIC_KEY_VERIFIERS` | `false` | Whether to use additional verifiers using public-keys and deprecated verifier implementation. If you set this to `true`, also set `RADAR_IS_CONFIG_LOCATION` and provide yaml file with public keys. Read more at radar-auth documentation. | -| `MANAGEMENTPORTAL_CATALOGUE_SERVER_ENABLE_AUTO_IMPORT` | `false` | Whether to enable or disable auto import of sources from the catalogue server | -| `MANAGEMENTPORTAL_CATALOGUE_SERVER_SERVER_URL` | None | URL to the catalogue server | +| `MANAGEMENTPORTAL_CATALOGUESERVER_ENABLEAUTOIMPORT` | `false` | Whether to enable or disable auto import of sources from the catalogue server (maps to `managementportal.catalogueServer.enableAutoImport` in application-prod.yml). | +| `MANAGEMENTPORTAL_CATALOGUESERVER_SERVERURL` | None | URL to the catalogue server (maps to `managementportal.catalogueServer.serverUrl` in application-prod.yml). | | `MANAGEMENTPORTAL_IDENTITYSERVER_INTERNAL` | `true` | Whether to use ManagementPortal’s internal identity management (`true`) or an external identity server such as Ory Kratos (`false`). | -| `MANAGEMENTPORTAL_IDENTITYSERVER_SERVERURL` | None | URL to the identity server. When using Ory Kratos externally, set this to the Kratos public URL as seen from ManagementPortal (for example `http://kratos:4433` when using Docker). | -| `MANAGEMENTPORTAL_IDENTITYSERVER_SERVERADMINURL` | None | Admin URL to the identity server. When using Ory Kratos externally, set this to the Kratos admin URL as seen from ManagementPortal (for example `http://kratos:4434`). | +| `MANAGEMENTPORTAL_IDENTITYSERVER_PUBLICURL` | None | Public URL of the identity server. When using Ory Kratos externally, set this to the Kratos public URL as seen from ManagementPortal (e.g. `http://kratos:4433`; maps to `managementportal.identityServer.publicUrl` in application-prod.yml). | +| `MANAGEMENTPORTAL_IDENTITYSERVER_ADMINURL` | None | Admin URL to the identity server. When using Ory Kratos externally, set this to the Kratos admin URL as seen from ManagementPortal (e.g. `http://kratos:4434` or `http://kratos-admin:4434`; maps to `managementportal.identityServer.adminUrl` in application-prod.yml). | | `MANAGEMENTPORTAL_IDENTITYSERVER_ADMINEMAIL` | None | Email-address to be linked to the admin account. | | `MANAGEMENTPORTAL_IDENTITYSERVER_USER_ACTIVATION_FLOW_TYPE` | `verification` | Kratos self-service flow used to send activation emails. Allowed: `verification` or `recovery`. | | `MANAGEMENTPORTAL_IDENTITYSERVER_USER_ACTIVATION_METHOD` | `code` | Kratos method used in the activation flow request payload. Allowed: `code` or `link` (depending on flow and Kratos configuration). | | `MANAGEMENTPORTAL_AUTHSERVER_INTERNAL` | `true` | Whether to use ManagementPortal’s internal OAuth2 authorization server (`true`) or an external server such as Ory Hydra (`false`). | -| `MANAGEMENTPORTAL_AUTHSERVER_SERVERURL` | None | Base URL of the external auth server’s public endpoints, used for the token endpoint. When using Ory Hydra in Docker, this is typically `http://hydra:4444`. | -| `MANAGEMENTPORTAL_AUTHSERVER_LOGINURL` | None | Base URL of the external auth server as seen from the browser, used to construct the `/oauth2/auth` redirect URL (for example `http://localhost:4444` for Hydra’s public endpoint). | -| `MANAGEMENTPORTAL_AUTHSERVER_SERVERADMINURL` | None | Admin URL of the external auth server (for example `http://hydra:4445` for the Hydra admin API), used for managing OAuth clients. | +| `MANAGEMENTPORTAL_AUTHSERVER_TOKENURL` | None | Full URL of the external auth server’s token endpoint. When using Ory Hydra, set to the token URL (e.g. `http://hydra:4444/hydra/oauth2/token`; maps to `managementportal.authServer.tokenUrl` in application-prod.yml). | +| `MANAGEMENTPORTAL_AUTHSERVER_AUTHURL` | None | Auth URL as seen from the browser, used for the authorize redirect (e.g. `http://localhost:4444/hydra/oauth2/auth`; maps to `managementportal.authServer.authUrl` in application-prod.yml). | +| `MANAGEMENTPORTAL_AUTHSERVER_ADMINURL` | None | Admin URL of the external auth server (e.g. `http://hydra:4445` for the Hydra admin API; maps to `managementportal.authServer.adminUrl` in application-prod.yml), used for managing OAuth clients. | +| `MANAGEMENTPORTAL_AUTHSERVER_JWKSURL` | None | Full JWKS URL used to verify JWTs (e.g. `http://hydra:4445/admin/keys/hydra.jwt.access-token`; maps to `managementportal.authServer.jwksUrl` in application-prod.yml). | | `MANAGEMENTPORTAL_COMMON_BASE_URL` | None | Resolvable baseUrl of the hosted platform | | `MANAGEMENTPORTAL_COMMON_MANAGEMENT_PORTAL_BASE_URL` | None | Resolvable baseUrl of this managementportal instance | | `MANAGEMENTPORTAL_COMMON_PRIVACY_POLICY_URL` | None | Resolvable URL to the common privacy policy url | @@ -290,13 +291,14 @@ ManagementPortal can be run either with its **internal identity and auth server* - **External Ory identity and auth (Kratos + Hydra)**: - Set `MANAGEMENTPORTAL_IDENTITYSERVER_INTERNAL=false` and `MANAGEMENTPORTAL_AUTHSERVER_INTERNAL=false`. - Configure the external identity server (typically Ory Kratos): - - `MANAGEMENTPORTAL_IDENTITYSERVER_SERVERURL` (e.g. `http://kratos:4433`) - - `MANAGEMENTPORTAL_IDENTITYSERVER_SERVERADMINURL` (e.g. `http://kratos:4434`) + - `MANAGEMENTPORTAL_IDENTITYSERVER_PUBLICURL` (e.g. `http://kratos:4433`) + - `MANAGEMENTPORTAL_IDENTITYSERVER_ADMINURL` (e.g. `http://kratos:4434`) - `MANAGEMENTPORTAL_IDENTITYSERVER_ADMINEMAIL` - Configure the external auth server (typically Ory Hydra): - - `MANAGEMENTPORTAL_AUTHSERVER_SERVERURL` (e.g. `http://hydra:4444`, reachable from ManagementPortal) - - `MANAGEMENTPORTAL_AUTHSERVER_LOGINURL` (e.g. `http://localhost:4444`, reachable from the browser) - - `MANAGEMENTPORTAL_AUTHSERVER_SERVERADMINURL` (e.g. `http://hydra:4445`) + - `MANAGEMENTPORTAL_AUTHSERVER_TOKENURL` — full token URL (e.g. `http://hydra:4444/hydra/oauth2/token`, reachable from ManagementPortal) + - `MANAGEMENTPORTAL_AUTHSERVER_AUTHURL` — auth URL as seen from the browser (e.g. `http://localhost:4444/hydra/oauth2/auth`) + - `MANAGEMENTPORTAL_AUTHSERVER_ADMINURL` (e.g. `http://hydra:4445`) + - `MANAGEMENTPORTAL_AUTHSERVER_JWKSURL` (e.g. `http://hydra:4445/admin/keys/hydra.jwt.access-token`) - Start the Ory stack using `docker-compose -f src/main/docker/ory_stack.yml up -d` and ensure the `radar-self-enrolment-ui` service is running. The ManagementPortal login flow redirects the user to Hydra, which in turn uses the self-enrolment UI for login and consent screens. diff --git a/src/main/docker/app.yml b/src/main/docker/app.yml index 77e7f6c7e..28aa28ebc 100644 --- a/src/main/docker/app.yml +++ b/src/main/docker/app.yml @@ -12,16 +12,16 @@ services: - SPRING_DATASOURCE_PASSWORD=radarbase - MANAGEMENTPORTAL_COMMON_ADMINPASSWORD=admin - MANAGEMENTPORTAL_FRONTEND_CLIENT_SECRET=secret + - MANAGEMENTPORTAL_OAUTH_CLIENTS_FILE=/mp-includes/config/oauth_client_details.csv - MANAGEMENTPORTAL_IDENTITYSERVER_INTERNAL=false # IF using external identity like ORY Kratos - MANAGEMENTPORTAL_IDENTITYSERVER_ADMINEMAIL=admin-email-here@radar-base.net - - MANAGEMENTPORTAL_IDENTITYSERVER_SERVERURL=http://kratos:4433 - - MANAGEMENTPORTAL_IDENTITYSERVER_LOGINURL=http://localhost:3000 - - MANAGEMENTPORTAL_IDENTITYSERVER_SERVERADMINURL=http://kratos:4434 + - MANAGEMENTPORTAL_IDENTITYSERVER_PUBLICURL=http://kratos:4433 + - MANAGEMENTPORTAL_IDENTITYSERVER_ADMINURL=http://kratos-admin:4434 - MANAGEMENTPORTAL_AUTHSERVER_INTERNAL=false # IF using external auth server like ORY Hydra - - MANAGEMENTPORTAL_AUTHSERVER_SERVERURL=http://hydra:4444 - - MANAGEMENTPORTAL_AUTHSERVER_LOGINURL=http://localhost:4444 - - MANAGEMENTPORTAL_AUTHSERVER_SERVERADMINURL=http://hydra:4445 - - MANAGEMENTPORTAL_OAUTH_CLIENTS_FILE=/mp-includes/config/oauth_client_details.csv + - MANAGEMENTPORTAL_AUTHSERVER_TOKENURL=http://hydra:4444/hydra/oauth2/token + - MANAGEMENTPORTAL_AUTHSERVER_ADMINURL=http://hydra:4445 + - MANAGEMENTPORTAL_AUTHSERVER_AUTHURL=http://localhost:4444/hydra/oauth2/auth + - MANAGEMENTPORTAL_AUTHSERVER_JWKSURL=http://hydra:4445/admin/keys/hydra.jwt.access-token - MANAGEMENTPORTAL_CATALOGUESERVER_SERVERURL=http://catalogue-server:8080 - JHIPSTER_SLEEP=10 # gives time for the database to boot before the application - JAVA_OPTS=-Xmx512m # maximum heap size for the JVM running ManagementPortal, increase this as necessary diff --git a/src/main/docker/managementportal.yml b/src/main/docker/managementportal.yml index 59d633b8f..c9583f87d 100644 --- a/src/main/docker/managementportal.yml +++ b/src/main/docker/managementportal.yml @@ -11,15 +11,16 @@ services: - SPRING_DATASOURCE_PASSWORD=radarbase - MANAGEMENTPORTAL_COMMON_ADMINPASSWORD=admin - MANAGEMENTPORTAL_FRONTEND_CLIENT_SECRET=secret + - MANAGEMENTPORTAL_OAUTH_CLIENTS_FILE=/mp-includes/config/oauth_client_details.csv - MANAGEMENTPORTAL_IDENTITYSERVER_INTERNAL=false # IF using external identity like ORY Kratos - MANAGEMENTPORTAL_IDENTITYSERVER_ADMINEMAIL=admin-email-here@radar-base.net - - MANAGEMENTPORTAL_IDENTITYSERVER_SERVERURL=http://kratos:4433 - - MANAGEMENTPORTAL_IDENTITYSERVER_LOGINURL=http://localhost:3000 - - MANAGEMENTPORTAL_IDENTITYSERVER_SERVERADMINURL=http://kratos:4434 + - MANAGEMENTPORTAL_IDENTITYSERVER_PUBLICURL=http://kratos:4433 + - MANAGEMENTPORTAL_IDENTITYSERVER_ADMINURL=http://kratos-admin:4434 - MANAGEMENTPORTAL_AUTHSERVER_INTERNAL=false # IF using external auth server like ORY Hydra - - MANAGEMENTPORTAL_AUTHSERVER_SERVERURL=http://hydra:4444 - - MANAGEMENTPORTAL_AUTHSERVER_LOGINURL=http://localhost:4444 - - MANAGEMENTPORTAL_AUTHSERVER_SERVERADMINURL=http://hydra:4445 + - MANAGEMENTPORTAL_AUTHSERVER_TOKENURL=http://hydra:4444/hydra/oauth2/token + - MANAGEMENTPORTAL_AUTHSERVER_ADMINURL=http://hydra:4445 + - MANAGEMENTPORTAL_AUTHSERVER_AUTHURL=http://localhost:4444/hydra/oauth2/auth + - MANAGEMENTPORTAL_AUTHSERVER_JWKSURL=http://hydra:4445/admin/keys/hydra.jwt.access-token - MANAGEMENTPORTAL_CATALOGUESERVER_SERVERURL=http://catalogue-server:8080 - JHIPSTER_SLEEP=10 # gives time for the database to boot before the application - JAVA_OPTS=-Xmx512m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 #enables remote debugging diff --git a/src/main/java/org/radarbase/management/config/ManagementPortalProperties.kt b/src/main/java/org/radarbase/management/config/ManagementPortalProperties.kt index 7782a3e2e..12627233b 100644 --- a/src/main/java/org/radarbase/management/config/ManagementPortalProperties.kt +++ b/src/main/java/org/radarbase/management/config/ManagementPortalProperties.kt @@ -17,8 +17,8 @@ data class ManagementPortalProperties @ConstructorBinding constructor( ) { data class IdentityServer @ConstructorBinding constructor( val internal: Boolean = true, - val serverUrl: String, - val serverAdminUrl: String, + val publicUrl: String, + val adminUrl: String, val adminEmail: String, val userActivationFlowType: String = "verification", val userActivationMethod: String = "code", @@ -26,10 +26,11 @@ data class ManagementPortalProperties @ConstructorBinding constructor( data class AuthServer @ConstructorBinding constructor( val internal: Boolean = true, - val serverUrl: String, - val serverAdminUrl: String, - val loginUrl: String, - val keysPath: String = "/admin/keys/hydra.jwt.access-token", + val tokenUrl: String, + val adminUrl: String, + val authUrl: String, + /** Full URL to a JWKS endpoint that publishes token verification keys. */ + val jwksUrl: String, ) data class Mail @ConstructorBinding constructor( diff --git a/src/main/java/org/radarbase/management/service/HydraOAuthClientService.kt b/src/main/java/org/radarbase/management/service/HydraOAuthClientService.kt index 6b6ad8e40..0382148f3 100644 --- a/src/main/java/org/radarbase/management/service/HydraOAuthClientService.kt +++ b/src/main/java/org/radarbase/management/service/HydraOAuthClientService.kt @@ -33,8 +33,7 @@ open class HydraOAuthClientService( @Autowired private val managementPortalProperties: ManagementPortalProperties ) : OAuthClientService { - private val adminUrl: String = managementPortalProperties.authServer.serverAdminUrl - private val publicUrl: String = managementPortalProperties.authServer.serverUrl + private val adminUrl: String = managementPortalProperties.authServer.adminUrl private val httpClient = HttpClient(CIO) { install(ContentNegotiation) { diff --git a/src/main/java/org/radarbase/management/service/KratosUserService.kt b/src/main/java/org/radarbase/management/service/KratosUserService.kt index 7ffcffaa8..acccd3180 100644 --- a/src/main/java/org/radarbase/management/service/KratosUserService.kt +++ b/src/main/java/org/radarbase/management/service/KratosUserService.kt @@ -85,8 +85,8 @@ class KratosUserService @Autowired constructor( } private val json = Json { ignoreUnknownKeys = true } - private val adminUrl = managementPortalProperties.identityServer.serverAdminUrl - private val publicUrl = managementPortalProperties.identityServer.serverUrl + private val adminUrl = managementPortalProperties.identityServer.adminUrl + private val publicUrl = managementPortalProperties.identityServer.publicUrl init { log.debug("Kratos serverUrl set to $publicUrl") diff --git a/src/main/java/org/radarbase/management/service/LoginService.kt b/src/main/java/org/radarbase/management/service/LoginService.kt index 868008551..16657cceb 100644 --- a/src/main/java/org/radarbase/management/service/LoginService.kt +++ b/src/main/java/org/radarbase/management/service/LoginService.kt @@ -44,7 +44,7 @@ class LoginService( emptyMap() ) false -> Pair( - "${managementPortalProperties.authServer.loginUrl}/oauth2/auth", + managementPortalProperties.authServer.authUrl, mapOf( "state" to state, "audience" to managementPortalProperties.frontend.audience, @@ -101,7 +101,7 @@ class LoginService( val tokenUrl = if (managementPortalProperties.authServer.internal) { "${managementPortalProperties.common.managementPortalBaseUrl}/oauth/token" } else { - "${managementPortalProperties.authServer.serverUrl}/oauth2/token" + managementPortalProperties.authServer.tokenUrl } return TokenRequestConfig( diff --git a/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt b/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt index 2cbd3c371..14cd7b05e 100644 --- a/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt +++ b/src/main/java/org/radarbase/management/service/config/OAuthClientServiceConfig.kt @@ -46,11 +46,11 @@ class OAuthClientServiceConfiguration { ): OAuthClientService { log.info("Using Hydra external OAuth client management") // Validate Hydra configuration - require(managementPortalProperties.authServer.serverUrl.isNotBlank()) { - "Hydra server URL must be configured when using external OAuth management" + require(managementPortalProperties.authServer.tokenUrl.isNotBlank()) { + "Hydra token URL must be configured when using external OAuth management" } - require(managementPortalProperties.authServer.serverAdminUrl.isNotBlank()) { - "Hydra admin server URL must be configured when using external OAuth management" + require(managementPortalProperties.authServer.adminUrl.isNotBlank()) { + "Hydra admin URL must be configured when using external OAuth management" } return HydraOAuthClientService(managementPortalProperties) } diff --git a/src/main/java/org/radarbase/management/service/config/UserServiceConfig.kt b/src/main/java/org/radarbase/management/service/config/UserServiceConfig.kt index d025a900f..db04175d9 100644 --- a/src/main/java/org/radarbase/management/service/config/UserServiceConfig.kt +++ b/src/main/java/org/radarbase/management/service/config/UserServiceConfig.kt @@ -61,7 +61,7 @@ class UserServiceConfiguration { authService: AuthService ): UserService { log.info("Using Kratos external user management") - require(managementPortalProperties.identityServer.serverUrl.isNotBlank()) { + require(managementPortalProperties.identityServer.publicUrl.isNotBlank()) { "Kratos server URL must be configured when using external identity management" } return KratosUserService( diff --git a/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt b/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt index 1d708262c..ad25eada2 100644 --- a/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt +++ b/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt @@ -31,7 +31,7 @@ constructor( @Autowired private val managementPortalProperties: ManagementPortalProperties, ) { private var sessionService: SessionService = - SessionService(managementPortalProperties.identityServer.serverUrl) + SessionService(managementPortalProperties.identityServer.publicUrl) /** * POST /subjects : Create a new subject. diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml index 13b6ef1ac..20a0bede6 100644 --- a/src/main/resources/config/application-dev.yml +++ b/src/main/resources/config/application-dev.yml @@ -168,14 +168,14 @@ managementportal: identityServer: internal: true adminEmail: admin-email-here@gmail.com - serverUrl: http://localhost:4433 - serverAdminUrl: http://localhost:4434 + publicUrl: http://localhost:4433 + adminUrl: http://localhost:4434 authServer: internal: true - serverUrl: http://localhost:4444 - serverAdminUrl: http://localhost:4445 - loginUrl: http://localhost:4444 - keysPath: /admin/keys/hydra.jwt.access-token + adminUrl: http://localhost:4445 + authUrl: http://localhost:4444/oauth2/auth + tokenUrl: http://localhost:4444/hydra/oauth2/token + jwksUrl: http://localhost:4445/admin/keys/hydra.jwt.access-token # =================================================================== diff --git a/src/main/resources/config/application-e2e-prod-test.yml b/src/main/resources/config/application-e2e-prod-test.yml index 8a6ff00de..816e46006 100644 --- a/src/main/resources/config/application-e2e-prod-test.yml +++ b/src/main/resources/config/application-e2e-prod-test.yml @@ -35,5 +35,5 @@ managementportal: require-aal2: false identityServer: adminEmail: bastiaan@thehyve.nl - serverUrl: http://127.0.0.1:4433 - serverAdminUrl: http://127.0.0.1:4434 + publicUrl: http://127.0.0.1:4433 + adminUrl: http://127.0.0.1:4434 diff --git a/src/main/resources/config/application-prod.yml b/src/main/resources/config/application-prod.yml index 3be1dfae2..7d3b78f47 100644 --- a/src/main/resources/config/application-prod.yml +++ b/src/main/resources/config/application-prod.yml @@ -153,14 +153,14 @@ managementportal: identityServer: internal: true adminEmail: bdegraaf1234@gmail.com - serverUrl: http://localhost:4433 - serverAdminUrl: http://kratos-admin + publicUrl: http://localhost:4433 + adminUrl: http://kratos-admin authServer: internal: true - serverUrl: http://hydra:4444 - serverAdminUrl: http://hydra:4445 - loginUrl: http://localhost:4444 - keysPath: /admin/keys/hydra.jwt.access-token + adminUrl: http://localhost:4445 + authUrl: http://localhost:4444/hydra/oauth2/auth + tokenUrl: http://localhost:4444/hydra/oauth2/token + jwksUrl: http://localhost:4445/admin/keys/hydra.jwt.access-token # =================================================================== # JHipster specific properties diff --git a/src/test/resources/config/application.yml b/src/test/resources/config/application.yml index 307e25e50..3bebefa03 100644 --- a/src/test/resources/config/application.yml +++ b/src/test/resources/config/application.yml @@ -132,14 +132,14 @@ managementportal: from: ManagementPortal@localhost identityServer: internal: true - serverUrl: http://127.0.0.1:4433 - serverAdminUrl: http://127.0.0.1:4434 - admin-email: admin@localhost + publicUrl: http://127.0.0.1:4433 + adminUrl: http://127.0.0.1:4434 + adminEmail: admin@localhost authServer: internal: true - serverUrl: http://hydra:4444 - serverAdminUrl: http://hydra:4445 - loginUrl: http://localhost:4444 - keysPath: /admin/keys/hydra.jwt.access-token + tokenUrl: http://hydra:4444/hydra/oauth2/token + adminUrl: http://hydra:4445 + authUrl: http://localhost:4444/hydra/oauth2/auth + jwksUrl: http://hydra:4445/admin/keys/hydra.jwt.access-token siteSettings: hiddenSubjectFields: [] From 00a939dbc8c1854035e8858ac8bd3330dca15aa3 Mon Sep 17 00:00:00 2001 From: Pauline Date: Tue, 10 Feb 2026 05:05:30 +0800 Subject: [PATCH 21/25] Fix tokenValidator jwksurl --- .../radarbase/management/config/SecurityConfiguration.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt b/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt index c83e81698..8f4b1fc24 100644 --- a/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt +++ b/src/main/java/org/radarbase/management/config/SecurityConfiguration.kt @@ -179,8 +179,7 @@ class InternalTokenValidatorConfiguration( fun internalTokenValidator(): TokenValidator { val loaderList = mutableListOf( JwksTokenVerifierLoader( - managementPortalProperties.authServer.serverAdminUrl + - managementPortalProperties.authServer.keysPath, + managementPortalProperties.authServer.jwksUrl, SecurityConfiguration.RES_MANAGEMENT_PORTAL, JwkAlgorithmParser(), ) @@ -213,8 +212,7 @@ class ExternalTokenValidatorConfiguration( fun externalTokenValidator(): TokenValidator { val loaderList = listOf( JwksTokenVerifierLoader( - managementPortalProperties.authServer.serverAdminUrl + - managementPortalProperties.authServer.keysPath, + managementPortalProperties.authServer.jwksUrl, SecurityConfiguration.RES_MANAGEMENT_PORTAL, JwkAlgorithmParser(), ) From ce19eae533cecbb4cc9e20cf0e822ab18cc9b23c Mon Sep 17 00:00:00 2001 From: Pauline Date: Tue, 17 Feb 2026 19:23:15 +0800 Subject: [PATCH 22/25] Fix user activated status in DefaultUserService --- .../java/org/radarbase/management/service/DefaultUserService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/radarbase/management/service/DefaultUserService.kt b/src/main/java/org/radarbase/management/service/DefaultUserService.kt index fc942f8d5..1c8900a77 100644 --- a/src/main/java/org/radarbase/management/service/DefaultUserService.kt +++ b/src/main/java/org/radarbase/management/service/DefaultUserService.kt @@ -159,7 +159,7 @@ class DefaultUserService @Autowired constructor( user.password = passwordService.generateEncodedPassword() user.resetKey = passwordService.generateResetKey() user.resetDate = ZonedDateTime.now() - user.activated = true + user.activated = false user.roles = getUserRoles(userDto.roles, mutableSetOf()) user = withContext(Dispatchers.IO) { userRepository.save(user) From 5c09b0076c4e55591c73177cd94251757f89b92a Mon Sep 17 00:00:00 2001 From: Pauline Date: Thu, 19 Feb 2026 11:02:13 +0000 Subject: [PATCH 23/25] Fix login redirects during password reset and activation --- src/main/webapp/app/account/activate/activate.component.ts | 4 ++-- .../password-reset/finish/password-reset-finish.component.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/app/account/activate/activate.component.ts b/src/main/webapp/app/account/activate/activate.component.ts index bf6825f3b..5f69450b6 100644 --- a/src/main/webapp/app/account/activate/activate.component.ts +++ b/src/main/webapp/app/account/activate/activate.component.ts @@ -16,10 +16,10 @@ export class ActivateComponent implements OnInit, OnDestroy { success: string; modalRef: NgbModalRef; subscription: Subscription; + private loginUrl = 'api/redirect/login'; constructor( private activate: Activate, - private loginModalService: LoginModalService, private route: ActivatedRoute, ) { } @@ -45,6 +45,6 @@ export class ActivateComponent implements OnInit, OnDestroy { } login() { - this.modalRef = this.loginModalService.open(); + window.location.href = this.loginUrl } } diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts index e4c42d987..d9c91d4a6 100644 --- a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts @@ -25,11 +25,11 @@ export class PasswordResetFinishComponent implements OnInit, AfterViewInit, OnDe modalRef: NgbModalRef; key: string; private subscriptions = new Subscription(); + private loginUrl = 'api/redirect/login'; constructor( private passwordResetFinish: PasswordResetFinish, private passwordService: Password, - private loginModalService: LoginModalService, private route: ActivatedRoute, ) { } @@ -78,6 +78,6 @@ export class PasswordResetFinishComponent implements OnInit, AfterViewInit, OnDe } login() { - this.modalRef = this.loginModalService.open(); + window.location.href = this.loginUrl } } From 5b5fd4d64834c64bcb20b4ece6a92cc2378552c9 Mon Sep 17 00:00:00 2001 From: Pauline Date: Thu, 19 Mar 2026 17:54:07 +0000 Subject: [PATCH 24/25] Fix researcher account creation and activation --- .../radarbase/auth/kratos/KratosSessionDTO.kt | 6 ++ .../management/service/DefaultUserService.kt | 10 ++- .../management/service/KratosUserService.kt | 48 ++++++----- .../management/service/MailService.kt | 20 +++++ .../management/service/UserService.kt | 2 +- .../service/config/UserServiceConfig.kt | 6 +- .../management/web/rest/AccountResource.kt | 12 +-- .../management/web/rest/WebhookResource.kt | 81 ++++++++++++++----- src/main/resources/i18n/messages.properties | 13 +++ .../resources/i18n/messages_en.properties | 13 +++ .../resources/i18n/messages_nl.properties | 13 +++ .../templates/externalIdpCreationEmail.html | 51 ++++++++++++ 12 files changed, 219 insertions(+), 56 deletions(-) create mode 100644 src/main/resources/templates/externalIdpCreationEmail.html diff --git a/radar-auth/src/main/java/org/radarbase/auth/kratos/KratosSessionDTO.kt b/radar-auth/src/main/java/org/radarbase/auth/kratos/KratosSessionDTO.kt index 85a56b305..2e2d01458 100644 --- a/radar-auth/src/main/java/org/radarbase/auth/kratos/KratosSessionDTO.kt +++ b/radar-auth/src/main/java/org/radarbase/auth/kratos/KratosSessionDTO.kt @@ -50,6 +50,12 @@ class KratosSessionDTO( val type: String? = null ) + @Serializable + data class RecoveryLinkResponse( + val recovery_link: String, + val expires_at: String? = null + ) + @Serializable data class Identity( var id: String? = null, diff --git a/src/main/java/org/radarbase/management/service/DefaultUserService.kt b/src/main/java/org/radarbase/management/service/DefaultUserService.kt index 1c8900a77..0d98697f4 100644 --- a/src/main/java/org/radarbase/management/service/DefaultUserService.kt +++ b/src/main/java/org/radarbase/management/service/DefaultUserService.kt @@ -108,13 +108,20 @@ class DefaultUserService @Autowired constructor( * @return an [Optional] which holds the user if an deactivated user was found with the * given login, and is empty otherwise */ - override fun requestActivationReset(login: String): User? { + override suspend fun requestActivationReset(login: String): User? { val user = userRepository.findOneByLogin(login) if (user?.activated != true) { user?.resetKey = passwordService.generateResetKey() user?.resetDate = ZonedDateTime.now() } + if (user != null) { // send activation email if user is deactivated + mailService.sendCreationEmail( + user, managementPortalProperties.common + .activationKeyTimeoutInSeconds.toLong() + ) + } + return user } @@ -129,6 +136,7 @@ class DefaultUserService @Autowired constructor( return if (user?.activated == true) { user.resetKey = passwordService.generateResetKey() user.resetDate = ZonedDateTime.now() + mailService.sendPasswordResetMail(user) user } else null diff --git a/src/main/java/org/radarbase/management/service/KratosUserService.kt b/src/main/java/org/radarbase/management/service/KratosUserService.kt index acccd3180..990d8ff6d 100644 --- a/src/main/java/org/radarbase/management/service/KratosUserService.kt +++ b/src/main/java/org/radarbase/management/service/KratosUserService.kt @@ -63,7 +63,8 @@ class KratosUserService @Autowired constructor( private val userMapper: UserMapper, private val revisionService: RevisionService, private val managementPortalProperties: ManagementPortalProperties, - private val authService: AuthService + private val authService: AuthService, + private val mailService: MailService ) : UserService { @Autowired @@ -124,12 +125,15 @@ class KratosUserService @Autowired constructor( } else null } - override fun requestActivationReset(login: String): User? { + override suspend fun requestActivationReset(login: String): User? { val user = userRepository.findOneByLogin(login) if (user?.activated != true) { user?.resetKey = passwordService.generateResetKey() user?.resetDate = ZonedDateTime.now() } + if (user != null) { // send activation email if user is deactivated + sendActivationEmail(user) + } return user } @@ -138,6 +142,7 @@ class KratosUserService @Autowired constructor( return if (user?.activated == true) { user.resetKey = passwordService.generateResetKey() user.resetDate = ZonedDateTime.now() + mailService.sendPasswordResetMail(user) user } else null } @@ -157,7 +162,7 @@ class KratosUserService @Autowired constructor( user.password = passwordService.generateEncodedPassword() user.resetKey = passwordService.generateResetKey() user.resetDate = ZonedDateTime.now() - user.activated = true + user.activated = false user.roles = getUserRoles(userDto.roles, mutableSetOf()) try { @@ -402,7 +407,8 @@ class KratosUserService @Autowired constructor( @Throws(IdpException::class) override suspend fun sendActivationEmail(user: User) { - sendKratosActivationEmail(user) + val recoveryLink = createKratosRecoveryLink(user) + mailService.sendExternalIdpCreationEmail(user, recoveryLink) } // Kratos-specific methods (from IdentityService) @@ -613,33 +619,25 @@ class KratosUserService @Autowired constructor( } @Throws(IdpException::class) - suspend fun sendKratosActivationEmail(user: User): String = + suspend fun createKratosRecoveryLink(user: User): String = withContext(Dispatchers.IO) { - val flowType = managementPortalProperties.identityServer.userActivationFlowType - val method = managementPortalProperties.identityServer.userActivationMethod - val flowResponse = httpClient - .get { - url("$publicUrl/self-service/$flowType/api") - contentType(ContentType.Application.Json) - accept(ContentType.Application.Json) - }.body() + val identityId = user.identity + ?: throw IdpException("User ${user.login} has no identity to create recovery link for") - val flowId = flowResponse.id - ?: throw IdpException("Failed to initiate $flowType flow for ${user.email}") - - val activationResponse = httpClient.post { - url("$publicUrl/self-service/$flowType?flow=$flowId") + val response = httpClient.post { + url("$adminUrl/admin/recovery/link") contentType(ContentType.Application.Json) accept(ContentType.Application.Json) - setBody(mapOf("email" to user.email, "method" to method)) + setBody(mapOf("identity_id" to identityId)) } - if (!activationResponse.status.isSuccess()) { - throw IdpException("Failed to trigger $flowType email for ${user.email}") - } - - flowId.also { - log.debug("Activation email sent for user ${user.login} with flow ID $it") + if (response.status.isSuccess()) { + val recoveryResponse = response.body() + recoveryResponse.recovery_link.also { + log.debug("Created recovery link for user ${user.login}") + } + } else { + throw IdpException("Failed to create recovery link for user ${user.login}") } } diff --git a/src/main/java/org/radarbase/management/service/MailService.kt b/src/main/java/org/radarbase/management/service/MailService.kt index 9b1e11971..ddca92559 100644 --- a/src/main/java/org/radarbase/management/service/MailService.kt +++ b/src/main/java/org/radarbase/management/service/MailService.kt @@ -123,6 +123,25 @@ class MailService( sendEmail(email, subject, content, false, true) } + /** + * Send account creation email for users managed by an external identity provider (e.g. Kratos). + * Includes a recovery link for initial password setup and the portal login URL. + * @param user the user + * @param recoveryLink the external IDP recovery URL for password setup + */ + @Async + fun sendExternalIdpCreationEmail(user: User, recoveryLink: String) { + log.debug("Sending external IDP creation email to '{}'", user.email) + val locale = Locale.forLanguageTag(user.langKey) + val context = Context(locale) + context.setVariable(USER, user) + context.setVariable(BASE_URL, managementPortalProperties.common.managementPortalBaseUrl) + context.setVariable(RECOVERY_LINK, recoveryLink) + val content = templateEngine.process("externalIdpCreationEmail", context) + val subject = messageSource.getMessage("email.external.creation.title", null, locale) + sendEmail(user.email, subject, content, isMultipart = false, isHtml = true) + } + /** * Send a password reset email to a given user. * @param user the user @@ -147,5 +166,6 @@ class MailService( private const val USER = "user" private const val BASE_URL = "baseUrl" private const val EXPIRY = "expiry" + private const val RECOVERY_LINK = "recoveryLink" } } diff --git a/src/main/java/org/radarbase/management/service/UserService.kt b/src/main/java/org/radarbase/management/service/UserService.kt index 34b923709..7328e39e8 100644 --- a/src/main/java/org/radarbase/management/service/UserService.kt +++ b/src/main/java/org/radarbase/management/service/UserService.kt @@ -39,7 +39,7 @@ interface UserService { * @param login the login of the user * @return the user if a deactivated user was found with the given login, null otherwise */ - fun requestActivationReset(login: String): User? + suspend fun requestActivationReset(login: String): User? /** * Set a user's reset key to a new random value and set their reset date to now. diff --git a/src/main/java/org/radarbase/management/service/config/UserServiceConfig.kt b/src/main/java/org/radarbase/management/service/config/UserServiceConfig.kt index db04175d9..e588b4d2e 100644 --- a/src/main/java/org/radarbase/management/service/config/UserServiceConfig.kt +++ b/src/main/java/org/radarbase/management/service/config/UserServiceConfig.kt @@ -58,7 +58,8 @@ class UserServiceConfiguration { userMapper: UserMapper, revisionService: RevisionService, managementPortalProperties: ManagementPortalProperties, - authService: AuthService + authService: AuthService, + mailService: MailService ): UserService { log.info("Using Kratos external user management") require(managementPortalProperties.identityServer.publicUrl.isNotBlank()) { @@ -70,7 +71,8 @@ class UserServiceConfiguration { userMapper, revisionService, managementPortalProperties, - authService + authService, + mailService ) } } diff --git a/src/main/java/org/radarbase/management/web/rest/AccountResource.kt b/src/main/java/org/radarbase/management/web/rest/AccountResource.kt index 1361d3fbf..b91fea94f 100644 --- a/src/main/java/org/radarbase/management/web/rest/AccountResource.kt +++ b/src/main/java/org/radarbase/management/web/rest/AccountResource.kt @@ -173,17 +173,12 @@ class AccountResource( */ @PostMapping(path = ["/account/reset-activation/init"]) @Timed - fun requestActivationReset(@RequestBody login: String): ResponseEntity { - val user = userService.requestActivationReset(login) + suspend fun requestActivationReset(@RequestBody login: String): ResponseEntity { + userService.requestActivationReset(login) ?: throw BadRequestException( "Cannot find a deactivated user with login $login", EntityName.Companion.USER, ErrorConstants.ERR_EMAIL_NOT_REGISTERED ) - - mailService.sendCreationEmail( - user, managementPortalProperties.common - .activationKeyTimeoutInSeconds.toLong() - ) return ResponseEntity(HttpStatus.NO_CONTENT) } @@ -197,12 +192,11 @@ class AccountResource( @PostMapping(path = ["/account/reset_password/init"]) @Timed fun requestPasswordReset(@RequestBody mail: String): ResponseEntity { - val user = userService.requestPasswordReset(mail) + userService.requestPasswordReset(mail) ?: throw BadRequestException( "email address not registered", EntityName.Companion.USER, ErrorConstants.ERR_EMAIL_NOT_REGISTERED ) - mailService.sendPasswordResetMail(user) return ResponseEntity(HttpStatus.NO_CONTENT) } diff --git a/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt b/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt index ad25eada2..fdcf84131 100644 --- a/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt +++ b/src/main/java/org/radarbase/management/web/rest/WebhookResource.kt @@ -99,39 +99,84 @@ constructor( @PostMapping("/subjects/activate") @Timed @Throws(URISyntaxException::class, NotAuthorizedException::class) - suspend fun activateSubject( + suspend fun activateUser( @RequestBody webhookDTO: KratosSubjectWebhookDTO, ): ResponseEntity { - val id = webhookDTO.identity?.id ?: throw IllegalArgumentException("Subject ID is required") - val token = - webhookDTO.cookies?.get("ory_kratos_session") - ?: throw IllegalArgumentException("Session token is required") - val kratosIdentity = sessionService.getSession(token).identity - val project = - kratosIdentity.traits?.projects?.firstOrNull() - ?: throw NotAuthorizedException("Cannot create subject without project") - val projectUserId = - project.userId ?: throw IllegalArgumentException("Project user ID is required") + val identityId = webhookDTO.identity?.id + ?: throw IllegalArgumentException("Identity ID is required") + val sessionToken = webhookDTO.cookies?.get("ory_kratos_session") + ?: throw IllegalArgumentException("Session token is required") + + val kratosIdentity = sessionService.getSession(sessionToken).identity + val schemaId = kratosIdentity.schema_id + ?: throw IllegalArgumentException("Schema ID is required") + + validatePermission(kratosIdentity, identityId) - if (!hasPermission(kratosIdentity, id)) { - throw NotAuthorizedException("Not authorized to activate subject") + return when (schemaId) { + KRATOS_SUBJECT_SCHEMA -> activateSubjectIdentity(webhookDTO, kratosIdentity, identityId) + KRATOS_RESEARCHER_SCHEMA -> activateResearcherIdentity(kratosIdentity, identityId) + else -> throw IllegalArgumentException("Unsupported schema type: $schemaId") } + } - val subject = subjectService.findOneByLogin(projectUserId) ?: createSubject(webhookDTO) + private suspend fun activateSubjectIdentity( + webhookDTO: KratosSubjectWebhookDTO, + kratosIdentity: KratosSessionDTO.Identity, + identityId: String + ): ResponseEntity { + val project = kratosIdentity.traits?.projects?.firstOrNull() + ?: throw NotAuthorizedException("Cannot create subject without project") + val projectUserId = project.userId + ?: throw IllegalArgumentException("Project user ID is required") + + // Ensure subject exists before activating + try { + subjectService.findOneByLogin(projectUserId) + } catch (e: NotFoundException) { + createSubject(webhookDTO) + } subjectService.activateSubject(projectUserId) + return ResponseEntity.ok() - .headers(HeaderUtil.createEntityUpdateAlert(EntityName.SUBJECT, id)) + .headers(HeaderUtil.createEntityUpdateAlert(EntityName.SUBJECT, identityId)) .build() } - private fun hasPermission( + private suspend fun activateResearcherIdentity( kratosIdentity: KratosSessionDTO.Identity, - identityId: String?, - ): Boolean = kratosIdentity.id == identityId + identityId: String + ): ResponseEntity { + val mpLogin = kratosIdentity.metadata_public?.mp_login + ?: throw IllegalArgumentException("Management Portal login is required for researcher activation") + + val userDto = userService.getUserDtoWithAuthoritiesByLogin(mpLogin) + ?: throw NotFoundException( + "User not found: $mpLogin", + EntityName.USER, + "userNotFound" + ) + + if (!userDto.isActivated) { + userDto.isActivated = true + userService.updateUser(userDto) + } + + return ResponseEntity.ok() + .headers(HeaderUtil.createEntityUpdateAlert(EntityName.USER, identityId)) + .build() + } + + private fun validatePermission(kratosIdentity: KratosSessionDTO.Identity, identityId: String) { + if (kratosIdentity.id != identityId) { + throw NotAuthorizedException("Not authorized to activate user") + } + } companion object { private val logger = LoggerFactory.getLogger(WebhookResource::class.java) private val KRATOS_SUBJECT_SCHEMA = "subject" + private val KRATOS_RESEARCHER_SCHEMA = "researcher" private val EMAIL_ATTRIBUTE_KEY = "email" } } diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties index 1c729464b..daa7e2ccc 100644 --- a/src/main/resources/i18n/messages.properties +++ b/src/main/resources/i18n/messages.properties @@ -24,6 +24,19 @@ email.creation.expiry=Please note that this link will expire in {0} from the tim email.creation.onetimeonly=For security reasons this link can only be used once to successfully reset the password. email.creation.regards=Regards, +# External IDP creation email +email.external.creation.title=Your account has been created in ManagementPortal from RADAR-Base. +email.external.creation.greeting=Dear +email.external.creation.text1=Your ManagementPortal account has been created by the RADAR-Base team. +email.external.creation.loginIntro=Your username is: {0} +email.external.creation.text2=To get started, please follow the steps below: +email.external.creation.step1=Step 1: Set your password +email.external.creation.step1detail=Click the link below to set your password through our identity provider: +email.external.creation.recoveryLinkText=Set Your Password +email.external.creation.step2=Step 2: Log in to ManagementPortal +email.external.creation.step2detail=Once your password has been set, go to the ManagementPortal to log in: +email.external.creation.regards=Regards, + # Reset email email.reset.title=ManagementPortal password reset email.reset.greeting=Dear {0} diff --git a/src/main/resources/i18n/messages_en.properties b/src/main/resources/i18n/messages_en.properties index 2bf395e17..02a86866e 100644 --- a/src/main/resources/i18n/messages_en.properties +++ b/src/main/resources/i18n/messages_en.properties @@ -24,6 +24,19 @@ email.creation.expiry=Please note that this link will expire in {0} hours from t email.creation.onetimeonly=For security reasons this link can only be used once to successfully reset the password. email.creation.regards=Regards, +# External IDP creation email +email.external.creation.title=Your account has been created in ManagementPortal from RADAR-Base. +email.external.creation.greeting=Dear +email.external.creation.text1=Your ManagementPortal account has been created by the RADAR-Base team. +email.external.creation.loginIntro=Your username is: {0} +email.external.creation.text2=To get started, please follow the steps below: +email.external.creation.step1=Step 1: Set your password +email.external.creation.step1detail=Click the link below to set your password through our identity provider: +email.external.creation.recoveryLinkText=Set Your Password +email.external.creation.step2=Step 2: Log in to ManagementPortal +email.external.creation.step2detail=Once your password has been set, go to the ManagementPortal to log in: +email.external.creation.regards=Regards, + # Reset email email.reset.title=ManagementPortal password reset email.reset.greeting=Dear {0} diff --git a/src/main/resources/i18n/messages_nl.properties b/src/main/resources/i18n/messages_nl.properties index 2b34a858b..1b094d676 100644 --- a/src/main/resources/i18n/messages_nl.properties +++ b/src/main/resources/i18n/messages_nl.properties @@ -21,6 +21,19 @@ email.creation.expiry=Houd er rekening mee dat deze link vervalt in {0} uur vana email.creation.onetimeonly=Om veiligheidsredenen kan deze link maar één keer worden gebruikt om het wachtwoord succesvol opnieuw in te stellen. email.creation.regards=Groeten, +# External IDP creation email +email.external.creation.title=Uw account is aangemaakt in ManagementPortal vanuit RADAR-Base. +email.external.creation.greeting=Beste +email.external.creation.text1=Uw ManagementPortal-account is aangemaakt door het RADAR-Base team. +email.external.creation.loginIntro=Uw gebruikersnaam is: {0} +email.external.creation.text2=Volg de onderstaande stappen om aan de slag te gaan: +email.external.creation.step1=Stap 1: Stel uw wachtwoord in +email.external.creation.step1detail=Klik op de onderstaande link om uw wachtwoord in te stellen via onze identiteitsprovider: +email.external.creation.recoveryLinkText=Stel uw wachtwoord in +email.external.creation.step2=Stap 2: Log in op ManagementPortal +email.external.creation.step2detail=Nadat uw wachtwoord is ingesteld, ga naar het ManagementPortal om in te loggen: +email.external.creation.regards=Met vriendelijke groeten, + # Reset email email.reset.title=ManagementPortal wachtwoord reset email.reset.greeting=Beste {0} diff --git a/src/main/resources/templates/externalIdpCreationEmail.html b/src/main/resources/templates/externalIdpCreationEmail.html new file mode 100644 index 000000000..1d0f08d6e --- /dev/null +++ b/src/main/resources/templates/externalIdpCreationEmail.html @@ -0,0 +1,51 @@ + + + + + Account created + + + + +

+ Dear +

+

+ Your ManagementPortal account has been created. +

+

+ Your user name is +

+ +

+ To get started, please follow the steps below: +

+ +

+ Step 1: Set your password +

+

+ Click the link below to set your password through our identity provider: +

+

+ Set Password +

+ +

+ Step 2: Log in to ManagementPortal +

+

+ Once your password is set, go to the ManagementPortal to log in: +

+

+ ManagementPortal +

+ +

+ Regards, +
+ ManagementPortal Team. +

+ + + \ No newline at end of file From 7c344608ad573df962d6935b6ee0fdc9505f8926 Mon Sep 17 00:00:00 2001 From: Pauline Date: Thu, 28 May 2026 04:10:53 +0800 Subject: [PATCH 25/25] Fix and remove unnecessary vars --- src/main/resources/config/application-dev.yml | 4 ++-- src/main/resources/config/application-prod.yml | 4 ++-- src/main/webapp/environments/environment.prod.ts | 2 -- src/main/webapp/environments/environment.ts | 2 -- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml index 20a0bede6..8484aacbd 100644 --- a/src/main/resources/config/application-dev.yml +++ b/src/main/resources/config/application-dev.yml @@ -161,13 +161,13 @@ managementportal: persistentMetaTokenTimeout: P31D #timeout should be specified as the ISO-8601 duration format {@code PnDTnHnMn.nS}. catalogueServer: enableAutoImport: false - serverUrl: http://cataologue-server:8080 + serverUrl: http://catalogue-server:8080 siteSettings: # The line below can be uncommented to add some hidden fields for UI testing #hiddenSubjectFields: [person_name, date_of_birth, group] identityServer: internal: true - adminEmail: admin-email-here@gmail.com + adminEmail: admin@example.com publicUrl: http://localhost:4433 adminUrl: http://localhost:4434 authServer: diff --git a/src/main/resources/config/application-prod.yml b/src/main/resources/config/application-prod.yml index 7d3b78f47..344b65bce 100644 --- a/src/main/resources/config/application-prod.yml +++ b/src/main/resources/config/application-prod.yml @@ -149,10 +149,10 @@ managementportal: persistentMetaTokenTimeout: P31D #timeout should be specified as the ISO-8601 duration format {@code PnDTnHnMn.nS}. catalogueServer: enableAutoImport: false - serverUrl: http://cataologue-server:8080 + serverUrl: http://catalogue-server:8080 identityServer: internal: true - adminEmail: bdegraaf1234@gmail.com + adminEmail: admin@example.com publicUrl: http://localhost:4433 adminUrl: http://kratos-admin authServer: diff --git a/src/main/webapp/environments/environment.prod.ts b/src/main/webapp/environments/environment.prod.ts index 6da4cd317..c9669790b 100644 --- a/src/main/webapp/environments/environment.prod.ts +++ b/src/main/webapp/environments/environment.prod.ts @@ -1,5 +1,3 @@ export const environment = { production: true, - KRATOS_URL: 'KRATOS_URL', - BASE_URL: 'BASE_URL', }; diff --git a/src/main/webapp/environments/environment.ts b/src/main/webapp/environments/environment.ts index 13cdf8763..66998ae9a 100644 --- a/src/main/webapp/environments/environment.ts +++ b/src/main/webapp/environments/environment.ts @@ -4,8 +4,6 @@ export const environment = { production: false, - KRATOS_URL: 'https://radar-k3s-test.thehyve.net/kratos-ui/', - BASE_URL: 'https://radar-k3s-test.thehyve.net/managementportal' }; /*