Compare commits

...

343 Commits
1.1.0 ... main

Author SHA1 Message Date
Maverick Studer
1fdd957e9e Merge branch 'RED-3965' into 'main'
RED-3965: RED_USERS_ADMIN can read dossier stats via swagger

See merge request fforesight/tenant-user-management-service!151
2025-01-27 13:33:54 +01:00
maverickstuder
18de1e52f5 RED-3965: RED_USERS_ADMIN can read dossier stats via swagger 2025-01-27 10:31:24 +01:00
Dominique Eifländer
00ef5f67a1 Merge branch 'OPS-284' into 'main'
OPS-284: add prometheus endpoint

See merge request fforesight/tenant-user-management-service!150
2025-01-24 11:00:58 +01:00
Christoph Schabert
74b289b38c OPS-284: add prometheus endpoint 2025-01-23 13:37:17 +01:00
Maverick Studer
100b1c4cc1 Merge branch 'RED-10169-fp2' into 'main'
RED-10619: Multiple bugs when creating or editing users

See merge request fforesight/tenant-user-management-service!149
2024-12-10 15:05:13 +01:00
Maverick Studer
f96adb2097 RED-10619: Multiple bugs when creating or editing users 2024-12-10 15:05:13 +01:00
Yannik Hampe
4e43e4e255 Merge branch 'feature/RED-9393' into 'main'
RED-9393

See merge request fforesight/tenant-user-management-service!145
2024-12-09 09:11:39 +01:00
yhampe
7bb15fe456 RED-9393
added permissions to roles
2024-12-02 09:20:07 +01:00
Maverick Studer
a169fb585c Merge branch 'RED-10521' into 'main'
RED-10521: Remove the simple get-tenants endpoint

See merge request fforesight/tenant-user-management-service!142
2024-11-25 15:08:35 +01:00
maverickstuder
af38bb2f29 RED-10521: Remove the simple get-tenants endpoint 2024-11-25 14:58:13 +01:00
Maverick Studer
f25253cb3b Merge branch 'RED-10196' into 'main'
RED-10196: Backend adaptions for RM/DM unification

See merge request fforesight/tenant-user-management-service!141
2024-11-19 09:38:45 +01:00
maverickstuder
ad7035d7cf RED-10196: Backend adaptions for RM/DM unification 2024-11-18 16:04:17 +01:00
Maverick Studer
dfa4009b84 Merge branch 'RED-10439-fp' into 'main'
RED-10439: Migration Issue: After migration, global smtp settings does not apply

See merge request fforesight/tenant-user-management-service!139
2024-11-13 12:15:05 +01:00
Maverick Studer
4eefde9ff6 RED-10439: Migration Issue: After migration, global smtp settings does not apply 2024-11-13 12:15:04 +01:00
Maverick Studer
45f6386104 Merge branch 'feature/RED-10196' into 'main'
RED-10196: Backend adaptions for RM/DM unification

See merge request fforesight/tenant-user-management-service!136
2024-11-11 12:30:03 +01:00
Maverick Studer
db495d17b3 RED-10196: Backend adaptions for RM/DM unification 2024-11-11 12:30:02 +01:00
Maverick Studer
e0a1df09ab Merge branch 'RED-9394-new' into 'main'
RED-9394: Global default SMTP configuration

See merge request fforesight/tenant-user-management-service!137
2024-10-23 17:25:14 +02:00
Maverick Studer
93e3315a8a RED-9394: Global default SMTP configuration 2024-10-23 17:25:14 +02:00
Maverick Studer
5e3d632846 Merge branch 'RED-10197' into 'main'
RED-10197: Migration preparations for RM/DM unification

See merge request fforesight/tenant-user-management-service!135
2024-10-17 16:32:45 +02:00
maverickstuder
6412b50ff9 RED-10197: Migration preparations for RM/DM unification 2024-10-17 16:01:56 +02:00
Maverick Studer
4e4a8d7089 Merge branch 'RED-9394' into 'main'
RED-9394: Global default SMTP configuration

See merge request fforesight/tenant-user-management-service!134
2024-10-16 10:59:35 +02:00
Maverick Studer
e2f84d9bbf RED-9394: Global default SMTP configuration 2024-10-16 10:59:35 +02:00
Maverick Studer
09a86f0a23 Merge branch 'RED-9933' into 'main'
RED-9933: DocuMine DateFormat config in dossier templates

See merge request fforesight/tenant-user-management-service!133
2024-09-24 09:28:31 +02:00
maverickstuder
8adeb37275 RED-9933: DocuMine DateFormat config in dossier templates 2024-09-23 15:13:42 +02:00
Andrei Isvoran
6968f94e6e Merge branch 'RED-9394-fixes' into 'main'
RED-9394 - Default SMTP configuration fixes

See merge request fforesight/tenant-user-management-service!132
2024-09-20 13:33:50 +02:00
Andrei Isvoran
01731bb7a4 RED-9394 - Default SMTP configuration fixes 2024-09-20 14:12:13 +03:00
Andrei Isvoran
47ffe83b8f Merge branch 'RED-9394' into 'main'
RED-9394 - Default SMTP configuration

See merge request fforesight/tenant-user-management-service!131
2024-09-13 14:17:23 +02:00
Andrei Isvoran
b3757876a7 RED-9394 - Default SMTP configuration 2024-09-13 14:17:23 +02:00
Dominique Eifländer
dddf2ba3d1 Merge branch 'RED-9750-main' into 'main'
RED-9750: Added permissions to download packages created from support controller

See merge request fforesight/tenant-user-management-service!130
2024-09-12 09:46:43 +02:00
Dominique Eifländer
38bbb71965 RED-9750: Added permissions to download packages created from support controller 2024-09-12 09:23:58 +02:00
Dominique Eifländer
014e82e9b3 Merge branch 'RED-9128' into 'main'
RED-9128: set revoke request token to true when creating a realm

See merge request fforesight/tenant-user-management-service!128
2024-09-02 13:26:18 +02:00
Dominique Eifländer
f5f08b215e RED-9128: set revoke request token to true when creating a realm 2024-09-02 13:20:19 +02:00
Maverick Studer
a540524d52 Merge branch 'RED-9872' into 'main'
RED-9872: Tenant Management issues

See merge request fforesight/tenant-user-management-service!127
2024-08-26 14:52:56 +02:00
Maverick Studer
8cba143c76 RED-9872: Tenant Management issues 2024-08-26 14:52:56 +02:00
Yannik Hampe
5e0a363aff Merge branch 'RED-3813-hotfix' into 'main'
RED-8343: change 500 to 403

See merge request fforesight/tenant-user-management-service!125
2024-08-20 15:48:55 +02:00
yhampe
26525d4baa Merge remote-tracking branch 'origin/RED-3813-hotfix' into RED-3813-hotfix 2024-08-20 14:14:36 +02:00
yhampe
c6ea2430bb RED-8343: change 500 to 403
fixed mistake in spelling
2024-08-20 14:14:28 +02:00
yhampe
f88dd799b0 RED-8343: change 500 to 403
fixed mistake in spelling
2024-08-20 14:00:31 +02:00
Yannik Hampe
f4555e1737 Merge branch 'RED-3813-hotfix' into 'main'
RED-3813: image similarity endpoint

See merge request fforesight/tenant-user-management-service!124
2024-08-19 09:00:18 +02:00
yhampe
50e6178060 RED-3813: image similarity endpoint
added roles for endpoint to admin roles
2024-08-16 09:04:00 +02:00
Maverick Studer
25b2db376b Merge branch 'RED-9872' into 'main'
RED-9872: Tenant Management issues

See merge request fforesight/tenant-user-management-service!122
2024-08-15 09:44:51 +02:00
Maverick Studer
0cba921ce2 RED-9872: Tenant Management issues 2024-08-15 09:44:51 +02:00
Kilian Schüttler
ce8cfff69e Merge branch 'RED-9255' into 'main'
RED-9255: add role for import files

See merge request fforesight/tenant-user-management-service!120
2024-07-25 14:49:14 +02:00
Kilian Schuettler
2c2f3464e9 RED-9255: add role for import files 2024-07-25 14:41:25 +02:00
Maverick Studer
eaa70cffb1 Merge branch 'RED-9628' into 'main'
RED-9628: Some actions for users without roles not possible

See merge request fforesight/tenant-user-management-service!117
2024-07-10 11:52:04 +02:00
maverickstuder
6bbe3316ac RED-9628: Some actions for users without roles not possible 2024-07-10 11:22:35 +02:00
Andrei Isvoran
3f94e66eda Merge branch 'RED-9489' into 'main'
RED-9489 - Implement graceful shutdown

See merge request fforesight/tenant-user-management-service!115
2024-07-04 13:07:20 +02:00
Andrei Isvoran
114fbfc62f RED-9489 - Implement graceful shutdown 2024-07-04 13:50:44 +03:00
Ali Oezyetimoglu
d3e575159f Merge branch 'RED-9512' into 'main'
RED-9512: added null check for SMTP password when updating configuration

See merge request fforesight/tenant-user-management-service!120
2024-07-03 10:37:26 +02:00
Ali Oezyetimoglu
5e7dcb1689 RED-9512: added null check for SMTP password when updating configuration 2024-07-03 09:59:12 +02:00
Ali Oezyetimoglu
f9668a3c07 Merge branch 'RED-9512' into 'main'
RED-9512: added null check for SMTP password when updating configuration

See merge request fforesight/tenant-user-management-service!116
2024-07-02 16:36:26 +02:00
Ali Oezyetimoglu
aa9fe087f5 RED-9512: added null check for SMTP password when updating configuration 2024-07-02 14:45:50 +02:00
Dominique Eifländer
310339a5d9 Merge branch 'RED-9128' into 'main'
RED-9128: Use refresh token rotation

See merge request fforesight/tenant-user-management-service!115
2024-07-02 09:07:16 +02:00
Dominique Eifländer
23c3b6787e RED-9128: Use refresh token rotation 2024-07-01 14:05:11 +02:00
Maverick Studer
ae21409577 Merge branch 'RED-8491' into 'main'
RED-8491: Hide all KNECON_* roles for any possible access in all endpoints

See merge request fforesight/tenant-user-management-service!113
2024-06-27 09:48:43 +02:00
Maverick Studer
ae04a6fd74 RED-8491: Hide all KNECON_* roles for any possible access in all endpoints 2024-06-27 09:48:43 +02:00
Maverick Studer
d5b2cb2af2 Merge branch 'hotfix-tenant-deletion' into 'main'
hotfix tenant deletion if no bucket present

See merge request fforesight/tenant-user-management-service!112
2024-06-14 13:31:10 +02:00
Maverick Studer
e71712450d hotfix tenant deletion if no bucket present 2024-06-14 13:31:10 +02:00
Maverick Studer
f0b484895a Merge branch 'RED-8491-9346' into 'main'
RED-8491: Hide all KNECON_* roles for any possible access in all endpoints && RED-9346: User without roles not displayed in GET endpoint

See merge request fforesight/tenant-user-management-service!111
2024-06-14 11:51:51 +02:00
Maverick Studer
c303d46d8e RED-8491: Hide all KNECON_* roles for any possible access in all endpoints && RED-9346: User without roles not displayed in GET endpoint 2024-06-14 11:51:51 +02:00
Maverick Studer
8ed4e8660f Merge branch 'RED-8491-9346' into 'main'
RED-8491: Hide all KNECON_* roles for any possible access in all endpoints && RED-9346: User without roles not displayed in GET endpoint

See merge request fforesight/tenant-user-management-service!110
2024-06-13 15:55:57 +02:00
Maverick Studer
119676e143 RED-8491: Hide all KNECON_* roles for any possible access in all endpoints && RED-9346: User without roles not displayed in GET endpoint 2024-06-13 15:55:57 +02:00
Maverick Studer
7b135dc48b Merge branch 'RED-8491-fix' into 'main'
RED-8491: Hide all KNECON_* roles for any possible access in all endpoints

See merge request fforesight/tenant-user-management-service!109
2024-06-11 10:47:23 +02:00
Maverick Studer
0692ac8309 RED-8491: Hide all KNECON_* roles for any possible access in all endpoints 2024-06-11 10:47:23 +02:00
Dominique Eifländer
bacb43c9db Merge branch 'RED-9225-perm-fp' into 'main'
RED-9225: Fixed wrong user permissions for customer api

See merge request fforesight/tenant-user-management-service!108
2024-06-07 13:08:10 +02:00
Dominique Eifländer
c2b14d176c RED-9225: Fixed wrong user permissions for customer api 2024-06-07 13:01:50 +02:00
Ali Oezyetimoglu
c7098c98ac Merge branch 'RED-9251' into 'main'
RED-9251: increased maximumPoolSize to 10

See merge request fforesight/tenant-user-management-service!106
2024-06-04 11:12:30 +02:00
Maverick Studer
ed86367ab8 Merge branch 'RED-8491' into 'main'
RED-8491: Hide all KNECON_* roles for any possible access in all endpoints

See merge request fforesight/tenant-user-management-service!105
2024-06-04 09:05:42 +02:00
Maverick Studer
8399251f44 RED-8491: Hide all KNECON_* roles for any possible access in all endpoints 2024-06-04 09:05:42 +02:00
Ali Oezyetimoglu
039b0fad13 RED-9251: increased maximumPoolSize to 10 2024-06-03 16:23:39 +02:00
Dominique Eifländer
537dc185a3 Merge branch 'RED-9225-fp' into 'main'
RED-9225: Added first customer api endpoints

See merge request fforesight/tenant-user-management-service!104
2024-06-03 12:55:36 +02:00
Dominique Eifländer
258bf63cd0 RED-9225: Added first customer api endpoints 2024-06-03 12:42:18 +02:00
Maverick Studer
1149a0809e Merge branch 'RED-9254' into 'main'
RED-9254: Support Controller (Part 1)

See merge request fforesight/tenant-user-management-service!103
2024-06-03 12:36:18 +02:00
Maverick Studer
534ace6a55 RED-9254: Support Controller (Part 1) 2024-06-03 12:36:17 +02:00
Maverick Studer
1c7698d2de Merge branch 'RED-3387' into 'main'
RED-3387: No Welcome Email for SSO User

See merge request fforesight/tenant-user-management-service!101
2024-05-02 11:36:46 +02:00
Maverick Studer
a2cd372d51 RED-3387: No Welcome Email for SSO User 2024-05-02 11:36:46 +02:00
Maverick Studer
628eda60b9 Merge branch 'RED-8702-clustered' into 'main'
RED-8702: Explore document databases to store entityLog

See merge request fforesight/tenant-user-management-service!100
2024-04-11 14:20:50 +02:00
Maverick Studer
534af6d061 RED-8702: Explore document databases to store entityLog 2024-04-11 14:20:49 +02:00
Timo Bejan
50bfc103b6 Merge branch 'clari-63' into 'main'
0mongoDb optional

See merge request fforesight/tenant-user-management-service!99
2024-04-07 21:08:21 +02:00
Timo Bejan
b9121b839b 0mongoDb optional 2024-04-07 22:02:32 +03:00
Timo Bejan
53e49f083d Merge branch 'clari-63' into 'main'
Clari 63

See merge request fforesight/tenant-user-management-service!98
2024-04-07 20:42:36 +02:00
Timo Bejan
20c0890ad0 mongoDb optional 2024-04-07 21:35:12 +03:00
Timo Bejan
a689835af6 Merge branch 'clari-63' into 'main'
not every fforesight application needs mongo ...

See merge request fforesight/tenant-user-management-service!97
2024-04-05 17:59:21 +02:00
Timo Bejan
cd159879f5 not every fforesight application needs mongo ... 2024-04-05 18:46:39 +03:00
Timo Bejan
14d9bfcb3b Merge branch 'clari-63' into 'main'
new perm issions

See merge request fforesight/tenant-user-management-service!96
2024-04-05 14:44:28 +02:00
Timo Bejan
9bdd2bb09a new perm issions 2024-04-05 15:38:50 +03:00
Maverick Studer
e3e01c8dbd Merge branch 'RED-8702' into 'main'
RED-8702: Explore document databases to store entityLog

See merge request fforesight/tenant-user-management-service!95
2024-04-03 12:48:43 +02:00
Maverick Studer
5bda827c21 RED-8702: Explore document databases to store entityLog 2024-04-03 12:48:43 +02:00
Timo Bejan
e821371806 Merge branch 'clari-43' into 'main'
FF_USER/FF_ADMIN permissions to access file-paragraphs tag operations

See merge request fforesight/tenant-user-management-service!94
2024-03-22 12:25:51 +01:00
Hanelore.Ianoseck
828c8a840c CLARI-43: Added FF_USER/FF_ADMIN permissions to access file-paragraphs tag operations 2024-03-21 08:40:38 +02:00
Timo Bejan
8a7b861ceb Merge branch 'implicitflow' into 'main'
added implicit flow swagger

See merge request fforesight/tenant-user-management-service!93
2024-03-13 16:25:38 +01:00
Timo Bejan
112d38d7be added implicit flow swagger 2024-03-12 16:31:35 +02:00
Yannik Hampe
2f49ee0908 Merge branch 'RED-7055' into 'main'
RED-7055 - Change status code for user creation to conflict

See merge request fforesight/tenant-user-management-service!92
2024-03-04 09:05:31 +01:00
Andrei Isvoran
6f70d1e333 RED-7055 - Change status code for user creation to conflict 2024-02-28 16:22:08 +02:00
Timo Bejan
6dc5dc6142 Merge branch 'swiss-legal-hotfix' into 'main'
permission rework

See merge request fforesight/tenant-user-management-service!91
2024-02-25 18:52:16 +01:00
Timo Bejan
087431678a clarifynd redirect uri adjustment 2024-02-26 00:38:56 +07:00
Timo Bejan
1107f91691 permission rework 2024-02-26 00:35:59 +07:00
Maverick Studer
99641a6688 Merge branch 'RED-8477' into 'main'
RED-8477: SSO settings endpoint for SAML

See merge request fforesight/tenant-user-management-service!90
2024-02-21 13:03:19 +01:00
maverickstuder
12e8c0f53f RED-8477: SSO settings endpoint for SAML
* added more tests
* refactoring
* added check for existing idp with same displayName on update
2024-02-21 12:01:53 +01:00
Maverick Studer
715d33b9e1 Merge branch 'RED-8477' into 'main'
RED-8477: SSO settings endpoint for SAML

See merge request fforesight/tenant-user-management-service!89
2024-02-20 09:02:10 +01:00
Maverick Studer
f98394c136 RED-8477: SSO settings endpoint for SAML 2024-02-20 09:02:10 +01:00
Timo Bejan
0ae6b2a77b Merge branch 'clari-11' into 'main'
CLARI-11: Added permissions to access favourite files operations in persistence-service

See merge request fforesight/tenant-user-management-service!87
2024-02-16 04:57:43 +01:00
Hanelore Ianoseck
b35386684e CLARI-11: Added permissions to access favourite files operations in persistence-service 2024-02-16 04:57:43 +01:00
Maverick Studer
af37030c68 Merge branch 'RED-8477' into 'main'
RED-8477: SSO settings endpoint for SAML

See merge request fforesight/tenant-user-management-service!88
2024-02-15 13:53:33 +01:00
Maverick Studer
6954a5cee3 RED-8477: SSO settings endpoint for SAML 2024-02-15 13:53:33 +01:00
Maverick Studer
7bec8f98e6 Merge branch 'RED-8477' into 'main'
RED-8477: SSO settings endpoint for SAML

See merge request fforesight/tenant-user-management-service!86
2024-02-13 12:42:21 +01:00
Maverick Studer
5af289479e RED-8477: SSO settings endpoint for SAML 2024-02-13 12:42:21 +01:00
Ali Oezyetimoglu
af16b4db01 Merge branch 'RED-6659' into 'main'
RED-6659 - Update KNECON_ADMIN rank & add error handling for acces denied

See merge request fforesight/tenant-user-management-service!85
2024-02-08 11:24:34 +01:00
Andrei Isvoran
68bc112660 RED-6659 - Update KNECON_ADMIN rank & add error handling for acces denied 2024-02-08 11:42:26 +02:00
Maverick Studer
b3dd62e3ca Merge branch 'RED-8333' into 'main'
RED-8333: Misleading error message when creating user with already existing e-mail

See merge request fforesight/tenant-user-management-service!84
2024-02-06 16:46:00 +01:00
Maverick Studer
ac1b86fa41 RED-8333: Misleading error message when creating user with already existing e-mail 2024-02-06 16:46:00 +01:00
Maverick Studer
abb11a4893 Merge branch 'RED-8454' into 'main'
RED-8454: Remove red-read-dossiers for RED_ADMIN and RED_USERS_ADMIN role

See merge request fforesight/tenant-user-management-service!83
2024-02-06 16:35:18 +01:00
maverickstuder
9241a191fa RED-8454: Remove red-read-dossiers for RED_ADMIN and RED_USERS_ADMIN role 2024-02-06 16:11:36 +01:00
Ali Oezyetimoglu
1e0fbd8e1d Merge branch 'RED-6659' into 'main'
Red-6659 - Don't allow users with lower rank roles to activate/deactivate users with higher rank roles

See merge request fforesight/tenant-user-management-service!81
2024-02-06 11:56:45 +01:00
Andrei Isvoran
5c0679f1fc Red-6659 - Don't allow users with lower rank roles to activate/deactivate users with higher rank roles 2024-02-06 11:56:45 +01:00
Maverick Studer
1272d91e74 Merge branch 'RED-8333' into 'main'
Red 8333: Misleading error message when creating user with already existing e-mail

See merge request fforesight/tenant-user-management-service!80
2024-02-05 15:13:38 +01:00
Maverick Studer
72def10e4a Red 8333: Misleading error message when creating user with already existing e-mail 2024-02-05 15:13:38 +01:00
Maverick Studer
2d34c1999d Merge branch 'RED-6625' into 'main'
RED-6625: Inconsistencies when creating a new user

See merge request fforesight/tenant-user-management-service!79
2024-02-05 11:24:29 +01:00
maverickstuder
f0f888a63d RED-6625: Inconsistencies when creating a new user 2024-02-05 11:18:39 +01:00
Yannik Hampe
7f220bd348 Merge branch 'RED-8343-fix' into 'main'
RED-8343: change 500 to 403

See merge request fforesight/tenant-user-management-service!78
2024-02-05 11:11:31 +01:00
Yannik Hampe
86e676cc51 Merge branch 'RED-8414-fix' into 'main'
RED-8414: create new role admin

See merge request fforesight/tenant-user-management-service!75
2024-02-05 11:10:40 +01:00
maverickstuder
a20484e486 RED-6625: Inconsistencies when creating a new user 2024-02-05 11:06:23 +01:00
yhampe
d151831ad9 RED-8343: change 500 to 403
removed 403
2024-02-05 11:02:35 +01:00
yhampe
e2c74b607b RED-8414: create new role admin
fixed failing tests
2024-02-05 10:58:29 +01:00
Timo Bejan
a54d08bcb0 Merge branch 'RED-8431' into 'main'
Updated KC client lib RED-8431

See merge request fforesight/tenant-user-management-service!77
2024-02-04 08:34:02 +01:00
Timo Bejan
2fc8c9fc65 Updated KC client lib RED-8431 2024-02-04 08:34:01 +01:00
Dominique Eifländer
631fe2bc1f Merge branch 'RED-8171' into 'main'
RED-8171: Traces do not stop at @Async

See merge request fforesight/tenant-user-management-service!76
2024-02-02 14:55:46 +01:00
Dominique Eifländer
cb88dca0ea RED-8171: Traces do not stop at @Async 2024-02-02 14:50:29 +01:00
yhampe
c0b98a1bef RED-8414: create new role admin
If users have only the KNECON_ADMIN role they should be filtered out completely

If users have the KNECON_ADMIN role and a RED_x role they should be included in the response, but only the RED_x roles should be listed
2024-02-02 14:02:20 +01:00
Yannik Hampe
dc2a11ac83 Merge branch 'RED-8343' into 'main'
RED-8343

See merge request fforesight/tenant-user-management-service!74
2024-02-02 12:41:23 +01:00
yhampe
b5a76fd483 Merge remote-tracking branch 'origin/RED-8343' into RED-8343 2024-02-02 12:35:35 +01:00
yhampe
fa71cb9633 RED-8343 Change response 500 to 403 Forbidden for Deleted User Handling
catching and throwing forbidden exception on specific method
2024-02-02 12:35:26 +01:00
yhampe
8f8b6787e9 RED-8343
working on debugging
2024-02-02 12:35:26 +01:00
yhampe
6493c75de4 RED-8343 Change response 500 to 403 Forbidden for Deleted User Handling
catching and throwing forbidden exception on specific method
2024-02-02 12:34:58 +01:00
Yannik Hampe
a6c97df229 Merge branch 'RED-8414' into 'main'
RED-8414 add role knecon admin

See merge request fforesight/tenant-user-management-service!73
2024-02-02 08:27:49 +01:00
yhampe
84a0c47ae6 RED-8414 add role knecon admin
added roles and rights from RED_ADMIN

also added 403 definition
2024-02-01 18:42:47 +01:00
yhampe
d0b6081000 RED-8343
working on debugging
2024-02-01 18:01:01 +01:00
Dominique Eifländer
f4b874c5a4 Merge branch 'RED-7939' into 'main'
RED-7939: Return 204 if user to delete does not exist

See merge request fforesight/tenant-user-management-service!72
2024-01-31 10:45:42 +01:00
Dominique Eifländer
2648d49c81 RED-7939: Return 204 if user to delete does not exist 2024-01-31 10:38:22 +01:00
Timo Bejan
5583f6613d Merge branch 'claryfind-hotfix' into 'main'
claryfind hotfix

See merge request fforesight/tenant-user-management-service!71
2024-01-31 10:03:56 +01:00
Timo Bejan
4eb7e1709e claryfind hotfix 2024-01-30 19:15:52 +08:00
Timo Bejan
b43cc69633 Merge branch 'claryfind-hotfix' into 'main'
claryfind hotfix

See merge request fforesight/tenant-user-management-service!70
2024-01-30 12:03:20 +01:00
Timo Bejan
6712f25890 claryfind hotfix 2024-01-30 18:57:47 +08:00
Yannik Hampe
9838dcf10a Merge branch 'RED-6668-fix' into 'main'
RED-6888: delete tenant endpoint

See merge request fforesight/tenant-user-management-service!69
2024-01-30 11:07:04 +01:00
yhampe
44a404f2da RED-6888: delete tenant endpoint
added missing roles
fixed routing key
2024-01-30 11:00:09 +01:00
Yannik Hampe
2d31712295 Merge branch 'RED-6668-fix' into 'main'
RED-6888: forgot to authorize for role

See merge request fforesight/tenant-user-management-service!68
2024-01-30 09:51:57 +01:00
yhampe
2feffb9b80 RED-6888: forgot to authorize for role 2024-01-30 09:46:03 +01:00
Timo Bejan
4a2203893c Merge branch 'RED-8085' into 'main'
RED-8085 - pmd/checkstyle cleanup

See merge request fforesight/tenant-user-management-service!67
2024-01-29 10:31:49 +01:00
Timo Bejan
7f45a73bce RED-8085 - pmd/checkstyle cleanup 2024-01-29 10:31:49 +01:00
Yannik Hampe
95778f7926 Merge branch 'RED-6668' into 'main'
RED-6888: Add delete tenant endpoint

See merge request fforesight/tenant-user-management-service!66
2024-01-26 11:55:01 +01:00
yhampe
d74d4217f8 RED-6888: Add delete tenant endpoint
review feedback
2024-01-26 09:29:21 +01:00
yhampe
0722b50584 RED-6888: Add delete tenant endpoint
fixed mistake in endpoint description
2024-01-26 08:37:25 +01:00
yhampe
51d1039866 RED-6888: Add delete tenant endpoint
removed import
2024-01-25 10:19:35 +01:00
yhampe
819730d970 RED-6888: Add delete tenant endpoint
removed extra topic exchange
removed not needed event
fixed code errors
tested on stack
2024-01-25 09:49:48 +01:00
yhampe
64cecc4a7d RED-6888: Add delete tenant endpoint
changed method signature to void
2024-01-24 09:45:46 +01:00
yhampe
96d03f756d RED-6888: Add delete tenant endpoint
changed delete tenant queue name
2024-01-23 14:58:04 +01:00
yhampe
8783dc91e7 RED-6888: Add delete tenant endpoint
added endpoint
added methods to service
deleting everything except cache
2024-01-23 12:51:29 +01:00
Ali Oezyetimoglu
10a834e7f8 Merge branch 'RED-7539' into 'main'
RED-7539: changed error message when validating password causes an error

See merge request fforesight/tenant-user-management-service!65
2024-01-22 10:12:58 +01:00
Ali Oezyetimoglu
08ed0f29c0 RED-7539: changed error message when validating password causes an error 2024-01-19 16:04:41 +01:00
Timo Bejan
93a1b1fcda Merge branch 'clari-6' into 'main'
Clari 6

See merge request fforesight/tenant-user-management-service!64
2024-01-10 16:33:55 +01:00
Timo Bejan
6edbd910c4 new valid url 2024-01-10 17:26:47 +02:00
Ali Oezyetimoglu
d39ca21c4b Merge branch 'RED-5012' into 'main'
RED-5012: delete created user, if no rights to create that user

See merge request fforesight/tenant-user-management-service!63
2024-01-10 09:25:34 +01:00
Ali Oezyetimoglu
1f3c862497 RED-5012: catching if user has insufficient rank while creating a user 2024-01-10 08:40:16 +01:00
Ali Oezyetimoglu
79a465aa40 RED-5012: catching if user has insufficient rank while creating a user 2024-01-09 22:06:31 +01:00
Timo Bejan
d655ea958b Merge branch 'RED-8123' into 'main'
RED-8123 - privilege escalation is not checked/validated

See merge request fforesight/tenant-user-management-service!62
2024-01-04 13:47:33 +01:00
Timo Bejan
8a41d70701 RED-8123 - privilege escalation is not checked/validated 2024-01-04 14:27:18 +02:00
Timo Bejan
980c59de78 Merge branch 'swagger-auth-error' into 'main'
Fixed dev profile and fixed not working swagger in production - issue relates...

See merge request fforesight/tenant-user-management-service!61
2024-01-04 10:02:10 +01:00
Timo Bejan
7462b38f9f Fixed dev profile and fixed not working swagger in production - issue relates to spring dependency updates and how cascade yamls are handled 2024-01-04 10:34:50 +02:00
Dominique Eifländer
755a6f0798 Merge branch 'RED-1137' into 'main'
RED-1137: Do not observe actuator endpoints

See merge request fforesight/tenant-user-management-service!60
2023-12-20 14:12:44 +01:00
Dominique Eifländer
0c66bacb49 RED-1137: Do not observe actuator endpoints 2023-12-20 14:06:36 +01:00
Dominique Eifländer
12fd15390d Merge branch 'RED-7384' into 'main'
RED-7384: fix rabbitTemplate configuration

See merge request fforesight/tenant-user-management-service!59
2023-12-14 16:52:57 +01:00
Kilian Schuettler
d935a0488e RED-7384: fix rabbitTemplate configuration 2023-12-14 16:48:05 +01:00
Dominique Eifländer
55f380a602 Merge branch 'RED-6661' into 'main'
Sample code and fixes for connecting to syngenta training data

See merge request fforesight/tenant-user-management-service!57
2023-12-14 09:27:58 +01:00
Timo Bejan
b0bf8359b9 fixed sample yaml 2023-12-14 10:19:55 +02:00
Dominique Eifländer
b57a12ce79 Merge branch 'RED-5223' into 'main'
RED-5223: Use tracing-commons from fforesight

See merge request fforesight/tenant-user-management-service!58
2023-12-13 16:13:22 +01:00
Timo Bejan
7ae7dc10db checkstyle fix 2023-12-13 16:23:00 +02:00
Timo Bejan
7dc03d7781 Sample code and fixes for connecting to syngenta training data 2023-12-13 16:22:04 +02:00
Dominique Eifländer
6d24d00a67 RED-5223: Use tracing-commons from fforesight 2023-12-13 15:19:30 +01:00
Dominique Eifländer
4bd7e8d09b Merge branch 'RED-5223' into 'main'
RED-5223: Enabled tracing, upgrade spring, use logstash-logback-encoder for json logs

See merge request fforesight/tenant-user-management-service!56
2023-12-12 15:24:42 +01:00
Dominique Eifländer
d55ef70442 RED-5223: Enabled tracing, upgrade spring, use logstash-logback-encoder for json logs 2023-12-12 13:30:01 +01:00
Timo Bejan
d2e52263fb Merge branch 'RED-6661' into 'main'
Added UI for migration testing

See merge request fforesight/tenant-user-management-service!55
2023-12-07 00:21:52 +01:00
Timo Bejan
921197a665 Added UI for migration testing 2023-12-07 01:16:59 +02:00
Timo Bejan
2692a7b1a9 Merge branch 'RED-8008' into 'main'
RED-8008: Ensure that new roles are added to existing tenants during upgrade

See merge request fforesight/tenant-user-management-service!54
2023-12-06 23:40:36 +01:00
Ali Oezyetimoglu
9a94879960 Merge branch 'RED-7715' into 'main'
RED-7715 - Add log4j config to enable switching between json/line logs

See merge request fforesight/tenant-user-management-service!53
2023-12-06 13:00:37 +01:00
Ali Oezyetimoglu
6242258e09 RED-8008: Ensure that new roles are added to existing tenants during upgrade 2023-12-06 11:40:43 +01:00
Andrei Isvoran
eebbc2d00d RED-7715 - Add log4j config to enable switching between json/line logs 2023-12-06 12:06:44 +02:00
Timo Bejan
362a86916a Merge branch 'RED-7967' into 'main'
Migration with multi arch images

See merge request fforesight/tenant-user-management-service!52
2023-12-05 08:38:41 +01:00
Timo Bejan
5dcf0dbef0 Migration with multi arch images 2023-12-05 09:23:49 +02:00
Timo Bejan
f6916dab3e Merge branch 'RED-7967' into 'main'
RED-7967 - KC post logout uri fix

See merge request fforesight/tenant-user-management-service!51
2023-11-28 16:29:50 +01:00
Timo Bejan
442b12edf9 RED-7967 - KC post logout uri fix 2023-11-28 16:43:33 +02:00
Dominique Eifländer
f6a0860ac1 Merge branch 'RED-7962' into 'main'
RED-7962: Added missing unmapped role red-experimental for documine

See merge request fforesight/tenant-user-management-service!50
2023-11-28 13:56:12 +01:00
Dominique Eifländer
c458b8e178 RED-7962: Added missing unmapped role red-experimental for documine 2023-11-28 13:49:48 +01:00
Dominique Eifländer
3bd9b72b3a Merge branch 'RED-7962' into 'main'
RED-7962: Added missing unmapped role red-experimental

See merge request fforesight/tenant-user-management-service!49
2023-11-28 12:55:13 +01:00
Dominique Eifländer
90c159f52d RED-7962: Added missing unmapped role red-experimental 2023-11-28 12:48:37 +01:00
Kilian Schüttler
b97bd1d9df Merge branch 'RED-7794' into 'main'
RED-7794 - Fix error page not showing correct name

See merge request fforesight/tenant-user-management-service!46
2023-11-24 10:36:06 +01:00
Andrei Isvoran
6435f1814b RED-7794 - Fix error page not showing correct name 2023-11-24 10:36:06 +01:00
Timo Bejan
0cd586552c Merge branch 'timo.bejan.ext-main-patch-30758' into 'main'
Update README.md

See merge request fforesight/tenant-user-management-service!48
2023-11-23 22:06:14 +01:00
Timo Bejan
8ce5b9bc30 Update README.md 2023-11-23 21:42:42 +01:00
Timo Bejan
c68e4033a3 Merge branch 'migration-fixes' into 'main'
Migration local testing

See merge request fforesight/tenant-user-management-service!47
2023-11-23 20:49:50 +01:00
Timo Bejan
59bd73c980 Migration local testing 2023-11-23 21:39:44 +02:00
Timo Bejan
18d982f6c2 Merge branch 'clarifynd-rename' into 'main'
taas - clarifynd rename

See merge request fforesight/tenant-user-management-service!45
2023-11-14 19:54:35 +01:00
Timo Bejan
736e4fd874 taas - clarifynd rename 2023-11-14 20:46:01 +02:00
Timo Bejan
7559ab64ef Merge branch 'clarifynd-rename' into 'main'
taas - clarifynd rename

See merge request fforesight/tenant-user-management-service!44
2023-11-14 18:53:01 +01:00
Timo Bejan
7cf39a0b68 Merge branch 'RED-7889' into 'main'
RED-7889 - migration tenant sync hook and liquibase patches

See merge request fforesight/tenant-user-management-service!43
2023-11-14 18:46:12 +01:00
Timo Bejan
381b30198b taas - clarifynd rename 2023-11-14 19:45:26 +02:00
Timo Bejan
d48263e189 pmd fix 2023-11-07 15:53:29 +02:00
Timo Bejan
7a57283868 RED-7889 - migration tenant sync hook and liquibase patches 2023-11-07 01:06:52 +02:00
Dominique Eifländer
68ec498bbe Merge branch 'RED-7775' into 'main'
RED-7775: Named database connections, removed idle connections

See merge request fforesight/tenant-user-management-service!41
2023-10-18 16:36:58 +02:00
Dominique Eifländer
bbe6307a9b RED-7775: Named database connections, removed idle connections 2023-10-18 16:22:07 +02:00
Timo Bejan
2edecc9915 Merge branch 'audit-service-taas' into 'main'
Added audit redirect uri and audit role on FF_ADMIN

See merge request fforesight/tenant-user-management-service!40
2023-09-26 18:16:49 +02:00
Hanelore.Ianoseck
78a455db44 Added audit redirect uri and audit role on FF_ADMIN 2023-09-21 18:55:36 +03:00
Kilian Schüttler
1508c8fad4 Merge branch 'RED-6903' into 'main'
RED-6903 - Adjust failure message to allow different username and from email

See merge request fforesight/tenant-user-management-service!39
2023-09-20 11:18:54 +02:00
Andrei Isvoran
6fbfbb17ce RED-6903 - Adjust failure message to allow different username and from email 2023-09-20 11:18:54 +02:00
Ali Oezyetimoglu
979e9069fa Merge branch 'RED-6903' into 'main'
RED-6903 - Adjust failure message

See merge request fforesight/tenant-user-management-service!38
2023-09-19 10:39:01 +02:00
Andrei Isvoran
57aa00e096 RED-6903 - Adjust failure message 2023-09-18 15:38:21 +03:00
Dominique Eifländer
9ace6e25a5 Merge branch 'RED-7578' into 'main'
RED-7578: Added /api/* as valid redirect uri

See merge request fforesight/tenant-user-management-service!37
2023-09-15 13:41:54 +02:00
deiflaender
5478bd443e RED-7578: Added /api/* as valid redirect uri 2023-09-15 13:26:20 +02:00
Ali Oezyetimoglu
87afed5aab Merge branch 'RED-6903' into 'main'
RED-6903 - Adjust failure message for wrong password

See merge request fforesight/tenant-user-management-service!36
2023-09-14 13:10:33 +02:00
Andrei Isvoran
47d8089a36 RED-6903 - Adjust failure message for wrong password 2023-09-14 14:04:58 +03:00
Lena Maldacker
96fbaf7ec5 Merge branch 'lena.maldacker-main-patch-60115' into 'main'
DM-462: Remove red-get-tables from RED_USER

See merge request fforesight/tenant-user-management-service!35
2023-09-14 11:15:10 +02:00
Lena Maldacker
d23fbc54f8 DM-462: Add red-get-tables to unmappedPermissions 2023-09-14 11:09:51 +02:00
Lena Maldacker
518cc6139f DM-462: Remove red-get-tables from RED_USER 2023-09-14 10:41:41 +02:00
Ali Oezyetimoglu
9a316d1c9f Merge branch 'RED-7175-5' into 'main'
RED-7175: changed index prefix; now it has two parts (application prefix and...

See merge request fforesight/tenant-user-management-service!34
2023-09-14 10:00:52 +02:00
Ali Oezyetimoglu
55c82c88a6 RED-7175: changed index prefix; now it has two parts (application prefix and tenantId) which are set here 2023-09-13 16:25:01 +02:00
Timo Bejan
76235935df Merge branch 'timo.bejan.ext-main-patch-57234' into 'main'
Update application-taas.yaml

See merge request fforesight/tenant-user-management-service!33
2023-09-13 15:23:14 +02:00
Timo Bejan
8a47cd9103 Update application-taas.yaml 2023-09-13 09:04:39 +02:00
Ali Oezyetimoglu
30a045364d Merge branch 'RED-7175-4' into 'main'
RED-7175: changed TenantContext.getTenantId() to tenantRequest.getTenantId()...

See merge request fforesight/tenant-user-management-service!32
2023-09-12 13:36:25 +02:00
Ali Oezyetimoglu
acd8f4369b RED-7175: changed TenantContext.getTenantId() to tenantRequest.getTenantId() to fix wrong index prefix when creating a new tenant with empty index prefix 2023-09-12 13:30:21 +02:00
Ali Oezyetimoglu
02f8689769 Merge branch 'RED-6903' into 'main'
RED-6903 - Return 400 for test SMTP connection in failure cases

See merge request fforesight/tenant-user-management-service!31
2023-09-12 11:21:51 +02:00
Andrei Isvoran
1c8a8e6302 RED-6903 - Return 400 for test SMTP connection in failure cases 2023-09-12 11:46:05 +03:00
Ali Oezyetimoglu
c2bd9a34f4 Merge branch 'RED-6903' into 'main'
RED-6903 - Adjust reply to testSMTPConnection to reflect the recipient/admin email used

See merge request fforesight/tenant-user-management-service!30
2023-09-11 09:50:17 +02:00
Andrei Isvoran
c2b7a69a08 RED-6903 - Adjust reply to testSMTPConnection to reflect the recipient/admin email used 2023-09-11 09:50:17 +02:00
Ali Oezyetimoglu
e012af658d Merge branch 'DM-409' into 'main'
DM-409 - Set default login theme

See merge request fforesight/tenant-user-management-service!29
2023-09-08 14:26:29 +02:00
Andrei Isvoran
81dbcb98ed DM-409 - Set default login theme 2023-09-08 14:26:29 +02:00
Timo Bejan
e88e76771c Merge branch 'TAAS-88-2' into 'main'
TAAS-88: updated documentation of endpoints and models

See merge request fforesight/tenant-user-management-service!27
2023-09-07 11:16:17 +02:00
Timo Bejan
171922c1a4 Merge branch 'DM-409' into 'main'
DM-406 - update master realm theme on tenant creation

See merge request fforesight/tenant-user-management-service!28
2023-09-07 11:15:56 +02:00
Andrei Isvoran
fc025ae440 DM-406 - update master realm theme on tenant creation 2023-09-07 11:15:56 +02:00
Ali Oezyetimoglu
5869719c15 TAAS-88: updated documentation of endpoints and models 2023-09-07 10:54:49 +02:00
Christoph Schabert
3b29517363 Merge branch 'RED-7175-3' into 'main'
RED-7175: renamed indexName to indexPrefix

See merge request fforesight/tenant-user-management-service!26
2023-09-07 10:28:13 +02:00
Timo Bejan
dda4a94274 Merge branch 'main' into 'RED-7175-3'
# Conflicts:
#   src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java
2023-09-07 08:23:05 +00:00
Timo Bejan
d40dd2bd57 Merge branch 'taas-updates' into 'main'
HotFix indexName for taas

See merge request fforesight/tenant-user-management-service!24
2023-09-07 09:52:51 +02:00
Ali Oezyetimoglu
ab44dae367 RED-7175: renamed indexName to indexPrefix 2023-09-07 08:33:42 +02:00
Timo Bejan
184031e760 HotFix indexName for taas 2023-09-06 16:10:40 +03:00
Timo Bejan
76aa088152 Merge branch 'DM-409' into 'main'
DM-406 - Add setting that allows the update of the keycloak theme in master realm

See merge request fforesight/tenant-user-management-service!23
2023-09-06 13:33:36 +02:00
Andrei Isvoran
ba0cbae9c4 DM-406 - Add setting that allows the update of the keycloak theme in master realm 2023-09-06 13:33:36 +02:00
Timo Bejan
3f7311b272 Merge branch 'taas-updates' into 'main'
Taas updates

See merge request fforesight/tenant-user-management-service!22
2023-09-05 18:43:28 +02:00
Timo Bejan
53fec52f86 Tenant Sync commented 2023-09-05 19:20:40 +03:00
Timo Bejan
e5c7dae219 Tenant Sync Utility 2023-09-05 18:41:18 +03:00
Timo Bejan
cf0f623bd6 taas permissions 2023-09-05 18:24:27 +03:00
Ali Oezyetimoglu
6bb8ee01b7 Merge branch 'RED-7175-2' into 'main'
RED-7175: catch if index name is null to avoid wrong search connections

See merge request fforesight/tenant-user-management-service!21
2023-09-05 15:14:43 +02:00
Ali Oezyetimoglu
9d9aa6f46d Merge branch 'RED-6903' into 'main'
Red-6903 - Implement email service in tenant user management service

See merge request fforesight/tenant-user-management-service!20
2023-09-05 15:03:28 +02:00
Andrei Isvoran
400ced9b39 Red-6903 - Implement email service in tenant user management service 2023-09-05 15:03:28 +02:00
Ali Oezyetimoglu
cb8b7a27c6 RED-7175: catch if index name is null to avoid wrong search connections 2023-09-05 14:34:16 +02:00
Ali Oezyetimoglu
1003f4a4a1 Merge branch 'RED-7175' into 'main'
RED-7175

See merge request fforesight/tenant-user-management-service!19
2023-09-04 18:17:01 +02:00
Ali Oezyetimoglu
e6314a220a RED-7175: added index name to search connection entity and adapted the code accordingly 2023-09-01 15:20:49 +02:00
Ali Oezyetimoglu
32d12d4155 RED-7175: updated dependencies and adapted tests 2023-09-01 11:31:30 +02:00
Ali Oezyetimoglu
e66082013b RED-7175: updated dependencies and adapted tests 2023-09-01 11:31:24 +02:00
Ali Oezyetimoglu
8bcd42990e Merge branch 'RED-7337' into 'main'
RED-7337 Fix health endpoint

See merge request fforesight/tenant-user-management-service!17
2023-08-31 11:06:58 +02:00
Andrei Isvoran
92ce09c49d RED-7337 Fix health endpoint 2023-08-31 11:47:17 +03:00
Timo Bejan
e8fe27d6df Merge branch 'taas-updates' into 'main'
Taas updates

See merge request fforesight/tenant-user-management-service!16
2023-08-29 18:51:57 +02:00
Timo Bejan
60d603e028 More permissions 2023-08-29 19:02:04 +03:00
Timo Bejan
2c9a7e06c1 Session settings for TaaS 2023-08-29 19:02:04 +03:00
Ali Oezyetimoglu
d91ae1f261 Merge branch 'RED-7297-B' into 'main'
RED-7297: error message when resetting password

See merge request fforesight/tenant-user-management-service!15
2023-08-29 09:57:30 +02:00
Ali Oezyetimoglu
d441a8c5a8 RED-7297: error message when resetting password 2023-08-29 09:51:29 +02:00
Ali Oezyetimoglu
8c6d55fe6f Merge branch 'TAAS-88' into 'main'
TAAS-88: added descriptions to fields and classes; added README

See merge request fforesight/tenant-user-management-service!12
2023-08-29 08:08:49 +02:00
Ali Oezyetimoglu
b2bc62694c Merge branch 'RED-7297-A' into 'main'
RED-7297: error message when resetting password

See merge request fforesight/tenant-user-management-service!14
2023-08-28 16:52:17 +02:00
Ali Oezyetimoglu
fc889b04a1 RED-7297: error message when resetting password 2023-08-28 16:44:23 +02:00
Ali Oezyetimoglu
e6150888ab TAAS-88: added descriptions to fields and classes; added README 2023-08-28 09:07:44 +02:00
Timo Bejan
fefcca2ddf Merge branch 'taas-updates' into 'main'
default taas theme

See merge request fforesight/tenant-user-management-service!11
2023-08-25 16:34:04 +02:00
Timo Bejan
3318143bb4 default taas theme 2023-08-25 14:59:43 +03:00
Ali Oezyetimoglu
f377d1a3ab Merge branch 'RED-7297-2' into 'main'
RED-7297: added password  policy and its validation

See merge request fforesight/tenant-user-management-service!10
2023-08-25 08:04:11 +02:00
Ali Oezyetimoglu
d7f0899c3b RED-7292: removed hard coded part of password policy and extracted set of password policy to after creation and availability of realm; now it is possible for the first users not to match the policy 2023-08-24 13:20:20 +02:00
Ali Oezyetimoglu
98c6f27190 RED-7292: added check, if KC password policy exists 2023-08-24 09:11:30 +02:00
Ali Oezyetimoglu
c0b897112a RED-7292: removed unintentionally added line 2023-08-24 08:17:18 +02:00
Ali Oezyetimoglu
d74dab8fef RED-7292: removed unneded sneaky throws annotation 2023-08-23 14:11:04 +02:00
Ali Oezyetimoglu
791b6b76da RED-7292 fixed email is null error 2023-08-22 09:40:31 +02:00
Ali Oezyetimoglu
2d10b2b5c6 added password policy and caught exception 2023-08-21 20:48:52 +02:00
Ali Oezyetimoglu
c904e5947c added password policy and caught exception 2023-08-21 09:06:20 +02:00
Ali Oezyetimoglu
a79165bb09 updated pmd checks 2023-08-18 15:14:10 +02:00
deiflaender
550c05e93b RED-6725: Fixed wrong file encoding in container, that leads to not working rules on terms with special chars 2023-08-11 11:20:45 +02:00
Timo Bejan
cdff7c09cf redirect updates for taas and swagger 2023-08-08 13:25:02 +03:00
Timo Bejan
c8860f09f3 Merge branch 'RED-7290' into 'main'
RED-7290 - Update storage commons version

See merge request fforesight/tenant-user-management-service!9
2023-08-04 09:28:32 +02:00
Andrei Isvoran
e0fd3b9133 RED-7290 - Update storage commons version 2023-08-04 09:54:44 +03:00
Timo Bejan
5cea2a7cf2 default secret 2023-08-02 12:16:07 +03:00
Timo Bejan
479168b029 keycloak setup 2023-08-02 12:12:31 +03:00
Kevin Tumma
cef0f2830b Publish function 2023-08-01 13:01:12 +02:00
deiflaender
0fd19a5798 DM-165: Added red-get-tables to documine 2023-07-31 15:48:53 +02:00
Ali Oezyetimoglu
9e51a8f5ea Merge branch 'RED-6593' into 'main'
RED-6593 - Allow changing from s3 to azure and vice versa

See merge request fforesight/tenant-user-management-service!8
2023-07-27 15:19:48 +02:00
Andrei Isvoran
d99502b762 RED-6593 - Allow changing from s3 to azure and vice versa 2023-07-27 15:19:48 +02:00
Timo Bejan
70b9042c54 Update build.gradle.kts 2023-07-27 08:51:45 +02:00
Timo Bejan
d5a14a5bf3 Merge branch 'RED-7006-passwords' into 'main'
Red-7006 Hide passwords only for external controller

See merge request fforesight/tenant-user-management-service!7
2023-07-26 13:09:59 +02:00
Andrei Isvoran
99c3239f70 Red-7006 Hide passwords only for external controller 2023-07-26 13:09:59 +02:00
Timo Bejan
c7b375d13d more reasonable defaults for token times 2023-07-26 12:42:43 +02:00
Ali Oezyetimoglu
a41dc2a4dd Merge branch 'RED-7006' into 'main'
Red 7006 - Fix error message + passwords for db/schema shown in response + don't allow to set both azure and s3 for tenant

See merge request fforesight/tenant-user-management-service!6
2023-07-25 12:44:40 +02:00
Andrei Isvoran
dccf84bfa8 Red 7006 - Fix error message + passwords for db/schema shown in response + don't allow to set both azure and s3 for tenant 2023-07-25 12:44:39 +02:00
Timo Bejan
e7fef6be34 Fixed schema and grant escapes 2023-07-21 10:45:23 +03:00
Timo Bejan
757e726339 Fixed sql escape 2023-07-20 12:27:35 +03:00
Timo Bejan
5c61cacef7 Fixed sql escape 2023-07-20 11:45:27 +03:00
Timo Bejan
3b693ebe1a logs for create schema 2023-07-20 11:34:12 +03:00
Timo Bejan
000b62ccc7 logs for create schema 2023-07-20 11:33:07 +03:00
Timo Bejan
e339d133e0 Logs for tenant creation 2023-07-20 11:24:10 +03:00
Timo Bejan
4d1a6295d9 documine fix? 2023-07-20 10:30:16 +03:00
Ali Oezyetimoglu
c116687aa1 Merge branch 'RED-7056' into 'main'
RED-7056 - Fix typo in RED_ADMIN permissions

See merge request fforesight/tenant-user-management-service!4
2023-07-19 13:32:03 +02:00
Lena Maldacker
74e6c6bf91 Merge branch 'DM-351' into 'main'
DM-351: remove watermark permissions from documine roles

See merge request fforesight/tenant-user-management-service!5
2023-07-19 10:46:29 +02:00
Lena Maldacker
ecf9477ed8 DM-351: remove watermark permissions from documine roles 2023-07-19 10:25:01 +02:00
Andrei Isvoran
fb72382a66 RED-7056 - Fix typo in RED_ADMIN permissions 2023-07-18 10:53:00 +03:00
Timo Bejan
7fc67b2c7e storage backend both 2023-07-18 09:56:04 +03:00
Timo Bejan
016980ee07 Merge branch 'RED-6593' into 'main'
RED-6593 - Fix azure test connection + move create schema logic

See merge request fforesight/tenant-user-management-service!3
2023-07-14 10:56:12 +02:00
Andrei Isvoran
81ad77fa59 RED-6593 - Fix azure test connection + move create schema logic 2023-07-14 10:39:57 +03:00
Timo Bejan
858be8c050 RED-7108 - migrate only should kill the app 2023-07-14 09:13:05 +03:00
Timo Bejan
384d0a1cee Merge branch 'RED-6593' into 'main'
Red-6593 Add connectivity test when creating/updating a tenant for azure/s3

See merge request fforesight/tenant-user-management-service!2
2023-07-13 18:40:06 +02:00
Andrei Isvoran
2b250e336d Red-6593 Add connectivity test when creating/updating a tenant for azure/s3 2023-07-13 18:40:06 +02:00
Timo Bejan
e896b5c808 dev profile cleanup 2023-07-13 18:52:05 +03:00
Timo Bejan
5f3418ad3b Merge branch 'RED-7006' into 'main'
Red 7006 - add endpoint to update tenant

See merge request fforesight/tenant-user-management-service!1
2023-07-13 11:16:10 +02:00
Andrei Isvoran
7266235983 Red 7006 - add endpoint to update tenant 2023-07-13 11:16:10 +02:00
Timo Bejan
04d56f3b47 file permissions 2023-07-13 09:36:06 +03:00
Timo Bejan
aa619e9012 added taas profile 2023-07-12 22:49:16 +03:00
Timo Bejan
58d53f2647 Tests 2023-07-11 15:18:41 +03:00
Timo Bejan
bad1226376 Tests 2023-07-10 15:49:45 +03:00
Timo Bejan
8a2f8c0cb1 Documine fix 2023-07-04 09:25:05 +03:00
Timo Bejan
5a21604036 Documine fix 2023-07-04 09:03:20 +03:00
Timo Bejan
fc701ffb04 Springdoc fixes 2023-06-29 09:21:49 +03:00
Timo Bejan
7e28e5574e Tenant Creation exception rework 2023-06-28 11:39:18 +03:00
Timo Bejan
1ede240122 RED-7008 2023-06-28 00:57:00 +03:00
Timo Bejan
fd2add72bc RED-6686 tenant commons update 2023-06-27 23:45:29 +03:00
Timo Bejan
7df651880f RED-6686 - token based web request interceptor 2023-06-27 23:07:24 +03:00
Timo Bejan
63742620a8 RED-6686 - cleanup for swagger 2023-06-27 20:56:30 +03:00
Timo Bejan
44ee097d83 RED-6686 - adaptations 2023-06-26 23:12:11 +03:00
Timo Bejan
0d151108df RED-6686 - fixed tenant name 2023-06-26 16:26:53 +03:00
Timo Bejan
5702e8abab RED-6686 - order of operations 2023-06-22 14:31:38 +03:00
Timo Bejan
e84ee1ae4f RED-6686 - fixes 2023-06-22 14:17:49 +03:00
Timo Bejan
a1a5741714 ggradle cleanup 2023-06-21 15:45:08 +03:00
Timo Bejan
e58012e4ea RED-6686 tenant details 2023-06-21 15:38:39 +03:00
Christoph Schabert
53b668f469 Update file build.gradle.kts 2023-06-21 13:56:56 +02:00
Christoph Schabert
ab6a9e8226 Update 3 files
- /gradle.properties
- /gradle.properties.kts
- /build.gradle.kts
2023-06-21 13:48:51 +02:00
Timo Bejan
4a44114cb4 RED-6686 cleanup 2023-06-21 14:30:37 +03:00
Timo Bejan
ffb0b282e7 RED-6686 path fix 2023-06-21 12:59:22 +03:00
Timo Bejan
aaae453cc6 RED-6686 health redis 2023-06-16 12:27:54 +03:00
Timo Bejan
53c5141ea6 RED-6686 health 2023-06-16 12:17:48 +03:00
Timo Bejan
0808131424 RED-6686 dev config 2023-06-16 11:25:52 +03:00
Timo Bejan
7f178f6b45 RED-6686 - fixed PMD 2023-06-14 16:42:55 +03:00
Timo Bejan
47ca03db93 RED-6686 - ignore gradle 2023-06-14 12:35:45 +03:00
Timo Bejan
073f4cee2d RED-6686 - conver tto gradle 2023-06-14 12:35:22 +03:00
178 changed files with 8592 additions and 2156 deletions

40
.dev/migration/README.md Normal file
View File

@ -0,0 +1,40 @@
### Hot to run locally and test
1. Start docker compose from this dir
2. Start persistence-service with profile: dev
* this will create the tenant sync exchange
* startup might fail due to tenant-usermanagement not being up. but you need the tenant sync queue first)
3. Start tenant-user-management with profiles: dev, redaction, migration
* this will migrate the master DB and sync KC
* it will also send a message to persistence service to run the migration
* in case the message doesn't arrive - restart persistence and/or this service - the tenant migration in this service is idempotent
4. Persistence service should now queue 1869 files to layout-parser
5. Start layout-parser with profile: dev
6. Start redaction-service with profile: dev
7. Debug issues & repeat
8. UI is available at htpp://localhost:4200/ui -> login with manageradmin/OsloImWinter!23
9. To start clean. docker compose down and up again.
Useful info:
* This docker compose contains a dump from staging from ~2 weeks ago
* KC credentials are: admin / secretPasswordForAdmin1234
* To use different data you need a tenant-manager database dump from an environment of your choice
* this can be obtained via kubectl port forward like so: `kubectl -n <namespace> port-forward services/main-db-postgresql 5432`
* followed by a dump: `pg_dumpall -U tenantmanager -h 127.0.0.1 > main.sql`
* And a tenant database:
* this can be obtained via kubectl port forward like so: `kubectl -n <namespace> port-forward services/tenant-db-postgresql 5432`
* followed by a dump: `pg_dumpall -U tenantmanager -h 127.0.0.1 > tenant.sql`
* And a KC database:
* this can be obtained via kubectl port forward like so: `kubectl -n <namespace> port-forward services/keycloak-postgresql 5432`
* followed by a dump: `pg_dumpall -U tenantmanager -h 127.0.0.1 > tenant.sql`
* You can now use the migration/utils/database Dockerfile to create a fat DB image with the data ( replace data.sql with your dump and tag accordingly)
* For a minio dump you need a similar command ( port fwd sometimes crashes sine clone command uses a lot of connections ):
* open port forward `while true; do kubectl -n redaction-staging port-forward statefulset/redaction-staging-minio 9000 --request-timeout=10m; done`
* Install mc minio command line utility
* create an alias `mc alias set <mirror_name> http://localhost:9000 minioadminuser minioadminpassword`
* run: `while true; do mc mirror <mirror_name>/<bucket_name> ./<bucket_name>; done`
* use migration/utils/minio/Dockerfile to create an image -> you need to copy it to where the export is.
* After you have the images, replace the images in compose with the ones you want

View File

@ -0,0 +1,86 @@
version: '3.9'
services:
tenant-database:
pull_policy: always
image: nexus.knecon.com:5001/migration/redtenant-db-staging-multi-arch
ports:
- 15432:5432
environment:
- POSTGRES_PASSWORD=r3dact3d
- POSTGRES_USER=tenant
- POSTGRES_DB=red-tenant
main-database:
image: nexus.knecon.com:5001/migration/tenantmanager-db-staging-multi-arch
pull_policy: always
ports:
- 25432:5432
environment:
- POSTGRES_PASSWORD=r3dact3d
- POSTGRES_USER=tenantmanager
- POSTGRES_DB=tenantmanager
keycloak-database:
image: nexus.knecon.com:5001/migration/keycloak-db-multi-arch
pull_policy: always
ports:
- 35432:5432
environment:
- POSTGRES_PASSWORD=some-password
- POSTGRES_USER=bn_keycloak
- POSTGRES_DB=bitnami_keycloak
keycloak:
command: ['start']
volumes:
- /tmp/export:/opt/export
depends_on:
- "keycloak-database"
image: quay.io/keycloak/keycloak:20.0.1
environment:
JAVA_OPTS_APPEND: -Dkeycloak.profile.feature.upload_scripts=enabled
KC_HOSTNAME: localhost
KC_HTTP_ENABLED: true
KC_HOSTNAME_STRICT_HTTPS: false
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://keycloak-database:5432/bitnami_keycloak
KC_DB_USERNAME: bn_keycloak
KC_DB_PASSWORD: some-password
ports:
- "8080:8080"
redis:
image: redis
ports:
- "6379:6379"
rabbitmq:
image: 'rabbitmq:3.9-alpine'
environment:
- RABBITMQ_DEFAULT_USER=user
- RABBITMQ_DEFAULT_PASS=rabbitmq
ports:
- 5672:5672
- 15672:15672
minio:
pull_policy: always
image: nexus.knecon.com:5001/migration/minio-staging-multi-arch
ports:
- "9001:9001"
- "9000:9000"
adminer:
image: adminer:latest
ports:
- "58080:8080"
ui:
pull_policy: always
image: nexus.knecon.com:5001/migration/test-ui-csp
environment:
API_URL: http://localhost:4200
APP_NAME: Local
FRONTEND_APP_VERSION: 42
OAUTH_URL: http://localhost:8080
OAUTH_CLIENT_ID: redaction
BASE_TRANSLATIONS_DIRECTORY: /assets/i18n/redact/
THEME: redact
ports:
- "4200:8080"
# pg_dump bitnami_keycloak -U bn_keycloak -h 127.0.0.1 -p 35432 > data.sql
# pg_dump tenantmanager -U tenantmanager -h 127.0.0.1 -p 25432 > data.sql
# pg_dump red-tenant -U tenant -h 127.0.0.1 -p 15432 > data.sql

View File

@ -0,0 +1,2 @@
FROM postgres:16.0
COPY data.sql /docker-entrypoint-initdb.d/

View File

@ -0,0 +1,16 @@
FROM docker.io/minio/minio:latest
COPY ./redaction /tmp/redaction
COPY --from=docker.io/minio/mc:latest /usr/bin/mc /usr/bin/mc
RUN mkdir /buckets
RUN minio server /buckets & \
server_pid=$!; \
until mc alias set local http://localhost:9000 minioadmin minioadmin; do \
sleep 1; \
done; \
mc mb local/redaction; \
mc mirror /tmp/redaction local/redaction; \
kill $server_pid
RUN rm -Rf /tmp/redaction
CMD ["minio", "server", "/buckets", "--address", ":9000", "--console-address", ":9001"]

View File

@ -0,0 +1,8 @@
# Setup
start docker-compose
login to http://localhost:8080 with admin/admin
go to "clients", select "import client" and drag-and-drop manager.json from this folder.
Follow import wizard steps.
Once done. select the manager client, go to tab "SERVICE ACCOUNT ROLES"
Click "assign roles" and assign all roles from "filter by realm roles".

View File

@ -2,10 +2,11 @@ version: '2'
services:
keycloak:
image: quay.io/keycloak/keycloak:20.0
image: quay.io/keycloak/keycloak:latest
command: start-dev
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_IMPORT: master.json
KEYCLOAK_ADMIN_PASSWORD: admin
ports:
- 8080:8080
@ -23,7 +24,7 @@ services:
POSTGRES_PASSWORD: fforesight
POSTGRES_DB: master
rabbitmq:
image: 'rabbitmq:3.9-alpine'
image: 'rabbitmq:3.9-management-alpine'
mem_limit: 500m
environment:
- RABBITMQ_DEFAULT_USER=user
@ -31,6 +32,8 @@ services:
ports:
- 5672:5672
- 15672:15672
- 5671:5671
- 4369:4369
minio:
mem_limit: 1000m
image: minio/minio

View File

@ -0,0 +1,99 @@
{
"clientId": "manager",
"name": "manager",
"description": "manager",
"rootUrl": "",
"adminUrl": "",
"baseUrl": "",
"surrogateAuthRequired": false,
"enabled": true,
"alwaysDisplayInConsole": false,
"clientAuthenticatorType": "client-secret",
"secret": "oE2DVrV45w0Tr5jBBcoufVxIkFWU69lP",
"redirectUris": [
"/*"
],
"webOrigins": [
"/*"
],
"notBefore": 0,
"bearerOnly": false,
"consentRequired": false,
"standardFlowEnabled": true,
"implicitFlowEnabled": true,
"directAccessGrantsEnabled": true,
"serviceAccountsEnabled": true,
"authorizationServicesEnabled": true,
"publicClient": false,
"frontchannelLogout": true,
"protocol": "openid-connect",
"attributes": {
"oidc.ciba.grant.enabled": "true",
"oauth2.device.authorization.grant.enabled": "true",
"client.secret.creation.time": "1690966874",
"backchannel.logout.session.required": "true",
"backchannel.logout.revoke.offline.tokens": "false"
},
"authenticationFlowBindingOverrides": {},
"fullScopeAllowed": true,
"nodeReRegistrationTimeout": -1,
"protocolMappers": [
{
"name": "Client IP Address",
"protocol": "openid-connect",
"protocolMapper": "oidc-usersessionmodel-note-mapper",
"consentRequired": false,
"config": {
"user.session.note": "clientAddress",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "clientAddress",
"jsonType.label": "String"
}
},
{
"name": "Client Host",
"protocol": "openid-connect",
"protocolMapper": "oidc-usersessionmodel-note-mapper",
"consentRequired": false,
"config": {
"user.session.note": "clientHost",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "clientHost",
"jsonType.label": "String"
}
},
{
"name": "Client ID",
"protocol": "openid-connect",
"protocolMapper": "oidc-usersessionmodel-note-mapper",
"consentRequired": false,
"config": {
"user.session.note": "client_id",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "client_id",
"jsonType.label": "String"
}
}
],
"defaultClientScopes": [
"web-origins",
"acr",
"profile",
"roles",
"email"
],
"optionalClientScopes": [
"address",
"phone",
"offline_access",
"microprofile-jwt"
],
"access": {
"view": true,
"configure": true,
"manage": true
}
}

6
.gitignore vendored
View File

@ -31,3 +31,9 @@ build/
### VS Code ###
.vscode/
.gradle/
gradle.properties
gradlew
gradlew.bat
.DS_Store

View File

@ -1,2 +0,0 @@
#Wed Jun 14 12:04:30 EEST 2023
gradle.version=8.1.1

Binary file not shown.

Binary file not shown.

View File

@ -1,18 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar

21
README.md Normal file
View File

@ -0,0 +1,21 @@
# Tenant User Management Micro-Service: tenant-user-management
## Introduction
The tenant-user-management micro-service profiles with storing and providing everything about the tenant environment. It makes use of tools like Spring Boot 3, Keycloak, Microsoft Azure and AWS S3 to keep your data consistent and secure according to the latest standards.
### Key features in the Tenant User Management Service
* **Tenant Management:**
This service gives you the option to do CRUD operations to manage your tenants.
You can also select between different storage platforms to store your tenants.
Some general configurations complements the settings options.
* **User Administration:**
Another feature is the handling of your users. You can manage the profiles, activate/deactivate them and give roles as you require.
If you wish you can administrate the users for each tenant separately.
Or just give a user the needed preferences.
* **SMTP configuration:**
Further you can adjust your SMTP settings and set properties as you aimed best for your company.

View File

@ -2,11 +2,18 @@ import org.springframework.boot.gradle.tasks.bundling.BootBuildImage
plugins {
java
id("org.springframework.boot") version "3.0.6"
id("org.springframework.boot") version "3.1.5"
id("io.spring.dependency-management") version "1.1.0"
id("org.sonarqube") version "4.0.0.2929"
// pmd
id("io.freefair.lombok") version "8.4"
pmd
`maven-publish`
checkstyle
jacoco
}
pmd {
isConsoleOutput = true
}
configurations {
@ -14,14 +21,30 @@ configurations {
extendsFrom(configurations.annotationProcessor.get())
}
}
tasks.pmdMain{
pmd.ruleSetFiles = files("${projectDir}/config/pmd/pmd.xml")
}
//tasks.pmdMain{
// pmd.ruleSetFiles = files("${projectDir}/config/pmd/pmd.xml")
//}
//
//tasks.pmdTest {
// pmd.ruleSetFiles = files("${projectDir}/config/pmd/test_pmd.xml")
//}
tasks.pmdTest {
pmd.ruleSetFiles = files("${projectDir}/config/pmd/test_pmd.xml")
}
publishing {
publications {
create<MavenPublication>(name) {
from(components["java"])
}
}
repositories {
maven {
url = uri("https://nexus.knecon.com/repository/red-platform-releases/")
credentials {
username = providers.gradleProperty("mavenUser").getOrNull()
password = providers.gradleProperty("mavenPassword").getOrNull()
}
}
}
}
@ -29,16 +52,20 @@ repositories {
mavenLocal()
mavenCentral()
maven {
url = uri("https://nexus.knecon.com/repository/gindev/");
url = uri("https://nexus.knecon.com/repository/gindev/")
credentials {
username = providers.gradleProperty("mavenUser").getOrNull();
password = providers.gradleProperty("mavenPassword").getOrNull();
username = providers.gradleProperty("mavenUser").getOrNull()
password = providers.gradleProperty("mavenPassword").getOrNull()
}
}
}
tasks.named<BootBuildImage>("bootBuildImage") {
environment.put("BPE_DELIM_JAVA_TOOL_OPTIONS", " ")
environment.put("BPE_APPEND_JAVA_TOOL_OPTIONS", "-Dfile.encoding=UTF-8")
imageName.set("nexus.knecon.com:5001/ff/${project.name}:${project.version}")
if (project.hasProperty("buildbootDockerHostNetwork")) {
network.set("host")
@ -59,13 +86,30 @@ tasks.named<BootBuildImage>("bootBuildImage") {
}
configurations {
all {
exclude(group = "commons-logging", module = "commons-logging")
exclude(group = "org.springframework.boot", module = "spring-boot-starter-log4j2")
exclude(group = "com.iqser.red.commons", module = "logging-commons")
}
}
val persistenceServiceVersion = "2.589.1-RED10196.2"
dependencies {
implementation("com.knecon.fforesight:keycloak-commons:0.9.0")
implementation("com.knecon.fforesight:swagger-commons:0.5.0")
implementation("com.iqser.red.service:persistence-service-internal-api-v1:${persistenceServiceVersion}")
implementation("com.knecon.fforesight:database-tenant-commons:0.28.0-RED10196.0")
implementation("com.knecon.fforesight:keycloak-commons:0.28.0")
implementation("com.knecon.fforesight:swagger-commons:0.7.0")
implementation("com.knecon.fforesight:tracing-commons:0.5.0")
implementation("com.knecon.fforesight:lifecycle-commons:0.6.0")
implementation("net.logstash.logback:logstash-logback-encoder:7.4")
implementation("ch.qos.logback:logback-classic")
implementation("org.postgresql:postgresql:42.5.4")
implementation("com.google.guava:guava:31.1-jre")
implementation("org.liquibase:liquibase-core:4.17.2")
implementation("org.keycloak:keycloak-admin-client:21.0.1")
implementation("com.google.guava:guava:33.0.0-jre")
implementation("org.liquibase:liquibase-core:4.20.0")
implementation("org.keycloak:keycloak-admin-client:23.0.6")
implementation("org.springframework.boot:spring-boot-starter-amqp")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.retry:spring-retry")
@ -74,28 +118,30 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
implementation("org.apache.commons:commons-lang3:3.12.0")
implementation("commons-validator:commons-validator:1.7")
implementation("commons-validator:commons-validator:1.8.0")
implementation("org.springframework.boot:spring-boot-configuration-processor")
implementation("com.iqser.red.commons:storage-commons:2.45.0")
implementation("jakarta.mail:jakarta.mail-api:2.1.2")
implementation("org.eclipse.angus:angus-mail:2.0.2")
testImplementation("org.springframework.boot:spring-boot-starter-test")
compileOnly("org.projectlombok:lombok")
testImplementation("org.springframework.cloud:spring-cloud-starter-openfeign")
developmentOnly("org.springframework.boot:spring-boot-devtools")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.amqp:spring-rabbit-test")
testImplementation("org.testcontainers:elasticsearch:1.17.6")
testAnnotationProcessor("org.projectlombok:lombok")
testImplementation("org.testcontainers:postgresql:1.19.7")
testImplementation("org.testcontainers:testcontainers:1.19.7")
testImplementation("org.testcontainers:junit-jupiter:1.19.7")
testImplementation("com.github.dasniko:testcontainers-keycloak:3.2.0")
}
extra["springCloudVersion"] = "2022.0.2"
extra["testcontainersVersion"] = "1.17.6"
extra["springCloudVersion"] = "2022.0.4"
extra["testcontainersVersion"] = "1.19.7"
group = "com.knecon.fforesight"
version = "1.0-SNAPSHOT"
description = "tenant-user-management-service"
java.sourceCompatibility = JavaVersion.VERSION_17
@ -119,3 +165,14 @@ sonarqube {
property("sonar.host.url", "https://sonarqube.knecon.com")
}
}
tasks.test {
finalizedBy(tasks.jacocoTestReport) // report is always generated after tests run
}
tasks.jacocoTestReport {
dependsOn(tasks.test) // tests are required to run before generating the report
reports {
xml.required.set(true)
csv.required.set(false)
}
}

View File

@ -1,14 +1,19 @@
<?xml version="1.0"?>
<ruleset name="Custom Rules"
<ruleset name="Custom ruleset"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
<description>Knecon main pmd rules</description>
<description>
Knecon ruleset checks the code for bad stuff
</description>
<rule ref="category/java/errorprone.xml">
<exclude name="DataflowAnomalyAnalysis"/>
<exclude name="MissingSerialVersionUID"/>
<exclude name="AvoidLiteralsInIfCondition"/>
<exclude name="AvoidDuplicateLiterals"/>
<exclude name="NullAssignment"/>
<exclude name="AssignmentInOperand"/>
</rule>
</ruleset>

View File

@ -1,14 +1,22 @@
<?xml version="1.0"?>
<ruleset name="Custom Rules"
<ruleset name="Custom ruleset"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
<description>
Knecon test ruleset checks the code for bad stuff
</description>
<description>Knecon test pmd rules</description>
<rule ref="category/java/errorprone.xml">
<exclude name="DataflowAnomalyAnalysis"/>
<exclude name="MissingSerialVersionUID"/>
<exclude name="AvoidLiteralsInIfCondition"/>
<exclude name="AvoidDuplicateLiterals"/>
<exclude name="NullAssignment"/>
<exclude name="AssignmentInOperand"/>
<exclude name="TestClassWithoutTestCases"/>
<exclude name="BeanMembersShouldSerialize"/>
</rule>
</ruleset>

1
gradle.properties.kts Normal file
View File

@ -0,0 +1 @@
version = 1.0-SNAPSHOT

Binary file not shown.

View File

@ -1,6 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

245
gradlew vendored
View File

@ -1,245 +0,0 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

92
gradlew.bat vendored
View File

@ -1,92 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

316
mvnw vendored
View File

@ -1,316 +0,0 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /usr/local/etc/mavenrc ] ; then
. /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`\\unset -f command; \\command -v java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
fi
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
fi
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=`cygpath --path --windows "$javaClass"`
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

188
mvnw.cmd vendored
View File

@ -1,188 +0,0 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM https://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%"=="on" pause
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
cmd /C exit /B %ERROR_CODE%

191
pom.xml
View File

@ -1,191 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knecon.fforesight</groupId>
<artifactId>tenant-user-management-service</artifactId>
<version>1.0-SNAPSHOT</version>
<name>tenant-user-management-service</name>
<description>tenant-user-management-service</description>
<properties>
<java.version>17</java.version>
<keycloak.version>21.0.1</keycloak.version>
<commons-validator.version>1.7</commons-validator.version>
<guava.version>31.1-jre</guava.version>
<swagger-commons.version>0.5.0</swagger-commons.version>
<keycloak-commons.version>0.9.0</keycloak-commons.version>
</properties>
<dependencies>
<dependency>
<groupId>com.knecon.fforesight</groupId>
<artifactId>keycloak-commons</artifactId>
<version>${keycloak-commons.version}</version>
</dependency>
<dependency>
<groupId>com.knecon.fforesight</groupId>
<artifactId>swagger-commons</artifactId>
<version>${swagger-commons.version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>${liquibase.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>${keycloak.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>${commons-validator.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.9.0.2155</version>
</plugin>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>6.3.1</version>
<configuration>
<format>ALL</format>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<goals>
<goal>report-aggregate</goal>
</goals>
<phase>verify</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

8
publish-custom-image.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
dir=${PWD##*/}
gradle assemble
buildNumber=${1:-1}
gradle bootBuildImage --cleanCache --publishImage -PbuildbootDockerHostNetwork=true -Pversion=$USER-$buildNumber
echo "nexus.knecon.com:5001/ff/${dir}:$USER-$buildNumber"

View File

@ -1,163 +0,0 @@
package com.knecon.fforesight.tenantusermanagement;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.StatementCallback;
import org.springframework.jdbc.datasource.SingleConnectionDataSource;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantcommons.model.DatabaseConnection;
import com.knecon.fforesight.tenantcommons.model.S3StorageConnection;
import com.knecon.fforesight.tenantcommons.model.SearchConnection;
import com.knecon.fforesight.tenantusermanagement.model.TenantRequest;
import com.knecon.fforesight.tenantusermanagement.properties.TenantUserManagementProperties;
import com.knecon.fforesight.tenantusermanagement.service.TenantManagementService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Profile("dev")
@Configuration
public class DevConfiguration {
@Autowired
private TenantManagementService tenantManagementService;
@Autowired
private DataSource dataSource;
@Value("${spring.datasource.url:}")
private String masterJDBCURL;
@Autowired
private TenantUserManagementProperties tenantUserManagementProperties;
@PostConstruct
@SneakyThrows
public void createDefaultTenant() {
log.info("Creating Redaction Tenant");
// tenantRepository.deleteAll();
if (tenantManagementService.getTenants().isEmpty()) {
var jdbcUrl = masterJDBCURL.substring(0, masterJDBCURL.lastIndexOf('/') + 1) + "redaction?currentSchema=redaction";
createDatabase("redaction", "redaction");
createSchema(jdbcUrl, "redaction", "redaction");
var tenantRequest = TenantRequest.builder()
.tenantId("redaction")
.displayName("Redaction default")
.guid(UUID.randomUUID().toString())
.databaseConnection(DatabaseConnection.builder()
.driver("postgresql")
.host("localhost")
.port("5432")
.database("redaction")
.schema("redaction")
.username("redaction")
.password("redaction")
.build())
.searchConnection(SearchConnection.builder()
.hosts(Set.of("localhost"))
.port(9200)
.scheme("http")
.numberOfShards("1")
.numberOfReplicas("5")
.build())
.s3StorageConnection(S3StorageConnection.builder().key("minioadmin").secret("minioadmin").bucketName("redaction").endpoint("http://localhost:9000").build())
.build();
tenantManagementService.createTenant(tenantRequest);
}
TenantContext.setTenantId("redaction");
}
private void createDatabase(String db, String password) {
var jdbcTemplate = new JdbcTemplate(dataSource);
try {
jdbcTemplate.execute((StatementCallback<Boolean>) stmt -> stmt.execute("CREATE DATABASE " + db));
} catch (Exception e) {
log.warn("DB already exists");
}
try {
jdbcTemplate.execute((StatementCallback<Boolean>) stmt -> stmt.execute("CREATE USER " + db + " WITH ENCRYPTED PASSWORD '" + password + "'"));
} catch (Exception e) {
log.warn("user already exists");
}
try {
jdbcTemplate.execute((StatementCallback<Boolean>) stmt -> stmt.execute("GRANT ALL PRIVILEGES ON DATABASE " + db + " TO " + db));
} catch (Exception e) {
log.warn("grant invalid");
}
}
@SneakyThrows
public void createSchema(String jdbcUrl, String username, String password) {
try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) {
DataSource tenantDataSource = new SingleConnectionDataSource(connection, false);
JdbcTemplate insert = new JdbcTemplate(tenantDataSource);
try {
insert.execute((StatementCallback<Boolean>) stmt -> stmt.execute("CREATE SCHEMA redaction"));
} catch (Exception e) {
log.warn("schema already exists");
}
try {
insert.execute((StatementCallback<Boolean>) stmt -> stmt.execute("GRANT USAGE ON SCHEMA redaction TO " + username));
} catch (Exception e) {
log.warn("grant invalid");
}
}
}
public byte[] pack(String sourceDirPath) throws IOException {
var bos = new ByteArrayOutputStream();
var p = Paths.get(sourceDirPath);
try (ZipOutputStream zs = new ZipOutputStream(bos)) {
Stream<Path> paths = Files.walk(p);
{
paths.filter(path -> !Files.isDirectory(path)).forEach(path -> {
ZipEntry zipEntry = new ZipEntry(p.relativize(path).toString());
try {
zs.putNextEntry(zipEntry);
Files.copy(path, zs);
zs.closeEntry();
} catch (IOException e) {
System.err.println(e);
}
});
}
}
return bos.toByteArray();
}
}

View File

@ -4,18 +4,26 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import com.knecon.fforesight.keycloakcommons.DefaultKeyCloakCommonsAutoConfiguration;
import com.knecon.fforesight.lifecyclecommons.LifecycleAutoconfiguration;
import com.knecon.fforesight.swaggercommons.SpringDocAutoConfiguration;
import com.knecon.fforesight.tenantcommons.MultiTenancyAutoConfiguration;
import com.knecon.fforesight.tenantusermanagement.client.LicenseClient;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@ImportAutoConfiguration({ MultiTenancyAutoConfiguration.class, LiquibaseAutoConfiguration.class, DefaultKeyCloakCommonsAutoConfiguration.class, SpringDocAutoConfiguration.class})
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, CassandraAutoConfiguration.class,})
@ImportAutoConfiguration({MultiTenancyAutoConfiguration.class, LiquibaseAutoConfiguration.class, DefaultKeyCloakCommonsAutoConfiguration.class, SpringDocAutoConfiguration.class, LifecycleAutoconfiguration.class})
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, CassandraAutoConfiguration.class, MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
@EnableAspectJAutoProxy
@EnableFeignClients(basePackageClasses = LicenseClient.class)
public class TenantUserManagementServiceApplication {
/**

View File

@ -1,60 +0,0 @@
package com.knecon.fforesight.tenantusermanagement.api;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.knecon.fforesight.tenantcommons.model.TenantResponse;
import com.knecon.fforesight.tenantusermanagement.model.DeploymentKeyResponse;
import com.knecon.fforesight.tenantusermanagement.model.SimpleTenantResponse;
import com.knecon.fforesight.tenantusermanagement.model.TenantRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ResponseStatus(value = HttpStatus.OK)
@RequestMapping("${fforesight.tenant-user-management.base-path:}")
public interface TenantsResource {
String TENANT_ID_PARAM = "tenantId";
String TENANT_ID_PATH_PARAM = "/{" + TENANT_ID_PARAM + "}";
@PostMapping(value = "/tenants", consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Creates a new Tenant", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
void createTenant(@RequestBody TenantRequest tenant);
@GetMapping(value = "/tenants", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets all existing tenant", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
List<TenantResponse> getTenants();
@GetMapping(value = "/tenants/{tenantId}", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets all existing tenant", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
TenantResponse getTenant(@PathVariable("tenantId") String tenantId);
@GetMapping(value = "/tenants/simple", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets all existing tenant in a simplified format", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
List<SimpleTenantResponse> getSimpleTenants();
@GetMapping(value = "/deploymentKey" + TENANT_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Returns the deployment key for a tenant", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
DeploymentKeyResponse getDeploymentKey(@PathVariable(TENANT_ID_PARAM) String tenantId);
}

View File

@ -1,11 +1,10 @@
package com.knecon.fforesight.tenantusermanagement.api;
package com.knecon.fforesight.tenantusermanagement.api.external;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
@ -15,8 +14,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@RequestMapping("${fforesight.tenant-user-management.base-path:}")
public interface GeneralSettingsResource {
String API_PATH = "/configuration/general";
@ -25,15 +22,15 @@ public interface GeneralSettingsResource {
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@GetMapping(value = API_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Returns the current general Configuration.")
@Operation(summary = "Returns the current general configuration")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK.")})
GeneralConfigurationModel getGeneralConfigurations();
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = API_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Write General Configurations to KeyCloak")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "General Configuration updated successful."), @ApiResponse(responseCode = "400", description = "General Configuration update failed.")})
@Operation(summary = "Write general configuration to KeyCloak")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "General configuration updated successful."), @ApiResponse(responseCode = "400", description = "General configuration update failed.")})
void updateGeneralConfigurations(@RequestBody GeneralConfigurationModel generalConfigurationModel);
}

View File

@ -0,0 +1,86 @@
package com.knecon.fforesight.tenantusermanagement.api.external;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.knecon.fforesight.tenantusermanagement.model.IdentityProviderList;
import com.knecon.fforesight.tenantusermanagement.model.IdentityProviderModel;
import com.knecon.fforesight.tenantusermanagement.model.IdentityProviderRequest;
import com.knecon.fforesight.tenantusermanagement.model.IdentityProviderWithDescriptorRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import org.springframework.web.bind.annotation.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
@Tag(name = "Identity provider configuration endpoints", description = "Provides operations related to the identity providers")
@ApiResponses(value = {@ApiResponse(responseCode = "429", description = "Too many requests."), @ApiResponse(responseCode = "403", description = "Forbidden")})
public interface IdentityProviderConfigurationResource {
String API_PATH = "/configuration/identity-providers";
String IDENTITY_PROVIDER_ALIAS_PARAM = "providerAlias";
String IDENTITY_PROVIDER_ALIAS_PATH_PARAM = "/{" + IDENTITY_PROVIDER_ALIAS_PARAM + "}";
String IMPORT_SUB_PATH = "/import";
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
@GetMapping(value = API_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets all existing identity providers.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
IdentityProviderList getIdentityProviders();
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
@GetMapping(value = API_PATH + IDENTITY_PROVIDER_ALIAS_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets an existing identity provider.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found")})
IdentityProviderModel getIdentityProvider(@Parameter(name = IDENTITY_PROVIDER_ALIAS_PARAM, description = "The alias of the identity provider to retrieve.", required = true) @PathVariable(IDENTITY_PROVIDER_ALIAS_PARAM) String identityProviderAlias);
@ResponseStatus(value = HttpStatus.CREATED)
@ResponseBody
@PostMapping(value = API_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Creates a new identity provider", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Successfully created the identity provider"), @ApiResponse(responseCode = "400", description = "Malformed request parameters or body"), @ApiResponse(responseCode = "409", description = "Duplicate")})
ResponseEntity<IdentityProviderModel> createIdentityProvider(@Valid @io.swagger.v3.oas.annotations.parameters.RequestBody @RequestBody IdentityProviderRequest identityProvider);
@ResponseStatus(value = HttpStatus.CREATED)
@ResponseBody
@PostMapping(value = API_PATH + IMPORT_SUB_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Creates a new identity provider", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Successfully created the identity provider"), @ApiResponse(responseCode = "400", description = "Malformed request parameters or body"), @ApiResponse(responseCode = "409", description = "Duplicate")})
ResponseEntity<IdentityProviderModel> createIdentityProviderFromDescriptor(@Valid @io.swagger.v3.oas.annotations.parameters.RequestBody @RequestBody IdentityProviderWithDescriptorRequest identityProvider);
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
@PutMapping(value = API_PATH + IDENTITY_PROVIDER_ALIAS_PATH_PARAM, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Updates an existing identity provider", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully updated the identity provider"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "400", description = "Malformed request parameters or body")})
ResponseEntity<IdentityProviderModel> updateIdentityProvider(@Parameter(name = IDENTITY_PROVIDER_ALIAS_PARAM, description = "The alias of the identity provider to retrieve.", required = true) @PathVariable(IDENTITY_PROVIDER_ALIAS_PARAM) String identityProviderAlias,
@Valid @io.swagger.v3.oas.annotations.parameters.RequestBody @RequestBody IdentityProviderRequest identityProvider);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@DeleteMapping(value = API_PATH + IDENTITY_PROVIDER_ALIAS_PATH_PARAM)
@Operation(summary = "Deletes an existing identity provider.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully deleted the identity provider."), @ApiResponse(responseCode = "404", description = "Not found")})
void deleteIdentityProvider(@Parameter(name = IDENTITY_PROVIDER_ALIAS_PARAM, description = "The alias of the identity provider to retrieve.", required = true) @PathVariable(IDENTITY_PROVIDER_ALIAS_PARAM) String identityProviderAlias);
}

View File

@ -0,0 +1,8 @@
package com.knecon.fforesight.tenantusermanagement.api.external;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("${fforesight.tenant-user-management.base-path:}")
public interface PublicResource {
}

View File

@ -1,4 +1,4 @@
package com.knecon.fforesight.tenantusermanagement.api;
package com.knecon.fforesight.tenantusermanagement.api.external;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@ -6,29 +6,27 @@ import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.knecon.fforesight.tenantusermanagement.model.SMTPConfiguration;
import com.knecon.fforesight.tenantusermanagement.model.SMTPResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@RequestMapping("${fforesight.tenant-user-management.base-path:}")
public interface SMTPConfigurationResource {
String SMTP_PATH = "/configuration/smtp";
String TEST_PATH = "/test";
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@GetMapping(value = SMTP_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Returns the current SMTP Configuration.")
@Operation(summary = "Returns the current SMTP configuration.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK."), @ApiResponse(responseCode = "404", description = "SMTP not configured.")})
SMTPConfiguration getCurrentSMTPConfiguration();
@ -36,21 +34,22 @@ public interface SMTPConfigurationResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = SMTP_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Write SMTP Settings to KeyCloak")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "SMTP Configuration updated successful."), @ApiResponse(responseCode = "400", description = "SMTP update failed.")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "SMTP configuration updated successful."), @ApiResponse(responseCode = "400", description = "SMTP update failed.")})
void updateSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfigurationModel);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = SMTP_PATH + TEST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(value = SMTP_PATH + TEST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Test SMTP Settings to KeyCloak")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "SMTP Configuration is valid."), @ApiResponse(responseCode = "400", description = "SMTP test failed.")})
void testSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfigurationModel);
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "SMTP configuration is valid."), @ApiResponse(responseCode = "400", description = "SMTP test failed.")})
SMTPResponse testSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfigurationModel);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@DeleteMapping(value = SMTP_PATH)
@Operation(summary = "Clear SMTP Settings to KeyCloak")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "SMTP Configuration has been cleared."), @ApiResponse(responseCode = "400", description = "Failed to clear SMTP Configuration.")})
@Operation(summary = "Deletes SMTP settings on KeyCloak")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "SMTP configuration has been deleted."), @ApiResponse(responseCode = "400", description = "Failed to delete SMTP configuration.")})
void clearSMTPConfiguration();
}

View File

@ -0,0 +1,85 @@
package com.knecon.fforesight.tenantusermanagement.api.external;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.fasterxml.jackson.databind.JsonNode;
import com.knecon.fforesight.tenantcommons.model.TenantResponse;
import com.knecon.fforesight.tenantcommons.model.UpdateDetailsRequest;
import com.knecon.fforesight.tenantusermanagement.model.DeploymentKeyResponse;
import com.knecon.fforesight.tenantusermanagement.model.CreateTenantRequest;
import com.knecon.fforesight.tenantusermanagement.model.UpdateTenantRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ResponseStatus(value = HttpStatus.OK)
public interface TenantsResource {
String TENANTS_PATH = "/tenants";
String TENANT_ID_PARAM = "tenantId";
String TENANT_ID_PATH_PARAM = "/{" + TENANT_ID_PARAM + "}";
String TENANTS_TENANT_ID_PATH = TENANTS_PATH + TENANT_ID_PATH_PARAM;
@PostMapping(value = TENANTS_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Create a new tenant", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
void createTenant(@RequestBody CreateTenantRequest tenant);
@ResponseBody
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@Operation(summary = "Deletes given tenant", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "403", description = "Forbidden access, you dont have rights to delete tenants"), @ApiResponse(responseCode = "405", description = "Operation is not allowed."), @ApiResponse(responseCode = "409", description = "Conflict while deleting tenant.")})
@DeleteMapping(value = TENANTS_TENANT_ID_PATH)
void deleteTenant(@PathVariable("tenantId") String tenantId);
@GetMapping(value = TENANTS_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets all existing tenants", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
List<TenantResponse> getTenants();
@GetMapping(value = TENANTS_TENANT_ID_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets an existing tenant", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
TenantResponse getTenant(@PathVariable("tenantId") String tenantId);
@PutMapping(value = TENANTS_TENANT_ID_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Update existing tenant", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
TenantResponse updateTenant(@PathVariable("tenantId") String tenantId, @RequestBody UpdateTenantRequest tenantRequest);
@PostMapping(value = TENANTS_TENANT_ID_PATH + "/details", consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Update details", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
void updateDetails(@PathVariable("tenantId") String tenantId, @RequestBody UpdateDetailsRequest request);
@GetMapping(value = "/deploymentKey" + TENANT_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Returns the deployment key for a tenant", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
DeploymentKeyResponse getDeploymentKey(@PathVariable(TENANT_ID_PARAM) String tenantId);
@PostMapping(value = TENANTS_TENANT_ID_PATH + "/sync", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Sync existing tenant", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
void syncTenant(@PathVariable("tenantId") String tenantId, @RequestBody JsonNode payload);
}

View File

@ -1,4 +1,4 @@
package com.knecon.fforesight.tenantusermanagement.api;
package com.knecon.fforesight.tenantusermanagement.api.external;
import java.util.List;
import java.util.Map;
@ -18,8 +18,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@RequestMapping("${fforesight.tenant-user-management.base-path:}")
public interface UserPreferenceResource {
String PREFERENCES_PATH = "/attributes";
@ -30,14 +28,14 @@ public interface UserPreferenceResource {
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@GetMapping(value = PREFERENCES_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Get User Attributes.", description = "None")
@Operation(summary = "Gets user attributes", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
Map<String, List<String>> getAllUserAttributes();
@ResponseStatus(HttpStatus.NO_CONTENT)
@PutMapping(value = PREFERENCES_PATH + KEY_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Store User Attribute by key.", description = "None")
@Operation(summary = "Store user attribute by key", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
void setAttribute(@PathVariable(KEY_PARAMETER_NAME) String key, @RequestBody List<String> values);
@ -45,7 +43,7 @@ public interface UserPreferenceResource {
@ResponseBody
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@DeleteMapping(value = PREFERENCES_PATH + KEY_PATH_VARIABLE)
@Operation(summary = "Delete User Preferences by key.", description = "None")
@Operation(summary = "Delete user preferences by key", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
void deleteAttribute(@PathVariable(KEY_PARAMETER_NAME) String key);

View File

@ -1,4 +1,4 @@
package com.knecon.fforesight.tenantusermanagement.api;
package com.knecon.fforesight.tenantusermanagement.api.external;
import java.util.List;
import java.util.Set;
@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
@ -26,8 +25,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ResponseStatus(value = HttpStatus.OK)
@RequestMapping("${fforesight.tenant-user-management.base-path:}")
public interface UserResource {
String USER_REST_PATH = "/user";
@ -47,33 +44,33 @@ public interface UserResource {
@ResponseBody
@Operation(summary = "Gets the users who contain application-specific roles.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Invalid " + "offset or limit specified.")})
@Operation(summary = "Gets the users holding application-specific roles", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Invalid offset or limit specified.")})
@GetMapping(value = RED_USER_REST_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
List<User> getApplicationSpecificUsers(@RequestParam(name = REFRESH_CACHE_PARAM, defaultValue = "false", required = false) boolean bypassCache);
@ResponseBody
@Operation(summary = "Gets all the users in realm with information of roles.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Invalid " + "offset or limit specified.")})
@Operation(summary = "Gets all users in realm including role info", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Invalid offset or limit specified.")})
@GetMapping(value = USER_REST_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
List<User> getAllUsers(@RequestParam(name = REFRESH_CACHE_PARAM, defaultValue = "false", required = false) boolean bypassCache);
@ResponseBody
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@Operation(summary = "Update your own user-profile.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "400", description = "Failed to update profile, e-mail cannot be empty")})
@PostMapping(value = UPDATE_USER_PROFILE_PATH + USER_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
void updateProfile(@PathVariable(USER_ID) String userId, @RequestBody UpdateProfileRequest updateProfileRequest);
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Update a user profile", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "400", description = "Failed to update profile, e-mail invalid"), @ApiResponse(responseCode = "404", description = "The userId cannot be found.")})
@PostMapping(value = UPDATE_USER_PROFILE_PATH + USER_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
User updateProfile(@PathVariable(USER_ID) String userId, @RequestBody UpdateProfileRequest updateProfileRequest);
@ResponseBody
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@Operation(summary = "Update your own user-profile.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "400", description = "Failed to update profile, e-mail cannot be empty")})
@PostMapping(value = UPDATE_MY_USER_PROFILE_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
void updateMyProfile(@RequestBody UpdateMyProfileRequest updateProfileRequest);
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Update your user profile", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "400", description = "Failed to update profile, e-mail invalid")})
@PostMapping(value = UPDATE_MY_USER_PROFILE_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
User updateMyProfile(@RequestBody UpdateMyProfileRequest updateProfileRequest);
@ResponseBody
@ -93,36 +90,36 @@ public interface UserResource {
@ResponseBody
@Operation(summary = "Create a new user.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Invalid Data."), @ApiResponse(responseCode = "409", description = "User already exists.")})
@Operation(summary = "Create a new user", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Invalid Data.")})
@PostMapping(value = USER_REST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
User createUser(@RequestBody CreateUserRequest user);
@ResponseBody
@Operation(summary = "Gets the user in realm with information of roles.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The " + "userId can not be found."), @ApiResponse(responseCode = "400", description = "The provided user id is empty or " + "null.")})
@Operation(summary = "Gets the user in realm including role info", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The userId cannot be found."), @ApiResponse(responseCode = "400", description = "The provided user id is empty or null.")})
@GetMapping(value = USER_REST_PATH + USER_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
User getUserById(@PathVariable(USER_ID) String userId);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@Operation(summary = "Add a role to users", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "No Content"), @ApiResponse(responseCode = "404", description = "The provided userId can not be found."), @ApiResponse(responseCode = "400", description = "One ore more roles are not valid.")})
@PostMapping(value = USER_ROLE_REST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
void setRoles(@PathVariable(USER_ID) String userId, @RequestBody Set<String> roles);
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Add roles to a user", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "No Content"), @ApiResponse(responseCode = "404", description = "The provided userId cannot be found."), @ApiResponse(responseCode = "400", description = "One ore more roles are not valid.")})
@PostMapping(value = USER_ROLE_REST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
User setRoles(@PathVariable(USER_ID) String userId, @RequestBody Set<String> roles);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@Operation(summary = "Reset a user's password", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "No Content"), @ApiResponse(responseCode = "404", description = "The provided userId can not be found.")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "No Content"), @ApiResponse(responseCode = "404", description = "The provided userId cannot be found.")})
@PostMapping(value = RESET_PASSWORD_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
void resetPassword(@PathVariable(USER_ID) String userId, @RequestBody ResetPasswordRequest resetPasswordRequest);
@ResponseBody
@Operation(summary = "Activate/ deactivate a user-profile.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Failed to activate/deactivate profile")})
@Operation(summary = "Activate/deactivate a user profile", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Failed to activate/deactivate profile"), @ApiResponse(responseCode = "403", description = "Cannot activate/deactivate users with higher rank roles"), @ApiResponse(responseCode = "404", description = "The userId cannot be found.")})
@PostMapping(value = ACTIVATE_USER_PROFILE_PATH + USER_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
User activateProfile(@PathVariable(USER_ID) String userId, @RequestParam(IS_ACTIVE_PARAM) boolean isActive);

View File

@ -0,0 +1,8 @@
package com.knecon.fforesight.tenantusermanagement.api.external.v2;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("${fforesight.tenant-user-management.base-path-v2:/api}")
public interface PublicResourceV2 {
}

View File

@ -0,0 +1,38 @@
package com.knecon.fforesight.tenantusermanagement.api.external.v2;
import java.util.List;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.knecon.fforesight.tenantusermanagement.api.external.v2.model.User;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
public interface UserResourceV2 {
String USER_REST_PATH = "/users";
String USER_ID = "userId";
String USER_ID_PATH_VARIABLE = "/{" + USER_ID + "}";
String USERNAME_PARAM = "username";
@ResponseBody
@Operation(summary = "Get a list of users", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Invalid offset or limit specified.")})
@GetMapping(value = USER_REST_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
List<User> getUsers(@RequestParam(name = USERNAME_PARAM, required = false) String username);
@ResponseBody
@Operation(summary = "Retrieve a specific user by its identifier.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The " + "userId cannot be found."), @ApiResponse(responseCode = "400", description = "The provided user id is empty or " + "null.")})
@GetMapping(value = USER_REST_PATH + USER_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
User getUserById(@PathVariable(USER_ID) String userId);
}

View File

@ -0,0 +1,29 @@
package com.knecon.fforesight.tenantusermanagement.api.external.v2.model;
import java.util.Set;
import java.util.TreeSet;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class User {
private String id;
private String username;
private String email;
private String firstName;
private String lastName;
private boolean active;
@Builder.Default
private Set<String> roles = new TreeSet<>();
}

View File

@ -0,0 +1,18 @@
package com.knecon.fforesight.tenantusermanagement.api.external.v2.model;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserList {
private List<User> users = new ArrayList<>();
}

View File

@ -0,0 +1,8 @@
package com.knecon.fforesight.tenantusermanagement.api.internal;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("/internal")
public interface InternalResource {
}

View File

@ -7,49 +7,57 @@ import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.fasterxml.jackson.databind.JsonNode;
import com.knecon.fforesight.tenantcommons.TenantApplicationType;
import com.knecon.fforesight.tenantcommons.model.TenantResponse;
import com.knecon.fforesight.tenantcommons.model.UpdateDetailsRequest;
import com.knecon.fforesight.tenantusermanagement.model.DeploymentKeyResponse;
import com.knecon.fforesight.tenantusermanagement.model.SimpleTenantResponse;
import com.knecon.fforesight.tenantusermanagement.model.TenantRequest;
import com.knecon.fforesight.tenantusermanagement.model.CreateTenantRequest;
import com.knecon.fforesight.tenantusermanagement.model.UpdateTenantRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ResponseStatus(value = HttpStatus.OK)
@RequestMapping("${fforesight.tenant-user-management.base-path:}/internal")
public interface InternalTenantsResource {
String TENANT_ID_PARAM = "tenantId";
String TENANT_ID_PATH_PARAM = "/{" + TENANT_ID_PARAM + "}";
@PostMapping(value = "/tenants/{tenantId}/details", consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Update details", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
void updateDetails(@PathVariable("tenantId") String tenantId, @RequestBody UpdateDetailsRequest request);
@PostMapping(value = "/tenants", consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Creates a new Tenant", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
void createTenant(@RequestBody TenantRequest tenant);
TenantResponse createTenant(@RequestBody CreateTenantRequest tenant);
@GetMapping(value = "/tenants", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets all existing tenant", description = "None")
@Operation(summary = "Gets all existing tenants", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
List<TenantResponse> getTenants();
@GetMapping(value = "/tenants/{tenantId}", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets all existing tenant", description = "None")
@Operation(summary = "Get the given tenant", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
TenantResponse getTenant(@PathVariable("tenantId") String tenantId);
@GetMapping(value = "/tenants/simple", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets all existing tenant in a simplified format", description = "None")
@PutMapping(value = "/tenants/{tenantId}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Update existing tenant", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
List<SimpleTenantResponse> getSimpleTenants();
TenantResponse updateTenant(@PathVariable("tenantId") String tenantId, @RequestBody UpdateTenantRequest tenantRequest);
@GetMapping(value = "/deploymentKey" + TENANT_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
@ -57,4 +65,16 @@ public interface InternalTenantsResource {
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
DeploymentKeyResponse getDeploymentKey(@PathVariable(TENANT_ID_PARAM) String tenantId);
@PostMapping(value = "/tenants/{tenantId}/sync", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Sync existing tenant", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
void syncTenant(@PathVariable("tenantId") String tenantId, @RequestBody JsonNode payload);
@GetMapping(value = {"/tenants/{tenantId}/application-type"}, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets the application type of the given tenant", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
TenantApplicationType getTenantApplicationType(@PathVariable("tenantId") String tenantId);
}

View File

@ -17,7 +17,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ResponseStatus(value = HttpStatus.OK)
@RequestMapping("${fforesight.tenant-user-management.base-path:}/internal")
public interface InternalUserResource {
String USER_REST_PATH = "/user";

View File

@ -0,0 +1,10 @@
package com.knecon.fforesight.tenantusermanagement.client;
import org.springframework.cloud.openfeign.FeignClient;
import com.iqser.red.service.persistence.service.v1.api.internal.resources.LicenseResource;
@FeignClient(name = "LicenseResource", url = "${persistence-service.url}")
public interface LicenseClient extends LicenseResource {
}

View File

@ -0,0 +1,32 @@
package com.knecon.fforesight.tenantusermanagement.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import com.iqser.red.storage.commons.service.azure.AzureBlobStorageService;
import com.iqser.red.storage.commons.service.s3.S3StorageService;
import lombok.Getter;
@Configuration
@Getter
@ComponentScan("com.iqser.red.storage.commons")
public class StorageConfiguration {
S3StorageService s3StorageService;
AzureBlobStorageService azureBlobStorageService;
@Autowired
public void setS3StorageService(@Lazy S3StorageService s3StorageService) {
this.s3StorageService = s3StorageService;
}
@Autowired
public void setAzureBlobStorageService(@Lazy AzureBlobStorageService azureBlobStorageService) {
this.azureBlobStorageService = azureBlobStorageService;
}
}

View File

@ -0,0 +1,92 @@
package com.knecon.fforesight.tenantusermanagement.controller;
import java.util.stream.Collectors;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.server.ResponseStatusException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.knecon.fforesight.tenantusermanagement.exception.ConflictException;
import com.knecon.fforesight.tenantusermanagement.model.ErrorMessage;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.ForbiddenException;
import jakarta.ws.rs.NotFoundException;
@RestControllerAdvice
public class ControllerAdvice {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorMessage> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
var errorList = e.getFieldErrors();
String errorListAsString = errorList.stream().map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage()).collect(Collectors.joining(", "));
return new ResponseEntity<>(new ErrorMessage(String.format("You have empty/wrong formatted parameters: %s", errorListAsString)), HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(NotFoundException.class)
public ResponseEntity<ErrorMessage> handleNotFound(NotFoundException e) {
return new ResponseEntity<>(new ErrorMessage(e.getMessage()), HttpStatus.NOT_FOUND);
}
@ExceptionHandler(ForbiddenException.class)
public ResponseEntity<ErrorMessage> handleForbiddenAccess(ForbiddenException e) {
return new ResponseEntity<>(new ErrorMessage(e.getMessage()), HttpStatus.FORBIDDEN);
}
@ExceptionHandler(ResponseStatusException.class)
public ResponseEntity<ErrorMessage> handleResponseStatusException(ResponseStatusException e) {
return new ResponseEntity<>(new ErrorMessage(e.getReason()), e.getStatusCode());
}
@ExceptionHandler(BadRequestException.class)
public ResponseEntity<ErrorMessage> handleBadRequestException(BadRequestException e) {
return new ResponseEntity<>(new ErrorMessage(e.getMessage()), HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<ErrorMessage> handleAccessDeniedException(AccessDeniedException e) {
return new ResponseEntity<>(new ErrorMessage(e.getMessage()), HttpStatus.FORBIDDEN);
}
@ExceptionHandler(ConflictException.class)
public ResponseEntity<ErrorMessage> handleConflictException(ConflictException e) {
return new ResponseEntity<>(new ErrorMessage(e.getMessage()), HttpStatus.CONFLICT);
}
@ExceptionHandler({HttpMessageNotReadableException.class})
public ResponseEntity<ErrorMessage> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
String errorMessage = e.getMessage();
var cause = e.getCause();
if (cause instanceof InvalidFormatException invalidFormatException) {
errorMessage = cause.getMessage();
Class<?> targetType = invalidFormatException.getTargetType();
if (targetType != null && targetType.isEnum()) {
errorMessage = String.format("Unsupported value for %s", targetType.getSimpleName());
}
}
return new ResponseEntity<>(new ErrorMessage(errorMessage), HttpStatus.BAD_REQUEST);
}
}

View File

@ -1,98 +0,0 @@
package com.knecon.fforesight.tenantusermanagement.controller;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.READ_SMTP_CONFIGURATION;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.WRITE_SMTP_CONFIGURATION;
import java.util.HashMap;
import java.util.Map;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.knecon.fforesight.tenantcommons.EncryptionDecryptionService;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantusermanagement.api.SMTPConfigurationResource;
import com.knecon.fforesight.tenantusermanagement.model.SMTPConfiguration;
import com.knecon.fforesight.tenantusermanagement.service.RealmService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequiredArgsConstructor
public class SMTPConfigurationController implements SMTPConfigurationResource {
private final static String DEFAULT_PASSWORD = "********";
private final static String SMTP_PASSWORD_KEY = "FFORESIGHT_SMTP_PASSWORD";
private final RealmService realmService;
private final ObjectMapper objectMapper;
private final EncryptionDecryptionService encryptionDecryptionService;
@Override
@PreAuthorize("hasAuthority('" + READ_SMTP_CONFIGURATION + "')")
public SMTPConfiguration getCurrentSMTPConfiguration() {
var realm = realmService.realm(TenantContext.getTenantId()).toRepresentation();
return objectMapper.convertValue(realm.getSmtpServer(), SMTPConfiguration.class);
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_SMTP_CONFIGURATION + "')")
public void updateSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfigurationModel) {
var realmRepresentation = realmService.realm(TenantContext.getTenantId()).toRepresentation();
var propertiesMap = convertSMTPConfigurationModelToMap(smtpConfigurationModel);
realmRepresentation.setSmtpServer(propertiesMap);
if (!smtpConfigurationModel.getPassword().equalsIgnoreCase(DEFAULT_PASSWORD)) {
realmRepresentation.getAttributesOrEmpty().put(SMTP_PASSWORD_KEY, encryptionDecryptionService.encrypt(smtpConfigurationModel.getPassword()));
}
realmService.realm(TenantContext.getTenantId()).update(realmRepresentation);
}
private Map<String, String> convertSMTPConfigurationModelToMap(SMTPConfiguration smtpConfigurationModel) {
Map<String, Object> propertiesMap = objectMapper.convertValue(smtpConfigurationModel, Map.class);
Map<String, String> stringPropertiesMap = new HashMap<>();
propertiesMap.forEach((key, value) -> {
if (value != null) {
stringPropertiesMap.put(key, value.toString());
} else {
stringPropertiesMap.put(key, "");
}
});
return stringPropertiesMap;
}
@SneakyThrows
@Override
@PreAuthorize("hasAuthority('" + WRITE_SMTP_CONFIGURATION + "')")
public void testSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfigurationModel) {
var propertiesMap = convertSMTPConfigurationModelToMap(smtpConfigurationModel);
realmService.realm(TenantContext.getTenantId()).testSMTPConnection(propertiesMap);
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_SMTP_CONFIGURATION + "')")
public void clearSMTPConfiguration() {
// also update in KC
var realmRepresentation = realmService.realm(TenantContext.getTenantId()).toRepresentation();
realmRepresentation.setSmtpServer(new HashMap<>());
realmRepresentation.getAttributesOrEmpty().remove(SMTP_PASSWORD_KEY);
realmService.realm(TenantContext.getTenantId()).update(realmRepresentation);
}
}

View File

@ -1,72 +0,0 @@
package com.knecon.fforesight.tenantusermanagement.controller;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.CREATE_TENANT;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.DEPLOYMENT_INFO;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.GET_TENANTS;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import com.knecon.fforesight.tenantcommons.model.TenantResponse;
import com.knecon.fforesight.tenantusermanagement.api.TenantsResource;
import com.knecon.fforesight.tenantusermanagement.model.DeploymentKeyResponse;
import com.knecon.fforesight.tenantusermanagement.model.SimpleTenantResponse;
import com.knecon.fforesight.tenantusermanagement.model.TenantRequest;
import com.knecon.fforesight.tenantusermanagement.service.DeploymentKeyService;
import com.knecon.fforesight.tenantusermanagement.service.TenantManagementService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class TenantsController implements TenantsResource {
private final TenantManagementService tenantManagementService;
private final DeploymentKeyService deploymentKeyService;
@PreAuthorize("hasAuthority('" + CREATE_TENANT + "')")
public void createTenant(@Valid @RequestBody TenantRequest tenantRequest) {
try {
tenantManagementService.createTenant(tenantRequest);
} catch (IllegalArgumentException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage(), e);
}
}
@PreAuthorize("hasAuthority('" + GET_TENANTS + "')")
public List<TenantResponse> getTenants() {
return tenantManagementService.getTenants();
}
@PreAuthorize("hasAuthority('" + GET_TENANTS + "')")
public TenantResponse getTenant(String tenantId) {
return tenantManagementService.getTenant(tenantId);
}
public List<SimpleTenantResponse> getSimpleTenants() {
return tenantManagementService.getTenants().stream().map(t -> new SimpleTenantResponse(t.getTenantId(), t.getDisplayName(), t.getGuid())).toList();
}
@PreAuthorize("hasAuthority('" + DEPLOYMENT_INFO + "')")
public DeploymentKeyResponse getDeploymentKey(@PathVariable(TENANT_ID_PARAM) String tenantId) {
return new DeploymentKeyResponse(deploymentKeyService.getDeploymentKey(tenantId));
}
}

View File

@ -1,4 +1,4 @@
package com.knecon.fforesight.tenantusermanagement.controller;
package com.knecon.fforesight.tenantusermanagement.controller.external;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.READ_GENERAL_CONFIGURATION;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.WRITE_GENERAL_CONFIGURATION;
@ -7,9 +7,12 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.knecon.fforesight.tenantusermanagement.api.GeneralSettingsResource;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantusermanagement.api.external.GeneralSettingsResource;
import com.knecon.fforesight.tenantusermanagement.api.external.PublicResource;
import com.knecon.fforesight.tenantusermanagement.model.GeneralConfigurationModel;
import com.knecon.fforesight.tenantusermanagement.service.GeneralConfigurationService;
import com.knecon.fforesight.tenantusermanagement.service.TenantManagementService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -17,16 +20,17 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequiredArgsConstructor
public class GeneralSettingsController implements GeneralSettingsResource {
public class GeneralSettingsController implements GeneralSettingsResource, PublicResource {
private final GeneralConfigurationService generalConfigurationService;
private final TenantManagementService tenantManagementService;
@Override
@PreAuthorize("hasAuthority('" + READ_GENERAL_CONFIGURATION + "')")
public GeneralConfigurationModel getGeneralConfigurations() {
return generalConfigurationService.getGeneralConfigurations();
return generalConfigurationService.getGeneralConfigurations(tenantManagementService.getTenantApplicationType(TenantContext.getTenantId()));
}
@ -34,7 +38,7 @@ public class GeneralSettingsController implements GeneralSettingsResource {
@PreAuthorize("hasAuthority('" + WRITE_GENERAL_CONFIGURATION + "')")
public void updateGeneralConfigurations(@RequestBody GeneralConfigurationModel generalConfigurationModel) {
generalConfigurationService.updateGeneralConfigurations(generalConfigurationModel);
generalConfigurationService.updateGeneralConfigurations(generalConfigurationModel, tenantManagementService.getTenantApplicationType(TenantContext.getTenantId()));
}
}

View File

@ -0,0 +1,269 @@
package com.knecon.fforesight.tenantusermanagement.controller.external;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.READ_IDENTITY_PROVIDER_CONFIGURATION;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.WRITE_IDENTITY_PROVIDER_CONFIGURATION;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ResponseStatusException;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantusermanagement.api.external.IdentityProviderConfigurationResource;
import com.knecon.fforesight.tenantusermanagement.api.external.PublicResource;
import com.knecon.fforesight.tenantusermanagement.exception.ConflictException;
import com.knecon.fforesight.tenantusermanagement.exception.IdentityProviderExistsAlreadyException;
import com.knecon.fforesight.tenantusermanagement.exception.IdentityProviderNotFoundException;
import com.knecon.fforesight.tenantusermanagement.model.IdentityProviderList;
import com.knecon.fforesight.tenantusermanagement.model.IdentityProviderModel;
import com.knecon.fforesight.tenantusermanagement.model.IdentityProviderRequest;
import com.knecon.fforesight.tenantusermanagement.model.IdentityProviderWithDescriptorRequest;
import com.knecon.fforesight.tenantusermanagement.properties.TenantUserManagementProperties;
import com.knecon.fforesight.tenantusermanagement.service.KeyCloakAdminClientService;
import com.knecon.fforesight.tenantusermanagement.service.RealmService;
import com.knecon.fforesight.tenantusermanagement.utils.IdentityProviderMappingService;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.InternalServerErrorException;
import jakarta.ws.rs.core.Response;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequiredArgsConstructor
public class IdentityProviderConfigurationController implements IdentityProviderConfigurationResource, PublicResource {
private final RealmService realmService;
private final TenantUserManagementProperties tenantUserManagementProperties;
private final KeyCloakAdminClientService keyCloakAdminClientService;
@Override
@PreAuthorize("hasAuthority('" + READ_IDENTITY_PROVIDER_CONFIGURATION + "')")
public IdentityProviderList getIdentityProviders() {
return new IdentityProviderList(getRealmRepresentation().getIdentityProviders()
.stream()
.map(IdentityProviderMappingService::toModelFromRepresentation)
.toList());
}
@Override
@PreAuthorize("hasAuthority('" + READ_IDENTITY_PROVIDER_CONFIGURATION + "')")
public IdentityProviderModel getIdentityProvider(String identityProviderAlias) {
return IdentityProviderMappingService.toModelFromRepresentation(getIdentityProviderRepresentation(identityProviderAlias));
}
private IdentityProviderRepresentation getIdentityProviderRepresentation(String identityProviderAlias) {
return getRealmRepresentation().getIdentityProviders()
.stream()
.filter(identityProviderRepresentation -> identityProviderRepresentation.getAlias().equals(identityProviderAlias))
.findFirst()
.orElseThrow(() -> new IdentityProviderNotFoundException(identityProviderAlias));
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_IDENTITY_PROVIDER_CONFIGURATION + "')")
public ResponseEntity<IdentityProviderModel> createIdentityProvider(IdentityProviderRequest identityProviderRequest) {
if (identityProviderRequest.getAlias() == null) {
throw new BadRequestException("Alias must not be null.");
}
if (identityProviderRequest.getDisplayName() == null || identityProviderRequest.getDisplayName().isEmpty()) {
identityProviderRequest.setDisplayName(identityProviderRequest.getAlias());
}
checkIfOtherIdpHasSameDisplayNameOnCreate(identityProviderRequest.getDisplayName());
IdentityProviderModel identityProviderModel = IdentityProviderMappingService.toModelFromRequest(identityProviderRequest);
return callKeyCloakIdentityProvidersCreateForModel(identityProviderModel);
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_IDENTITY_PROVIDER_CONFIGURATION + "')")
public ResponseEntity<IdentityProviderModel> createIdentityProviderFromDescriptor(IdentityProviderWithDescriptorRequest identityProviderWithDescriptorRequest) {
if (identityProviderWithDescriptorRequest.getDisplayName() == null || identityProviderWithDescriptorRequest.getDisplayName().isEmpty()) {
identityProviderWithDescriptorRequest.setDisplayName(identityProviderWithDescriptorRequest.getAlias());
}
checkIfOtherIdpHasSameDisplayNameOnCreate(identityProviderWithDescriptorRequest.getDisplayName());
Map<String, Object> requestMap = new HashMap<>();
requestMap.put("providerId", identityProviderWithDescriptorRequest.getProviderId());
requestMap.put("fromUrl", identityProviderWithDescriptorRequest.getSamlEntityDescriptorURL());
try {
Map<String, String> configurationsMap = realmService.realm(getTenantId()).identityProviders().importFrom(requestMap);
if (configurationsMap == null || configurationsMap.isEmpty()) {
throw new BadRequestException("Could not set config from provided descriptor.");
}
configurationsMap.put("entityId", identityProviderWithDescriptorRequest.getEntityId());
IdentityProviderModel identityProviderModel = IdentityProviderMappingService.toModelFromDescriptorRequestAndConfig(identityProviderWithDescriptorRequest,
configurationsMap);
return callKeyCloakIdentityProvidersCreateForModel(identityProviderModel);
} catch (InternalServerErrorException internalServerErrorException) {
throw new BadRequestException("Provided descriptor is malformed.");
}
}
private ResponseEntity<IdentityProviderModel> callKeyCloakIdentityProvidersCreateForModel(IdentityProviderModel identityProviderModel) {
identityProviderModel.setSpecificDefaults();
try (Response response = realmService.realm(getTenantId()).identityProviders().create(IdentityProviderMappingService.toRepresentationFromModel(identityProviderModel))) {
var httpStatus = HttpStatus.valueOf(response.getStatus());
switch (httpStatus) {
case CREATED -> {
IdentityProviderModel createdIdentityProvider = getIdentityProvider(identityProviderModel.getAlias());
return new ResponseEntity<>(createdIdentityProvider, HttpStatus.valueOf(response.getStatus()));
}
case CONFLICT -> throw new IdentityProviderExistsAlreadyException(identityProviderModel.getAlias());
default -> throw new ResponseStatusException(httpStatus, extractKeycloakErrorMessageInfos(response.readEntity(String.class)));
}
}
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_IDENTITY_PROVIDER_CONFIGURATION + "')")
public ResponseEntity<IdentityProviderModel> updateIdentityProvider(String identityProviderAlias, IdentityProviderRequest identityProviderRequest) {
identityProviderRequest.setAlias(identityProviderAlias);
getIdentityProviderRepresentation(identityProviderAlias);
checkIfOtherIdpHasSameDisplayNameOnUpdate(identityProviderAlias, identityProviderRequest.getDisplayName());
IdentityProviderModel identityProviderModel = IdentityProviderMappingService.toModelFromRequest(identityProviderRequest);
identityProviderModel.setSpecificDefaults();
var restTemplate = new RestTemplate();
var url = getKeycloakIdentityProviderInstancesUrl() + identityProviderRequest.getAlias();
var headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("Authorization", "Bearer " + getToken());
HttpEntity<IdentityProviderModel> httpEntity = new HttpEntity<>(identityProviderModel, headers);
try {
ResponseEntity<?> response = restTemplate.exchange(url, HttpMethod.PUT, httpEntity, String.class);
var httpStatus = HttpStatus.valueOf(response.getStatusCode().value());
if (httpStatus == HttpStatus.NO_CONTENT) {
IdentityProviderModel createdIdentityProvider = getIdentityProvider(identityProviderRequest.getAlias());
return new ResponseEntity<>(createdIdentityProvider, HttpStatus.OK);
} else if (httpStatus.is4xxClientError()) {
throw new ResponseStatusException(httpStatus, "Bad request to keycloak API");
} else {
throw new ResponseStatusException(httpStatus, httpStatus.getReasonPhrase());
}
} catch (HttpClientErrorException e) {
throw new ResponseStatusException(e.getStatusCode(), extractKeycloakErrorMessageInfos(e.getMessage()));
}
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_IDENTITY_PROVIDER_CONFIGURATION + "')")
public void deleteIdentityProvider(String identityProviderAlias) {
getIdentityProviderRepresentation(identityProviderAlias);
var restTemplate = new RestTemplate();
var url = getKeycloakIdentityProviderInstancesUrl() + identityProviderAlias;
var headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("Authorization", "Bearer " + getToken());
HttpEntity<?> httpEntity = new HttpEntity<>(headers);
try {
restTemplate.exchange(url, HttpMethod.DELETE, httpEntity, String.class);
} catch (HttpClientErrorException e) {
throw new ResponseStatusException(e.getStatusCode(), extractKeycloakErrorMessageInfos(e.getMessage()));
}
}
private void checkIfOtherIdpHasSameDisplayNameOnUpdate(String alias, String displayName) {
checkIfOtherIdpHasSameDisplayName(getIdentityProviders().getIdentityProviders()
.stream()
.filter(identityProviderModel -> !identityProviderModel.getAlias().equals(alias))
.toList(), displayName);
}
private void checkIfOtherIdpHasSameDisplayNameOnCreate(String displayName) {
checkIfOtherIdpHasSameDisplayName(getIdentityProviders().getIdentityProviders(), displayName);
}
private void checkIfOtherIdpHasSameDisplayName(List<IdentityProviderModel> identityProviderModelList, String displayName) {
if (identityProviderModelList.stream()
.anyMatch(idp -> idp.getDisplayName().isEmpty() ? idp.getAlias().equals(displayName) : idp.getDisplayName().equals(displayName))) {
throw new ConflictException("Identity provider with this display name already exists.");
}
}
private static String extractKeycloakErrorMessageInfos(String keyCloakErrorMessage) {
String errorMessageRegex = "\\{\"errorMessage\":\"(.*?)\"}";
Pattern pattern = Pattern.compile(errorMessageRegex);
Matcher matcher = pattern.matcher(keyCloakErrorMessage);
if (matcher.find()) {
return matcher.group(1);
}
return keyCloakErrorMessage;
}
private String getTenantId() {
return TenantContext.getTenantId();
}
private RealmRepresentation getRealmRepresentation() {
return realmService.realm(getTenantId()).toRepresentation();
}
private String getKeycloakIdentityProviderInstancesUrl() {
return tenantUserManagementProperties.getServerUrl() + "/admin/realms/" + getTenantId() + "/identity-provider/instances/";
}
private String getToken() {
return keyCloakAdminClientService.getAdminClient().tokenManager().getAccessToken().getToken();
}
}

View File

@ -0,0 +1,91 @@
package com.knecon.fforesight.tenantusermanagement.controller.external;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.READ_SMTP_CONFIGURATION;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.WRITE_SMTP_CONFIGURATION;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.knecon.fforesight.tenantcommons.EncryptionDecryptionService;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantusermanagement.api.external.PublicResource;
import com.knecon.fforesight.tenantusermanagement.api.external.SMTPConfigurationResource;
import com.knecon.fforesight.tenantusermanagement.model.SMTPConfiguration;
import com.knecon.fforesight.tenantusermanagement.model.SMTPResponse;
import com.knecon.fforesight.tenantusermanagement.service.EmailService;
import com.knecon.fforesight.tenantusermanagement.service.RealmService;
import com.knecon.fforesight.tenantusermanagement.service.SMTPService;
import jakarta.ws.rs.BadRequestException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequiredArgsConstructor
public class SMTPConfigurationController implements SMTPConfigurationResource, PublicResource {
private final RealmService realmService;
private final EmailService emailService;
private final SMTPService smtpService;
@Override
@PreAuthorize("hasAuthority('" + READ_SMTP_CONFIGURATION + "')")
public SMTPConfiguration getCurrentSMTPConfiguration() {
return smtpService.getSMTPConfiguration();
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_SMTP_CONFIGURATION + "')")
public void updateSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfigurationModel) {
if (!smtpService.canUpdateSMTPConfig()) {
throw new BadRequestException("Current license does not allow updating the SMTP configuration!");
}
smtpService.updateSMTPConfiguration(smtpConfigurationModel);
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_SMTP_CONFIGURATION + "')")
public SMTPResponse testSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfiguration) {
String targetEmail = realmService.getEmail(realmService.realm(TenantContext.getTenantId()));
boolean adminEmail = true;
if (StringUtils.isEmpty(targetEmail)) {
// will send e-mail to self in case targetEmail is not set
targetEmail = smtpConfiguration.getFrom();
adminEmail = false;
}
SMTPResponse.SMTPResponseBuilder smtpResponseBuilder = emailService.send(smtpService.convertSMTPConfigToMap(smtpConfiguration),
targetEmail,
"Redaction Test message",
"This is a test message");
SMTPResponse smtpResponse = smtpResponseBuilder.adminEmail(adminEmail).recipientEmail(targetEmail).build();
log.info("Test SMTP Configuration status: {}, reason: {}, recipient: {}", smtpResponse.getStatusCode(), smtpResponse.getReasonPhrase(), smtpResponse.getRecipientEmail());
return smtpResponse;
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_SMTP_CONFIGURATION + "')")
public void clearSMTPConfiguration() {
smtpService.createDefaultSMTPConfiguration();
}
}

View File

@ -0,0 +1,108 @@
package com.knecon.fforesight.tenantusermanagement.controller.external;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.CREATE_TENANT;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.DELETE_TENANT;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.DEPLOYMENT_INFO;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.GET_TENANTS;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.UPDATE_TENANT;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import com.fasterxml.jackson.databind.JsonNode;
import com.knecon.fforesight.tenantcommons.model.TenantResponse;
import com.knecon.fforesight.tenantcommons.model.UpdateDetailsRequest;
import com.knecon.fforesight.tenantusermanagement.api.external.PublicResource;
import com.knecon.fforesight.tenantusermanagement.api.external.TenantsResource;
import com.knecon.fforesight.tenantusermanagement.model.DeploymentKeyResponse;
import com.knecon.fforesight.tenantusermanagement.model.CreateTenantRequest;
import com.knecon.fforesight.tenantusermanagement.model.UpdateTenantRequest;
import com.knecon.fforesight.tenantusermanagement.service.DeploymentKeyService;
import com.knecon.fforesight.tenantusermanagement.service.TenantManagementService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class TenantsController implements TenantsResource, PublicResource {
private final TenantManagementService tenantManagementService;
private final DeploymentKeyService deploymentKeyService;
@PreAuthorize("hasAuthority('" + CREATE_TENANT + "')")
public void createTenant(@Valid @RequestBody CreateTenantRequest tenantRequest) {
try {
tenantManagementService.createTenant(tenantRequest);
} catch (IllegalArgumentException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage(), e);
}
}
@PreAuthorize("hasAuthority('" + DELETE_TENANT + "')")
public void deleteTenant(String tenantId) {
try {
tenantManagementService.deleteTenant(tenantId);
} catch (IllegalArgumentException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage(), e);
}
}
@PreAuthorize("hasAuthority('" + GET_TENANTS + "')")
public List<TenantResponse> getTenants() {
List<TenantResponse> tenants = tenantManagementService.getTenants();
return tenants.stream().map(tenantManagementService::removePasswords).collect(Collectors.toList());
}
@PreAuthorize("hasAuthority('" + GET_TENANTS + "')")
public TenantResponse getTenant(String tenantId) {
TenantResponse tenantResponse = tenantManagementService.getTenant(tenantId);
return tenantManagementService.removePasswords(tenantResponse);
}
@PreAuthorize("hasAuthority('" + UPDATE_TENANT + "')")
public TenantResponse updateTenant(String tenantId, @RequestBody UpdateTenantRequest tenantRequest) {
TenantResponse tenantResponse = tenantManagementService.updateTenant(tenantId, tenantRequest);
return tenantManagementService.removePasswords(tenantResponse);
}
@PreAuthorize("hasAuthority('" + UPDATE_TENANT + "')")
public void updateDetails(@PathVariable("tenantId") String tenantId, @RequestBody UpdateDetailsRequest request) {
tenantManagementService.updateDetails(tenantId, request);
}
@PreAuthorize("hasAuthority('" + DEPLOYMENT_INFO + "')")
public DeploymentKeyResponse getDeploymentKey(@PathVariable(TENANT_ID_PARAM) String tenantId) {
return new DeploymentKeyResponse(deploymentKeyService.getDeploymentKey(tenantId));
}
@Override
public void syncTenant(@PathVariable(TENANT_ID_PARAM) String tenantId, @RequestBody JsonNode payload) {
tenantManagementService.syncTenant(tenantId, payload);
}
}

View File

@ -1,5 +1,6 @@
package com.knecon.fforesight.tenantusermanagement.controller;
package com.knecon.fforesight.tenantusermanagement.controller.external;
import static com.knecon.fforesight.tenantusermanagement.permissions.ApplicationRoles.KNECON_ROLE_FILTER;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.READ_ALL_USERS;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.READ_USERS;
import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.UPDATE_MY_PROFILE;
@ -18,13 +19,15 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import com.knecon.fforesight.tenantusermanagement.api.UserResource;
import com.knecon.fforesight.tenantusermanagement.api.external.PublicResource;
import com.knecon.fforesight.tenantusermanagement.api.external.UserResource;
import com.knecon.fforesight.tenantusermanagement.model.CreateUserRequest;
import com.knecon.fforesight.tenantusermanagement.model.ResetPasswordRequest;
import com.knecon.fforesight.tenantusermanagement.model.UpdateMyProfileRequest;
import com.knecon.fforesight.tenantusermanagement.model.UpdateProfileRequest;
import com.knecon.fforesight.tenantusermanagement.model.User;
import com.knecon.fforesight.tenantusermanagement.properties.TenantUserManagementProperties;
import com.knecon.fforesight.tenantusermanagement.permissions.ApplicationRoles;
import com.knecon.fforesight.tenantusermanagement.service.TenantApplicationTypeService;
import com.knecon.fforesight.tenantusermanagement.service.UserService;
import jakarta.validation.Valid;
@ -34,10 +37,11 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequiredArgsConstructor
public class UserController implements UserResource {
public class UserController implements UserResource, PublicResource {
private final UserService userService;
private final TenantUserManagementProperties tenantUserManagementProperties;
private final TenantApplicationTypeService tenantApplicationTypeService;
@Override
@ -47,9 +51,15 @@ public class UserController implements UserResource {
if (bypassCache) {
userService.evictUserCache();
}
var mappedRoles = tenantApplicationTypeService.getCurrentProperties().getKcRoleMapping().getAllRoles();
var allRoles = tenantUserManagementProperties.getKcRoleMapping().getAllRoles();
return userService.getAllUsers().stream().filter(user -> user.getRoles().stream().anyMatch(allRoles::contains)).collect(Collectors.toList());
return userService.getAllUsers()
.stream()
.filter(user -> user.getRoles()
.stream()
.anyMatch(mappedRoles::contains))
.filter(KNECON_ROLE_FILTER)
.toList();
}
@ -61,23 +71,26 @@ public class UserController implements UserResource {
userService.evictUserCache();
}
return userService.getAllUsers();
return userService.getAllUsers()
.stream()
.filter(KNECON_ROLE_FILTER)
.toList();
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_USERS + "')")
public void updateProfile(@PathVariable(USER_ID) String userId, @RequestBody UpdateProfileRequest updateProfileRequest) {
public User updateProfile(@PathVariable(USER_ID) String userId, @RequestBody UpdateProfileRequest updateProfileRequest) {
this.userService.updateProfile(userId, updateProfileRequest);
return this.userService.updateProfile(userId, updateProfileRequest);
}
@Override
@PreAuthorize("hasAuthority('" + UPDATE_MY_PROFILE + "')")
public void updateMyProfile(@Valid @RequestBody UpdateMyProfileRequest updateProfileRequest) {
public User updateMyProfile(@Valid @RequestBody UpdateMyProfileRequest updateProfileRequest) {
this.userService.updateMyProfile(updateProfileRequest);
return this.userService.updateMyProfile(updateProfileRequest);
}
@ -112,15 +125,28 @@ public class UserController implements UserResource {
if (StringUtils.isEmpty(userId)) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "The userId should not be empty.");
}
return userService.getUserById(userId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found"));
var user = userService.getUserById(userId)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found"));
Set<String> filteredRoles = user.getRoles()
.stream()
.filter(ApplicationRoles::isNoKneconRole)
.collect(Collectors.toSet());
if (!user.getRoles().isEmpty() && filteredRoles.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found");
}
user.setRoles(filteredRoles);
return user;
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_USERS + "')")
public void setRoles(@PathVariable(USER_ID) String userId, @RequestBody Set<String> roles) {
public User setRoles(@PathVariable(USER_ID) String userId, @RequestBody Set<String> roles) {
userService.setRoles(userId, roles);
return userService.setRoles(userId, roles);
}

View File

@ -1,23 +1,27 @@
package com.knecon.fforesight.tenantusermanagement.controller;
package com.knecon.fforesight.tenantusermanagement.controller.external;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.knecon.fforesight.tenantusermanagement.api.UserPreferenceResource;
import com.knecon.fforesight.tenantusermanagement.api.external.PublicResource;
import com.knecon.fforesight.tenantusermanagement.api.external.UserPreferenceResource;
import com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions;
import com.knecon.fforesight.tenantusermanagement.service.UserService;
import jakarta.ws.rs.ForbiddenException;
import jakarta.ws.rs.NotFoundException;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class UserPreferenceController implements UserPreferenceResource {
public class UserPreferenceController implements UserPreferenceResource, PublicResource {
private final UserService userService;

View File

@ -0,0 +1,57 @@
package com.knecon.fforesight.tenantusermanagement.controller.external.v2;
import java.util.List;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.knecon.fforesight.tenantusermanagement.api.external.v2.PublicResourceV2;
import com.knecon.fforesight.tenantusermanagement.api.external.v2.UserResourceV2;
import com.knecon.fforesight.tenantusermanagement.api.external.v2.model.User;
import com.knecon.fforesight.tenantusermanagement.controller.external.UserController;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequiredArgsConstructor
@Tag(name = "6. Users endpoints", description = "Operations related to users.")
public class UserControllerV2 implements UserResourceV2, PublicResourceV2 {
private final UserController userController;
public List<User> getUsers(@RequestParam(name = USERNAME_PARAM, required = false) String username) {
var users = userController.getApplicationSpecificUsers(false)
.stream()
.map(this::convertUser);
if (username == null) {
return users.toList();
}
return users.filter(user -> user.getUsername().equals(username)).toList();
}
public User getUserById(@PathVariable(USER_ID) String userId) {
return convertUser(userController.getUserById(userId));
}
private User convertUser(com.knecon.fforesight.tenantusermanagement.model.User user){
return User.builder()
.id(user.getUserId())
.username(user.getUsername())
.email(user.getEmail())
.firstName(user.getFirstName())
.lastName(user.getLastName())
.active(user.isActive())
.roles(user.getRoles())
.build();
}
}

View File

@ -8,11 +8,15 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import com.fasterxml.jackson.databind.JsonNode;
import com.knecon.fforesight.tenantcommons.TenantApplicationType;
import com.knecon.fforesight.tenantcommons.model.TenantResponse;
import com.knecon.fforesight.tenantcommons.model.UpdateDetailsRequest;
import com.knecon.fforesight.tenantusermanagement.api.internal.InternalResource;
import com.knecon.fforesight.tenantusermanagement.api.internal.InternalTenantsResource;
import com.knecon.fforesight.tenantusermanagement.model.CreateTenantRequest;
import com.knecon.fforesight.tenantusermanagement.model.DeploymentKeyResponse;
import com.knecon.fforesight.tenantusermanagement.model.SimpleTenantResponse;
import com.knecon.fforesight.tenantusermanagement.model.TenantRequest;
import com.knecon.fforesight.tenantusermanagement.model.UpdateTenantRequest;
import com.knecon.fforesight.tenantusermanagement.service.DeploymentKeyService;
import com.knecon.fforesight.tenantusermanagement.service.TenantManagementService;
@ -21,16 +25,23 @@ import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class InternalTenantsController implements InternalTenantsResource {
public class InternalTenantsController implements InternalTenantsResource, InternalResource {
private final TenantManagementService tenantManagementService;
private final DeploymentKeyService deploymentKeyService;
public void createTenant(@Valid @RequestBody TenantRequest tenantRequest) {
@Override
public void updateDetails(@PathVariable("tenantId") String tenantId, @RequestBody UpdateDetailsRequest request) {
tenantManagementService.updateDetails(tenantId, request);
}
public TenantResponse createTenant(@Valid @RequestBody CreateTenantRequest tenantRequest) {
try {
tenantManagementService.createTenant(tenantRequest);
return tenantManagementService.createTenant(tenantRequest);
} catch (IllegalArgumentException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage(), e);
}
@ -43,15 +54,16 @@ public class InternalTenantsController implements InternalTenantsResource {
}
public TenantResponse getTenant(String tenantId) {
public TenantResponse getTenant(@PathVariable(TENANT_ID_PARAM) String tenantId) {
return tenantManagementService.getTenant(tenantId);
}
public List<SimpleTenantResponse> getSimpleTenants() {
@Override
public TenantResponse updateTenant(String tenantId, UpdateTenantRequest tenantRequest) {
return tenantManagementService.getTenants().stream().map(t -> new SimpleTenantResponse(t.getTenantId(), t.getDisplayName(), t.getGuid())).toList();
return tenantManagementService.updateTenant(tenantId, tenantRequest);
}
@ -60,4 +72,18 @@ public class InternalTenantsController implements InternalTenantsResource {
return new DeploymentKeyResponse(deploymentKeyService.getDeploymentKey(tenantId));
}
@Override
public void syncTenant(@PathVariable(TENANT_ID_PARAM) String tenantId, @RequestBody JsonNode payload) {
tenantManagementService.syncTenant(tenantId, payload);
}
public TenantApplicationType getTenantApplicationType(@PathVariable(TENANT_ID_PARAM) String tenantId) {
return tenantManagementService.getTenantApplicationType(tenantId);
}
}

View File

@ -5,6 +5,7 @@ import java.util.List;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.knecon.fforesight.tenantusermanagement.api.internal.InternalResource;
import com.knecon.fforesight.tenantusermanagement.api.internal.InternalUserResource;
import com.knecon.fforesight.tenantusermanagement.model.User;
import com.knecon.fforesight.tenantusermanagement.service.UserService;
@ -15,7 +16,7 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequiredArgsConstructor
public class InternalUserController implements InternalUserResource {
public class InternalUserController implements InternalUserResource, InternalResource {
private final UserService userService;

View File

@ -0,0 +1,35 @@
package com.knecon.fforesight.tenantusermanagement.dev;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import com.knecon.fforesight.tenantusermanagement.model.TenantUser;
import com.knecon.fforesight.tenantusermanagement.service.TenantManagementService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
@Service
@Profile("dev")
@RequiredArgsConstructor
public class DevApplicationRunner implements ApplicationRunner {
private final DevTestTenantService devTestTenantService;
private final TenantManagementService tenantManagementService;
@Override
@SneakyThrows
public void run(ApplicationArguments args) {
devTestTenantService.createTestTenantIfNotExists("redaction", new ArrayList<>());
tenantManagementService.syncTenant("redaction", null);
}
}

View File

@ -0,0 +1,12 @@
package com.knecon.fforesight.tenantusermanagement.dev;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
@Profile("dev")
@EnableConfigurationProperties(DevTenantProperties.class)
public class DevConfiguration {
}

View File

@ -0,0 +1,60 @@
package com.knecon.fforesight.tenantusermanagement.dev;
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Data;
@Data
@ConfigurationProperties("dev.tenant")
public class DevTenantProperties {
private boolean recreateTenant;
private DevDatabaseProperties db = new DevDatabaseProperties();
private DevStorageProperties storage = new DevStorageProperties();
@Data
public static class DevStorageProperties {
private DevStorageMode mode = DevStorageMode.S3;
private DevS3StorageProperties s3 = new DevS3StorageProperties();
private DevAzureStorageProperties azure = new DevAzureStorageProperties();
}
public enum DevStorageMode {
S3,
AZURE
}
@Data
public static class DevS3StorageProperties {
private String key;
private String secret;
private String bucket;
private String endpoint;
}
@Data
public static class DevAzureStorageProperties {
private String containerName;
private String connectionString;
}
@Data
public static class DevDatabaseProperties {
private int port;
private String host;
private String database;
private String schema;
private String username;
private String password;
}
}

View File

@ -0,0 +1,152 @@
package com.knecon.fforesight.tenantusermanagement.dev;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.StatementCallback;
import org.springframework.jdbc.datasource.SingleConnectionDataSource;
import org.springframework.stereotype.Service;
import com.knecon.fforesight.tenantcommons.model.AzureStorageConnection;
import com.knecon.fforesight.tenantcommons.model.DatabaseConnection;
import com.knecon.fforesight.tenantcommons.model.S3StorageConnection;
import com.knecon.fforesight.tenantusermanagement.model.SearchConnectionRequest;
import com.knecon.fforesight.tenantusermanagement.model.CreateTenantRequest;
import com.knecon.fforesight.tenantusermanagement.model.TenantUser;
import com.knecon.fforesight.tenantusermanagement.repository.TenantRepository;
import com.knecon.fforesight.tenantusermanagement.service.TenantManagementService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Profile("dev")
@Service
@RequiredArgsConstructor
public class DevTestTenantService {
private final TenantManagementService tenantManagementService;
private final TenantRepository tenantRepository;
private final DataSource dataSource;
@Value("${spring.datasource.url:}")
private String masterJDBCURL;
private final DevTenantProperties devTenantProperties;
@SneakyThrows
public void createTestTenantIfNotExists(String tenantId, List<TenantUser> users) {
if (devTenantProperties.isRecreateTenant()) {
tenantRepository.deleteByQuery(tenantId);
}
try {
tenantManagementService.getTenant(tenantId);
} catch (Exception e) {
tenantRepository.deleteByQuery(tenantId);
createTestTenant(tenantId, users);
}
}
@SneakyThrows
public void createTestTenant(String tenantId, List<TenantUser> users) {
String tenantsDBName = "tenants";
String tenantsDBPassword = "tenants";
log.info("Creating Tenant {} ", tenantId);
var jdbcUrl = masterJDBCURL.substring(0, masterJDBCURL.lastIndexOf('/') + 1) + tenantsDBName + "?currentSchema=" + tenantId;
createDatabase(tenantsDBName, tenantsDBPassword);
createSchema(jdbcUrl, tenantId, tenantsDBName, tenantsDBPassword);
var tenantRequest = CreateTenantRequest.builder()
.tenantId(tenantId)
.displayName(tenantId)
.guid(UUID.randomUUID().toString())
.defaultUsers(users != null ? users : new ArrayList<>())
.databaseConnection(DatabaseConnection.builder()
.driver("postgresql")
.host(devTenantProperties.getDb().getHost())
.port(devTenantProperties.getDb().getPort() + "")
.database(devTenantProperties.getDb().getDatabase())
.schema(devTenantProperties.getDb().getSchema())
.username(devTenantProperties.getDb().getUsername())
.password(devTenantProperties.getDb().getPassword())
.build())
.searchConnection(SearchConnectionRequest.builder()
.hosts(Set.of("localhost"))
.port(9200)
.scheme("http")
.numberOfShards("1")
.numberOfReplicas("5")
.build());
if (devTenantProperties.getStorage().getMode() == DevTenantProperties.DevStorageMode.S3) {
tenantRequest.s3StorageConnection(S3StorageConnection.builder().key(devTenantProperties.getStorage().getS3().getKey())
.secret(devTenantProperties.getStorage().getS3().getSecret())
.region("eu-central-1")
.bucketName(devTenantProperties.getStorage().getS3().getBucket()).endpoint(devTenantProperties.getStorage().getS3().getEndpoint()).build());
} else {
tenantRequest.azureStorageConnection(AzureStorageConnection.builder().connectionString(devTenantProperties.getStorage().getAzure().getConnectionString())
.containerName(devTenantProperties.getStorage().getAzure().getContainerName()).build());
}
tenantManagementService.createTenant(tenantRequest.build());
}
private void createDatabase(String db, String password) {
var jdbcTemplate = new JdbcTemplate(dataSource);
try {
jdbcTemplate.execute((StatementCallback<Boolean>) stmt -> stmt.execute("CREATE DATABASE " + db));
} catch (Exception e) {
log.warn("DB already exists");
}
try {
jdbcTemplate.execute((StatementCallback<Boolean>) stmt -> stmt.execute("CREATE USER " + db + " WITH ENCRYPTED PASSWORD '" + password + "'"));
} catch (Exception e) {
log.warn("user already exists");
}
try {
jdbcTemplate.execute((StatementCallback<Boolean>) stmt -> stmt.execute("GRANT ALL PRIVILEGES ON DATABASE " + db + " TO " + db));
} catch (Exception e) {
log.warn("grant invalid");
}
}
@SneakyThrows
public void createSchema(String jdbcUrl, String tenantName, String username, String password) {
try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) {
DataSource tenantDataSource = new SingleConnectionDataSource(connection, false);
JdbcTemplate insert = new JdbcTemplate(tenantDataSource);
try {
insert.execute((StatementCallback<Boolean>) stmt -> stmt.execute("CREATE SCHEMA " + tenantName));
} catch (Exception e) {
log.warn("schema already exists");
}
try {
insert.execute((StatementCallback<Boolean>) stmt -> stmt.execute("GRANT USAGE ON SCHEMA " + tenantName + " TO " + username));
} catch (Exception e) {
log.warn("grant invalid");
}
}
}
}

View File

@ -0,0 +1,60 @@
package com.knecon.fforesight.tenantusermanagement.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "global_smtp_configuration")
public class GlobalSMTPConfigurationEntity {
@Id
@Column(nullable = false, updatable = false)
private String id = "singleton";
@Column
private Boolean auth;
@Column
private String envelopeFrom;
@Column
private String fromEmail;
@Column
private String fromDisplayName;
@Column
private String host;
@Column
private String password;
@Column
private Integer port;
@Column
private String replyTo;
@Column
private String replyToDisplayName;
@Column
private Boolean ssl;
@Column
private Boolean starttls;
@Column
private String userName;
}

View File

@ -0,0 +1,30 @@
package com.knecon.fforesight.tenantusermanagement.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Embeddable
public class MongoDBConnectionEntity {
@Column(name = "mongodb_prefix")
private String prefix;
@Column(name = "mongodb_username")
private String username;
@Column(name = "mongodb_password")
private String password;
@Column(name = "mongodb_address")
private String address;
@Column(name = "mongodb_database")
private String database;
@Column(name = "mongodb_options")
private String options;
}

View File

@ -23,6 +23,7 @@ public class SearchConnectionEntity {
@Convert(converter = JSONStringSetConverter.class)
private Set<String> hosts;
@Column(name = "search_port")
@Builder.Default
private int port = 9300;
@Column(name = "search_scheme")
private String scheme;
@ -34,5 +35,7 @@ public class SearchConnectionEntity {
private String numberOfShards;
@Column(name = "search_number_of_replicas")
private String numberOfReplicas;
@Column(name = "search_index_prefix")
private String indexPrefix;
}

View File

@ -1,8 +1,19 @@
package com.knecon.fforesight.tenantusermanagement.entity;
import java.util.HashMap;
import java.util.Map;
import com.knecon.fforesight.tenantcommons.TenantApplicationType;
import com.knecon.fforesight.tenantusermanagement.utils.JSONMapConverter;
import jakarta.persistence.Basic;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
@ -37,4 +48,17 @@ public class TenantEntity {
@Embedded
private S3StorageConnectionEntity s3StorageConnection;
@Embedded
private MongoDBConnectionEntity mongoDBConnection;
@Basic(fetch = FetchType.EAGER)
@Column(columnDefinition = "text")
@Convert(converter = JSONMapConverter.class)
@Builder.Default
private Map<String, Object> details = new HashMap<>();
@Column
@Enumerated(EnumType.STRING)
private TenantApplicationType applicationType;
}

View File

@ -0,0 +1,17 @@
package com.knecon.fforesight.tenantusermanagement.events;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TenantSyncEvent {
private String tenantId;
private JsonNode payload;
}

View File

@ -0,0 +1,23 @@
package com.knecon.fforesight.tenantusermanagement.exception;
public class ConflictException extends RuntimeException {
public ConflictException(String message) {
super(message);
}
public ConflictException(String message, Throwable t) {
super(message, t);
}
public static ConflictException withObjectName(String objectName) {
return new ConflictException(String.format("An object of type %s already exists.", objectName));
}
}

View File

@ -0,0 +1,14 @@
package com.knecon.fforesight.tenantusermanagement.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(code = HttpStatus.CONFLICT)
public class IdentityProviderExistsAlreadyException extends ConflictException {
private static final String IDENTITY_PROVIDER_ALREADY_EXISTS_MESSAGE = "Identity provider with alias %s already exists in keycloak.";
public IdentityProviderExistsAlreadyException(String identityProviderAlias) {
super(String.format(IDENTITY_PROVIDER_ALREADY_EXISTS_MESSAGE, identityProviderAlias));
}
}

View File

@ -0,0 +1,16 @@
package com.knecon.fforesight.tenantusermanagement.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
import jakarta.ws.rs.NotFoundException;
@ResponseStatus(code = HttpStatus.NOT_FOUND)
public class IdentityProviderNotFoundException extends NotFoundException {
private static final String IDENTITY_PROVIDER_NOT_FOUND_MESSAGE = "Identity provider with alias %s not found in keycloak.";
public IdentityProviderNotFoundException(String identityProviderAlias) {
super(String.format(IDENTITY_PROVIDER_NOT_FOUND_MESSAGE, identityProviderAlias));
}
}

View File

@ -0,0 +1,45 @@
package com.knecon.fforesight.tenantusermanagement.initializer;
import com.knecon.fforesight.tenantusermanagement.service.TenantManagementService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import com.knecon.fforesight.tenantcommons.TenantProvider;
import com.knecon.fforesight.tenantusermanagement.service.KeyCloakRoleManagerService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RequiredArgsConstructor
public class MigrateOnlyHook {
@Value("${fforesight.tenant-user-management.migrate-only:false}")
private boolean isMigrateOnly;
private final ApplicationContext ctx;
private final TenantManagementService tenantManagementService;
private final KeyCloakRoleManagerService keyCloakRoleManagerService;
@SuppressWarnings("PMD.DoNotTerminateVM")
@EventListener(ApplicationReadyEvent.class)
public void migrate() {
tenantManagementService.getTenants().forEach(tenant -> keyCloakRoleManagerService.updateRoles(tenant.getTenantId(), tenant.getApplicationType()));
// This should only run in post upgrade hook
if (isMigrateOnly) {
System.exit(SpringApplication.exit(ctx, () -> 0));
}
}
}

View File

@ -20,29 +20,10 @@ public class MessagingConfiguration {
return new TopicExchange(tenantExchangeName);
}
@Bean
TopicExchange userExchange(@Value("${fforesight.user-exchange.name}") String userExchangeName) {
return new TopicExchange(userExchangeName);
}
@Bean
public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) {
final var rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(producerJackson2MessageConverter());
return rabbitTemplate;
}
@Bean
public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
return new Jackson2JsonMessageConverter(mapper);
}
}

View File

@ -0,0 +1,55 @@
package com.knecon.fforesight.tenantusermanagement.model;
import java.util.ArrayList;
import java.util.List;
import com.knecon.fforesight.tenantcommons.TenantApplicationType;
import com.knecon.fforesight.tenantcommons.model.AzureStorageConnection;
import com.knecon.fforesight.tenantcommons.model.DatabaseConnection;
import com.knecon.fforesight.tenantcommons.model.S3StorageConnection;
import com.knecon.fforesight.tenantcommons.model.MongoDBConnection;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Object containing the request to create a tenant.")
public class CreateTenantRequest {
@NotBlank
@Pattern(regexp = "[A-Za-z0-9_-]*", message = "Tenant Id must match [A-Za-z0-9_-]")
@Schema(description = "Parameter containing the ID of the tenant.")
private String tenantId;
@NotBlank
@Schema(description = "Parameter containing the display name of the tenant.")
private String displayName;
@Schema(description = "Parameter containing the global unique ID of the tenant.")
private String guid;
@Schema(description = "Parameter containing data of the database connection.")
private DatabaseConnection databaseConnection;
@Schema(description = "Parameter containing data of the search connection.")
private SearchConnectionRequest searchConnection;
@Schema(description = "Parameter containing data of the Azure storage connection.")
private AzureStorageConnection azureStorageConnection;
@Schema(description = "Parameter containing data of the S3 storage connection.")
private S3StorageConnection s3StorageConnection;
@Schema(description = "Parameter containing data of the MongoDB connection.")
private MongoDBConnection mongoDBConnection;
@Builder.Default
@Schema(description = "Parameter containing a list of users of the tenant.")
private List<TenantUser> defaultUsers = new ArrayList<>();
@Schema(description = "Parameter containing the application type of the tenant.")
private TenantApplicationType applicationType;
}

View File

@ -7,6 +7,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "Object containing the request to create a profile.")
public class CreateUserRequest {
@Schema(description = "Email of user.")
@ -21,7 +22,10 @@ public class CreateUserRequest {
@Schema(description = "Last name of user.")
private String lastName;
@Schema(description = "The list of RED_* roles.")
@Schema(description = "Roles to assign to user.")
private Set<String> roles = new HashSet<>();
@Schema(description = "Whether a set password mail should be sent")
private boolean sendSetPasswordMail;
}

View File

@ -1,5 +1,6 @@
package com.knecon.fforesight.tenantusermanagement.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -7,8 +8,10 @@ import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "Object containing data about the deployment key.")
public class DeploymentKeyResponse {
@Schema(description = "Parameter containing the deployment key.")
private String value;
}

View File

@ -0,0 +1,15 @@
package com.knecon.fforesight.tenantusermanagement.model;
import java.time.OffsetDateTime;
import lombok.Data;
import lombok.RequiredArgsConstructor;
@Data
@RequiredArgsConstructor
public class ErrorMessage {
private OffsetDateTime timestamp = OffsetDateTime.now();
private final String message;
}

View File

@ -0,0 +1,10 @@
package com.knecon.fforesight.tenantusermanagement.model;
import java.util.Map;
public interface ExtensibleModel {
void setNotMappedFields(Map<String, String> notMappedFields);
Map<String, String> getNotMappedFields();
}

View File

@ -1,18 +1,25 @@
package com.knecon.fforesight.tenantusermanagement.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode
@Schema(description = "Object containing data about general configurations.")
public class GeneralConfigurationModel {
@Schema(description = "Parameter indicating if the 'ForgotPassword' function is enabled.")
private boolean forgotPasswordFunctionEnabled;
@Schema(description = "Parameter containing the auxiliary name.")
private String auxiliaryName;
@Schema(description = "Parameter containing the display name of the application.")
private String displayName;
}

View File

@ -0,0 +1,128 @@
package com.knecon.fforesight.tenantusermanagement.model;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Object containing information about an identity provider configuration.")
public class IdentityProviderConfigModel implements ExtensibleModel {
@Builder.Default
@Schema(description = "Whether the external identity provider is allowed to create a new identifier to represent the principal")
private Boolean allowCreate = true;
@Builder.Default
@Schema(description = "Display order : It defines the order of the providers in GUI (for example, on the Login page). The lowest number will be applied first.")
private Integer guiOrder = 0;
@Schema(description = "Service provider entity ID : It is used to uniquely identify this SAML Service Provider.")
private String entityId;
@Schema(description = "Identity provider entity ID : It is used to validate the Issuer for received SAML assertions. If empty, no Issuer validation is performed.")
private String idpEntityId;
@Schema(description = "The Url that must be used to send authentication requests (SAML AuthnRequest).")
private String singleSignOnServiceUrl;
@Builder.Default
@Schema(description = "The Url that must be used to send logout requests.")
private String singleLogoutServiceUrl = "";
@Builder.Default
@Schema(description = "Name of the Attribute Consuming Service profile to advertise in the SP metadata.")
private String attributeConsumingServiceName = "";
@Builder.Default
@Schema(description = "Backchannel logout : Does the external IDP support backchannel logout?")
private Boolean backchannelSupported = false;
@Builder.Default
@Schema(description = "Specifies the URI reference corresponding to a name identifier format.")
private IdentityProviderNameIDPolicyFormat nameIDPolicyFormat = IdentityProviderNameIDPolicyFormat.PERSISTENT;
@Builder.Default
@Schema(description = "Used to identify and track external users from the assertion. Default is using Subject NameID, alternatively can be set up as identifying attribute.")
private IdentityProviderPrincipalType principalType = IdentityProviderPrincipalType.SUBJECT;
@Builder.Default
@Schema(description = "HTTP-POST binding response : Indicates whether to respond to requests using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.")
private Boolean postBindingResponse = false;
@Builder.Default
@Schema(description = "HTTP-POST binding for AuthnRequest : Indicates whether the AuthnRequest must be sent using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.")
private Boolean postBindingAuthnRequest = false;
@Builder.Default
@Schema(description = "HTTP-POST binding logout : Indicates whether to respond to requests using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.")
private Boolean postBindingLogout = false;
@Builder.Default
@Schema(description = "Indicates whether the identity provider expects a signed AuthnRequest.")
private Boolean wantAuthnRequestsSigned = false;
@Builder.Default
@Schema(description = "Indicates whether this service provider expects a signed Assertion.")
private Boolean wantAssertionsSigned = false;
@Builder.Default
@Schema(description = "Indicates whether this service provider expects an encrypted Assertion.")
private Boolean wantAssertionsEncrypted = false;
@Builder.Default
@Schema(description = "Force authentication : Indicates whether the identity provider must authenticate the presenter directly rather than rely on a previous security context.")
private Boolean forceAuthn = false;
@Builder.Default
@Schema(description = "Enable/disable signature validation of external IDP signatures.")
private Boolean validateSignature = false;
@Builder.Default
@Schema(description = "Sign service provider metadata : Enable/disable signature of the provider SAML metadata.")
private Boolean signSpMetadata = false;
@Builder.Default
@Schema(description = "Pass subject : During login phase, forward an optional login_hint query parameter to SAML AuthnRequest's Subject.")
private Boolean loginHint = false;
@Builder.Default
@Schema(description = "Clock skew in seconds that is tolerated when validating identity provider tokens. Default value is zero.")
private Integer allowedClockSkew = 0;
@Builder.Default
@Schema(description = "Index of the Attribute Consuming Service profile to request during authentication.")
private Integer attributeConsumingServiceIndex = 0;
@Builder.Default
@Schema(description = "If hidden, login with this provider is possible only if requested explicitly, for example using the 'kc_idp_hint' parameter.")
private Boolean hideOnLoginPage = false;
@Builder.Default
@Schema(description = "Default sync mode for all mappers. The sync mode determines when user data will be synced using the mappers. Possible values are: 'legacy' to keep the behaviour before this option was introduced, 'import' to only import the user once during first login of the user with this identity provider, 'force' to always update the user during every login with this identity provider.")
@JsonInclude(JsonInclude.Include.NON_NULL)
private IdentityProviderSyncMode syncMode = IdentityProviderSyncMode.IMPORT;
@Schema(description = "The signature algorithm to use to sign documents. Note that 'SHA1' based algorithms are deprecated and can be removed in the future. It is recommended to stick to some more secure algorithm instead of '*_SHA1'.")
@JsonInclude(JsonInclude.Include.NON_NULL)
private IdentityProviderSignatureAlgorithm signatureAlgorithm;
@Schema(description = "SAML signature key name : Signed SAML documents contain identification of signing key in KeyName element. For Keycloak / RH-SSO counter-party, use KEY_ID, for MS AD FS use CERT_SUBJECT, for others check and use NONE if no other option works.")
@JsonInclude(JsonInclude.Include.NON_NULL)
private IdentityProviderSAMLSignatureKeyName xmlSigKeyInfoKeyNameTransformer;
@Builder.Default
@Schema(description = "All not mapped configurations")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Map<String, String> notMappedFields = new HashMap<>();
}

View File

@ -0,0 +1,65 @@
package com.knecon.fforesight.tenantusermanagement.model;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Create request for the identity provider configuration.")
public class IdentityProviderConfigRequest {
@Builder.Default
@Schema(description = "Whether the external identity provider is allowed to create a new identifier to represent the principal (default: true)", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Boolean allowCreate = true;
@Builder.Default
@Schema(description = "Display order : It defines the order of the providers in GUI (for example, on the Login page). The lowest number will be applied first. (default: 0)", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Integer guiOrder = 0;
@Schema(description = "Service provider entity ID : It is used to uniquely identify this SAML Service Provider.", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull
private String entityId;
@Schema(description = "Identity provider entity ID : It is used to validate the Issuer for received SAML assertions. If empty, no Issuer validation is performed.", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull
private String idpEntityId;
@Schema(description = "The Url that must be used to send authentication requests (SAML AuthnRequest).", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull
private String singleSignOnServiceUrl;
@Builder.Default
@Schema(description = "Specifies the URI reference corresponding to a name identifier format. (default: PERSISTENT)", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private IdentityProviderNameIDPolicyFormat nameIDPolicyFormat = IdentityProviderNameIDPolicyFormat.PERSISTENT;
@Builder.Default
@Schema(description = "Used to identify and track external users from the assertion. Default is using Subject NameID, alternatively can be set up as identifying attribute. (default: SUBJECT)", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private IdentityProviderPrincipalType principalType = IdentityProviderPrincipalType.SUBJECT;
@Builder.Default
@Schema(description = "HTTP-POST binding response : Indicates whether to respond to requests using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used. (default: false)", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Boolean postBindingResponse = false;
@Builder.Default
@Schema(description = "HTTP-POST binding for AuthnRequest : Indicates whether the AuthnRequest must be sent using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used. (default: false)", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Boolean postBindingAuthnRequest = false;
@Builder.Default
@Schema(description = "Indicates whether the identity provider expects a signed AuthnRequest. (default: false)", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Boolean wantAuthnRequestsSigned = false;
@Builder.Default
@Schema(description = "The signature algorithm to use to sign documents. Note that 'SHA1' based algorithms are deprecated and can be removed in the future. It is recommended to stick to some more secure algorithm instead of '*_SHA1'. (only used when wantAuthnRequestsSigned is true, default: RSA_SHA256)", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private IdentityProviderSignatureAlgorithm signatureAlgorithm = IdentityProviderSignatureAlgorithm.RSA_SHA256;
@Builder.Default
@Schema(description = "SAML signature key name : Signed SAML documents contain identification of signing key in KeyName element. For Keycloak / RH-SSO counter-party, use KEY_ID, for MS AD FS use CERT_SUBJECT, for others check and use NONE if no other option works. (only used when wantAuthnRequestsSigned is true, default: KEY_ID)", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private IdentityProviderSAMLSignatureKeyName xmlSigKeyInfoKeyNameTransformer = IdentityProviderSAMLSignatureKeyName.KEY_ID;
}

View File

@ -1,5 +1,8 @@
package com.knecon.fforesight.tenantusermanagement.model;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@ -7,12 +10,10 @@ import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SimpleTenantResponse {
private String tenantId;
private String displayName;
private String guid;
@AllArgsConstructor
public class IdentityProviderList {
@Builder.Default
private List<IdentityProviderModel> identityProviders = new ArrayList<>();
}

View File

@ -0,0 +1,69 @@
package com.knecon.fforesight.tenantusermanagement.model;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.Builder;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Object containing information about an identity provider.")
public class IdentityProviderModel implements ExtensibleModel {
@Schema(description = "Alias of the identity provider used for identification")
private String alias;
@Builder.Default
@Schema(description = "Configuration of the identity provider")
private IdentityProviderConfigModel config = new IdentityProviderConfigModel();
@Builder.Default
@Schema(description = "Display name of the identity provider in keycloak")
private String displayName = "";
@Builder.Default
@Schema(description = "The ID of the SSO technology provider")
private String providerId = "saml";
@Builder.Default
@Schema(description = "Enable/disable if tokens must be stored after authenticating users.")
private Boolean storeToken = false;
@Builder.Default
@Schema(description = "Stored tokens readable : Enable/disable if new users can read any stored tokens. This assigns the broker.read-token role.")
private Boolean addReadTokenRoleOnCreate = false;
@Builder.Default
@Schema(description = "If enabled, email provided by this provider is not verified even if verification is enabled for the realm.")
private Boolean trustEmail = false;
@Builder.Default
@Schema(description = "Account linking only : If true, users cannot log in through this provider. They can only link to this provider. This is useful if you don't want to allow login from the provider, but want to integrate with a provider")
private Boolean linkOnly = false;
@Builder.Default
@Schema(description = "First login flow : Alias of authentication flow, which is triggered after first login with this identity provider. Term 'First Login' means that no Keycloak account is currently linked to the authenticated identity provider account.")
private String firstBrokerLoginFlowAlias = "first broker login";
@Builder.Default
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Map<String, String> notMappedFields = new HashMap<>();
public void setSpecificDefaults() {
this.setStoreToken(true);
this.setTrustEmail(true);
this.getConfig().setSyncMode(IdentityProviderSyncMode.FORCE);
}
}

View File

@ -0,0 +1,43 @@
package com.knecon.fforesight.tenantusermanagement.model;
import com.fasterxml.jackson.annotation.JsonValue;
import jakarta.ws.rs.BadRequestException;
import lombok.Getter;
@Getter
public enum IdentityProviderNameIDPolicyFormat {
PERSISTENT("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"),
TRANSIENT("urn:oasis:names:tc:SAML:2.0:nameid-format:transient"),
EMAIL("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"),
KERBEROS("urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos"),
X_509_SUBJECT_NAME("urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"),
WINDOWS_DOMAIN_QUALIFIED_NAME("urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName"),
UNSPECIFIED("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified");
private final String representation;
IdentityProviderNameIDPolicyFormat(String representation) {
this.representation = representation;
}
@Override
@JsonValue
public String toString() {
return representation;
}
public static IdentityProviderNameIDPolicyFormat fromRepresentation(String value) {
for (IdentityProviderNameIDPolicyFormat v : values()) {
if (v.getRepresentation().equalsIgnoreCase(value)) {
return v;
}
}
throw new BadRequestException("Unsupported identity provider name ID policy format");
}
}

View File

@ -0,0 +1,24 @@
package com.knecon.fforesight.tenantusermanagement.model;
import jakarta.ws.rs.BadRequestException;
public enum IdentityProviderPrincipalType {
SUBJECT,
ATTRIBUTE,
FRIENDLY_ATTRIBUTE;
public static IdentityProviderPrincipalType fromStringValue(String value) {
for (IdentityProviderPrincipalType v : values()) {
if (v.toString().equalsIgnoreCase(value)) {
return v;
}
if (value.equalsIgnoreCase("Subject NameID")) {
return SUBJECT;
}
}
throw new BadRequestException("Unsupported identity provider principal type");
}
}

View File

@ -0,0 +1,34 @@
package com.knecon.fforesight.tenantusermanagement.model;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Create or update request for an identity provider.")
public class IdentityProviderRequest {
@Schema(description = "Alias of the identity provider used for identification", requiredMode = Schema.RequiredMode.REQUIRED)
private String alias;
@Builder.Default
@Schema(description = "Configuration of the identity provider", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull
private @Valid IdentityProviderConfigRequest config = new IdentityProviderConfigRequest();
@Builder.Default
@Schema(description = "Display name of the identity provider in keycloak (optional, fallbacks to alias if empty)", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String displayName = "";
@Builder.Default
@Schema(description = "The ID of the SSO technology provider (default: saml)", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String providerId = "saml";
}

View File

@ -0,0 +1,7 @@
package com.knecon.fforesight.tenantusermanagement.model;
public enum IdentityProviderSAMLSignatureKeyName {
NONE,
KEY_ID,
CERT_SUBJECT
}

View File

@ -0,0 +1,10 @@
package com.knecon.fforesight.tenantusermanagement.model;
public enum IdentityProviderSignatureAlgorithm {
RSA_SHA1,
RSA_SHA256,
RSA_SHA256_MGF1,
RSA_SHA512,
RSA_SHA512_MGF1,
DSA_SHA1
}

Some files were not shown because too many files have changed in this diff Show More