Compare commits

..
Author SHA1 Message Date
Lukas Kubik 7552eebd1b Change version to 1.2.0.Final-redhat-2 2015-05-21 18:40:21 +02:00
Lukas Kubik 71a721a80a Change dependencies to use redhat or final versions 2015-05-21 17:41:20 +02:00
Lukas Kubik 3a992ce05f Change version to 1.2.0.Final-redhat-1 2015-05-20 21:56:44 +02:00
Lukas Kubik 20d2da15db Remove repositories 2015-05-20 21:45:56 +02:00
Lukas Kubik fc019f7150 Remove distribution management 2015-05-20 21:45:16 +02:00
Lukas Kubik 7505c9d0b7 Remove skipped deployment 2015-05-20 21:44:43 +02:00
Stian Thorgersen e9f6bf2c6b Fixes to distro 2015-05-20 21:41:07 +02:00
Stian Thorgersen 336372a23d Prod changes 2015-05-20 21:39:19 +02:00
Stian Thorgersen efff6bf2e0 KEYCLOAK-1312 Add clearToken to keycloak.js 2015-05-13 13:14:26 +02:00
Stian Thorgersen b9532419ef KEYCLOAK-1288 2015-05-13 11:50:17 +02:00
Stian Thorgersen f4811b423c Merge pull request #1260 from stianst/1.2.0
Fix welcome-theme in test server
2015-05-13 11:31:39 +02:00
Stian Thorgersen 00d1e0fce3 Fix welcome-theme in test server 2015-05-13 11:25:24 +02:00
Stian Thorgersen 6cab6532ed Merge pull request #1258 from stianst/1.2.0
KEYCLOAK-1310 Don't log migration model for fresh instal
2015-05-13 11:18:13 +02:00
Stian Thorgersen 430108455f KEYCLOAK-1310 Don't log migration model for fresh instal 2015-05-13 11:17:33 +02:00
Stian Thorgersen 28acd183cc Merge pull request #1255 from stianst/1.2.0
KEYCLOAK-1277 Fix upgrading from 1.0.x to 1.2.0.Beta1
2015-05-13 10:53:56 +02:00
Stian Thorgersen 5b5405c1eb KEYCLOAK-1277 Fix upgrading from 1.0.x to 1.2.0.Beta1 2015-05-13 10:52:56 +02:00
Stian Thorgersen 7ff64078fd Merge pull request #1253 from stianst/1.2.0
KEYCLOAK-1307 Fix docs for databaseSchema
2015-05-13 08:11:43 +02:00
Stian Thorgersen 08f8b06de2 KEYCLOAK-1307 Fix docs for databaseSchema 2015-05-13 08:10:29 +02:00
Stian Thorgersen 45b58de49f Merge pull request #1241 from stianst/1.2.0
KEYCLOAK-1289 Support environment variables in keycloak.json
2015-05-11 11:03:47 +02:00
Stian Thorgersen a0368908aa KEYCLOAK-1289 Support environment variables in keycloak.json 2015-05-11 10:16:06 +02:00
mposolda f3f4b8dee7 KEYCLOAK-1284 Allow users to easily inject or configure MongoClient for MongoConnectionProvider 2015-05-07 20:46:17 +02:00
Bill Burke 783f484386 fix nameid policy 2015-05-07 11:02:53 -04:00
Stian Thorgersen cf97bf48bf Merge pull request #1228 from stianst/1.2.0
KEYCLOAK-1282
2015-05-07 09:41:28 +02:00
Stian Thorgersen 72c5afbeb7 KEYCLOAK-1282 2015-05-07 09:41:15 +02:00
Stian Thorgersen fec74f2621 Merge pull request #1226 from stianst/1.2.0
KEYCLOAK-1279
2015-05-07 06:46:48 +02:00
Stian Thorgersen ba740c13c6 KEYCLOAK-1279 2015-05-07 06:45:57 +02:00
Stian Thorgersen bc78e878d2 Merge pull request #1223 from stianst/1.2.0
Fix doc for installing onto EAP
2015-05-07 06:29:29 +02:00
Stian Thorgersen 5da1858d99 Fix doc for installing onto EAP 2015-05-07 06:27:06 +02:00
Stian Thorgersen 4833dd82cb Versions bump 2015-05-05 16:02:51 +02:00
Stian Thorgersen dd74ec97f5 Fix docker cluster deps£ 2015-05-05 11:42:11 +02:00
Stian Thorgersen 76ccc9ea63 Version bump 2015-05-05 09:34:05 +02:00
Stian Thorgersen a917939441 Don't deploy saml examples to maven 2015-05-05 09:33:48 +02:00
Stian Thorgersen f793e883ef Don't deploy saml examples to maven 2015-05-05 08:46:51 +02:00
Stian Thorgersen d2780c0b2e Fix broker examples poms 2015-05-05 07:55:22 +02:00
Stian Thorgersen 896e309866 Fix version setting 2015-05-05 07:31:07 +02:00
Stian Thorgersen a6dbfc7861 Fix version setting 2015-05-05 07:29:14 +02:00
Stian Thorgersen 070ed88337 Fix version setting 2015-05-05 07:24:40 +02:00
Stian Thorgersen bd52908df4 Fix version setting 2015-05-05 07:22:50 +02:00
Stian Thorgersen 5399c20a8c KEYCLOAK-1259 2015-05-05 07:02:00 +02:00
Stian Thorgersen 4a82ed73a4 KEYCLOAK-1252 2015-05-05 07:00:15 +02:00
Stian Thorgersen f388c3d385 KEYCLOAK-1253 2015-05-05 06:49:54 +02:00
Stian Thorgersen 2de0748e95 KEYCLOAK-1255 2015-05-05 06:48:01 +02:00
Stian Thorgersen 997ede06fb KEYCLOAK-1256 2015-05-05 06:39:58 +02:00
Stian Thorgersen 68be5dd12a KEYCLOAK-1257 2015-05-05 06:33:49 +02:00
Stian Thorgersen ad751e419c Merge pull request #1218 from stianst/master
KEYCLOAK-1263
2015-05-05 06:20:30 +02:00
Stian Thorgersen c862479dcd KEYCLOAK-1263 2015-05-05 06:20:09 +02:00
Stian Thorgersen 5cb11b9036 Merge pull request #1217 from stianst/master
KEYCLOAK-1254
2015-05-05 06:16:12 +02:00
Stian Thorgersen f63d52af04 KEYCLOAK-1254 2015-05-05 06:15:46 +02:00
Stian Thorgersen 3c0959640b Merge pull request #1215 from stianst/master
Fixes to brokering
2015-05-05 06:06:22 +02:00
Marek Posolda dea4f4b931 Merge pull request #1216 from mposolda/master
KEYCLOAK-1265 Fix import realm in admin console on EAP 6.4, Update docke...
2015-05-04 23:34:34 +02:00
mposolda 3d26ab8817 KEYCLOAK-1265 Fix import realm in admin console on EAP 6.4, Update docker-cluster 2015-05-04 23:20:13 +02:00
Stian Thorgersen 3f3b21a75e Fix login with Twitter 2015-05-04 21:23:07 +02:00
Stian Thorgersen b0002d6a09 Fix broker examples 2015-05-04 21:09:01 +02:00
Stian Thorgersen c1708ea7d5 Merge pull request #1214 from stianst/master
Fixes
2015-05-04 20:56:17 +02:00
Stian Thorgersen 937ab313e5 Added distribution changes to migration docs 2015-05-04 20:29:30 +02:00
Stian Thorgersen 523f746745 Added providers classpath for standalone/configuration/providers 2015-05-04 20:13:08 +02:00
Stian Thorgersen f5f5d934c8 Fix saml examples 2015-05-04 19:50:05 +02:00
Stian Thorgersen d67804cd0b Renamed change logs to CR1 2015-05-04 19:35:16 +02:00
Stian Thorgersen dcbdb59e00 Updated instructions to deploy cors to use wildfly 2015-05-04 19:35:16 +02:00
Stian Thorgersen 92db89f9e8 Merge pull request #1213 from stianst/master
Updated instructions on deploying example themes
2015-05-04 15:26:50 +02:00
Stian Thorgersen aafc46657a Updated instructions on deploying example themes 2015-05-04 15:26:33 +02:00
Marek Posolda 667e014971 Merge pull request #1212 from mposolda/master
Workaround to have SAMLKeyCloakServerBrokerWithSignatureTest.testAccountManagementLinkIdentity passing with JPA profile
2015-05-04 14:54:18 +02:00
mposolda ed525f8ed9 Workaround to have SAMLKeyCloakServerBrokerWithSignatureTest.testAccountManagementLinkIdentity passing with JPA profile 2015-05-04 14:42:25 +02:00
mposolda e03140a9f2 Fix redirect after unlink federated identity and logout from account management 2015-05-04 14:42:25 +02:00
Stian Thorgersen 6377cb71f7 Merge pull request #1211 from stianst/master
Updated example themes
2015-05-04 12:49:35 +02:00
Stian Thorgersen a3c40f311c Updated example themes 2015-05-04 12:49:18 +02:00
Stian Thorgersen c736fab75e Merge pull request #1210 from stianst/master
Fixes
2015-05-04 11:55:32 +02:00
Stian Thorgersen df442a6ab8 Fix admin-client example 2015-05-04 11:55:09 +02:00
Stian Thorgersen d355d53262 Don't deploy demo bundle and subsystem war to Maven repo 2015-05-04 11:40:06 +02:00
Stian Thorgersen a7bebadf20 Merge pull request #1209 from stianst/master
Fixes to AS7 adapter
2015-05-04 11:35:57 +02:00
Stian Thorgersen 59d7ff3f0a Fixes to AS7 adapter 2015-05-04 11:35:37 +02:00
Stian Thorgersen 7f02ac84ad Merge pull request #1208 from jean-merelis/translate-pt-br
aligning translation pt-br with master
2015-05-04 07:50:57 +02:00
jean b3723cf323 aligning translation pt-br with master 2015-05-01 16:14:21 -03:00
Bill Burke edfe7bd285 Merge pull request #1207 from patriot1burke/master
bump default key sizes
2015-05-01 10:59:20 -04:00
Bill Burke 311fca5a72 exception handling fix 2015-05-01 10:31:03 -04:00
Bill Burke 98d82d2cae stupid 2015-05-01 10:13:57 -04:00
Bill Burke 9372d23ad9 bump default keysize to 2048 2015-05-01 10:11:49 -04:00
Stian Thorgersen de2ce8a04e Merge pull request #1206 from stianst/master
Add Xdoclint option to JavaDoc only if JDK > 1.8
2015-04-30 15:22:19 +02:00
Stian Thorgersen 7eafb14622 Add Xdoclint option to JavaDoc only if JDK > 1.8 2015-04-30 15:05:17 +02:00
Stian Thorgersen fd91d46b5b Merge pull request #1205 from stianst/master
KEYCLOAK-1248 Fix direct grants only switch
2015-04-30 14:41:55 +02:00
Stian Thorgersen 3768c1f1ad KEYCLOAK-1243
Federated Identities table is empty for user synced from classpath properties provider
2015-04-30 14:31:20 +02:00
Stian Thorgersen dcbf91eff0 Fix to social provider buttons 2015-04-30 14:16:25 +02:00
Stian Thorgersen 66242b4176 KEYCLOAK-1244 Save button on user federation redirects to user federation list 2015-04-30 14:00:44 +02:00
Stian Thorgersen 39627f0661 KEYCLOAK-1248 Fix direct grants only switch 2015-04-30 13:44:46 +02:00
Stian Thorgersen 71f2bf77f8 Merge pull request #1204 from stianst/master
Add -Xdoclint:none to make javadoc work on jdk8
2015-04-30 13:31:24 +02:00
Marek Posolda 99c79f9fd7 Merge pull request #1203 from mposolda/master
Minor fix in example
2015-04-30 13:31:10 +02:00
mposolda c942f44437 Minor fix in example 2015-04-30 13:30:05 +02:00
Marek Posolda efaca72631 Merge pull request #1202 from mposolda/master
Docs fix for Application and OAuth client merge. Migration. Docs fix for...
2015-04-30 13:18:09 +02:00
mposolda 6fb98c547a Docs fix for Application and OAuth client merge. Migration. Docs fix for direct access grants 2015-04-30 13:14:38 +02:00
Stian Thorgersen 25bddb8ee7 Add -Xdoclint:none to make javadoc work on jdk8 2015-04-30 13:01:19 +02:00
Stian Thorgersen f1bff01999 Merge pull request #1201 from stianst/master
Misc
2015-04-30 12:52:46 +02:00
Stian Thorgersen 74e5c62069 Updated server installation docs 2015-04-30 12:33:27 +02:00
Marek Posolda e52e13d611 Merge pull request #1200 from mposolda/master
Fix tooltips on FF
2015-04-30 11:33:58 +02:00
mposolda 8f6e7d275e Fix tooltips on FF 2015-04-30 11:32:16 +02:00
Stian Thorgersen ad42b4dde3 Updated demo-dist 2015-04-30 09:40:24 +02:00
Stian Thorgersen 973fa0d4ec Replace Keycloak account theme with PatternFly account theme 2015-04-30 08:24:01 +02:00
Stian Thorgersen 640e6efefd Rename Cache Config tab to Cache 2015-04-30 08:20:27 +02:00
Stian Thorgersen e026c8b206 Fix dark font on login page 2015-04-30 08:16:34 +02:00
Bill Burke 761be66362 Merge pull request #1199 from patriot1burke/master
apache http client fixes
2015-04-29 21:59:50 -04:00
Bill Burke e54bf4a16f hc config and docs 2015-04-29 21:42:32 -04:00
Bill Burke 35c33f132e demo fix and error message fix 2015-04-29 21:16:59 -04:00
Bill Burke 666827b7cb remove resteasy ClientRequest 2015-04-29 20:46:14 -04:00
Bill Burke 10998eb35b Merge pull request #1193 from behana/master
Let admin-client acquire new token if refresh_token is stale
2015-04-29 16:33:20 -04:00
Bill Burke 10d02e7442 Merge pull request #1198 from patriot1burke/master
fix acct theme, broker examples
2015-04-29 15:53:23 -04:00
Bill Burke 8d493381a9 fix account theme 2015-04-29 15:52:15 -04:00
Bill Burke d353add130 Merge remote-tracking branch 'upstream/master' 2015-04-29 14:40:04 -04:00
Bill Burke 77d17001bf broker examples 2015-04-29 14:39:41 -04:00
Stian Thorgersen df317b8368 Merge pull request #1197 from stianst/master
Removed PatternFly themes
2015-04-29 19:51:27 +02:00
Stian Thorgersen ee358ccbc4 Removed PatternFly themes 2015-04-29 19:47:56 +02:00
Stian Thorgersen 4450416905 Merge pull request #1196 from stianst/master
KEYCLOAK-887 Updated admin console theme
2015-04-29 19:34:58 +02:00
Stian Thorgersen 0b821a71e3 KEYCLOAK-887 Updated admin console theme 2015-04-29 19:34:08 +02:00
Bill Burke fd620ef505 Merge pull request #1194 from patriot1burke/master
READ_TOKEN -> read-token
2015-04-29 10:21:48 -04:00
Marek Posolda e05f60b66e Merge pull request #1195 from mposolda/master
When revoke consent from admin console, logout clientSessions similarly ...
2015-04-29 16:21:38 +02:00
Bill Burke 947a2f0c20 READ_TOKEN -> read-token 2015-04-29 09:47:56 -04:00
mposolda 063cefa18c When revoke consent from admin console, logout clientSessions similarly like done in Account mgmt 2015-04-29 15:35:33 +02:00
behana 8dfc7d9e8d Let admin-client acquire new token if refresh_token is stale 2015-04-29 05:01:59 +02:00
Bill Burke d632d22d2a Merge pull request #1188 from shivasaxena/patch-1
corrected URL of XML entity descriptor for the IDP
2015-04-28 21:49:55 -04:00
Bill Burke 4a89fe93b5 Merge pull request #1191 from Smartling/KEYCLOAK-1238
Add Spring Security adapter
2015-04-28 21:49:33 -04:00
Bill Burke 08e1f455e2 Merge pull request #1192 from patriot1burke/master
broker backchannel logout
2015-04-28 19:18:45 -04:00
Bill Burke e65c0d9893 broker backchannel logout console 2015-04-28 19:02:37 -04:00
Bill Burke be656b2dba Merge remote-tracking branch 'upstream/master' 2015-04-28 18:52:42 -04:00
Bill Burke bfcf595346 backchannel broker logout 2015-04-28 18:52:30 -04:00
Scott Rossillo 06a7938aa6 Add Spring Security adapter 2015-04-28 15:05:56 -04:00
Marek Posolda e51cb4df60 Merge pull request #1190 from mposolda/master
KEYCLOAK-1240 Fix stale cache issue
2015-04-28 20:44:26 +02:00
mposolda 553f8a6f27 KEYCLOAK-1240 Fix stale cache issue 2015-04-28 20:20:50 +02:00
Marek Posolda 4d288f1ffc Merge pull request #1189 from mposolda/master
Fix JPA migration of OAuth clients
2015-04-28 18:20:13 +02:00
mposolda 3e5854c651 Fix JPA migration of OAuth clients 2015-04-28 17:44:06 +02:00
Marek Posolda 53fc940952 Merge pull request #1187 from mposolda/master
Fix MySQL
2015-04-28 12:45:33 +02:00
Shiva Saxena c95e21a49a corrected URL of XML entity descriptor for the IDP 2015-04-28 16:09:16 +05:30
mposolda 692898501e Fix MySQL 2015-04-28 12:34:22 +02:00
Marek Posolda 1fbbdb5f4a Merge pull request #1186 from mposolda/master
Fix mongo migration
2015-04-28 11:12:13 +02:00
mposolda d4c69e98ce Fix mongo migration 2015-04-28 11:10:48 +02:00
Marek Posolda 8224f3ba48 Merge pull request #1185 from mposolda/master
KEYCLOAK-1070 and other fixes
2015-04-28 10:16:56 +02:00
mposolda 5e0f560fdc KEYCLOAK-1070 Migration for adding names to builtin clients 2015-04-28 09:35:57 +02:00
mposolda 1d4bf4ddaa UserSessionNote mapper have combobox for claim type in admin console 2015-04-28 07:56:45 +02:00
mposolda 775d1f096c Fix mongo model 2015-04-28 07:56:45 +02:00
mposolda 316053018b KEYCLOAK-1070 Support for view and revoke consents in admin console 2015-04-28 07:56:45 +02:00
mposolda 1d4ef83b81 Show how to retrieve user details in REST endpoint 2015-04-28 07:56:45 +02:00
mposolda 074b6d261a Test how to retrieve underlying error message from the admin-client 2015-04-28 07:56:45 +02:00
Bill Burke bbe2532acb Merge pull request #1184 from patriot1burke/master
model migration
2015-04-27 16:41:54 -04:00
Bill Burke 4166393396 model migration 2015-04-27 16:12:43 -04:00
Stian Thorgersen 9ce452443d Merge pull request #1183 from stianst/master
KEYCLOAK-1189 Add apache httpclient module with slot 4.3
2015-04-27 19:14:44 +02:00
Stian Thorgersen 215a3497ae KEYCLOAK-1189 Add apache httpclient module with slot 4.3 2015-04-27 16:33:48 +02:00
Bill Burke f17863041a Merge pull request #1182 from patriot1burke/master
more broker mappers
2015-04-26 11:20:15 -04:00
Bill Burke 40510683a1 fix tomcat version 2015-04-26 11:10:49 -04:00
Bill Burke 2b1ec74fba docs for broker 2015-04-26 11:04:13 -04:00
Bill Burke 206b44d9c2 Merge remote-tracking branch 'upstream/master' 2015-04-26 10:32:04 -04:00
Bill Burke 44787d83d2 more broker mappers 2015-04-26 10:31:53 -04:00
Bill Burke 23a3aa023c Merge pull request #1181 from patriot1burke/master
filter oidc broker import keys
2015-04-24 19:37:25 -04:00
Bill Burke 9151d2aebd Merge remote-tracking branch 'upstream/master' 2015-04-24 18:37:18 -04:00
Bill Burke 5e1ac684a5 broker logout fixes 2015-04-24 16:32:32 -04:00
Bill Burke 5fca18b3c0 filter oidc broker import keys 2015-04-24 15:03:35 -04:00
Bill Burke 7395d6585c filter oidc broker import keys 2015-04-24 15:03:20 -04:00
Marek Posolda 05d0f1d2ee Merge pull request #1180 from mposolda/master
KEYCLOAK-1216
2015-04-24 19:19:02 +02:00
mposolda a338626d2b KEYCLOAK-1216 Click on 'Logout all sessions' in Account mgmt should propagate logout to the apps 2015-04-24 18:42:18 +02:00
mposolda 767d1fcc20 Don't use JPA eventsStore in mongo tests 2015-04-24 18:42:17 +02:00
Marek Posolda b8d23829aa Merge pull request #1179 from mposolda/master
KEYCLOAK-1070 Improve Applications page and add available roles. Add tes...
2015-04-24 17:15:21 +02:00
mposolda ce747ad039 Change title of Applications page 2015-04-24 16:49:12 +02:00
mposolda 3a4670e8ff KEYCLOAK-1070 Improve Applications page and add available roles. Add tests 2015-04-24 16:40:52 +02:00
Stian Thorgersen 68c3652078 Merge pull request #1178 from stianst/master
More dist updates
2015-04-24 09:24:10 +02:00
Stian Thorgersen 1f49ae8a2f Updated xslts 2015-04-24 09:19:40 +02:00
Stian Thorgersen ac49ba2f44 More tweaks to dist 2015-04-24 07:30:59 +02:00
Stian Thorgersen f0d3fa783a Moved docs into separate dist to reduce size of server-overlay 2015-04-24 07:15:11 +02:00
Bill Burke d007ee3630 Merge pull request #1177 from patriot1burke/master
broker token role
2015-04-23 16:46:22 -04:00
Bill Burke 84fd38316c stupid 2015-04-23 16:34:42 -04:00
Bill Burke 104a9f9842 Merge remote-tracking branch 'upstream/master' 2015-04-23 16:31:09 -04:00
Bill Burke d49e0eda9e broker token role 2015-04-23 15:26:07 -04:00
Marek Posolda 76f406e993 Merge pull request #1176 from mposolda/master
KEYCLOAK-1070 persistent grants - another step
2015-04-23 19:01:37 +02:00
mposolda d1e6b7523e Fix mongo 2015-04-23 18:32:18 +02:00
mposolda 14b3d085a0 KEYCLOAK-1070 Rename 'Access' to 'Applications' 2015-04-23 18:21:21 +02:00
mposolda c942a8ac4f KEYCLOAK-1070 ClientModel.setName/getName 2015-04-23 18:21:21 +02:00
mposolda 535023d800 KEYCLOAK-1070 add setProtocolMappers/getProtocolMappers to ClientSession 2015-04-23 18:20:34 +02:00
Bill Burke dae219d32e Merge pull request #1175 from patriot1burke/master
broker token exchange refactor
2015-04-23 12:08:12 -04:00
Bill Burke a7d245b1fb finish 2015-04-23 11:40:29 -04:00
Bill Burke 1f4df58e6c broker token exchange refactor 2015-04-23 11:26:44 -04:00
Stian Thorgersen d314198e40 Merge pull request #1174 from stianst/master
Build javadoc as part of release process
2015-04-23 16:09:59 +02:00
Stian Thorgersen 35b6c88500 Don't replace welcome-content in server-overlay 2015-04-23 15:53:04 +02:00
Stian Thorgersen f74e1463df Updated docs for server dist changes 2015-04-23 15:53:04 +02:00
Stian Thorgersen 37243d058c Fixes 2015-04-23 15:53:03 +02:00
Stian Thorgersen 927032997e Updated release instructions 2015-04-23 15:53:03 +02:00
Stian Thorgersen e64a2d8de5 Fixes 2015-04-23 15:53:03 +02:00
Stian Thorgersen e697d5ebf5 Merge pull request #1173 from stianst/master
Updates to deps
2015-04-23 13:24:13 +02:00
Stian Thorgersen 9d5cd2091e Updates to deps 2015-04-23 13:23:22 +02:00
Stian Thorgersen 870f29d797 Merge pull request #1172 from stianst/master
Dist work
2015-04-23 12:40:38 +02:00
Stian Thorgersen f037aaa1af Removed project-integrations as it's moved to a separate repo 2015-04-23 11:19:20 +02:00
Stian Thorgersen e17105cc8e Updated distribution and cleaning of maven modules 2015-04-23 11:15:05 +02:00
Stian Thorgersen cf75a97f08 Merge pull request #1169 from gkfirst8/KEYCLOAK-1220
log the right value when keycloak.config.file could not be found or read
2015-04-22 14:08:29 +02:00
Marek Posolda 8e0252ecb1 Merge pull request #1171 from mposolda/master
KEYCLOAK-1070
2015-04-22 12:09:51 +02:00
mposolda 34e033e351 KEYCLOAK-1070 JSON representation and export/import for userConsent 2015-04-22 11:29:43 +02:00
mposolda 11035dbe1b KEYCLOAK-1070 Rename GrantedConsentModel to UserConsentModel. Mongo support for consents. Mongo fixes 2015-04-22 11:29:36 +02:00
gkfirst8 0b782e9910 Fix logging call 2015-04-22 10:54:43 +02:00
Stian Thorgersen 136fd37b1b Merge pull request #1170 from stianst/master
KEYCLOAK-1218 Better security for ServerInfoAdminResource
2015-04-22 09:43:11 +02:00
Stian Thorgersen b5f3efe272 KEYCLOAK-1218 Better security for ServerInfoAdminResource 2015-04-22 08:59:00 +02:00
Marek Posolda 955967f78a Merge pull request #1167 from panga/master
KEYCLOAK-1222 - BasicAuth KeycloakSecurityContext token issue
2015-04-22 08:39:44 +02:00
Leonardo Loch Zanivan 642fc996fb Basic Auth token fix 2015-04-21 16:22:14 -03:00
Leonardo Loch Zanivan 6ce0285315 Basic Auth token fix 2015-04-21 12:32:17 -03:00
gkfirst8 aef61411e9 log the right value when keycloak.config.file could not be found or read
Fix for [KEYCLOAK-1220]
2015-04-21 11:46:33 +02:00
Stian Thorgersen 83a409bbf6 Merge pull request #1165 from stianst/master
KEYCLOAK-1208 Allow same-origin if cors is enabled
2015-04-21 10:57:47 +02:00
Stian Thorgersen 5ed864fbbc KEYCLOAK-1208 Allow same-origin if cors is enabled 2015-04-21 10:42:13 +02:00
Bill Burke 416e71fa97 Merge pull request #1164 from patriot1burke/master
broker mapper tests
2015-04-20 18:35:15 -04:00
Bill Burke f9b099d49d Merge remote-tracking branch 'upstream/master' 2015-04-20 18:20:04 -04:00
Bill Burke 193aa23581 broker mapper tests 2015-04-20 18:04:30 -04:00
Bill Burke 3a4750c6de Merge pull request #1163 from patriot1burke/master
broker user attribute mapper
2015-04-20 15:01:02 -04:00
Bill Burke 63201c10f5 turn off ide test 2015-04-20 14:36:24 -04:00
Bill Burke 65f058a088 Merge remote-tracking branch 'upstream/master' 2015-04-20 14:33:44 -04:00
Bill Burke c95c9fa022 broker user attribute mapper 2015-04-20 14:33:31 -04:00
Bill Burke af736a29f1 Merge pull request #1144 from TFaga/master
[KEYCLOAK-1211] Fixed AD users authenticating without providing a password
2015-04-20 12:31:58 -04:00
Bill Burke 22f26a6221 Merge pull request #1162 from patriot1burke/master
saml broker role mapper
2015-04-20 12:31:31 -04:00
Bill Burke d30b81144e ignore test 2015-04-20 12:00:22 -04:00
Bill Burke e32ef66e2a Merge remote-tracking branch 'upstream/master' 2015-04-20 11:55:01 -04:00
Bill Burke 3ceb818ee5 saml broker role mapper 2015-04-20 11:54:39 -04:00
Stian Thorgersen 6b1e4b2ab3 Merge pull request #1161 from stianst/master
KEYCLOAK-1210 Link to Google Authenticator is broken
2015-04-20 09:59:34 +02:00
Stian Thorgersen 3a4141f5b5 KEYCLOAK-1210 Link to Google Authenticator is broken 2015-04-20 09:44:26 +02:00
Stian Thorgersen 5ea6169214 Merge pull request #1157 from girirajsharma/master
Minor issue: Fixed wildfly ide-configs link parsing.
2015-04-20 07:11:22 +02:00
Bill Burke 46c63d9169 Merge pull request #1160 from patriot1burke/master
oidc broker role mapper
2015-04-17 18:36:57 -04:00
Bill Burke db1d6e79d9 more surefire mem needed 2015-04-17 17:44:10 -04:00
Bill Burke 8eb2f35423 Merge remote-tracking branch 'upstream/master' 2015-04-17 16:41:16 -04:00
Bill Burke a7c563b0eb oidc broker role mapper 2015-04-17 16:41:04 -04:00
Marek Posolda 6a6ddc8a0c Merge pull request #1159 from mposolda/master
Improve kerberos documentation a bit
2015-04-17 18:44:07 +02:00
mposolda 6965364813 Improve kerberos documentation a bit 2015-04-17 18:43:04 +02:00
Marek Posolda 9937be1cdf Merge pull request #1158 from mposolda/master
KEYCLOAK-1070 Persistent grants - step 1
2015-04-17 17:11:53 +02:00
mposolda 78830836b8 KEYCLOAK-1070 Persistent grants - step 1 2015-04-17 14:03:23 +02:00
Stian Thorgersen aea5055b30 Merge branch 'fiorenzino-KEYCLOAK-1215' 2015-04-17 13:06:19 +02:00
Giriraj Sharma a75d608f87 Minor issue: Fixed wildfly ide-configs link parsing. 2015-04-17 16:36:17 +05:30
Stian Thorgersen d52a365766 Add it to admin console language options and fix test 2015-04-17 13:06:07 +02:00
Stian Thorgersen c071601660 Merge branch 'KEYCLOAK-1215' of https://github.com/fiorenzino/keycloak into fiorenzino-KEYCLOAK-1215 2015-04-17 12:58:22 +02:00
Stian Thorgersen fad0e80bcf Merge pull request #1155 from girirajsharma/master
[KEYCLOAK-402] - Force password changes at regular intervals
2015-04-17 12:55:45 +02:00
girirajsharma f959332117 [KEYCLOAK-402] - Force password changes at regular intervals 2015-04-16 23:41:45 +05:30
fiorenzo pizza fb7ca4928d some single quote 2015-04-16 17:17:34 +02:00
fiorenzo pizza f5bc936e0d admin messages 2015-04-16 17:14:37 +02:00
fiorenzo pizza 1ef5bf2abf it translation 2015-04-16 16:27:02 +02:00
Marek Posolda 14b2401aa8 Merge pull request #1154 from fiorenzino/KEYCLOAK-1214
support Novell eDirectory in federation module
2015-04-16 15:24:48 +02:00
Stian Thorgersen 6e5820ea11 Merge pull request #1153 from stianst/master
KEYCLOAK-1061 Add back to application link to error page
2015-04-16 14:41:10 +02:00
Stian Thorgersen 115e533a71 KEYCLOAK-1061 Add back to application link to error page
KEYCLOAK-1148 space in redirect uri gives error
2015-04-16 14:17:32 +02:00
Stian Thorgersen 495ab25301 Merge pull request #1152 from stianst/master
KEYCLOAK-1207 Invalid direct access example
2015-04-16 12:54:46 +02:00
Stian Thorgersen afe4f84135 KEYCLOAK-1207 Invalid direct access example 2015-04-16 12:54:14 +02:00
Stian Thorgersen 7d853dabe4 Merge pull request #1151 from stianst/master
KEYCLOAK-1212 CORS requests blocked on OPTIONS request
2015-04-16 12:50:55 +02:00
fiorenzo pizza e32d7c52fc first commit 2015-04-16 12:47:17 +02:00
Stian Thorgersen 5addabe893 KEYCLOAK-1212 CORS requests blocked on OPTIONS request 2015-04-16 11:27:30 +02:00
Stian Thorgersen 0daa3ee29f Merge pull request #1150 from stianst/master
Update not found and forbidden in admin console
2015-04-16 10:36:24 +02:00
Stian Thorgersen 9c5e38a023 Updated not found and forbidden access to show a proper page instead of just notification 2015-04-16 10:35:52 +02:00
Stian Thorgersen d42c917448 Fix styling on not found page 2015-04-16 09:32:37 +02:00
Stian Thorgersen 8fb60226c8 Merge pull request #1149 from stianst/master
Hide realm caret if only one realm
2015-04-16 09:23:42 +02:00
Stian Thorgersen fda190de01 Hide realm caret if only one realm 2015-04-16 09:23:25 +02:00
Stian Thorgersen 58dd353238 Merge pull request #1148 from stianst/kc-context
KEYCLOAK-1213 Installation file for bearer-only doesn't include auth-ser...
2015-04-16 09:20:22 +02:00
Stian Thorgersen 0027d22964 KEYCLOAK-1213 Installation file for bearer-only doesn't include auth-server-url 2015-04-16 09:19:55 +02:00
Stian Thorgersen 1a05f0f3b4 Merge pull request #1145 from ssilvert/run-kc-as-root-context
Document how to run Keycloak Server as root context.
2015-04-16 09:11:55 +02:00
Bill Burke 476da4e3e0 Merge pull request #1146 from patriot1burke/master
phased broker mapper changes
2015-04-15 11:21:26 -04:00
Bill Burke edb9f0cecf merge conflicts 2015-04-15 11:18:47 -04:00
Bill Burke 0addc4cf58 broker mapper mgmt 2015-04-15 11:10:53 -04:00
Tilen Faganel 770d2d8a4c [KEYCLOAK-1211] Fixed Active Directory users authenticating without providing a password 2015-04-15 16:59:55 +02:00
Stan Silvert f4898884e4 Document how to run Keycloak Server as root context. 2015-04-15 10:36:38 -04:00
Stian Thorgersen 5ef1ddb9f1 Merge pull request #1143 from stianst/kc-context
Added KeycloakContext
2015-04-15 14:02:14 +02:00
Stian Thorgersen f3e5541b7d KEYCLOAK-1109 Added KeycloakContext
KEYCLOAK-1042 Access to Cookies within Events
KEYCLOAK-1209 Enable jboss-logging event listener by default and allow configuring log level
2015-04-15 13:34:54 +02:00
Stian Thorgersen a19a3f4277 Merge pull request #1142 from stianst/master
Improvements to password history policy and tests
2015-04-15 12:57:42 +02:00
Stian Thorgersen 3f327326aa Improvements to password history policy and tests 2015-04-15 12:14:28 +02:00
Stian Thorgersen 29ea24c8fb Merge pull request #1141 from dbarentine/master
[KEYCLOAK-1205] Fix BearerAuthFilter refreshing token
2015-04-15 11:09:26 +02:00
Dane Barentine 4fe328002a [KEYCLOAK-1206] Change role mapping path in admin client to match API changes 2015-04-14 22:21:28 -07:00
Dane Barentine b1b149e0c3 [KEYCLOAK-1205] Fix BearerAuthFilter refreshing token after it's expired 2015-04-14 22:19:07 -07:00
Stian Thorgersen 47da227809 Merge pull request #1132 from girirajsharma/master
[KEYCLOAK-405] - Feature that doesn't allow old password to be reused
2015-04-15 06:53:07 +02:00
Stian Thorgersen 43d33260c9 Merge pull request #1140 from jean-merelis/work
update translation (pt-BR) for version 1.2.0.RC1
2015-04-15 06:52:04 +02:00
girirajsharma ad715371a3 Added password policy test cases for regex and password history. 2015-04-15 02:08:06 +05:30
girirajsharma 0ff4b6dd93 [KEYCLOAK-405]-Fixed deletion of expired Credentials if policy removed. 2015-04-15 02:08:05 +05:30
girirajsharma e3bb61248a [KEYCLOAK-405] - Feature that doesn't allow old password to be reused 2015-04-15 02:08:05 +05:30
jean e75e85cf80 update translation (pt-BR) for version 1.2.0.RC1 2015-04-14 10:25:17 -03:00
Stian Thorgersen 375d061f58 Merge pull request #1139 from stianst/master
Updates to dev docs
2015-04-14 10:39:00 +02:00
Stian Thorgersen 78eb5acb8e Updates to README.md 2015-04-14 10:38:25 +02:00
Stian Thorgersen 656a5c4baa Updated hacking on KC guide 2015-04-14 10:31:07 +02:00
Stian Thorgersen b8d05c452c Merge pull request #1138 from stianst/master
KEYCLOAK-1187
2015-04-14 09:28:26 +02:00
Stian Thorgersen f6631a6163 KEYCLOAK-1187 2015-04-14 09:28:05 +02:00
Stian Thorgersen c0f6b6d8d0 Updated README.md and started HackingOnKeycloak guide 2015-04-14 09:27:51 +02:00
Stian Thorgersen d13acaa9ef Merge pull request #1137 from stianst/master
KEYCLOAK-1187
2015-04-14 09:27:24 +02:00
Stian Thorgersen 8ebac35706 Removed Thread.sleep from tests and replaced with Time.offset 2015-04-14 08:59:22 +02:00
Stian Thorgersen 517e42f13d KEYCLOAK-1187 2015-04-14 08:05:24 +02:00
Stian Thorgersen 9c6e6c8284 KEYCLOAK-1187 2015-04-14 07:49:18 +02:00
Stian Thorgersen a9ed193826 KEYCLOAK-1187 2015-04-14 07:36:30 +02:00
Stian Thorgersen a94fd9a098 APPLICATION_DEFAULT_ROLES and APP_NODE_REGISTRATIONS to CLIENT_... 2015-04-14 07:36:30 +02:00
Stian Thorgersen 411ff56499 RealmEntity.applications --> RealmEntity.clients 2015-04-14 07:36:30 +02:00
Stian Thorgersen c70d12a411 Renamed entity master admin app to master admin client 2015-04-14 07:36:30 +02:00
Bill Burke d4f138cadc Merge pull request #1136 from dbarentine/master
KEYCLOAK-1202 Set AudienceRestriction to the issuer from the original re...
2015-04-13 20:15:49 -04:00
Dane Barentine da70391677 KEYCLOAK-1202 Set AudienceRestriction to the issuer from the original request. 2015-04-13 12:47:40 -07:00
Bill Burke f61fb4ba18 fix conflicts 2015-04-13 09:59:59 -04:00
Bill Burke d2a5403527 refactor for broker mappers 2015-04-13 09:45:23 -04:00
Stian Thorgersen 06ac706057 Merge pull request #1135 from stianst/master
Updated example jsons
2015-04-13 14:23:58 +02:00
Stian Thorgersen 4abd62661f Updated example jsons 2015-04-13 14:23:41 +02:00
Stian Thorgersen f56f1c85b9 Merge pull request #1134 from stianst/master
KEYCLOAK-1187
2015-04-13 14:18:18 +02:00
Stian Thorgersen 46e386cd43 KEYCLOAK-1187 2015-04-13 13:54:30 +02:00
Stian Thorgersen 600353899a KEYCLOAK-1187 2015-04-13 13:32:18 +02:00
Stian Thorgersen 4fbbf39c51 KEYCLOAK-1187 Admin console and endpoints 2015-04-13 13:29:31 +02:00
Stian Thorgersen 9a047eda36 KEYCLOAK-1187 Updated representations 2015-04-13 10:13:32 +02:00
Stian Thorgersen 716bf104db Merge pull request #1133 from stianst/master
More work on KEYCLOAK-1187
2015-04-10 14:46:41 +02:00
Stian Thorgersen f435a8c6df Remove @Test on methods in AdapterTestStrategy. These are not executed from other test classes. Having the @Test on methods in a non-empty constructor causes IntelliJ to complain 2015-04-10 13:35:27 +02:00
Stian Thorgersen a18715a774 Deprecate OAuthClientRepresentation and ApplicationRepresentation and added ClientRepresentation 2015-04-10 13:33:29 +02:00
Stian Thorgersen f0f0aae88d Merge pull request #1131 from stianst/master
KEYCLOAK-1187 DB migration support for oauth/application to client
2015-04-10 09:48:34 +02:00
Stian Thorgersen 4ae9310f83 KEYCLOAK-1187 DB migration support for oauth/application to client 2015-04-10 09:21:34 +02:00
Stian Thorgersen 1567982f0b Merge pull request #1128 from ssilvert/KEYCLOAK-1174-NPE-on-WF9
KEYCLOAK-1174: NPE on WildFly 9
2015-04-10 07:05:46 +02:00
Stan Silvert 875aae91fc Add owner attribute to Keycloak server deployment 2015-04-09 14:52:47 -04:00
Stan Silvert bbef4e2be1 KEYCLOAK-1174: Refactor KeycloakAdapterConfigService to be a simple
singleton
2015-04-09 10:30:39 -04:00
Stian Thorgersen 4955f36115 Merge pull request #1125 from velias/KEYCLOAK-1195
KEYCLOAK-1195 - added identity brokes ordering for login page
2015-04-09 15:44:43 +02:00
Vlastimil Elias 0606cb4a5e KEYCLOAK-1195 - added identity brokes ordering for login page 2015-04-09 14:39:00 +02:00
Stian Thorgersen 85e7cf5783 Merge pull request #1127 from stianst/master
KEYCLOAK-1187 First round
2015-04-09 13:13:01 +02:00
Stian Thorgersen 6fbc0975c0 KEYCLOAK-1187 First round: Combined ApplicationModel and OAuthClientModel into ClientModel. Removed OAuth Clients from Admin console and renamed Applications to Clients. 2015-04-09 12:27:30 +02:00
Marek Posolda b92a178142 Merge pull request #1126 from mposolda/master
KEYCLOAK-1007 Fork Picketlink LDAP code. Remove picketlink dependencies ...
2015-04-08 22:52:57 +02:00
mposolda eb5ae4aae9 KEYCLOAK-1007 Fork Picketlink LDAP code. Remove picketlink dependencies from LDAP Federation provider 2015-04-08 22:04:02 +02:00
Bill Burke dc23ef6188 Merge pull request #1124 from jpkrohling/JPK-Typos
KEYCLOAK-1192 KEYCLOAK-1193 KEYCLOAK-1194 - Fixed typos in tooltip and documentation.
2015-04-08 11:21:43 -04:00
Juraci Paixão Kröhling b0b28a9f38 KEYCLOAK-1192 KEYCLOAK-1193 KEYCLOAK-1194 - Fixed typos in tooltip and documentation. 2015-04-08 14:20:01 +02:00
Stian Thorgersen e6f6f92403 Merge pull request #1121 from velias/KEYCLOAK-1182
KEYCLOAK-1182 - string values for user profile are now sanitized during reading from social providers
2015-04-08 09:49:43 +02:00
Bill Burke 63f8d92a8b Merge pull request #1123 from patriot1burke/master
broker mapper datamodel
2015-04-07 20:44:50 -04:00
Bill Burke a42a750ebb Merge remote-tracking branch 'upstream/master' 2015-04-07 18:04:15 -04:00
Bill Burke 335832d553 broker mapper datamodel 2015-04-07 18:04:03 -04:00
Vlastimil Elias ac1e8e8389 KEYCLOAK-1182 - string values for user profile are now sanitized during
reading from social providers. Related unit tests added.
2015-04-07 14:01:31 +02:00
Marek Posolda 448fb35a72 Merge pull request #1120 from mposolda/master
KEYCLOAK-1181 Running liquibase updates via maven plugin doesn't work
2015-04-07 11:24:08 +02:00
mposolda 2ba2e7768d KEYCLOAK-1181 Running Liquibase updates via maven plugin doesn't work 2015-04-07 10:27:44 +02:00
mposolda d0ead0f0a0 More logging 2015-04-07 08:46:39 +02:00
Bill Burke 463310e1c3 Merge pull request #1113 from girirajsharma/master
[KEYCLOAK-400]Provide a configuration for regex in the password policies
2015-04-03 15:00:57 -04:00
Bill Burke 5ca9af0b1d Merge pull request #1118 from patriot1burke/master
initial Picketlink SAML fork
2015-04-03 14:32:21 -04:00
Bill Burke e795bdc16c Merge remote-tracking branch 'upstream/master' 2015-04-03 14:04:35 -04:00
Bill Burke 37306e689d initial Picketlink SAML fork 2015-04-03 14:04:19 -04:00
girirajsharma ca13e3c4ba [KEYCLOAK-400]Provide a configuration for regex in the password policies 2015-04-02 23:37:25 +05:30
Bill Burke 3a5861c624 Merge pull request #1117 from patriot1burke/master
bump pom version
2015-04-02 09:37:15 -04:00
Bill Burke 326818ea45 bump pom versio 2015-04-02 09:36:43 -04:00
Stian Thorgersen 148b466ce4 Merge pull request #1115 from mposolda/master
Fix clustering when auth-server-url-for-backend-requests is used
2015-04-02 13:13:56 +02:00
Stian Thorgersen 18e4be3cc9 Merge pull request #1116 from stianst/master
Comment out store tokens in admin console, and store/retrieve tokens in ...
2015-04-02 13:13:41 +02:00
mposolda 6a34ad36f5 Fix clustering when auth-server-url-for-backend-requests is used 2015-04-02 13:02:24 +02:00
mposolda 56a9451128 minor improve in fuse example 2015-04-02 10:43:29 +02:00
1622 changed files with 138852 additions and 42739 deletions

No files matched your search

+67 -34
View File
@@ -1,39 +1,72 @@
keycloak
Keycloak
========
Please visit [http://keycloak.org](http://keycloak.org) for more information on Keycloak including how to download, documentation,
and video tutorials.
Keycloak is an SSO Service for web apps and REST services. For more information visit [http://keycloak.org](http://keycloak.org).
Keycloak is an SSO Service for web apps and REST services.
It can be used for social applications as well as enterprise applications. It is based on OpenID Connect with support for SAML 2.0 as well.
Here's some of the features:
* SSO and Single Log Out for browser applications
* Social Broker. Enable Google, Facebook, Yahoo, Twitter, GitHub, LinkedIn social login with no code required.
* Optional LDAP/Active Directory integration
* Optional User Registration
* Password and TOTP support (via Google Authenticator or FreeOTP). Client cert auth coming soon.
* User session management from both admin and user perspective
* Customizable themes for user facing pages: login, grant pages, account management, emails, and admin console all customizable!
* OAuth Bearer token auth for REST Services
* Integrated Browser App to REST Service token propagation
* Admin REST API
* OAuth 2.0 Grant requests
* CORS Support
* CORS Web Origin management and validation
* Completely centrally managed user and role mapping metadata. Minimal configuration at the application side
* Admin Console for managing users, roles, role mappings, applications, user sessions, allowed CORS web origins, and OAuth clients.
* Deployable as a WAR, appliance, or an Openshift cloud service (SaaS).
* Supports JBoss AS7, EAP 6.x, Wildfly, Tomcat, and Jetty applications. Plans to support Node.js, RAILS, GRAILS, and other non-Java applications.
* Javascript/HTML 5 adapter for pure Javascript apps
* Session management from admin console
* Revocation policies
* Password policies
* OpenID Connect Support
* SAML Support
* Token claim and SAML assertion mappings, role name mappings, etc. Ability to configure exactly what information you want in your tokens and SAML documents
* IDP brokering or chaining. You can set up Keycloak to be a child IDP to another SAML or OIDC IDP.
* Kerberos bridging. Logged in Kerberos users can access Keycloak SAML or OIDC applications via our Kerberos bridge.
Building
--------
Please visit [http://keycloak.org](http://keycloak.org) for more information on Keycloak including how to download, documentation,
and video tutorials.
Ensure you have JDK 7 (or newer), Maven 3.2.1 (or newer) and Git installed
java -version
mvn -version
git --version
First clone the Keycloak repository:
git clone https://github.com/keycloak/keycloak.git
cd keycloak
To build Keycloak run:
mvn install
This will build all modules and run the testsuite.
To build the distribution run:
mvn install -Pdistribution
Once completed you will find distribution archives in `distribution`.
Starting Keycloak
-----------------
To start Keycloak during development first build as specficied above, then run:
mvn -f testsuite/integration/pom.xml exec:java -Pkeycloak-server
To start Keycloak from the appliance distribution first build the distribution it as specified above, then run:
tar xfz distribution/appliance-dist/target/keycloak-appliance-dist-all-<VERSION>.tar.gz
cd keycloak-appliance-dist-all-<VERSION>/keycloak
bin/standalone.sh
To stop the server press `Ctrl + C`.
Contributing
------------
* See [Hacking on Keycloak](misc/HackingOnKeycloak.md)
Documentation
-------------
* [User Guide, Admin REST API and Javadocs](http://keycloak.jboss.org/docs)
* Developer documentation
* [Hacking on Keycloak](misc/HackingOnKeycloak.md) - how to become a Keycloak contributor
* [Testsuite](misc/Testsuite.md) - details about testsuite, but also how to quickly run Keycloak during development and a few test tools (OTP generation, LDAP server, Mail server)
* [Database Testing](misc/DatabaseTesting.md) - how to do testing of Keycloak on different databases
* [Updating Database](misc/UpdatingDatabaseSchema.md) - how to change the Keycloak database
* [Release Process](misc/ReleaseProcess.md) - how to release Keycloak
License
-------
* [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)
+50 -53
View File
@@ -1,53 +1,50 @@
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-core</artifactId>
<name>Keycloak Broker Core</name>
<description/>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-events-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Final-redhat-2</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-core</artifactId>
<name>Keycloak Broker Core</name>
<description/>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-events-api</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
@@ -18,8 +18,11 @@
package org.keycloak.broker.provider;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import javax.ws.rs.core.Response;
@@ -59,4 +62,24 @@ public abstract class AbstractIdentityProvider<C extends IdentityProviderModel>
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
return null;
}
@Override
public void backchannelLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
}
@Override
public void attachUserSession(UserSessionModel userSession, ClientSessionModel clientSession, BrokeredIdentityContext context) {
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context) {
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context) {
}
}
@@ -0,0 +1,31 @@
package org.keycloak.broker.provider;
import org.keycloak.broker.provider.IdentityProviderMapper;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public abstract class AbstractIdentityProviderMapper implements IdentityProviderMapper {
@Override
public void close() {
}
@Override
public IdentityProviderMapper create(KeycloakSession session) {
return null;
}
@Override
public void init(org.keycloak.Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
}
@@ -17,25 +17,33 @@
*/
package org.keycloak.broker.provider;
import org.keycloak.models.IdentityProviderModel;
import java.util.HashMap;
import java.util.Map;
/**
* <p>Represents all identity information obtained from an {@link IdentityProvider} after a
* <p>Represents all identity information obtained from an {@link org.keycloak.broker.provider.IdentityProvider} after a
* successful authentication.</p>
*
* @author Pedro Igor
*/
public class FederatedIdentity {
public class BrokeredIdentityContext {
private String id;
private String username;
private String email;
private String firstName;
private String lastName;
private String email;
private String token;
private String identityProviderId;
private String brokerSessionId;
private String brokerUserId;
private String code;
private String token;
private IdentityProviderModel idpConfig;
private IdentityProvider idp;
private Map<String, Object> contextData = new HashMap<>();
public FederatedIdentity(String id) {
public BrokeredIdentityContext(String id) {
if (id == null) {
throw new RuntimeException("No identifier provider for identity.");
}
@@ -59,26 +67,6 @@ public class FederatedIdentity {
this.username = username;
}
public String getFirstName() {
return firstName;
}
public void setName(String name) {
if (name != null) {
int i = name.lastIndexOf(' ');
if (i != -1) {
firstName = name.substring(0, i);
lastName = name.substring(i + 1);
} else {
firstName = name;
}
}
}
public String getLastName() {
return lastName;
}
public String getEmail() {
return email;
}
@@ -87,23 +75,6 @@ public class FederatedIdentity {
this.email = email;
}
public void setToken(String token) {
this.token = token;
}
public String getToken() {
return this.token;
}
public String getIdentityProviderId() {
return this.identityProviderId;
}
public void setIdentityProviderId(String identityProviderId) {
this.identityProviderId = identityProviderId;
}
public String getBrokerSessionId() {
return brokerSessionId;
}
@@ -120,16 +91,81 @@ public class FederatedIdentity {
this.brokerUserId = brokerUserId;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public IdentityProviderModel getIdpConfig() {
return idpConfig;
}
public void setIdpConfig(IdentityProviderModel idpConfig) {
this.idpConfig = idpConfig;
}
public IdentityProvider getIdp() {
return idp;
}
public void setIdp(IdentityProvider idp) {
this.idp = idp;
}
public Map<String, Object> getContextData() {
return contextData;
}
public void setContextData(Map<String, Object> contextData) {
this.contextData = contextData;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setName(String name) {
if (name != null) {
int i = name.lastIndexOf(' ');
if (i != -1) {
firstName = name.substring(0, i);
lastName = name.substring(i + 1);
} else {
firstName = name;
}
}
}
@Override
public String toString() {
return "{" +
"id='" + id + '\'' +
", username='" + username + '\'' +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", token='" + token + '\'' +
", identityProviderId='" + identityProviderId + '\'' +
'}';
}
}
@@ -0,0 +1,110 @@
package org.keycloak.broker.provider;
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderConfigProperty;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class HardcodedRoleMapper extends AbstractIdentityProviderMapper {
public static final String ROLE = "role";
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty();
property.setName(ROLE);
property.setLabel("Role");
property.setHelpText("Role to grant to user. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
}
public static String[] parseRole(String role) {
int scopeIndex = role.indexOf('.');
if (scopeIndex > -1) {
String appName = role.substring(0, scopeIndex);
role = role.substring(scopeIndex + 1);
String[] rtn = {appName, role};
return rtn;
} else {
String[] rtn = {null, role};
return rtn;
}
}
public static RoleModel getRoleFromString(RealmModel realm, String roleName) {
String[] parsedRole = parseRole(roleName);
RoleModel role = null;
if (parsedRole[0] == null) {
role = realm.getRole(parsedRole[1]);
} else {
ClientModel client = realm.getClientByClientId(parsedRole[0]);
role = client.getRole(parsedRole[1]);
}
return role;
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getDisplayCategory() {
return "Role Importer";
}
@Override
public String getDisplayType() {
return "Hardcoded Role";
}
public static final String[] COMPATIBLE_PROVIDERS = {ANY_PROVIDER};
public static final String PROVIDER_ID = "oidc-hardcoded-role-idp-mapper";
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String[] getCompatibleProviders() {
return COMPATIBLE_PROVIDERS;
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String roleName = mapperModel.getConfig().get(ROLE);
RoleModel role = getRoleFromString(realm, roleName);
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
user.grantRole(role);
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
}
@Override
public String getHelpText() {
return "When user is imported from provider, hardcode a role mapping for it.";
}
}
@@ -18,15 +18,17 @@
package org.keycloak.broker.provider;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.provider.Provider;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.Map;
/**
* @author Pedro Igor
@@ -38,15 +40,16 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
* This method should be called by provider after the JAXRS callback endpoint has finished authentication
* with the remote IDP
*
* @param userNotes notes to add to the UserSessionModel
* @param identityProviderConfig provider config
* @param federatedIdentity federated identity
* @param code relayState or state parameter used to identity the client session
* @param context
* @return
*/
public Response authenticated(Map<String, String> userNotes, IdentityProviderModel identityProviderConfig, FederatedIdentity federatedIdentity, String code);
public Response authenticated(BrokeredIdentityContext context);
}
void attachUserSession(UserSessionModel userSession, ClientSessionModel clientSession, BrokeredIdentityContext context);
void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context);
void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context);
/**
* JAXRS callback endpoint for when the remote IDP wants to callback to keycloak.
*
@@ -58,17 +61,11 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
* <p>Initiates the authentication process by sending an authentication request to an identity provider. This method is called
* only once during the authentication.</p>
*
* <p>Depending on how the authentication is performed, this method may redirect the user to the identity provider for authentication.
* In this case, the response would contain a {@link javax.ws.rs.core.Response} that will be used to redirect the user.</p>
*
* <p>However, if the authentication flow does not require a redirect to the identity provider (eg.: simple challenge/response mechanism), this method may return a response containing
* a {@link FederatedIdentity} representing the identity information for an user. In this case, the authentication flow stops.</p>
*
* @param request The initial authentication request. Contains all the contextual information in order to build an authentication request to the
* identity provider.
* @return
*/
Response handleRequest(AuthenticationRequest request);
Response performLogin(AuthenticationRequest request);
/**
* <p>Returns a {@link javax.ws.rs.core.Response} containing the token previously stored during the authentication process for a
@@ -79,6 +76,8 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
*/
Response retrieveToken(FederatedIdentityModel identity);
void backchannelLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm);
/**
* Called when a Keycloak application initiates a logout through the browser. This is expected to do a logout
* with the IDP
@@ -0,0 +1,26 @@
package org.keycloak.broker.provider;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ConfiguredProvider;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface IdentityProviderMapper extends Provider, ProviderFactory<IdentityProviderMapper>,ConfiguredProvider {
public static final String ANY_PROVIDER = "*";
String[] getCompatibleProviders();
String getDisplayCategory();
String getDisplayType();
void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
}
@@ -0,0 +1,27 @@
package org.keycloak.broker.provider;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class IdentityProviderMapperSpi implements Spi {
@Override
public String getName() {
return "identity-provider-mapper";
}
@Override
public Class<? extends Provider> getProviderClass() {
return IdentityProviderMapper.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return IdentityProviderMapper.class;
}
}
@@ -1,7 +1,4 @@
package org.keycloak.broker.oidc.util;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
package org.keycloak.broker.provider.util;
import java.io.IOException;
import java.io.InputStream;
@@ -21,14 +18,13 @@ import java.util.zip.GZIPInputStream;
*/
public class SimpleHttp {
private static ObjectMapper mapper = new ObjectMapper();
private String url;
private String method;
private Map<String, String> headers;
private Map<String, String> params;
private SimpleHttp(String url, String method) {
protected SimpleHttp(String url, String method) {
this.url = url;
this.method = method;
}
@@ -57,9 +53,6 @@ public class SimpleHttp {
return this;
}
public JsonNode asJson() throws IOException {
return mapper.readTree(asString());
}
public String asString() throws IOException {
boolean get = method.equals("GET");
@@ -126,11 +119,105 @@ public class SimpleHttp {
return toString(is);
} finally {
if (os != null) {
os.close();
try {
os.close();
} catch (IOException e) {
}
}
if (is != null) {
is.close();
try {
is.close();
} catch (IOException e) {
}
}
if (connection != null) {
try {
connection.disconnect();
} catch (Exception e) {
}
}
}
}
public int asStatus() throws IOException {
boolean get = method.equals("GET");
boolean post = method.equals("POST");
StringBuilder sb = new StringBuilder();
if (get) {
sb.append(url);
}
if (params != null) {
boolean f = true;
for (Map.Entry<String, String> p : params.entrySet()) {
if (f) {
f = false;
if (get) {
sb.append("?");
}
} else {
sb.append("&");
}
sb.append(URLEncoder.encode(p.getKey(), "UTF-8"));
sb.append("=");
sb.append(URLEncoder.encode(p.getValue(), "UTF-8"));
}
}
if (get) {
url = sb.toString();
}
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
OutputStream os = null;
InputStream is = null;
try {
connection.setRequestMethod(method);
if (headers != null) {
for (Map.Entry<String, String> h : headers.entrySet()) {
connection.setRequestProperty(h.getKey(), h.getValue());
}
}
if (post) {
String data = sb.toString();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Length", String.valueOf(data.length()));
os = connection.getOutputStream();
os.write(data.getBytes());
} else {
connection.setDoOutput(false);
}
is = connection.getInputStream();
return connection.getResponseCode();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
}
}
if (connection != null) {
try {
connection.disconnect();
} catch (Exception e) {
}
}
}
}
@@ -0,0 +1 @@
org.keycloak.broker.provider.HardcodedRoleMapper
@@ -1 +1,2 @@
org.keycloak.broker.provider.IdentityProviderSpi
org.keycloak.broker.provider.IdentityProviderSpi
org.keycloak.broker.provider.IdentityProviderMapperSpi
+58 -57
View File
@@ -1,57 +1,58 @@
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-oidc</artifactId>
<name>Keycloak Broker - OpenID Connect Identity Provider</name>
<description/>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-broker-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-events-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Final-redhat-2</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-oidc</artifactId>
<name>Keycloak Broker - OpenID Connect Identity Provider</name>
<description/>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-broker-core</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-events-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -22,10 +22,10 @@ import org.codehaus.jackson.map.ObjectMapper;
import org.jboss.logging.Logger;
import org.keycloak.ClientConnection;
import org.keycloak.OAuth2Constants;
import org.keycloak.broker.oidc.util.SimpleHttp;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.broker.provider.AbstractIdentityProvider;
import org.keycloak.broker.provider.AuthenticationRequest;
import org.keycloak.broker.provider.FederatedIdentity;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
@@ -33,8 +33,8 @@ import org.keycloak.events.EventType;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.services.ErrorPage;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.flows.Flows;
import javax.ws.rs.GET;
import javax.ws.rs.QueryParam;
@@ -45,8 +45,6 @@ import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -87,7 +85,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
}
@Override
public Response handleRequest(AuthenticationRequest request) {
public Response performLogin(AuthenticationRequest request) {
try {
URI authorizationUrl = createAuthorizationUrl(request).build();
@@ -108,11 +106,22 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
}
protected String extractTokenFromResponse(String response, String tokenName) {
if(response == null)
return null;
if (response.startsWith("{")) {
try {
return mapper.readTree(response).get(tokenName).getTextValue();
JsonNode node = mapper.readTree(response);
if(node.has(tokenName)){
String s = node.get(tokenName).getTextValue();
if(s == null || s.trim().isEmpty())
return null;
return s;
} else {
return null;
}
} catch (IOException e) {
throw new IdentityBrokerException("Could not extract token [" + tokenName + "] from response [" + response + "].", e);
throw new IdentityBrokerException("Could not extract token [" + tokenName + "] from response [" + response + "] due: " + e.getMessage(), e);
}
} else {
Matcher matcher = Pattern.compile(tokenName + "=([^&]+)").matcher(response);
@@ -125,22 +134,21 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
return null;
}
protected FederatedIdentity getFederatedIdentity(Map<String, String> notes, String response) {
protected BrokeredIdentityContext getFederatedIdentity(String response) {
String accessToken = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);
if (accessToken == null) {
throw new IdentityBrokerException("No access token from server.");
throw new IdentityBrokerException("No access token available in OAuth server response: " + response);
}
return doGetFederatedIdentity(accessToken);
}
protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
return null;
}
;
protected UriBuilder createAuthorizationUrl(AuthenticationRequest request) {
return UriBuilder.fromUri(getConfig().getAuthorizationUrl())
@@ -151,9 +159,20 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
.queryParam(OAUTH2_PARAMETER_REDIRECT_URI, request.getRedirectUri());
}
/**
* Get JSON property as text. JSON numbers and booleans are converted to text. Empty string is converted to null.
*
* @param jsonNode to get property from
* @param name of property to get
* @return string value of the property or null.
*/
protected String getJsonProperty(JsonNode jsonNode, String name) {
if (jsonNode.has(name)) {
return jsonNode.get(name).asText();
if (jsonNode.has(name) && !jsonNode.get(name).isNull()) {
String s = jsonNode.get(name).asText();
if(s != null && !s.isEmpty())
return s;
else
return null;
}
return null;
@@ -196,7 +215,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
//logger.error("Failed " + getConfig().getAlias() + " broker login: " + error);
event.event(EventType.LOGIN);
event.error(error);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
}
try {
@@ -204,21 +223,24 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
if (authorizationCode != null) {
String response = generateTokenRequest(authorizationCode).asString();
HashMap<String, String> userNotes = new HashMap<String, String>();
FederatedIdentity federatedIdentity = getFederatedIdentity(userNotes, response);
BrokeredIdentityContext federatedIdentity = getFederatedIdentity(response);
if (getConfig().isStoreToken()) {
federatedIdentity.setToken(response);
}
return callback.authenticated(userNotes, getConfig(), federatedIdentity, state);
federatedIdentity.setCode(state);
federatedIdentity.setIdpConfig(getConfig());
federatedIdentity.setIdp(AbstractOAuth2IdentityProvider.this);
return callback.authenticated(federatedIdentity);
}
} catch (Exception e) {
logger.error("Failed to make identity provider oauth callback", e);
}
event.event(EventType.LOGIN);
event.error(Errors.IDENTITY_PROVIDER_LOGIN_FAILURE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
}
public SimpleHttp generateTokenRequest(String authorizationCode) {
@@ -1,17 +1,18 @@
package org.keycloak.broker.oidc;
import org.keycloak.broker.oidc.util.SimpleHttp;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.events.EventBuilder;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.JsonWebToken;
import org.keycloak.representations.adapters.action.AdminAction;
import org.keycloak.representations.adapters.action.LogoutAction;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.util.JsonSerialization;
import org.keycloak.util.PemUtils;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
@@ -25,6 +26,8 @@ import java.security.PublicKey;
*/
public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
public static final String VALIDATED_ACCESS_TOKEN = "VALIDATED_ACCESS_TOKEN";
public KeycloakOIDCIdentityProvider(OIDCIdentityProviderConfig config) {
super(config);
}
@@ -34,6 +37,12 @@ public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
return new KeycloakEndpoint(callback, realm, event);
}
@Override
protected void processAccessTokenResponse(BrokeredIdentityContext context, PublicKey idpKey, AccessTokenResponse response) {
JsonWebToken access = validateToken(idpKey, response.getToken());
context.getContextData().put(VALIDATED_ACCESS_TOKEN, access);
}
protected class KeycloakEndpoint extends OIDCEndpoint {
public KeycloakEndpoint(AuthenticationCallback callback, RealmModel realm, EventBuilder event) {
super(callback, realm, event);
@@ -61,8 +70,11 @@ public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
for (String sessionId : action.getKeycloakSessionIds()) {
String brokerSessionId = getConfig().getAlias() + "." + sessionId;
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId);
if (userSession != null) {
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers);
if (userSession != null
&& userSession.getState() != UserSessionModel.State.LOGGING_OUT
&& userSession.getState() != UserSessionModel.State.LOGGED_OUT
) {
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, false);
}
}
@@ -90,11 +102,13 @@ public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
@Override
public SimpleHttp generateTokenRequest(String authorizationCode) {
return super.generateTokenRequest(authorizationCode)
.param(AdapterConstants.APPLICATION_SESSION_STATE, "n/a"); // hack to get backchannel logout to work
.param(AdapterConstants.CLIENT_SESSION_STATE, "n/a"); // hack to get backchannel logout to work
}
}
}
@@ -19,10 +19,7 @@ package org.keycloak.broker.oidc;
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
@@ -19,25 +19,27 @@ package org.keycloak.broker.oidc;
import org.codehaus.jackson.JsonNode;
import org.jboss.logging.Logger;
import org.keycloak.RSATokenVerifier;
import org.keycloak.broker.oidc.util.SimpleHttp;
import org.keycloak.broker.oidc.util.JsonSimpleHttp;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.broker.provider.AuthenticationRequest;
import org.keycloak.broker.provider.FederatedIdentity;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken;
import org.keycloak.representations.JsonWebToken;
import org.keycloak.services.ErrorPage;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.IdentityBrokerService;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.resources.flows.Flows;
import org.keycloak.util.JsonSerialization;
import org.keycloak.util.PemUtils;
@@ -50,7 +52,6 @@ import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.security.PublicKey;
import java.util.Map;
/**
* @author Pedro Igor
@@ -61,6 +62,9 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
public static final String OAUTH2_PARAMETER_PROMPT = "prompt";
public static final String SCOPE_OPENID = "openid";
public static final String FEDERATED_ID_TOKEN = "FEDERATED_ID_TOKEN";
public static final String USER_INFO = "UserInfo";
public static final String FEDERATED_ACCESS_TOKEN_RESPONSE = "FEDERATED_ACCESS_TOKEN_RESPONSE";
public static final String VALIDATED_ID_TOKEN = "VALIDATED_ID_TOKEN";
public OIDCIdentityProvider(OIDCIdentityProviderConfig config) {
super(config);
@@ -92,13 +96,6 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
}
protected boolean verify(JWSInput jws, PublicKey key) {
if (key == null) return true;
if (!getConfig().isValidateSignature()) return true;
return RSAProvider.verify(jws, key);
}
protected class OIDCEndpoint extends Endpoint {
public OIDCEndpoint(AuthenticationCallback callback, RealmModel realm, EventBuilder event) {
super(callback, realm, event);
@@ -116,33 +113,66 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
EventBuilder event = new EventBuilder(realm, session, clientConnection);
event.event(EventType.LOGOUT);
event.error(Errors.USER_SESSION_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
}
if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {
logger.error("usersession in different state");
EventBuilder event = new EventBuilder(realm, session, clientConnection);
event.event(EventType.LOGOUT);
event.error(Errors.USER_SESSION_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.SESSION_NOT_ACTIVE);
return ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE);
}
return AuthenticationManager.finishBrowserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
}
}
@Override
public void backchannelLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
if (getConfig().getLogoutUrl() == null || getConfig().getLogoutUrl().trim().equals("") || !getConfig().isBackchannelSupported()) return;
String idToken = userSession.getNote(FEDERATED_ID_TOKEN);
if (idToken == null) return;
backchannelLogout(userSession, idToken);
}
protected void backchannelLogout(UserSessionModel userSession, String idToken) {
String sessionId = userSession.getId();
UriBuilder logoutUri = UriBuilder.fromUri(getConfig().getLogoutUrl())
.queryParam("state", sessionId);
logoutUri.queryParam("id_token_hint", idToken);
String url = logoutUri.build().toString();
try {
int status = JsonSimpleHttp.doGet(url).asStatus();
boolean success = status >=200 && status < 400;
if (!success) {
logger.warn("Failed backchannel broker logout to: " + url);
}
} catch (Exception e) {
logger.warn("Failed backchannel broker logout to: " + url, e);
}
}
@Override
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
if (getConfig().getLogoutUrl() == null || getConfig().getLogoutUrl().trim().equals("")) return null;
UriBuilder logoutUri = UriBuilder.fromUri(getConfig().getLogoutUrl())
.queryParam("state", userSession.getId());
String idToken = userSession.getNote(FEDERATED_ID_TOKEN);
if (idToken != null) logoutUri.queryParam("id_token_hint", idToken);
String redirect = RealmsResource.brokerUrl(uriInfo)
.path(IdentityBrokerService.class, "getEndpoint")
.path(OIDCEndpoint.class, "logoutResponse")
.build(realm.getName(), getConfig().getAlias()).toString();
logoutUri.queryParam("post_logout_redirect_uri", redirect);
return Response.status(302).location(logoutUri.build()).build();
if (idToken != null && getConfig().isBackchannelSupported()) {
backchannelLogout(userSession, idToken);
return null;
} else {
String sessionId = userSession.getId();
UriBuilder logoutUri = UriBuilder.fromUri(getConfig().getLogoutUrl())
.queryParam("state", sessionId);
if (idToken != null) logoutUri.queryParam("id_token_hint", idToken);
String redirect = RealmsResource.brokerUrl(uriInfo)
.path(IdentityBrokerService.class, "getEndpoint")
.path(OIDCEndpoint.class, "logoutResponse")
.build(realm.getName(), getConfig().getAlias()).toString();
logoutUri.queryParam("post_logout_redirect_uri", redirect);
Response response = Response.status(302).location(logoutUri.build()).build();
return response;
}
}
@Override
@@ -157,8 +187,12 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
return authorizationUrl;
}
protected void processAccessTokenResponse(BrokeredIdentityContext context, PublicKey idpKey, AccessTokenResponse response) {
}
@Override
protected FederatedIdentity getFederatedIdentity(Map<String, String> notes, String response) {
protected BrokeredIdentityContext getFederatedIdentity(String response) {
AccessTokenResponse tokenResponse = null;
try {
tokenResponse = JsonSerialization.readValue(response, AccessTokenResponse.class);
@@ -170,32 +204,31 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
String encodedIdToken = tokenResponse.getIdToken();
notes.put(FEDERATED_ACCESS_TOKEN, accessToken);
notes.put(FEDERATED_ID_TOKEN, encodedIdToken);
notes.put(FEDERATED_REFRESH_TOKEN, tokenResponse.getRefreshToken());
notes.put(FEDERATED_TOKEN_EXPIRATION, Long.toString(tokenResponse.getExpiresIn()));
IDToken idToken = validateIdToken(key, encodedIdToken);
JsonWebToken idToken = validateToken(key, encodedIdToken);
try {
String id = idToken.getSubject();
String name = idToken.getName();
String preferredUsername = idToken.getPreferredUsername();
String email = idToken.getEmail();
BrokeredIdentityContext identity = new BrokeredIdentityContext(id);
String name = (String)idToken.getOtherClaims().get(IDToken.NAME);
String preferredUsername = (String)idToken.getOtherClaims().get(IDToken.PREFERRED_USERNAME);
String email = (String)idToken.getOtherClaims().get(IDToken.EMAIL);
if (id == null || name == null || preferredUsername == null || email == null && getConfig().getUserInfoUrl() != null) {
JsonNode userInfo = SimpleHttp.doGet(getConfig().getUserInfoUrl())
.header("Authorization", "Bearer " + accessToken)
.asJson();
if (getConfig().getUserInfoUrl() != null && (id == null || name == null || preferredUsername == null || email == null) ) {
SimpleHttp request = JsonSimpleHttp.doGet(getConfig().getUserInfoUrl())
.header("Authorization", "Bearer " + accessToken);
JsonNode userInfo = JsonSimpleHttp.asJson(request);
id = getJsonProperty(userInfo, "sub");
name = getJsonProperty(userInfo, "name");
preferredUsername = getJsonProperty(userInfo, "preferred_username");
email = getJsonProperty(userInfo, "email");
identity.getContextData().put(USER_INFO, userInfo);
}
FederatedIdentity identity = new FederatedIdentity(id);
identity.getContextData().put(FEDERATED_ACCESS_TOKEN_RESPONSE, tokenResponse);
identity.getContextData().put(VALIDATED_ID_TOKEN, idToken);
processAccessTokenResponse(identity, key, tokenResponse);
identity.setId(id);
identity.setName(name);
@@ -235,23 +268,34 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
return accessToken;
}
private IDToken validateIdToken(PublicKey key, String encodedToken) {
protected boolean verify(JWSInput jws, PublicKey key) {
if (key == null) return true;
if (!getConfig().isValidateSignature()) return true;
return RSAProvider.verify(jws, key);
}
protected JsonWebToken validateToken(PublicKey key, String encodedToken) {
if (encodedToken == null) {
throw new IdentityBrokerException("No id_token from server.");
throw new IdentityBrokerException("No token from server.");
}
try {
JWSInput jws = new JWSInput(encodedToken);
if (!verify(jws, key)) {
throw new IdentityBrokerException("IDToken signature validation failed");
throw new IdentityBrokerException("token signature validation failed");
}
IDToken idToken = jws.readJsonContent(IDToken.class);
JsonWebToken token = jws.readJsonContent(JsonWebToken.class);
String aud = idToken.getAudience();
String iss = idToken.getIssuer();
String aud = token.getAudience();
String iss = token.getIssuer();
if (aud != null && !aud.equals(getConfig().getClientId())) {
throw new IdentityBrokerException("Wrong audience from id_token..");
throw new IdentityBrokerException("Wrong audience from token.");
}
if (!token.isActive()) {
throw new IdentityBrokerException("Token is no longer valid");
}
String trustedIssuers = getConfig().getIssuer();
@@ -261,18 +305,25 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
for (String trustedIssuer : issuers) {
if (iss != null && iss.equals(trustedIssuer.trim())) {
return idToken;
return token;
}
}
throw new IdentityBrokerException("Wrong issuer from id_token. Got: " + iss + " expected: " + getConfig().getIssuer());
throw new IdentityBrokerException("Wrong issuer from token. Got: " + iss + " expected: " + getConfig().getIssuer());
}
return idToken;
return token;
} catch (IOException e) {
throw new IdentityBrokerException("Could not decode id token.", e);
throw new IdentityBrokerException("Could not decode token.", e);
}
}
@Override
public void attachUserSession(UserSessionModel userSession, ClientSessionModel clientSession, BrokeredIdentityContext context) {
AccessTokenResponse tokenResponse = (AccessTokenResponse)context.getContextData().get(FEDERATED_ACCESS_TOKEN_RESPONSE);
userSession.setNote(FEDERATED_ACCESS_TOKEN, tokenResponse.getToken());
userSession.setNote(FEDERATED_ID_TOKEN, tokenResponse.getIdToken());
}
@Override
protected String getDefaultScopes() {
return "openid";
@@ -70,6 +70,14 @@ public class OIDCIdentityProviderConfig extends OAuth2IdentityProviderConfig {
getConfig().put("validateSignature", String.valueOf(validateSignature));
}
public boolean isBackchannelSupported() {
return Boolean.valueOf(getConfig().get("backchannelSupported"));
}
public void setBackchannelSupported(boolean backchannel) {
getConfig().put("backchannelSupported", String.valueOf(backchannel));
}
@@ -17,8 +17,7 @@
*/
package org.keycloak.broker.oidc;
import org.codehaus.jackson.annotate.JsonProperty;
import org.keycloak.broker.oidc.util.SimpleHttp;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
import org.keycloak.jose.jwk.JWK;
import org.keycloak.jose.jwk.JWKParser;
@@ -27,7 +26,6 @@ import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.representations.JSONWebKeySet;
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
import org.keycloak.util.JsonSerialization;
import org.keycloak.util.PemUtils;
import java.io.IOException;
import java.io.InputStream;
@@ -82,7 +80,7 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
JSONWebKeySet keySet = JsonSerialization.readValue(keySetString, JSONWebKeySet.class);
for (JWK jwk : keySet.getKeys()) {
JWKParser parse = JWKParser.create(jwk);
if (parse.getJwk().getPublicKeyUse().equals(JWK.SIG_USE)) {
if (parse.getJwk().getPublicKeyUse().equals(JWK.SIG_USE) && keyTypeSupported(jwk.getKeyType())) {
PublicKey key = parse.toPublicKey();
config.setPublicKeySignatureVerifier(KeycloakModelUtils.getPemFromKey(key));
config.setValidateSignature(true);
@@ -97,4 +95,8 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
}
return config.getConfig();
}
protected static boolean keyTypeSupported(String type) {
return type != null && type.equals("RSA");
}
}
@@ -0,0 +1,93 @@
package org.keycloak.broker.oidc.mappers;
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProvider;
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.JsonWebToken;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public abstract class AbstractClaimMapper extends AbstractIdentityProviderMapper {
public static final String CLAIM = "claim";
public static final String CLAIM_VALUE = "claim.value";
public static Object getClaimValue(JsonWebToken token, String claim) {
String[] split = claim.split("\\.");
Map<String, Object> jsonObject = token.getOtherClaims();
for (int i = 0; i < split.length; i++) {
if (i == split.length - 1) {
return jsonObject.get(split[i]);
} else {
Object val = jsonObject.get(split[i]);
if (!(val instanceof Map)) return null;
jsonObject = (Map<String, Object>)val;
}
}
return null;
}
protected Object getClaimValue(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String claim = mapperModel.getConfig().get(CLAIM);
{ // search access token
JsonWebToken token = (JsonWebToken)context.getContextData().get(KeycloakOIDCIdentityProvider.VALIDATED_ACCESS_TOKEN);
if (token != null) {
Object value = getClaimValue(token, claim);
if (value != null) return value;
}
}
{ // search ID Token
JsonWebToken token = (JsonWebToken)context.getContextData().get(KeycloakOIDCIdentityProvider.VALIDATED_ID_TOKEN);
if (token != null) {
Object value = getClaimValue(token, claim);
if (value != null) return value;
}
}
return null;
}
protected boolean hasClaimValue(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
Object value = getClaimValue(mapperModel, context);
String desiredValue = mapperModel.getConfig().get(CLAIM_VALUE);
return valueEquals(desiredValue, value);
}
public boolean valueEquals(String desiredValue, Object value) {
if (value instanceof String) {
if (desiredValue.equals(value)) return true;
} else if (value instanceof Double) {
try {
if (Double.valueOf(desiredValue).equals(value)) return true;
} catch (Exception e) {
}
} else if (value instanceof Integer) {
try {
if (Integer.valueOf(desiredValue).equals(value)) return true;
} catch (Exception e) {
}
} else if (value instanceof Boolean) {
try {
if (Boolean.valueOf(desiredValue).equals(value)) return true;
} catch (Exception e) {
}
} else if (value instanceof List) {
List list = (List)value;
for (Object val : list) {
return valueEquals(desiredValue, val);
}
}
return false;
}
}
@@ -0,0 +1,105 @@
package org.keycloak.broker.oidc.mappers;
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory;
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.HardcodedRoleMapper;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderConfigProperty;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ClaimToRoleMapper extends AbstractClaimMapper {
public static final String[] COMPATIBLE_PROVIDERS = {KeycloakOIDCIdentityProviderFactory.PROVIDER_ID, OIDCIdentityProviderFactory.PROVIDER_ID};
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
ProviderConfigProperty property;
ProviderConfigProperty property1;
property1 = new ProviderConfigProperty();
property1.setName(CLAIM);
property1.setLabel("Claim");
property1.setHelpText("Name of claim to search for in token. You can reference nested claims using a '.', i.e. 'address.locality'.");
property1.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property1);
property1 = new ProviderConfigProperty();
property1.setName(CLAIM_VALUE);
property1.setLabel("Claim Value");
property1.setHelpText("Value the claim must have. If the claim is an array, then the value must be contained in the array.");
property1.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property1);
property = new ProviderConfigProperty();
property.setName(HardcodedRoleMapper.ROLE);
property.setLabel("Role");
property.setHelpText("Role to grant to user if claim is present. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
}
public static final String PROVIDER_ID = "oidc-role-idp-mapper";
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String[] getCompatibleProviders() {
return COMPATIBLE_PROVIDERS;
}
@Override
public String getDisplayCategory() {
return "Role Importer";
}
@Override
public String getDisplayType() {
return "Claim to Role";
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
if (hasClaimValue(mapperModel, context)) {
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
user.grantRole(role);
}
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
if (!hasClaimValue(mapperModel, context)) {
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
user.deleteRoleMapping(role);
}
}
@Override
public String getHelpText() {
return "If a claim exists, grant the user the specified realm or application role.";
}
}
@@ -0,0 +1,118 @@
package org.keycloak.broker.oidc.mappers;
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProvider;
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory;
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.HardcodedRoleMapper;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.JsonWebToken;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ExternalKeycloakRoleToRoleMapper extends AbstractClaimMapper {
public static final String[] COMPATIBLE_PROVIDERS = {KeycloakOIDCIdentityProviderFactory.PROVIDER_ID};
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
private static final String EXTERNAL_ROLE = "external.role";
static {
ProviderConfigProperty property;
ProviderConfigProperty property1;
property1 = new ProviderConfigProperty();
property1.setName(EXTERNAL_ROLE);
property1.setLabel("External role");
property1.setHelpText("External role to check for. To reference an application role the syntax is appname.approle, i.e. myapp.myrole.");
property1.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property1);
property = new ProviderConfigProperty();
property.setName(HardcodedRoleMapper.ROLE);
property.setLabel("Role");
property.setHelpText("Role to grant to user if external role is present. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
}
public static final String PROVIDER_ID = "keycloak-oidc-role-to-role-idp-mapper";
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String[] getCompatibleProviders() {
return COMPATIBLE_PROVIDERS;
}
@Override
public String getDisplayCategory() {
return "Role Importer";
}
@Override
public String getDisplayType() {
return "External Role to Role";
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
RoleModel role = hasRole(realm, mapperModel, context);
if (role != null) {
user.grantRole(role);
}
}
private RoleModel hasRole(RealmModel realm,IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
JsonWebToken token = (JsonWebToken)context.getContextData().get(KeycloakOIDCIdentityProvider.VALIDATED_ACCESS_TOKEN);
//if (token == null) return;
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
String[] parseRole = HardcodedRoleMapper.parseRole(mapperModel.getConfig().get(EXTERNAL_ROLE));
String externalRoleName = parseRole[1];
String claimName = null;
if (parseRole[0] == null) {
claimName = "realm_access.roles";
} else {
claimName = "resource_access." + parseRole[0] + ".roles";
}
Object claim = getClaimValue(token, claimName);
if (valueEquals(externalRoleName, claim)) {
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
return role;
}
return null;
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
RoleModel role = hasRole(realm, mapperModel, context);
if (role == null) {
user.deleteRoleMapping(role);
}
}
@Override
public String getHelpText() {
return "Looks for an external role in a keycloak access token. If external role exists, grant the user the specified realm or application role.";
}
}
@@ -0,0 +1,100 @@
package org.keycloak.broker.oidc.mappers;
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory;
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderConfigProperty;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserAttributeMapper extends AbstractClaimMapper {
public static final String[] COMPATIBLE_PROVIDERS = {KeycloakOIDCIdentityProviderFactory.PROVIDER_ID, OIDCIdentityProviderFactory.PROVIDER_ID};
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
public static final String USER_ATTRIBUTE = "user.attribute";
static {
ProviderConfigProperty property;
ProviderConfigProperty property1;
property1 = new ProviderConfigProperty();
property1.setName(CLAIM);
property1.setLabel("Claim");
property1.setHelpText("Name of claim to search for in token. You can reference nested claims using a '.', i.e. 'address.locality'.");
property1.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property1);
property = new ProviderConfigProperty();
property.setName(USER_ATTRIBUTE);
property.setLabel("User Attribute Name");
property.setHelpText("User attribute name to store claim.");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
}
public static final String PROVIDER_ID = "oidc-user-attribute-idp-mapper";
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String[] getCompatibleProviders() {
return COMPATIBLE_PROVIDERS;
}
@Override
public String getDisplayCategory() {
return "Attribute Importer";
}
@Override
public String getDisplayType() {
return "Attribute Importer";
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
Object value = getClaimValue(mapperModel, context);
if (value != null) {
user.setAttribute(attribute, value.toString());
}
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
Object value = getClaimValue(mapperModel, context);
String current = user.getAttribute(attribute);
if (value != null && !value.equals(current)) {
user.setAttribute(attribute, value.toString());
} else if (value == null) {
user.removeAttribute(attribute);
}
}
@Override
public String getHelpText() {
return "Import declared claim if it exists in ID or access token into the specified user attribute.";
}
}
@@ -0,0 +1,32 @@
package org.keycloak.broker.oidc.util;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.keycloak.broker.provider.util.SimpleHttp;
import java.io.IOException;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JsonSimpleHttp extends SimpleHttp {
public JsonSimpleHttp(String url, String method) {
super(url, method);
}
public static JsonSimpleHttp doGet(String url) {
return new JsonSimpleHttp(url, "GET");
}
public static JsonSimpleHttp doPost(String url) {
return new JsonSimpleHttp(url, "POST");
}
private static ObjectMapper mapper = new ObjectMapper();
public static JsonNode asJson(SimpleHttp request) throws IOException {
return mapper.readTree(request.asString());
}
}
@@ -0,0 +1,3 @@
org.keycloak.broker.oidc.mappers.ClaimToRoleMapper
org.keycloak.broker.oidc.mappers.ExternalKeycloakRoleToRoleMapper
org.keycloak.broker.oidc.mappers.UserAttributeMapper
@@ -0,0 +1,133 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
*/
package org.keycloak.broker.oidc;
import org.codehaus.jackson.JsonNode;
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.models.IdentityProviderModel;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* Unit test for {@link AbstractOAuth2IdentityProvider}
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public class AbstractOAuth2IdentityProviderTest {
@Test
public void constructor_defaultScopeHandling(){
TestProvider tested = getTested();
//default scope is set from the provider if not provided in the configuration
Assert.assertEquals( tested.getDefaultScopes(), tested.getConfig().getDefaultScope());
//default scope is preserved if provided in the configuration
IdentityProviderModel model = new IdentityProviderModel();
OAuth2IdentityProviderConfig config = new OAuth2IdentityProviderConfig(model);
config.setDefaultScope("myscope");
tested = new TestProvider(config);
Assert.assertEquals("myscope", tested.getConfig().getDefaultScope());
}
@Test
public void getJsonProperty_asJsonNode() throws IOException {
TestProvider tested = getTested();
JsonNode jsonNode = tested
.asJsonNode("{\"nullone\":null, \"emptyone\":\"\", \"blankone\": \" \", \"withvalue\" : \"my value\", \"withbooleanvalue\" : true, \"withnumbervalue\" : 10}");
Assert.assertNull(tested.getJsonProperty(jsonNode, "nonexisting"));
Assert.assertNull(tested.getJsonProperty(jsonNode, "nullone"));
Assert.assertNull(tested.getJsonProperty(jsonNode, "emptyone"));
Assert.assertEquals(" ", tested.getJsonProperty(jsonNode, "blankone"));
Assert.assertEquals("my value", tested.getJsonProperty(jsonNode, "withvalue"));
Assert.assertEquals("true", tested.getJsonProperty(jsonNode, "withbooleanvalue"));
Assert.assertEquals("10", tested.getJsonProperty(jsonNode, "withnumbervalue"));
}
@Test(expected = IdentityBrokerException.class)
public void getFederatedIdentity_responseUrlLine_tokenNotFound() {
TestProvider tested = getTested();
tested.getFederatedIdentity("cosi=sss");
}
@Test(expected = IdentityBrokerException.class)
public void getFederatedIdentity_responseJSON_tokenNotFound() {
TestProvider tested = getTested();
Map<String, String> notes = new HashMap<>();
tested.getFederatedIdentity("{\"cosi\":\"sss\"}");
}
@Test(expected = IdentityBrokerException.class)
public void getFederatedIdentity_responseJSON_invalidFormat() {
TestProvider tested = getTested();
Map<String, String> notes = new HashMap<>();
tested.getFederatedIdentity("{\"cosi\":\"sss\"");
}
@Test(expected = IdentityBrokerException.class)
public void getFederatedIdentity_responseJSON_emptyTokenField() {
TestProvider tested = getTested();
tested.getFederatedIdentity("{\""
+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "\" : \"\"}");
}
@Test(expected = IdentityBrokerException.class)
public void getFederatedIdentity_responseJSON_nullTokenField() {
TestProvider tested = getTested();
tested.getFederatedIdentity("{\""
+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "\" : null}");
}
@Test
public void getFederatedIdentity_responseJSON() {
TestProvider tested = getTested();
BrokeredIdentityContext fi = tested.getFederatedIdentity("{\""
+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "\" : \"458rt\"}");
Assert.assertNotNull(fi);
Assert.assertEquals("458rt", fi.getId());
}
@Test
public void getFederatedIdentity_responseUrlLine() {
TestProvider tested = getTested();
BrokeredIdentityContext fi = tested.getFederatedIdentity("cosi=sss&"
+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "=458rtf&kdesi=ss}");
Assert.assertNotNull(fi);
Assert.assertEquals("458rtf", fi.getId());
}
private TestProvider getTested() {
IdentityProviderModel model = new IdentityProviderModel();
OAuth2IdentityProviderConfig config = new OAuth2IdentityProviderConfig(model);
return new TestProvider(config);
}
private static class TestProvider extends AbstractOAuth2IdentityProvider<OAuth2IdentityProviderConfig> {
public TestProvider(OAuth2IdentityProviderConfig config) {
super(config);
}
@Override
protected String getDefaultScopes() {
return "default";
}
protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
return new BrokeredIdentityContext(accessToken);
};
};
}
+23 -23
View File
@@ -1,23 +1,23 @@
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-parent</artifactId>
<name>Keycloak Broker Parent</name>
<description/>
<packaging>pom</packaging>
<modules>
<module>core</module>
<module>oidc</module>
<module>saml</module>
</modules>
</project>
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Final-redhat-2</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-parent</artifactId>
<name>Keycloak Broker Parent</name>
<description/>
<packaging>pom</packaging>
<modules>
<module>core</module>
<module>oidc</module>
<module>saml</module>
</modules>
</project>
+50 -53
View File
@@ -1,53 +1,50 @@
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-saml</artifactId>
<name>Keycloak Broker - SAML Identity Provider</name>
<description/>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-broker-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-protocol</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-federation</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-events-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Final-redhat-2</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-saml</artifactId>
<name>Keycloak Broker - SAML Identity Provider</name>
<description/>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-broker-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-protocol</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-events-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -3,9 +3,20 @@ package org.keycloak.broker.saml;
import org.jboss.logging.Logger;
import org.keycloak.ClientConnection;
import org.keycloak.VerificationException;
import org.keycloak.broker.provider.FederatedIdentity;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.broker.provider.IdentityProvider;
import org.keycloak.dom.saml.v2.assertion.AssertionType;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.dom.saml.v2.assertion.AttributeType;
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
import org.keycloak.dom.saml.v2.assertion.EncryptedAssertionType;
import org.keycloak.dom.saml.v2.assertion.NameIDType;
import org.keycloak.dom.saml.v2.assertion.SubjectType;
import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
import org.keycloak.dom.saml.v2.protocol.RequestAbstractType;
import org.keycloak.dom.saml.v2.protocol.ResponseType;
import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
@@ -17,31 +28,24 @@ import org.keycloak.protocol.saml.SAML2LogoutResponseBuilder;
import org.keycloak.protocol.saml.SAMLRequestParser;
import org.keycloak.protocol.saml.SamlProtocol;
import org.keycloak.protocol.saml.SamlProtocolUtils;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.constants.JBossSAMLConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ConfigurationException;
import org.keycloak.saml.common.exceptions.ProcessingException;
import org.keycloak.saml.common.util.DocumentUtil;
import org.keycloak.saml.common.util.StaxParserUtil;
import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response;
import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
import org.keycloak.saml.processing.core.util.JAXPValidationUtil;
import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
import org.keycloak.saml.processing.web.util.PostBindingUtil;
import org.keycloak.services.ErrorPage;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.flows.Flows;
import org.picketlink.common.constants.GeneralConstants;
import org.picketlink.common.constants.JBossSAMLConstants;
import org.picketlink.common.constants.JBossSAMLURIConstants;
import org.picketlink.common.exceptions.ConfigurationException;
import org.picketlink.common.exceptions.ProcessingException;
import org.picketlink.common.util.DocumentUtil;
import org.picketlink.common.util.StaxParserUtil;
import org.picketlink.identity.federation.api.saml.v2.response.SAML2Response;
import org.picketlink.identity.federation.core.parsers.saml.SAMLParser;
import org.picketlink.identity.federation.core.saml.v2.common.SAMLDocumentHolder;
import org.picketlink.identity.federation.core.util.JAXPValidationUtil;
import org.picketlink.identity.federation.core.util.XMLEncryptionUtil;
import org.picketlink.identity.federation.core.util.XMLSignatureUtil;
import org.picketlink.identity.federation.saml.v2.assertion.AssertionType;
import org.picketlink.identity.federation.saml.v2.assertion.AuthnStatementType;
import org.picketlink.identity.federation.saml.v2.assertion.EncryptedAssertionType;
import org.picketlink.identity.federation.saml.v2.assertion.NameIDType;
import org.picketlink.identity.federation.saml.v2.assertion.SubjectType;
import org.picketlink.identity.federation.saml.v2.protocol.LogoutRequestType;
import org.picketlink.identity.federation.saml.v2.protocol.RequestAbstractType;
import org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
import org.picketlink.identity.federation.saml.v2.protocol.StatusResponseType;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -62,9 +66,7 @@ import java.io.IOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -75,10 +77,14 @@ public class SAMLEndpoint {
public static final String SAML_FEDERATED_SESSION_INDEX = "SAML_FEDERATED_SESSION_INDEX";
public static final String SAML_FEDERATED_SUBJECT = "SAML_FEDERATED_SUBJECT";
public static final String SAML_FEDERATED_SUBJECT_NAMEFORMAT = "SAML_FEDERATED_SUBJECT_NAMEFORMAT";
public static final String SAML_LOGIN_RESPONSE = "SAML_LOGIN_RESPONSE";
public static final String SAML_ASSERTION = "SAML_ASSERTION";
public static final String SAML_AUTHN_STATEMENT = "SAML_AUTHN_STATEMENT";
protected RealmModel realm;
protected EventBuilder event;
protected SAMLIdentityProviderConfig config;
protected IdentityProvider.AuthenticationCallback callback;
protected SAMLIdentityProvider provider;
@Context
private UriInfo uriInfo;
@@ -93,10 +99,11 @@ public class SAMLEndpoint {
private HttpHeaders headers;
public SAMLEndpoint(RealmModel realm, SAMLIdentityProviderConfig config, IdentityProvider.AuthenticationCallback callback) {
public SAMLEndpoint(RealmModel realm, SAMLIdentityProvider provider, SAMLIdentityProviderConfig config, IdentityProvider.AuthenticationCallback callback) {
this.realm = realm;
this.config = config;
this.callback = callback;
this.provider = provider;
}
@GET
@@ -130,18 +137,18 @@ public class SAMLEndpoint {
if (!checkSsl()) {
event.event(EventType.LOGIN);
event.error(Errors.SSL_REQUIRED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
}
if (!realm.isEnabled()) {
event.event(EventType.LOGIN_ERROR);
event.error(Errors.REALM_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
}
if (samlRequest == null && samlResponse == null) {
event.event(EventType.LOGIN);
event.error(Errors.INVALID_REQUEST);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST );
return ErrorPage.error(session, Messages.INVALID_REQUEST);
}
return null;
@@ -177,7 +184,7 @@ public class SAMLEndpoint {
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
event.error(Errors.INVALID_SAML_RESPONSE);
event.detail(Details.REASON, "invalid_destination");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
return ErrorPage.error(session, Messages.INVALID_REQUEST);
}
if (config.isValidateSignature()) {
try {
@@ -186,7 +193,7 @@ public class SAMLEndpoint {
logger.error("validation failed", e);
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
event.error(Errors.INVALID_SIGNATURE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUESTER);
return ErrorPage.error(session, Messages.INVALID_REQUESTER);
}
}
@@ -199,7 +206,7 @@ public class SAMLEndpoint {
} else {
event.event(EventType.LOGIN);
event.error(Errors.INVALID_TOKEN);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
return ErrorPage.error(session, Messages.INVALID_REQUEST);
}
}
@@ -209,7 +216,7 @@ public class SAMLEndpoint {
List<UserSessionModel> userSessions = session.sessions().getUserSessionByBrokerUserId(realm, brokerUserId);
for (UserSessionModel userSession : userSessions) {
try {
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers);
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, false);
} catch (Exception e) {
logger.warn("failed to do backchannel logout for userSession", e);
}
@@ -221,7 +228,7 @@ public class SAMLEndpoint {
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId);
if (userSession != null) {
try {
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers);
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, false);
} catch (Exception e) {
logger.warn("failed to do backchannel logout for userSession", e);
}
@@ -265,10 +272,11 @@ public class SAMLEndpoint {
SubjectType subject = assertion.getSubject();
SubjectType.STSubType subType = subject.getSubType();
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
Map<String, String> notes = new HashMap<>();
notes.put(SAML_FEDERATED_SUBJECT, subjectNameID.getValue());
if (subjectNameID.getFormat() != null) notes.put(SAML_FEDERATED_SUBJECT_NAMEFORMAT, subjectNameID.getFormat().toString());
FederatedIdentity identity = new FederatedIdentity(subjectNameID.getValue());
//Map<String, String> notes = new HashMap<>();
BrokeredIdentityContext identity = new BrokeredIdentityContext(subjectNameID.getValue());
identity.setCode(relayState);
identity.getContextData().put(SAML_LOGIN_RESPONSE, responseType);
identity.getContextData().put(SAML_ASSERTION, assertion);
identity.setUsername(subjectNameID.getValue());
@@ -284,24 +292,41 @@ public class SAMLEndpoint {
for (Object statement : assertion.getStatements()) {
if (statement instanceof AuthnStatementType) {
authn = (AuthnStatementType)statement;
identity.getContextData().put(SAML_AUTHN_STATEMENT, authn);
break;
}
}
if (assertion.getAttributeStatements() != null ) {
for (AttributeStatementType attrStatement : assertion.getAttributeStatements()) {
for (AttributeStatementType.ASTChoiceType choice : attrStatement.getAttributes()) {
AttributeType attribute = choice.getAttribute();
if (X500SAMLProfileConstants.EMAIL.getFriendlyName().equals(attribute.getFriendlyName())
|| X500SAMLProfileConstants.EMAIL.get().equals(attribute.getName())) {
if (!attribute.getAttributeValue().isEmpty()) identity.setEmail(attribute.getAttributeValue().get(0).toString());
}
}
}
}
String brokerUserId = config.getAlias() + "." + subjectNameID.getValue();
identity.setBrokerUserId(brokerUserId);
identity.setIdpConfig(config);
identity.setIdp(provider);
if (authn != null && authn.getSessionIndex() != null) {
identity.setBrokerSessionId(identity.getBrokerUserId() + "." + authn.getSessionIndex());
notes.put(SAML_FEDERATED_SESSION_INDEX, authn.getSessionIndex());
}
return callback.authenticated(notes, config, identity, relayState);
}
return callback.authenticated(identity);
} catch (Exception e) {
throw new IdentityBrokerException("Could not process response from SAML identity provider.", e);
}
}
private AssertionType getAssertion(ResponseType responseType) throws ProcessingException {
List<ResponseType.RTChoiceType> assertions = responseType.getAssertions();
@@ -327,7 +352,7 @@ public class SAMLEndpoint {
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
event.error(Errors.INVALID_SAML_RESPONSE);
event.detail(Details.REASON, "invalid_destination");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
return ErrorPage.error(session, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
}
if (config.isValidateSignature()) {
try {
@@ -336,7 +361,7 @@ public class SAMLEndpoint {
logger.error("validation failed", e);
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
event.error(Errors.INVALID_SIGNATURE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
return ErrorPage.error(session, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
}
}
if (statusResponse instanceof ResponseType) {
@@ -355,20 +380,20 @@ public class SAMLEndpoint {
logger.error("no valid user session");
event.event(EventType.LOGOUT);
event.error(Errors.USER_SESSION_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
}
UserSessionModel userSession = session.sessions().getUserSession(realm, relayState);
if (userSession == null) {
logger.error("no valid user session");
event.event(EventType.LOGOUT);
event.error(Errors.USER_SESSION_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
}
if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {
logger.error("usersession in different state");
event.event(EventType.LOGOUT);
event.error(Errors.USER_SESSION_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.SESSION_NOT_ACTIVE);
return ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE);
}
return AuthenticationManager.finishBrowserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
}
@@ -420,7 +445,9 @@ public class SAMLEndpoint {
}
@Override
protected SAMLDocumentHolder extractResponseDocument(String response) {
return SAMLRequestParser.parseResponsePostBinding(response);
byte[] samlBytes = PostBindingUtil.base64Decode(response);
String xml = new String(samlBytes);
return SAMLRequestParser.parseResponseDocument(samlBytes);
}
@Override
@@ -17,22 +17,36 @@
*/
package org.keycloak.broker.saml;
import org.jboss.logging.Logger;
import org.keycloak.broker.provider.AbstractIdentityProvider;
import org.keycloak.broker.provider.AuthenticationRequest;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.dom.saml.v2.assertion.AssertionType;
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
import org.keycloak.dom.saml.v2.assertion.NameIDType;
import org.keycloak.dom.saml.v2.assertion.SubjectType;
import org.keycloak.dom.saml.v2.protocol.ResponseType;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
import org.keycloak.protocol.saml.SAML2LogoutRequestBuilder;
import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder;
import org.picketlink.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ConfigurationException;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.exceptions.ProcessingException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
@@ -41,17 +55,18 @@ import java.security.PublicKey;
* @author Pedro Igor
*/
public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityProviderConfig> {
protected static final Logger logger = Logger.getLogger(SAMLIdentityProvider.class);
public SAMLIdentityProvider(SAMLIdentityProviderConfig config) {
super(config);
}
@Override
public Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event) {
return new SAMLEndpoint(realm, getConfig(), callback);
return new SAMLEndpoint(realm, this, getConfig(), callback);
}
@Override
public Response handleRequest(AuthenticationRequest request) {
public Response performLogin(AuthenticationRequest request) {
try {
UriInfo uriInfo = request.getUriInfo();
RealmModel realm = request.getRealm();
@@ -112,30 +127,78 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
return UriBuilder.fromUri(uriInfo.getBaseUri()).path("realms").path(realm.getName()).build().toString();
}
@Override
public void attachUserSession(UserSessionModel userSession, ClientSessionModel clientSession, BrokeredIdentityContext context) {
ResponseType responseType = (ResponseType)context.getContextData().get(SAMLEndpoint.SAML_LOGIN_RESPONSE);
AssertionType assertion = (AssertionType)context.getContextData().get(SAMLEndpoint.SAML_ASSERTION);
SubjectType subject = assertion.getSubject();
SubjectType.STSubType subType = subject.getSubType();
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
userSession.setNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT, subjectNameID.getValue());
if (subjectNameID.getFormat() != null) userSession.setNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT_NAMEFORMAT, subjectNameID.getFormat().toString());
AuthnStatementType authn = (AuthnStatementType)context.getContextData().get(SAMLEndpoint.SAML_AUTHN_STATEMENT);
if (authn != null && authn.getSessionIndex() != null) {
userSession.setNote(SAMLEndpoint.SAML_FEDERATED_SESSION_INDEX, authn.getSessionIndex());
}
}
@Override
public Response retrieveToken(FederatedIdentityModel identity) {
return Response.ok(identity.getToken()).build();
}
@Override
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
if (getConfig().getSingleLogoutServiceUrl() == null || getConfig().getSingleLogoutServiceUrl().trim().equals("")) return null;
public void backchannelLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
String singleLogoutServiceUrl = getConfig().getSingleLogoutServiceUrl();
if (singleLogoutServiceUrl == null || singleLogoutServiceUrl.trim().equals("") || !getConfig().isBackchannelSupported()) return;
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
try {
int status = SimpleHttp.doPost(singleLogoutServiceUrl)
.param(GeneralConstants.SAML_REQUEST_KEY, logoutBuilder.postBinding().encoded())
.param(GeneralConstants.RELAY_STATE, userSession.getId()).asStatus();
boolean success = status >=200 && status < 400;
if (!success) {
logger.warn("Failed saml backchannel broker logout to: " + singleLogoutServiceUrl);
}
} catch (Exception e) {
logger.warn("Failed saml backchannel broker logout to: " + singleLogoutServiceUrl, e);
}
}
@Override
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
String singleLogoutServiceUrl = getConfig().getSingleLogoutServiceUrl();
if (singleLogoutServiceUrl == null || singleLogoutServiceUrl.trim().equals("")) return null;
if (getConfig().isBackchannelSupported()) {
backchannelLogout(userSession, uriInfo, realm);
return null;
} else {
try {
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
return logoutBuilder.postBinding().request();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
protected SAML2LogoutRequestBuilder buildLogoutRequest(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm, String singleLogoutServiceUrl) {
SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
.assertionExpiration(realm.getAccessCodeLifespan())
.issuer(getEntityId(uriInfo, realm))
.sessionIndex(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SESSION_INDEX))
.userPrincipal(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT), userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT_NAMEFORMAT))
.destination(getConfig().getSingleLogoutServiceUrl());
.destination(singleLogoutServiceUrl)
.relayState(userSession.getId());
if (getConfig().isWantAuthnRequestsSigned()) {
logoutBuilder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signDocument();
}
try {
return logoutBuilder.relayState(userSession.getId()).postBinding().request();
} catch (Exception e) {
throw new RuntimeException(e);
}
return logoutBuilder;
}
@Override
@@ -110,4 +110,13 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
public void setPostBindingResponse(boolean postBindingResponse) {
getConfig().put("postBindingResponse", String.valueOf(postBindingResponse));
}
public boolean isBackchannelSupported() {
return Boolean.valueOf(getConfig().get("backchannelSupported"));
}
public void setBackchannelSupported(boolean backchannel) {
getConfig().put("backchannelSupported", String.valueOf(backchannel));
}
}
@@ -18,17 +18,17 @@
package org.keycloak.broker.saml;
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
import org.keycloak.dom.saml.v2.metadata.EndpointType;
import org.keycloak.dom.saml.v2.metadata.EntitiesDescriptorType;
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
import org.keycloak.dom.saml.v2.metadata.IDPSSODescriptorType;
import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType;
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
import org.keycloak.models.IdentityProviderModel;
import org.picketlink.common.constants.JBossSAMLURIConstants;
import org.picketlink.common.exceptions.ParsingException;
import org.picketlink.common.util.DocumentUtil;
import org.picketlink.identity.federation.core.parsers.saml.SAMLParser;
import org.picketlink.identity.federation.saml.v2.metadata.EndpointType;
import org.picketlink.identity.federation.saml.v2.metadata.EntitiesDescriptorType;
import org.picketlink.identity.federation.saml.v2.metadata.EntityDescriptorType;
import org.picketlink.identity.federation.saml.v2.metadata.IDPSSODescriptorType;
import org.picketlink.identity.federation.saml.v2.metadata.KeyDescriptorType;
import org.picketlink.identity.federation.saml.v2.metadata.KeyTypes;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.DocumentUtil;
import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
import org.w3c.dom.Element;
import javax.xml.namespace.QName;
@@ -0,0 +1,140 @@
package org.keycloak.broker.saml.mappers;
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.HardcodedRoleMapper;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.broker.saml.SAMLEndpoint;
import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
import org.keycloak.dom.saml.v2.assertion.AssertionType;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.dom.saml.v2.assertion.AttributeType;
import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.JsonWebToken;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AttributeToRoleMapper extends AbstractIdentityProviderMapper {
public static final String[] COMPATIBLE_PROVIDERS = {SAMLIdentityProviderFactory.PROVIDER_ID};
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
public static final String ATTRIBUTE_NAME = "attribute.name";
public static final String ATTRIBUTE_FRIENDLY_NAME = "attribute.friendly.name";
public static final String ATTRIBUTE_VALUE = "attribute.value";
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty();
property.setName(ATTRIBUTE_NAME);
property.setLabel("Attribute Name");
property.setHelpText("Name of attribute to search for in assertion. You can leave this blank and specify a friendly name instead.");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
property = new ProviderConfigProperty();
property.setName(ATTRIBUTE_FRIENDLY_NAME);
property.setLabel("Friendly Name");
property.setHelpText("Friendly name of attribute to search for in assertion. You can leave this blank and specify a name instead.");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
property = new ProviderConfigProperty();
property.setName(ATTRIBUTE_VALUE);
property.setLabel("Attribute Value");
property.setHelpText("Value the attribute must have. If the attribute is a list, then the value must be contained in the list.");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
property = new ProviderConfigProperty();
property.setName(HardcodedRoleMapper.ROLE);
property.setLabel("Role");
property.setHelpText("Role to grant to user. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
}
public static final String PROVIDER_ID = "saml-role-idp-mapper";
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String[] getCompatibleProviders() {
return COMPATIBLE_PROVIDERS;
}
@Override
public String getDisplayCategory() {
return "Role Mapper";
}
@Override
public String getDisplayType() {
return "SAML Attribute to Role";
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
if (isAttributePresent(mapperModel, context)) {
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
user.grantRole(role);
}
}
protected boolean isAttributePresent(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String name = mapperModel.getConfig().get(ATTRIBUTE_NAME);
if (name != null && name.trim().equals("")) name = null;
String friendly = mapperModel.getConfig().get(ATTRIBUTE_FRIENDLY_NAME);
if (friendly != null && friendly.trim().equals("")) friendly = null;
String desiredValue = mapperModel.getConfig().get(ATTRIBUTE_VALUE);
AssertionType assertion = (AssertionType)context.getContextData().get(SAMLEndpoint.SAML_ASSERTION);
for (AttributeStatementType statement : assertion.getAttributeStatements()) {
for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) {
AttributeType attr = choice.getAttribute();
if (name != null && !name.equals(attr.getName())) continue;
if (friendly != null && !name.equals(attr.getFriendlyName())) continue;
for (Object val : attr.getAttributeValue()) {
if (val.equals(desiredValue)) return true;
}
}
}
return false;
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
if (!isAttributePresent(mapperModel, context)) {
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
user.deleteRoleMapping(role);
}
}
@Override
public String getHelpText() {
return "If a claim exists, grant the user the specified realm or application role.";
}
}
@@ -0,0 +1,132 @@
package org.keycloak.broker.saml.mappers;
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.broker.saml.SAMLEndpoint;
import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
import org.keycloak.dom.saml.v2.assertion.AssertionType;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.dom.saml.v2.assertion.AttributeType;
import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderConfigProperty;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserAttributeMapper extends AbstractIdentityProviderMapper {
public static final String[] COMPATIBLE_PROVIDERS = {SAMLIdentityProviderFactory.PROVIDER_ID};
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
public static final String ATTRIBUTE_NAME = "attribute.name";
public static final String ATTRIBUTE_FRIENDLY_NAME = "attribute.friendly.name";
public static final String USER_ATTRIBUTE = "user.attribute";
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty();
property.setName(ATTRIBUTE_NAME);
property.setLabel("Attribute Name");
property.setHelpText("Name of attribute to search for in assertion. You can leave this blank and specify a friendly name instead.");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
property = new ProviderConfigProperty();
property.setName(ATTRIBUTE_FRIENDLY_NAME);
property.setLabel("Friendly Name");
property.setHelpText("Friendly name of attribute to search for in assertion. You can leave this blank and specify a name instead.");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
property = new ProviderConfigProperty();
property.setName(USER_ATTRIBUTE);
property.setLabel("User Attribute Name");
property.setHelpText("User attribute name to store saml attribute.");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
}
public static final String PROVIDER_ID = "saml-user-attribute-idp-mapper";
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String[] getCompatibleProviders() {
return COMPATIBLE_PROVIDERS;
}
@Override
public String getDisplayCategory() {
return "Attribute Importer";
}
@Override
public String getDisplayType() {
return "Attribute Importer";
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
Object value = getAttribute(mapperModel, context);
if (value != null) {
user.setAttribute(attribute, value.toString());
}
}
protected String getAttribute(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String name = mapperModel.getConfig().get(ATTRIBUTE_NAME);
if (name != null && name.trim().equals("")) name = null;
String friendly = mapperModel.getConfig().get(ATTRIBUTE_FRIENDLY_NAME);
if (friendly != null && friendly.trim().equals("")) friendly = null;
AssertionType assertion = (AssertionType)context.getContextData().get(SAMLEndpoint.SAML_ASSERTION);
for (AttributeStatementType statement : assertion.getAttributeStatements()) {
for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) {
AttributeType attr = choice.getAttribute();
if (name != null && !name.equals(attr.getName())) continue;
if (friendly != null && !name.equals(attr.getFriendlyName())) continue;
List<Object> attributeValue = attr.getAttributeValue();
if (attributeValue == null || attributeValue.isEmpty()) return null;
return attributeValue.get(0).toString();
}
}
return null;
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
Object value = getAttribute(mapperModel, context);
String current = user.getAttribute(attribute);
if (value != null && !value.equals(current)) {
user.setAttribute(attribute, value.toString());
} else if (value == null) {
user.removeAttribute(attribute);
}
}
@Override
public String getHelpText() {
return "Import declared saml attribute if it exists in assertion into the specified user attribute.";
}
}
@@ -0,0 +1,2 @@
org.keycloak.broker.saml.mappers.AttributeToRoleMapper
org.keycloak.broker.saml.mappers.UserAttributeMapper
Regular → Executable
+1 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<version>1.2.0.Final-redhat-2</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -17,22 +17,18 @@
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-export-import-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-export-import-single-file</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
@@ -24,6 +24,8 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.codehaus.jackson.JsonToken;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.exportimport.Strategy;
@@ -84,7 +86,22 @@ public class DefaultFileConnectionProviderFactory implements FileConnectionProvi
FileInputStream fis = null;
try {
fis = new FileInputStream(kcdata);
Model model = JsonSerialization.readValue(fis, Model.class);
ImportUtils.importFromStream(session, JsonSerialization.mapper, fis, Strategy.IGNORE_EXISTING);
session.realms().getMigrationModel().setStoredVersion(model.getModelVersion());
List<RealmRepresentation> realmReps = new ArrayList<RealmRepresentation>();
for (RealmRepresentation realmRep : model.getRealms()) {
if (Config.getAdminRealm().equals(realmRep.getRealm())) {
realmReps.add(0, realmRep);
} else {
realmReps.add(realmRep);
}
}
for (RealmRepresentation realmRep : realmReps) {
ImportUtils.importRealm(session, realmRep, Strategy.IGNORE_EXISTING);
}
} catch (IOException ioe) {
logger.error("Unable to read model file " + kcdata.getAbsolutePath(), ioe);
} finally {
@@ -128,8 +145,10 @@ public class DefaultFileConnectionProviderFactory implements FileConnectionProvi
for (RealmModel realm : realms) {
reps.add(ExportUtils.exportRealm(session, realm, true));
}
JsonSerialization.prettyMapper.writeValue(outStream, reps);
Model model = new Model();
model.setRealms(reps);
model.setModelVersion(session.realms().getMigrationModel().getStoredVersion());
JsonSerialization.prettyMapper.writeValue(outStream, model);
}
@Override
@@ -37,6 +37,8 @@ public class InMemoryModel {
// realmId, userId, userModel
private final Map<String, Map<String,UserModel>> allUsers = new HashMap<String, Map<String,UserModel>>();
private String modelVersion;
public InMemoryModel() {
}
@@ -45,6 +47,14 @@ public class InMemoryModel {
allUsers.put(id, new HashMap<String, UserModel>());
}
public String getModelVersion() {
return modelVersion;
}
public void setModelVersion(String modelVersion) {
this.modelVersion = modelVersion;
}
public RealmModel getRealm(String id) {
return allRealms.get(id);
}
@@ -0,0 +1,30 @@
package org.keycloak.connections.file;
import org.keycloak.representations.idm.RealmRepresentation;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class Model {
private String modelVersion;
private List<RealmRepresentation> realms;
public String getModelVersion() {
return modelVersion;
}
public void setModelVersion(String modelVersion) {
this.modelVersion = modelVersion;
}
public List<RealmRepresentation> getRealms() {
return realms;
}
public void setRealms(List<RealmRepresentation> realms) {
this.realms = realms;
}
}
+36
View File
@@ -0,0 +1,36 @@
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Final-redhat-2</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-http-client</artifactId>
<name>Keycloak Connections Apache HttpClient</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,139 @@
package org.keycloak.connections.httpclient;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.util.EnvUtil;
import org.keycloak.util.KeystoreUtil;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.util.concurrent.TimeUnit;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class DefaultHttpClientFactory implements HttpClientFactory {
private static final Logger logger = Logger.getLogger(DefaultHttpClientFactory.class);
private volatile CloseableHttpClient httpClient;
@Override
public HttpClientProvider create(KeycloakSession session) {
return new HttpClientProvider() {
@Override
public HttpClient getHttpClient() {
return httpClient;
}
@Override
public void close() {
}
@Override
public int postText(String uri, String text) throws IOException {
HttpPost request = new HttpPost(uri);
request.setEntity(EntityBuilder.create().setText(text).setContentType(ContentType.TEXT_PLAIN).build());
HttpResponse response = httpClient.execute(request);
try {
return response.getStatusLine().getStatusCode();
} finally {
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream is = entity.getContent();
if (is != null) is.close();
}
}
}
@Override
public InputStream get(String uri) throws IOException {
HttpGet request = new HttpGet(uri);
HttpResponse response = httpClient.execute(request);
HttpEntity entity = response.getEntity();
if (entity == null) return null;
return entity.getContent();
}
};
}
@Override
public void close() {
try {
httpClient.close();
} catch (IOException e) {
}
}
@Override
public String getId() {
return "default";
}
@Override
public void init(Config.Scope config) {
long socketTimeout = config.getLong("socket-timeout-millis", -1L);
long establishConnectionTimeout = config.getLong("establish-connection-timeout-millis", -1L);
int maxPooledPerRoute = config.getInt("max-pooled-per-route", 0);
int connectionPoolSize = config.getInt("connection-pool-size", 200);
boolean disableTrustManager = config.getBoolean("disable-trust-manager", false);
boolean disableCookies = config.getBoolean("disable-cookies", true);
String hostnameVerificationPolicy = config.get("hostname-verification-policy", "WILDCARD");
HttpClientBuilder.HostnameVerificationPolicy hostnamePolicy = HttpClientBuilder.HostnameVerificationPolicy.valueOf(hostnameVerificationPolicy);
String truststore = config.get("truststore");
String truststorePassword = config.get("truststore-password");
String clientKeystore = config.get("client-keystore");
String clientKeystorePassword = config.get("client-keystore-password");
String clientPrivateKeyPassword = config.get("client-key-password");
HttpClientBuilder builder = new HttpClientBuilder();
builder.socketTimeout(socketTimeout, TimeUnit.MILLISECONDS)
.establishConnectionTimeout(establishConnectionTimeout, TimeUnit.MILLISECONDS)
.maxPooledPerRoute(maxPooledPerRoute)
.connectionPoolSize(connectionPoolSize)
.hostnameVerification(hostnamePolicy)
.disableCookies(disableCookies);
if (disableTrustManager) builder.disableTrustManager();
if (truststore != null) {
truststore = EnvUtil.replace(truststore);
try {
builder.trustStore(KeystoreUtil.loadKeyStore(truststore, truststorePassword));
} catch (Exception e) {
throw new RuntimeException("Failed to load truststore", e);
}
}
if (clientKeystore != null) {
clientKeystore = EnvUtil.replace(clientKeystore);
try {
KeyStore clientCertKeystore = KeystoreUtil.loadKeyStore(clientKeystore, clientKeystorePassword);
builder.keyStore(clientCertKeystore, clientPrivateKeyPassword);
} catch (Exception e) {
throw new RuntimeException("Failed to load keystore", e);
}
}
httpClient = builder.build();
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
}
@@ -0,0 +1,281 @@
package org.keycloak.connections.httpclient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.StrictHostnameVerifier;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;
/**
* Abstraction for creating HttpClients. Allows SSL configuration.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class HttpClientBuilder {
public static enum HostnameVerificationPolicy {
/**
* Hostname verification is not done on the server's certificate
*/
ANY,
/**
* Allows wildcards in subdomain names i.e. *.foo.com
*/
WILDCARD,
/**
* CN must match hostname connecting to
*/
STRICT
}
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
private static class PassthroughTrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
protected KeyStore truststore;
protected KeyStore clientKeyStore;
protected String clientPrivateKeyPassword;
protected boolean disableTrustManager;
protected HostnameVerificationPolicy policy = HostnameVerificationPolicy.WILDCARD;
protected SSLContext sslContext;
protected int connectionPoolSize = 100;
protected int maxPooledPerRoute = 0;
protected long connectionTTL = -1;
protected TimeUnit connectionTTLUnit = TimeUnit.MILLISECONDS;
protected HostnameVerifier verifier = null;
protected long socketTimeout = -1;
protected TimeUnit socketTimeoutUnits = TimeUnit.MILLISECONDS;
protected long establishConnectionTimeout = -1;
protected TimeUnit establishConnectionTimeoutUnits = TimeUnit.MILLISECONDS;
protected boolean disableCookies = false;
/**
* Socket inactivity timeout
*
* @param timeout
* @param unit
* @return
*/
public HttpClientBuilder socketTimeout(long timeout, TimeUnit unit)
{
this.socketTimeout = timeout;
this.socketTimeoutUnits = unit;
return this;
}
/**
* When trying to make an initial socket connection, what is the timeout?
*
* @param timeout
* @param unit
* @return
*/
public HttpClientBuilder establishConnectionTimeout(long timeout, TimeUnit unit)
{
this.establishConnectionTimeout = timeout;
this.establishConnectionTimeoutUnits = unit;
return this;
}
public HttpClientBuilder connectionTTL(long ttl, TimeUnit unit) {
this.connectionTTL = ttl;
this.connectionTTLUnit = unit;
return this;
}
public HttpClientBuilder maxPooledPerRoute(int maxPooledPerRoute) {
this.maxPooledPerRoute = maxPooledPerRoute;
return this;
}
public HttpClientBuilder connectionPoolSize(int connectionPoolSize) {
this.connectionPoolSize = connectionPoolSize;
return this;
}
/**
* Disable trust management and hostname verification. <i>NOTE</i> this is a security
* hole, so only set this option if you cannot or do not want to verify the identity of the
* host you are communicating with.
*/
public HttpClientBuilder disableTrustManager() {
this.disableTrustManager = true;
return this;
}
/**
* Disable cookie management.
*/
public HttpClientBuilder disableCookies(boolean disable) {
this.disableTrustManager = disable;
return this;
}
/**
* SSL policy used to verify hostnames
*
* @param policy
* @return
*/
public HttpClientBuilder hostnameVerification(HostnameVerificationPolicy policy) {
this.policy = policy;
return this;
}
public HttpClientBuilder sslContext(SSLContext sslContext) {
this.sslContext = sslContext;
return this;
}
public HttpClientBuilder trustStore(KeyStore truststore) {
this.truststore = truststore;
return this;
}
public HttpClientBuilder keyStore(KeyStore keyStore, String password) {
this.clientKeyStore = keyStore;
this.clientPrivateKeyPassword = password;
return this;
}
public HttpClientBuilder keyStore(KeyStore keyStore, char[] password) {
this.clientKeyStore = keyStore;
this.clientPrivateKeyPassword = new String(password);
return this;
}
static class VerifierWrapper implements X509HostnameVerifier {
protected HostnameVerifier verifier;
VerifierWrapper(HostnameVerifier verifier) {
this.verifier = verifier;
}
@Override
public void verify(String host, SSLSocket ssl) throws IOException {
if (!verifier.verify(host, ssl.getSession())) throw new SSLException("Hostname verification failure");
}
@Override
public void verify(String host, X509Certificate cert) throws SSLException {
throw new SSLException("This verification path not implemented");
}
@Override
public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
throw new SSLException("This verification path not implemented");
}
@Override
public boolean verify(String s, SSLSession sslSession) {
return verifier.verify(s, sslSession);
}
}
public CloseableHttpClient build() {
X509HostnameVerifier verifier = null;
if (this.verifier != null) verifier = new VerifierWrapper(this.verifier);
else {
switch (policy) {
case ANY:
verifier = new AllowAllHostnameVerifier();
break;
case WILDCARD:
verifier = new BrowserCompatHostnameVerifier();
break;
case STRICT:
verifier = new StrictHostnameVerifier();
break;
}
}
try {
SSLConnectionSocketFactory sslsf = null;
SSLContext theContext = sslContext;
if (disableTrustManager) {
theContext = SSLContext.getInstance("TLS");
theContext.init(null, new TrustManager[]{new PassthroughTrustManager()},
new SecureRandom());
verifier = new AllowAllHostnameVerifier();
sslsf = new SSLConnectionSocketFactory(theContext, verifier);
} else if (theContext != null) {
sslsf = new SSLConnectionSocketFactory(theContext, verifier);
} else if (clientKeyStore != null || truststore != null) {
theContext = createSslContext("TLS", clientKeyStore, clientPrivateKeyPassword, truststore, null);
sslsf = new SSLConnectionSocketFactory(theContext, verifier);
} else {
final SSLContext tlsContext = SSLContext.getInstance("TLS");
tlsContext.init(null, null, null);
sslsf = new SSLConnectionSocketFactory(tlsContext, verifier);
}
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout((int) establishConnectionTimeout)
.setSocketTimeout((int) socketTimeout).build();
org.apache.http.impl.client.HttpClientBuilder builder = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.setSSLSocketFactory(sslsf)
.setMaxConnTotal(connectionPoolSize)
.setMaxConnPerRoute(maxPooledPerRoute);
if (disableCookies) builder.disableCookieManagement();
return builder.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private SSLContext createSslContext(
final String algorithm,
final KeyStore keystore,
final String keyPassword,
final KeyStore truststore,
final SecureRandom random)
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
return SSLContexts.custom()
.useProtocol(algorithm)
.setSecureRandom(random)
.loadKeyMaterial(keystore, keyPassword != null ? keyPassword.toCharArray() : null)
.loadTrustMaterial(truststore)
.build();
}
}
@@ -0,0 +1,11 @@
package org.keycloak.connections.httpclient;
import org.apache.http.client.HttpClient;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface HttpClientFactory extends ProviderFactory<HttpClientProvider> {
}
@@ -0,0 +1,34 @@
package org.keycloak.connections.httpclient;
import org.apache.http.client.HttpClient;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import java.io.IOException;
import java.io.InputStream;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface HttpClientProvider extends Provider {
HttpClient getHttpClient();
/**
* Helper method
*
* @param uri
* @param text
* @return http response status
* @throws IOException
*/
public int postText(String uri, String text) throws IOException;
/**
* Helper method
*
* @param uri
* @return response stream, you must close this stream or leaks will happen
* @throws IOException
*/
public InputStream get(String uri) throws IOException;
}
@@ -1,4 +1,4 @@
package org.keycloak.picketlink;
package org.keycloak.connections.httpclient;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
@@ -7,19 +7,21 @@ import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class PartitionManagerSpi implements Spi {
public class HttpClientSpi implements Spi {
@Override
public String getName() {
return "picketlink-idm";
return "connectionsHttpClient";
}
@Override
public Class<? extends Provider> getProviderClass() {
return PartitionManagerProvider.class;
return HttpClientProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return PartitionManagerProviderFactory.class;
return HttpClientFactory.class;
}
}
@@ -0,0 +1 @@
org.keycloak.connections.httpclient.DefaultHttpClientFactory
@@ -0,0 +1 @@
org.keycloak.connections.httpclient.HttpClientSpi
+31 -33
View File
@@ -1,33 +1,31 @@
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-infinispan</artifactId>
<name>Keycloak Connections Infinispan</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Final-redhat-2</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-infinispan</artifactId>
<name>Keycloak Connections Infinispan</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+76 -78
View File
@@ -1,78 +1,76 @@
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-jpa-liquibase</artifactId>
<name>Keycloak Connections JPA Liquibase Updater</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-connections-jpa</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<exclusions>
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<configuration>
<changeLogFile>META-INF/jpa-changelog-master.xml</changeLogFile>
<url>${url}</url>
<driver>${driver}</driver>
<username>${username}</username>
<password>${password}</password>
<referenceUrl>${referenceUrl}</referenceUrl>
<referenceDriver>${driver}</referenceDriver>
<referenceUsername>${username}</referenceUsername>
<referencePassword>${password}</referencePassword>
</configuration>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<properties>
<username>sa</username>
<password></password>
<driver>org.h2.Driver</driver>
</properties>
</project>
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Final-redhat-2</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-jpa-liquibase</artifactId>
<name>Keycloak Connections JPA Liquibase Updater</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-connections-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<exclusions>
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<configuration>
<changeLogFile>META-INF/jpa-changelog-master.xml</changeLogFile>
<url>${url}</url>
<driver>${driver}</driver>
<username>${username}</username>
<password>${password}</password>
<referenceUrl>${referenceUrl}</referenceUrl>
<referenceDriver>${driver}</referenceDriver>
<referenceUsername>${username}</referenceUsername>
<referencePassword>${password}</referencePassword>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<username>sa</username>
<password></password>
<driver>org.h2.Driver</driver>
</properties>
</project>
@@ -15,14 +15,18 @@ import liquibase.resource.ResourceAccessor;
import liquibase.snapshot.SnapshotGeneratorFactory;
import liquibase.statement.SqlStatement;
import liquibase.structure.core.Table;
import org.jboss.logging.Logger;
import org.keycloak.connections.jpa.updater.liquibase.ThreadLocalSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.services.DefaultKeycloakSessionFactory;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public abstract class CustomKeycloakTask implements CustomSqlChange {
private final Logger logger = Logger.getLogger(getClass());
protected KeycloakSession kcSession;
protected Database database;
@@ -49,8 +53,18 @@ public abstract class CustomKeycloakTask implements CustomSqlChange {
@Override
public void setUp() throws SetupException {
this.kcSession = ThreadLocalSessionContext.getCurrentSession();
if (this.kcSession == null) {
throw new SetupException("No KeycloakSession provided in ThreadLocal");
// Probably running Liquibase from maven plugin. Try to create kcSession programmatically
logger.info("No KeycloakSession provided in ThreadLocal. Initializing KeycloakSessionFactory");
try {
DefaultKeycloakSessionFactory factory = new DefaultKeycloakSessionFactory();
factory.init();
this.kcSession = factory.create();
} catch (Exception e) {
throw new SetupException("Exception when initializing factory", e);
}
}
}
@@ -0,0 +1,50 @@
package org.keycloak.connections.jpa.updater.liquibase.custom;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import liquibase.exception.CustomChangeException;
import liquibase.statement.core.InsertStatement;
import liquibase.structure.core.Table;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class JpaUpdate1_2_0_CR1 extends CustomKeycloakTask {
@Override
protected void generateStatementsImpl() throws CustomChangeException {
String realmClientTableName = database.correctObjectName("REALM_CLIENT", Table.class);
try {
PreparedStatement statement = jdbcConnection.prepareStatement("select CLIENT.REALM_ID, CLIENT.ID CLIENT_ID from CLIENT where CLIENT.CONSENT_REQUIRED = true");
try {
ResultSet resultSet = statement.executeQuery();
try {
while (resultSet.next()) {
String realmId = resultSet.getString("REALM_ID");
String oauthClientId = resultSet.getString("CLIENT_ID");
InsertStatement realmClientInsert = new InsertStatement(null, null, realmClientTableName)
.addColumnValue("REALM_ID", realmId)
.addColumnValue("CLIENT_ID", oauthClientId);
statements.add(realmClientInsert);
}
} finally {
resultSet.close();
}
} finally {
statement.close();
}
confirmationMessage.append("Inserted " + statements.size() + " OAuth Clients to REALM_CLIENT table");
} catch (Exception e) {
throw new CustomChangeException(getTaskId() + ": Exception when updating data from previous version", e);
}
}
@Override
protected String getTaskId() {
return "Update 1.2.0.CR1";
}
}
@@ -0,0 +1,147 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="bburke@redhat.com" id="1.2.0.RC1">
<delete tableName="CLIENT_SESSION_ROLE"/>
<delete tableName="CLIENT_SESSION_NOTE"/>
<delete tableName="CLIENT_SESSION"/>
<delete tableName="USER_SESSION_NOTE"/>
<delete tableName="USER_SESSION"/>
<createTable tableName="MIGRATION_MODEL">
<column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="VERSION" type="VARCHAR(36)">
<constraints nullable="true"/>
</column>
</createTable>
<createTable tableName="IDENTITY_PROVIDER_MAPPER">
<column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="NAME" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="IDP_ALIAS" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="IDP_MAPPER_NAME" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="REALM_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
</createTable>
<createTable tableName="IDP_MAPPER_CONFIG">
<column name="IDP_MAPPER_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="VALUE" type="CLOB"/>
<column name="NAME" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
</createTable>
<addColumn tableName="CREDENTIAL">
<column name="CREATED_DATE" type="BIGINT"/>
</addColumn>
<createTable tableName="USER_CONSENT">
<column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="CLIENT_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="USER_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
</createTable>
<createTable tableName="USER_CONSENT_ROLE">
<column name="USER_CONSENT_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="ROLE_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
</createTable>
<createTable tableName="USER_CONSENT_PROT_MAPPER">
<column name="USER_CONSENT_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="PROTOCOL_MAPPER_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
</createTable>
<createTable tableName="CLIENT_SESSION_PROT_MAPPER">
<column name="PROTOCOL_MAPPER_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="CLIENT_SESSION" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
</createTable>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_MIGMOD" tableName="MIGRATION_MODEL"/>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_IDPM" tableName="IDENTITY_PROVIDER_MAPPER"/>
<addPrimaryKey columnNames="IDP_MAPPER_ID, NAME" constraintName="CONSTRAINT_IDPMConfig" tableName="IDP_MAPPER_CONFIG"/>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_GRNTCSNT_PM" tableName="USER_CONSENT"/>
<addPrimaryKey columnNames="USER_CONSENT_ID, ROLE_ID" constraintName="CONSTRAINT_GRNTCSNT_ROLE_PM" tableName="USER_CONSENT_ROLE"/>
<addPrimaryKey columnNames="USER_CONSENT_ID, PROTOCOL_MAPPER_ID" constraintName="CONSTRAINT_GRNTCSNT_PRM_PM" tableName="USER_CONSENT_PROT_MAPPER"/>
<addPrimaryKey columnNames="CLIENT_SESSION, PROTOCOL_MAPPER_ID" constraintName="CONSTRAINT_CS_PMP_PK" tableName="CLIENT_SESSION_PROT_MAPPER"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="IDENTITY_PROVIDER_MAPPER" constraintName="FK_IDPM_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
<addForeignKeyConstraint baseColumnNames="IDP_MAPPER_ID" baseTableName="IDP_MAPPER_CONFIG" constraintName="FK_IDPMConfig" referencedColumnNames="ID" referencedTableName="IDENTITY_PROVIDER_MAPPER"/>
<addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_CONSENT" constraintName="FK_GRNTCSNT_USER" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
<addForeignKeyConstraint baseColumnNames="USER_CONSENT_ID" baseTableName="USER_CONSENT_ROLE" constraintName="FK_GRNTCSNT_ROLE_GR" referencedColumnNames="ID" referencedTableName="USER_CONSENT"/>
<addForeignKeyConstraint baseColumnNames="USER_CONSENT_ID" baseTableName="USER_CONSENT_PROT_MAPPER" constraintName="FK_GRNTCSNT_PRM_GR" referencedColumnNames="ID" referencedTableName="USER_CONSENT"/>
<addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_PROT_MAPPER" constraintName="FK_33A8SGQW18I532811V7O2DK89" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
<renameColumn tableName="CLIENT" newColumnName="CLIENT_ID" oldColumnName="NAME" columnDataType="VARCHAR(255)"/>
<addColumn tableName="CLIENT">
<column name="CONSENT_REQUIRED" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="NAME" type="VARCHAR(255)" />
</addColumn>
<addColumn tableName="IDENTITY_PROVIDER">
<column name="ADD_TOKEN_ROLE" type="BOOLEAN" defaultValueBoolean="true">
<constraints nullable="false"/>
</column>
</addColumn>
<update tableName="CLIENT">
<column name="CONSENT_REQUIRED" valueBoolean="true"/>
<where>DTYPE = 'OAuthClientEntity'</where>
</update>
<dropColumn tableName="CLIENT" columnName="DTYPE"/>
<dropForeignKeyConstraint baseTableName="REALM" constraintName="FK_RSAF444KK6QRKMS7N56AIWQ5Y" />
<renameColumn tableName="REALM" newColumnName="MASTER_ADMIN_CLIENT" oldColumnName="MASTER_ADMIN_APP" columnDataType="VARCHAR(36)"/>
<addForeignKeyConstraint baseColumnNames="MASTER_ADMIN_CLIENT" baseTableName="REALM" constraintName="FK_TRAF444KK6QRKMS7N56AIWQ5Y" referencedColumnNames="ID" referencedTableName="CLIENT"/>
<dropForeignKeyConstraint baseTableName="REALM_APPLICATION" constraintName="FK_82S3P0DIUXAWWQQSA528UBY2Q" />
<renameTable oldTableName="REALM_APPLICATION" newTableName="REALM_CLIENT"/>
<renameColumn tableName="REALM_CLIENT" newColumnName="CLIENT_ID" oldColumnName="APPLICATION_ID" columnDataType="VARCHAR(36)"/>
<addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="REALM_CLIENT" constraintName="FK_93S3P0DIUXAWWQQSA528UBY2Q" referencedColumnNames="ID" referencedTableName="CLIENT"/>
<dropForeignKeyConstraint baseTableName="APPLICATION_DEFAULT_ROLES" constraintName="FK_MAYLTS7KLWQW2H8M2B5JOYTKY" />
<renameTable oldTableName="APPLICATION_DEFAULT_ROLES" newTableName="CLIENT_DEFAULT_ROLES"/>
<renameColumn tableName="CLIENT_DEFAULT_ROLES" newColumnName="CLIENT_ID" oldColumnName="APPLICATION_ID" columnDataType="VARCHAR(36)"/>
<addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_DEFAULT_ROLES" constraintName="FK_NUILTS7KLWQW2H8M2B5JOYTKY" referencedColumnNames="ID" referencedTableName="CLIENT"/>
<dropForeignKeyConstraint baseTableName="APP_NODE_REGISTRATIONS" constraintName="FK8454723BA992F594" />
<renameTable oldTableName="APP_NODE_REGISTRATIONS" newTableName="CLIENT_NODE_REGISTRATIONS"/>
<renameColumn tableName="CLIENT_NODE_REGISTRATIONS" newColumnName="CLIENT_ID" oldColumnName="APPLICATION_ID" columnDataType="VARCHAR(36)"/>
<addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_NODE_REGISTRATIONS" constraintName="FK4129723BA992F594" referencedColumnNames="ID" referencedTableName="CLIENT"/>
<dropForeignKeyConstraint baseTableName="KEYCLOAK_ROLE" constraintName="FK_PIMO5LE2C0RAL09FL8CM9WFW9" />
<renameColumn tableName="KEYCLOAK_ROLE" newColumnName="CLIENT" oldColumnName="APPLICATION" columnDataType="VARCHAR(36)"/>
<renameColumn tableName="KEYCLOAK_ROLE" newColumnName="CLIENT_ROLE" oldColumnName="APPLICATION_ROLE" columnDataType="BOOLEAN"/>
<renameColumn tableName="KEYCLOAK_ROLE" newColumnName="CLIENT_REALM_CONSTRAINT" oldColumnName="APP_REALM_CONSTRAINT" columnDataType="VARCHAR(36)"/>
<addForeignKeyConstraint baseColumnNames="CLIENT" baseTableName="KEYCLOAK_ROLE" constraintName="FK_KJHO5LE2C0RAL09FL8CM9WFW9" referencedColumnNames="ID" referencedTableName="CLIENT"/>
<dropUniqueConstraint tableName="KEYCLOAK_ROLE" constraintName="UK_J3RWUVD56ONTGSUHOGM184WW2"/>
<addUniqueConstraint columnNames="NAME,CLIENT_REALM_CONSTRAINT" constraintName="UK_J3RWUVD56ONTGSUHOGM184WW2-2" tableName="KEYCLOAK_ROLE"/>
<addUniqueConstraint columnNames="CLIENT_ID, USER_ID" constraintName="UK_JKUWUVD56ONTGSUHOGM8UEWRT" tableName="USER_CONSENT"/>
<customChange class="org.keycloak.connections.jpa.updater.liquibase.custom.JpaUpdate1_2_0_CR1"/>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="keycloak" id="1.2.0.Final">
<!-- KEYCLOAK-1277 -->
<update tableName="CLIENT">
<column name="DIRECT_GRANTS_ONLY" valueBoolean="false"/>
<where>DIRECT_GRANTS_ONLY is null</where>
</update>
<update tableName="CLIENT">
<column name="BEARER_ONLY" valueBoolean="false"/>
<where>BEARER_ONLY is null</where>
</update>
<update tableName="CLIENT">
<column name="SURROGATE_AUTH_REQUIRED" valueBoolean="false"/>
<where>SURROGATE_AUTH_REQUIRED is null</where>
</update>
</changeSet>
</databaseChangeLog>
@@ -4,4 +4,6 @@
<include file="META-INF/jpa-changelog-1.1.0.Beta1.xml"/>
<include file="META-INF/jpa-changelog-1.1.0.Final.xml"/>
<include file="META-INF/jpa-changelog-1.2.0.Beta1.xml"/>
<include file="META-INF/jpa-changelog-1.2.0.CR1.xml"/>
<include file="META-INF/jpa-changelog-1.2.0.Final.xml"/>
</databaseChangeLog>
+36 -39
View File
@@ -1,39 +1,36 @@
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-jpa</artifactId>
<name>Keycloak Connections JPA</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.entitymanager.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Final-redhat-2</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-jpa</artifactId>
<name>Keycloak Connections JPA</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -12,7 +12,7 @@ public interface JpaUpdaterProvider extends Provider {
public String FIRST_VERSION = "1.0.0.Final";
public String LAST_VERSION = "1.2.0.Beta1";
public String LAST_VERSION = "1.2.0.RC1";
public String getCurrentVersionSql();
@@ -3,27 +3,32 @@
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="keycloak-default" transaction-type="RESOURCE_LOCAL">
<class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
<class>org.keycloak.models.jpa.entities.ClientEntity</class>
<class>org.keycloak.models.jpa.entities.CredentialEntity</class>
<class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
<class>org.keycloak.models.jpa.entities.RealmEntity</class>
<class>org.keycloak.models.jpa.entities.RealmAttributeEntity</class>
<class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
<class>org.keycloak.models.jpa.entities.UserFederationProviderEntity</class>
<class>org.keycloak.models.jpa.entities.RoleEntity</class>
<class>org.keycloak.models.jpa.entities.FederatedIdentityEntity</class>
<class>org.keycloak.models.jpa.entities.MigrationModelEntity</class>
<class>org.keycloak.models.jpa.entities.UserEntity</class>
<class>org.keycloak.models.jpa.entities.UserRequiredActionEntity</class>
<class>org.keycloak.models.jpa.entities.UserAttributeEntity</class>
<class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
<class>org.keycloak.models.jpa.entities.ScopeMappingEntity</class>
<class>org.keycloak.models.jpa.entities.IdentityProviderEntity</class>
<class>org.keycloak.models.jpa.entities.IdentityProviderMapperEntity</class>
<class>org.keycloak.models.jpa.entities.ClientIdentityProviderMappingEntity</class>
<class>org.keycloak.models.jpa.entities.ProtocolMapperEntity</class>
<class>org.keycloak.models.jpa.entities.UserConsentEntity</class>
<class>org.keycloak.models.jpa.entities.UserConsentRoleEntity</class>
<class>org.keycloak.models.jpa.entities.UserConsentProtocolMapperEntity</class>
<!-- JpaUserSessionProvider -->
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionEntity</class>
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionRoleEntity</class>
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionProtocolMapperEntity</class>
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionNoteEntity</class>
<class>org.keycloak.models.sessions.jpa.entities.UserSessionNoteEntity</class>
<class>org.keycloak.models.sessions.jpa.entities.UserSessionEntity</class>
Regular → Executable
+1 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<version>1.2.0.Final-redhat-2</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -17,17 +17,14 @@
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-connections-mongo</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
@@ -10,6 +10,7 @@ import org.keycloak.connections.mongo.updater.impl.updates.Update;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_0_0_Final;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_1_0_Beta1;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_2_0_Beta1;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_2_0_CR1;
import org.keycloak.models.KeycloakSession;
import java.util.Date;
@@ -28,7 +29,8 @@ public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider {
private Class<? extends Update>[] updates = new Class[]{
Update1_0_0_Final.class,
Update1_1_0_Beta1.class,
Update1_2_0_Beta1.class
Update1_2_0_Beta1.class,
Update1_2_0_CR1.class
};
@Override
@@ -53,15 +53,8 @@ public abstract class Update {
log.debugv("Deleted entries from {0}", collection);
}
protected String insertApplicationRole(DBCollection roles, String roleName, String applicationId) {
BasicDBObject role = new BasicDBObject();
String roleId = KeycloakModelUtils.generateId();
role.append("_id", roleId);
role.append("name", roleName);
role.append("applicationId", applicationId);
role.append("nameIndex", applicationId + "//" + roleName);
roles.insert(role);
return roleId;
protected void renameCollection(String collection, String newName) {
db.getCollection(collection).rename(newName);
}
public void setLog(Logger log) {
@@ -266,4 +266,15 @@ public class Update1_2_0_Beta1 extends Update {
}
}
private String insertApplicationRole(DBCollection roles, String roleName, String applicationId) {
BasicDBObject role = new BasicDBObject();
String roleId = KeycloakModelUtils.generateId();
role.append("_id", roleId);
role.append("name", roleName);
role.append("applicationId", applicationId);
role.append("nameIndex", applicationId + "//" + roleName);
roles.insert(role);
return roleId;
}
}
@@ -0,0 +1,64 @@
package org.keycloak.connections.mongo.updater.impl.updates;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import org.keycloak.models.KeycloakSession;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class Update1_2_0_CR1 extends Update {
@Override
public String getId() {
return "1.2.0.CR1";
}
@Override
public void update(KeycloakSession session) {
deleteEntries("clientSessions");
deleteEntries("sessions");
convertApplicationsToClients();
convertOAuthClientsToClients();
db.getCollection("realms").update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("adminAppId", "masterAdminClient")), false, true);
ensureIndex("userConsents", new String[]{"clientId", "userId"}, true, false);
}
private void convertApplicationsToClients() {
DBCollection applications = db.getCollection("applications");
applications.dropIndex("realmId_1_name_1");
applications.update(new BasicDBObject(), new BasicDBObject("$set", new BasicDBObject("consentRequired", false)), false, true);
applications.update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("name", "clientId")), false, true);
renameCollection("applications", "clients");
log.debugv("Converted applications to clients");
DBCollection roles = db.getCollection("roles");
roles.update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("applicationId", "clientId")), false, true);
log.debugv("Renamed roles.applicationId to roles.clientId");
ensureIndex("clients", new String[]{"realmId", "clientId"}, true, false);
}
private void convertOAuthClientsToClients() {
DBCollection clients = db.getCollection("clients");
DBCollection oauthClients = db.getCollection("oauthClients");
oauthClients.dropIndex("realmId_1_name_1");
oauthClients.update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("name", "clientId")), false, true);
oauthClients.update(new BasicDBObject(), new BasicDBObject("$set", new BasicDBObject("consentRequired", true)), false, true);
DBCursor curs = oauthClients.find();
while (curs.hasNext()) {
clients.insert(curs.next());
}
oauthClients.drop();
log.debugv("Converted oauthClients to clients");
}
}
+35 -37
View File
@@ -1,37 +1,35 @@
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-mongo</artifactId>
<name>Keycloak Connections Mongo</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
</dependencies>
</project>
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Final-redhat-2</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-mongo</artifactId>
<name>Keycloak Connections Mongo</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
</dependencies>
</project>
@@ -16,6 +16,7 @@ import org.keycloak.models.KeycloakSessionFactory;
import javax.net.ssl.SSLSocketFactory;
import java.lang.reflect.Method;
import java.net.UnknownHostException;
import java.util.Collections;
/**
@@ -33,13 +34,15 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
"org.keycloak.models.entities.RequiredCredentialEntity",
"org.keycloak.models.entities.CredentialEntity",
"org.keycloak.models.entities.FederatedIdentityEntity",
"org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity",
"org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity",
"org.keycloak.models.mongo.keycloak.entities.MongoClientEntity",
"org.keycloak.models.sessions.mongo.entities.MongoUsernameLoginFailureEntity",
"org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity",
"org.keycloak.models.sessions.mongo.entities.MongoClientSessionEntity",
"org.keycloak.models.entities.UserFederationProviderEntity",
"org.keycloak.models.entities.ProtocolMapperEntity"
"org.keycloak.models.entities.ProtocolMapperEntity",
"org.keycloak.models.entities.IdentityProviderMapperEntity",
"org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity",
"org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity"
};
private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class);
@@ -48,7 +51,7 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
private MongoStore mongoStore;
private DB db;
private Config.Scope config;
protected Config.Scope config;
@Override
public MongoConnectionProvider create(KeycloakSession session) {
@@ -75,21 +78,9 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
synchronized (this) {
if (client == null) {
try {
String host = config.get("host", ServerAddress.defaultHost());
int port = config.getInt("port", ServerAddress.defaultPort());
this.client = createMongoClient();
String dbName = config.get("db", "keycloak");
String user = config.get("user");
String password = config.get("password");
MongoClientOptions clientOptions = getClientOptions();
if (user != null && password != null) {
MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential), clientOptions);
} else {
client = new MongoClient(new ServerAddress(host, port), clientOptions);
}
this.db = client.getDB(dbName);
String databaseSchema = config.get("databaseSchema");
@@ -108,8 +99,6 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
}
this.mongoStore = new MongoStoreImpl(db, getManagedEntities());
logger.debugv("Initialized mongo model. host: %s, port: %d, db: %s", host, port, dbName);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -138,6 +127,38 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
return "default";
}
/**
* Override this method if you want more possibility to configure Mongo client. It can be also used to inject mongo client
* from different source.
*
* This method can assume that "config" is already set and can use it.
*
* @return mongoClient instance, which will be shared for whole Keycloak
*
* @throws UnknownHostException
*/
protected MongoClient createMongoClient() throws UnknownHostException {
String host = config.get("host", ServerAddress.defaultHost());
int port = config.getInt("port", ServerAddress.defaultPort());
String dbName = config.get("db", "keycloak");
String user = config.get("user");
String password = config.get("password");
MongoClientOptions clientOptions = getClientOptions();
MongoClient client;
if (user != null && password != null) {
MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential), clientOptions);
} else {
client = new MongoClient(new ServerAddress(host, port), clientOptions);
}
logger.debugv("Initialized mongo model. host: %s, port: %d, db: %s", host, port, dbName);
return client;
}
protected MongoClientOptions getClientOptions() {
MongoClientOptions.Builder builder = MongoClientOptions.builder();
checkIntOption("connectionsPerHost", builder);
@@ -24,13 +24,35 @@ public interface MongoStore {
*/
void updateEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context);
/**
* Bulk update of more entities of some type
*
* @param type
* @param query
* @param update
* @param context
* @return count of updated entities
*/
<T extends MongoIdentifiableEntity> int updateEntities(Class<T> type, DBObject query, DBObject update, MongoStoreInvocationContext context);
<T extends MongoIdentifiableEntity> T loadEntity(Class<T> type, String id, MongoStoreInvocationContext context);
<T extends MongoIdentifiableEntity> T loadSingleEntity(Class<T> type, DBObject query, MongoStoreInvocationContext context);
/**
* @param type
* @param query
* @param context
* @return query result or empty list if no results available for the query. Doesn't return null
*/
<T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context);
/**
* @param type
* @param query
* @param context
* @return query result or empty list if no results available for the query. Doesn't return null
*/
<T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, DBObject sort, int firstResult, int maxResults, MongoStoreInvocationContext context);
<T extends MongoIdentifiableEntity> int countEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context);
@@ -39,7 +61,16 @@ public interface MongoStore {
boolean removeEntity(Class<? extends MongoIdentifiableEntity> type, String id, MongoStoreInvocationContext context);
boolean removeEntities(Class<? extends MongoIdentifiableEntity> type, DBObject query, MongoStoreInvocationContext context);
/**
*
* @param type
* @param query
* @param callback if true, then store will first load all entities, then call "afterRemove" for every entity. If false, the entities are removed directly without load and calling "afterRemove" callback
* false has better performance (especially if we are going to remove big number of entities)
* @param context
* @return count of removed entities
*/
int removeEntities(Class<? extends MongoIdentifiableEntity> type, DBObject query, boolean callback, MongoStoreInvocationContext context);
<S> boolean pushItemToList(MongoIdentifiableEntity entity, String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent, MongoStoreInvocationContext context);
@@ -22,6 +22,8 @@ public interface MongoStoreInvocationContext {
void beforeDBSearch(Class<? extends MongoIdentifiableEntity> entityType);
void beforeDBBulkUpdateOrRemove(Class<? extends MongoIdentifiableEntity> entityType);
void begin();
void commit();
@@ -7,6 +7,7 @@ import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
import com.mongodb.WriteResult;
import org.jboss.logging.Logger;
import org.keycloak.connections.mongo.api.MongoCollection;
import org.keycloak.connections.mongo.api.MongoEntity;
@@ -127,7 +128,7 @@ public class MongoStoreImpl implements MongoStore {
throw convertException(e);
}
// Treat object as created in this transaction (It is already submited to transaction)
// Treat object as created in this transaction (It is already submitted to transaction)
context.addCreatedEntity(entity);
}
@@ -170,6 +171,16 @@ public class MongoStoreImpl implements MongoStore {
context.addUpdateTask(entity, fullUpdateTask);
}
@Override
public <T extends MongoIdentifiableEntity> int updateEntities(Class<T> type, DBObject query, DBObject update, MongoStoreInvocationContext context) {
context.beforeDBBulkUpdateOrRemove(type);
DBCollection collection = getDBCollectionForType(type);
WriteResult wr = collection.update(query, update, false, true);
logger.debugf("Updated %d collections of type %s", wr.getN(), type);
return wr.getN();
}
@Override
public <T extends MongoIdentifiableEntity> T loadEntity(Class<T> type, String id, MongoStoreInvocationContext context) {
@@ -275,19 +286,32 @@ public class MongoStoreImpl implements MongoStore {
@Override
public boolean removeEntities(Class<? extends MongoIdentifiableEntity> type, DBObject query, MongoStoreInvocationContext context) {
List<? extends MongoIdentifiableEntity> foundObjects = loadEntities(type, query, context);
if (foundObjects.size() == 0) {
return false;
} else {
DBCollection dbCollection = getDBCollectionForType(type);
dbCollection.remove(query);
//logger.debug("Removed %d" + foundObjects.size() + " entities of type: " + type + ", query: " + query);
public int removeEntities(Class<? extends MongoIdentifiableEntity> type, DBObject query, boolean callback, MongoStoreInvocationContext context) {
if (callback) {
List<? extends MongoIdentifiableEntity> foundObjects = loadEntities(type, query, context);
if (foundObjects.size() == 0) {
return 0;
} else {
DBCollection dbCollection = getDBCollectionForType(type);
dbCollection.remove(query);
for (MongoIdentifiableEntity found : foundObjects) {
context.addRemovedEntity(found);;
logger.debugf("Removed %d entities of type: %s, query: %s", foundObjects.size(), type, query);
for (MongoIdentifiableEntity found : foundObjects) {
context.addRemovedEntity(found);;
}
return foundObjects.size();
}
return true;
} else {
context.beforeDBBulkUpdateOrRemove(type);
DBCollection dbCollection = getDBCollectionForType(type);
WriteResult writeResult = dbCollection.remove(query);
int removedCount = writeResult.getN();
logger.debugf("Removed directly %d entities of type: %s, query: %s", removedCount, type, query);
return removedCount;
}
}
@@ -45,6 +45,10 @@ public class SimpleMongoStoreInvocationContext implements MongoStoreInvocationCo
public void beforeDBSearch(Class<? extends MongoIdentifiableEntity> entityType) {
}
@Override
public void beforeDBBulkUpdateOrRemove(Class<? extends MongoIdentifiableEntity> entityType) {
}
@Override
public void begin() {
}
@@ -50,10 +50,6 @@ public class TransactionMongoStoreInvocationContext implements MongoStoreInvocat
@Override
public void addUpdateTask(MongoIdentifiableEntity entityToUpdate, MongoTask task) {
if (!loadedObjects.containsValue(entityToUpdate)) {
throw new IllegalStateException("Entity " + entityToUpdate + " not found in loaded objects");
}
Set<MongoTask> currentObjectTasks = pendingUpdateTasks.get(entityToUpdate);
if (currentObjectTasks == null) {
currentObjectTasks = new LinkedHashSet<MongoTask>();
@@ -106,6 +102,24 @@ public class TransactionMongoStoreInvocationContext implements MongoStoreInvocat
}
}
@Override
public void beforeDBBulkUpdateOrRemove(Class<? extends MongoIdentifiableEntity> entityType) {
beforeDBSearch(entityType);
Set<String> toRemove = new HashSet<String>();
for (Map.Entry<String, MongoIdentifiableEntity> entry : loadedObjects.entrySet()) {
MongoIdentifiableEntity entity = entry.getValue();
if (entity.getClass().equals(entityType)) {
toRemove.add(entry.getKey());
}
}
// Now remove all loadedObjects
for (String objectId : toRemove) {
loadedObjects.remove(objectId);
}
}
@Override
public void begin() {
loadedObjects.clear();
+24 -35
View File
@@ -1,35 +1,24 @@
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
</parent>
<name>Connections Parent</name>
<description/>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-pom</artifactId>
<packaging>pom</packaging>
<modules>
<module>jpa</module>
<module>jpa-liquibase</module>
<module>infinispan</module>
<module>mongo</module>
<module>file</module>
<module>mongo-update</module>
</modules>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Final-redhat-2</version>
</parent>
<name>Connections Parent</name>
<description/>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-pom</artifactId>
<packaging>pom</packaging>
<modules>
<module>jpa</module>
<module>jpa-liquibase</module>
<module>infinispan</module>
<module>mongo</module>
<module>file</module>
<module>mongo-update</module>
<module>http-client</module>
</modules>
</project>
+66 -67
View File
@@ -1,67 +1,66 @@
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-core-jaxrs</artifactId>
<name>Keycloak Core JAX-RS</name>
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Final-redhat-2</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-core-jaxrs</artifactId>
<name>Keycloak Core JAX-RS</name>
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
+112 -118
View File
@@ -1,118 +1,112 @@
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-core</artifactId>
<name>Keycloak Core</name>
<packaging>jar</packaging>
<description/>
<properties>
<timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
<keycloak.osgi.export>
org.keycloak.*
</keycloak.osgi.export>
<keycloak.osgi.import>
net.iharder;version=${base64.version},
*;resolution:=optional
</keycloak.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.iharder</groupId>
<artifactId>base64</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>3.9</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-ClassPath>.</Bundle-ClassPath>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
<?xml version="1.0"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Final-redhat-2</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-core</artifactId>
<name>Keycloak Core</name>
<packaging>jar</packaging>
<description/>
<properties>
<timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
<keycloak.osgi.export>
org.keycloak.*
</keycloak.osgi.export>
<keycloak.osgi.import>
net.iharder;version=${base64.version},
*;resolution:=optional
</keycloak.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.iharder</groupId>
<artifactId>base64</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-ClassPath>.</Bundle-ClassPath>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -39,7 +39,7 @@ public class RSATokenVerifier {
throw new VerificationException("Realm URL is null. Make sure to add auth-server-url to the configuration of your adapter!");
}
if (!realmUrl.equals(token.getIssuer())) {
throw new VerificationException("Token audience doesn't match domain.");
throw new VerificationException("Token audience doesn't match domain. Token issuer is " + token.getIssuer() + ", but URL from configuration is " + realmUrl);
}
if (checkActive && !token.isActive()) {
@@ -19,13 +19,13 @@ public interface AdapterConstants {
String AUTH_DATA_PARAM_NAME = "org.keycloak.json.adapterConfig";
// Attribute passed in codeToToken request from adapter to Keycloak and saved in ClientSession. Contains ID of HttpSession on adapter
public static final String APPLICATION_SESSION_STATE = "application_session_state";
public static final String CLIENT_SESSION_STATE = "client_session_state";
// Attribute passed in codeToToken request from adapter to Keycloak and saved in ClientSession. Contains hostname of adapter where HttpSession is served
public static final String APPLICATION_SESSION_HOST = "application_session_host";
public static final String CLIENT_SESSION_HOST = "client_session_host";
// Attribute passed in registerNode request for register new application cluster node once he joined cluster
public static final String APPLICATION_CLUSTER_HOST = "application_cluster_host";
public static final String CLIENT_CLUSTER_HOST = "client_cluster_host";
// Cookie used on adapter side to store token info. Used only when tokenStore is 'COOKIE'
public static final String KEYCLOAK_ADAPTER_STATE_COOKIE = "KEYCLOAK_ADAPTER_STATE";
@@ -7,22 +7,29 @@ import org.codehaus.jackson.annotate.JsonProperty;
* @version $Revision: 1 $
*/
public class AddressClaimSet {
@JsonProperty("formatted")
public static final String FORMATTED = "formatted";
public static final String STREET_ADDRESS = "street_address";
public static final String LOCALITY = "locality";
public static final String REGION = "region";
public static final String POSTAL_CODE = "postal_code";
public static final String COUNTRY = "country";
@JsonProperty(FORMATTED)
protected String formattedAddress;
@JsonProperty("street_address")
@JsonProperty(STREET_ADDRESS)
protected String streetAddress;
@JsonProperty("locality")
@JsonProperty(LOCALITY)
protected String locality;
@JsonProperty("region")
@JsonProperty(REGION)
protected String region;
@JsonProperty("postal_code")
@JsonProperty(POSTAL_CODE)
protected String postalCode;
@JsonProperty("country")
@JsonProperty(COUNTRY)
protected String country;
public String getFormattedAddress() {
@@ -13,76 +13,96 @@ import java.util.Map;
* @version $Revision: 1 $
*/
public class IDToken extends JsonWebToken {
public static final String NONCE = "nonce";
public static final String SESSION_STATE = "session_state";
public static final String NAME = "name";
public static final String GIVEN_NAME = "given_name";
public static final String FAMILY_NAME = "family_name";
public static final String MIDDLE_NAME = "middle_name";
public static final String NICKNAME = "nickname";
public static final String PREFERRED_USERNAME = "preferred_username";
public static final String PROFILE = "profile";
public static final String PICTURE = "picture";
public static final String WEBSITE = "website";
public static final String EMAIL = "email";
public static final String EMAIL_VERIFIED = "email_verified";
public static final String GENDER = "gender";
public static final String BIRTHDATE = "birthdate";
public static final String ZONEINFO = "zoneinfo";
public static final String LOCALE = "locale";
public static final String PHONE_NUMBER = "phone_number";
public static final String PHONE_NUMBER_VERIFIED = "phone_number_verified";
public static final String ADDRESS = "address";
public static final String UPDATED_AT = "updated_at";
public static final String CLAIMS_LOCALES = "claims_locales";
// NOTE!!! WE used to use @JsonUnwrapped on a UserClaimSet object. This screws up otherClaims and the won't work
// anymore. So don't have any @JsonUnwrapped!
@JsonProperty("nonce")
@JsonProperty(NONCE)
protected String nonce;
@JsonProperty("session_state")
@JsonProperty(SESSION_STATE)
protected String sessionState;
@JsonProperty("name")
@JsonProperty(NAME)
protected String name;
@JsonProperty("given_name")
@JsonProperty(GIVEN_NAME)
protected String givenName;
@JsonProperty("family_name")
@JsonProperty(FAMILY_NAME)
protected String familyName;
@JsonProperty("middle_name")
@JsonProperty(MIDDLE_NAME)
protected String middleName;
@JsonProperty("nickname")
@JsonProperty(NICKNAME)
protected String nickName;
@JsonProperty("preferred_username")
@JsonProperty(PREFERRED_USERNAME)
protected String preferredUsername;
@JsonProperty("profile")
@JsonProperty(PROFILE)
protected String profile;
@JsonProperty("picture")
@JsonProperty(PICTURE)
protected String picture;
@JsonProperty("website")
@JsonProperty(WEBSITE)
protected String website;
@JsonProperty("email")
@JsonProperty(EMAIL)
protected String email;
@JsonProperty("email_verified")
@JsonProperty(EMAIL_VERIFIED)
protected Boolean emailVerified;
@JsonProperty("gender")
@JsonProperty(GENDER)
protected String gender;
@JsonProperty("birthdate")
@JsonProperty(BIRTHDATE)
protected String birthdate;
@JsonProperty("zoneinfo")
@JsonProperty(ZONEINFO)
protected String zoneinfo;
@JsonProperty("locale")
@JsonProperty(LOCALE)
protected String locale;
@JsonProperty("phone_number")
@JsonProperty(PHONE_NUMBER)
protected String phoneNumber;
@JsonProperty("phone_number_verified")
@JsonProperty(PHONE_NUMBER_VERIFIED)
protected Boolean phoneNumberVerified;
@JsonProperty("address")
@JsonProperty(ADDRESS)
protected AddressClaimSet address;
@JsonProperty("updated_at")
@JsonProperty(UPDATED_AT)
protected Long updatedAt;
@JsonProperty("claims_locales")
@JsonProperty(CLAIMS_LOCALES)
protected String claimsLocales;
protected Map<String, Object> otherClaims = new HashMap<String, Object>();
public String getNonce() {
return nonce;
}
@@ -259,18 +279,4 @@ public class IDToken extends JsonWebToken {
this.claimsLocales = claimsLocales;
}
/**
* This is a map of any other claims and data that might be in the IDToken. Could be custom claims set up by the auth server
*
* @return
*/
@JsonAnyGetter
public Map<String, Object> getOtherClaims() {
return otherClaims;
}
@JsonAnySetter
public void setOtherClaims(String name, Object value) {
otherClaims.put(name, value);
}
}
@@ -1,10 +1,14 @@
package org.keycloak.representations;
import org.codehaus.jackson.annotate.JsonAnyGetter;
import org.codehaus.jackson.annotate.JsonAnySetter;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonProperty;
import org.keycloak.util.Time;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -29,6 +33,7 @@ public class JsonWebToken implements Serializable {
protected String type;
@JsonProperty("azp")
public String issuedFor;
protected Map<String, Object> otherClaims = new HashMap<String, Object>();
public String getId() {
return id;
@@ -153,4 +158,19 @@ public class JsonWebToken implements Serializable {
this.issuedFor = issuedFor;
return this;
}
/**
* This is a map of any other claims and data that might be in the IDToken. Could be custom claims set up by the auth server
*
* @return
*/
@JsonAnyGetter
public Map<String, Object> getOtherClaims() {
return otherClaims;
}
@JsonAnySetter
public void setOtherClaims(String name, Object value) {
otherClaims.put(name, value);
}
}
@@ -1,38 +0,0 @@
package org.keycloak.representations.idm;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ApplicationMappingsRepresentation {
protected String applicationId;
protected String application;
protected List<RoleRepresentation> mappings;
public String getApplicationId() {
return applicationId;
}
public void setApplicationId(String applicationId) {
this.applicationId = applicationId;
}
public String getApplication() {
return application;
}
public void setApplication(String application) {
this.application = application;
}
public List<RoleRepresentation> getMappings() {
return mappings;
}
public void setMappings(List<RoleRepresentation> mappings) {
this.mappings = mappings;
}
}
@@ -1,44 +1,14 @@
package org.keycloak.representations.idm;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ApplicationRepresentation {
protected String id;
@Deprecated
public class ApplicationRepresentation extends ClientRepresentation {
protected String name;
protected String adminUrl;
protected String baseUrl;
protected Boolean surrogateAuthRequired;
protected Boolean enabled;
protected String secret;
protected String[] defaultRoles;
protected List<String> redirectUris;
protected List<String> webOrigins;
@Deprecated
protected ClaimRepresentation claims;
protected Integer notBefore;
protected Boolean bearerOnly;
protected Boolean publicClient;
protected Boolean frontchannelLogout;
protected String protocol;
protected Map<String, String> attributes;
protected Boolean fullScopeAllowed;
protected Integer nodeReRegistrationTimeout;
protected Map<String, Integer> registeredNodes;
protected List<ClientIdentityProviderMappingRepresentation> identityProviders;
protected List<ProtocolMapperRepresentation> protocolMappers;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
@@ -48,70 +18,6 @@ public class ApplicationRepresentation {
this.name = name;
}
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public Boolean isSurrogateAuthRequired() {
return surrogateAuthRequired;
}
public void setSurrogateAuthRequired(Boolean surrogateAuthRequired) {
this.surrogateAuthRequired = surrogateAuthRequired;
}
public String getAdminUrl() {
return adminUrl;
}
public void setAdminUrl(String adminUrl) {
this.adminUrl = adminUrl;
}
public String getBaseUrl() {
return baseUrl;
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public List<String> getRedirectUris() {
return redirectUris;
}
public void setRedirectUris(List<String> redirectUris) {
this.redirectUris = redirectUris;
}
public List<String> getWebOrigins() {
return webOrigins;
}
public void setWebOrigins(List<String> webOrigins) {
this.webOrigins = webOrigins;
}
public String[] getDefaultRoles() {
return defaultRoles;
}
public void setDefaultRoles(String[] defaultRoles) {
this.defaultRoles = defaultRoles;
}
public ClaimRepresentation getClaims() {
return claims;
}
@@ -119,92 +25,4 @@ public class ApplicationRepresentation {
public void setClaims(ClaimRepresentation claims) {
this.claims = claims;
}
public Integer getNotBefore() {
return notBefore;
}
public void setNotBefore(Integer notBefore) {
this.notBefore = notBefore;
}
public Boolean isBearerOnly() {
return bearerOnly;
}
public void setBearerOnly(Boolean bearerOnly) {
this.bearerOnly = bearerOnly;
}
public Boolean isPublicClient() {
return publicClient;
}
public void setPublicClient(Boolean publicClient) {
this.publicClient = publicClient;
}
public Boolean isFullScopeAllowed() {
return fullScopeAllowed;
}
public void setFullScopeAllowed(Boolean fullScopeAllowed) {
this.fullScopeAllowed = fullScopeAllowed;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public Map<String, String> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes;
}
public Integer getNodeReRegistrationTimeout() {
return nodeReRegistrationTimeout;
}
public void setNodeReRegistrationTimeout(Integer nodeReRegistrationTimeout) {
this.nodeReRegistrationTimeout = nodeReRegistrationTimeout;
}
public Map<String, Integer> getRegisteredNodes() {
return registeredNodes;
}
public void setRegisteredNodes(Map<String, Integer> registeredNodes) {
this.registeredNodes = registeredNodes;
}
public Boolean isFrontchannelLogout() {
return frontchannelLogout;
}
public void setFrontchannelLogout(Boolean frontchannelLogout) {
this.frontchannelLogout = frontchannelLogout;
}
public List<ClientIdentityProviderMappingRepresentation> getIdentityProviders() {
return this.identityProviders;
}
public void setIdentityProviders(List<ClientIdentityProviderMappingRepresentation> identityProviders) {
this.identityProviders = identityProviders;
}
public List<ProtocolMapperRepresentation> getProtocolMappers() {
return protocolMappers;
}
public void setProtocolMappers(List<ProtocolMapperRepresentation> protocolMappers) {
this.protocolMappers = protocolMappers;
}
}
@@ -0,0 +1,38 @@
package org.keycloak.representations.idm;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ClientMappingsRepresentation {
protected String id;
protected String client;
protected List<RoleRepresentation> mappings;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClient() {
return client;
}
public void setClient(String client) {
this.client = client;
}
public List<RoleRepresentation> getMappings() {
return mappings;
}
public void setMappings(List<RoleRepresentation> mappings) {
this.mappings = mappings;
}
}
@@ -0,0 +1,218 @@
package org.keycloak.representations.idm;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ClientRepresentation {
protected String id;
protected String clientId;
protected String name;
protected String adminUrl;
protected String baseUrl;
protected Boolean surrogateAuthRequired;
protected Boolean enabled;
protected String secret;
protected String[] defaultRoles;
protected List<String> redirectUris;
protected List<String> webOrigins;
protected Integer notBefore;
protected Boolean bearerOnly;
protected Boolean consentRequired;
protected Boolean directGrantsOnly;
protected Boolean publicClient;
protected Boolean frontchannelLogout;
protected String protocol;
protected Map<String, String> attributes;
protected Boolean fullScopeAllowed;
protected Integer nodeReRegistrationTimeout;
protected Map<String, Integer> registeredNodes;
protected List<ProtocolMapperRepresentation> protocolMappers;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public Boolean isSurrogateAuthRequired() {
return surrogateAuthRequired;
}
public void setSurrogateAuthRequired(Boolean surrogateAuthRequired) {
this.surrogateAuthRequired = surrogateAuthRequired;
}
public String getAdminUrl() {
return adminUrl;
}
public void setAdminUrl(String adminUrl) {
this.adminUrl = adminUrl;
}
public String getBaseUrl() {
return baseUrl;
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public List<String> getRedirectUris() {
return redirectUris;
}
public void setRedirectUris(List<String> redirectUris) {
this.redirectUris = redirectUris;
}
public List<String> getWebOrigins() {
return webOrigins;
}
public void setWebOrigins(List<String> webOrigins) {
this.webOrigins = webOrigins;
}
public String[] getDefaultRoles() {
return defaultRoles;
}
public void setDefaultRoles(String[] defaultRoles) {
this.defaultRoles = defaultRoles;
}
public Integer getNotBefore() {
return notBefore;
}
public void setNotBefore(Integer notBefore) {
this.notBefore = notBefore;
}
public Boolean isBearerOnly() {
return bearerOnly;
}
public void setBearerOnly(Boolean bearerOnly) {
this.bearerOnly = bearerOnly;
}
public Boolean isConsentRequired() {
return consentRequired;
}
public void setConsentRequired(Boolean consentRequired) {
this.consentRequired = consentRequired;
}
public Boolean isDirectGrantsOnly() {
return directGrantsOnly;
}
public void setDirectGrantsOnly(Boolean directGrantsOnly) {
this.directGrantsOnly = directGrantsOnly;
}
public Boolean isPublicClient() {
return publicClient;
}
public void setPublicClient(Boolean publicClient) {
this.publicClient = publicClient;
}
public Boolean isFullScopeAllowed() {
return fullScopeAllowed;
}
public void setFullScopeAllowed(Boolean fullScopeAllowed) {
this.fullScopeAllowed = fullScopeAllowed;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public Map<String, String> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes;
}
public Integer getNodeReRegistrationTimeout() {
return nodeReRegistrationTimeout;
}
public void setNodeReRegistrationTimeout(Integer nodeReRegistrationTimeout) {
this.nodeReRegistrationTimeout = nodeReRegistrationTimeout;
}
public Map<String, Integer> getRegisteredNodes() {
return registeredNodes;
}
public void setRegisteredNodes(Map<String, Integer> registeredNodes) {
this.registeredNodes = registeredNodes;
}
public Boolean isFrontchannelLogout() {
return frontchannelLogout;
}
public void setFrontchannelLogout(Boolean frontchannelLogout) {
this.frontchannelLogout = frontchannelLogout;
}
public List<ProtocolMapperRepresentation> getProtocolMappers() {
return protocolMappers;
}
public void setProtocolMappers(List<ProtocolMapperRepresentation> protocolMappers) {
this.protocolMappers = protocolMappers;
}
}
@@ -0,0 +1,53 @@
package org.keycloak.representations.idm;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ConfigPropertyRepresentation {
protected String name;
protected String label;
protected String helpText;
protected String type;
protected Object defaultValue;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Object getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(Object defaultValue) {
this.defaultValue = defaultValue;
}
public String getHelpText() {
return helpText;
}
public void setHelpText(String helpText) {
this.helpText = helpText;
}
}
@@ -0,0 +1,57 @@
package org.keycloak.representations.idm;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class IdentityProviderMapperRepresentation {
protected String id;
protected String name;
protected String identityProviderAlias;
protected String identityProviderMapper;
protected Map<String, String> config = new HashMap<String, String>();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIdentityProviderAlias() {
return identityProviderAlias;
}
public void setIdentityProviderAlias(String identityProviderAlias) {
this.identityProviderAlias = identityProviderAlias;
}
public String getIdentityProviderMapper() {
return identityProviderMapper;
}
public void setIdentityProviderMapper(String identityProviderMapper) {
this.identityProviderMapper = identityProviderMapper;
}
public Map<String, String> getConfig() {
return config;
}
public void setConfig(Map<String, String> config) {
this.config = config;
}
}
@@ -0,0 +1,57 @@
package org.keycloak.representations.idm;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class IdentityProviderMapperTypeRepresentation {
protected String id;
protected String name;
protected String category;
protected String helpText;
protected List<ConfigPropertyRepresentation> properties = new LinkedList<>();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getHelpText() {
return helpText;
}
public void setHelpText(String helpText) {
this.helpText = helpText;
}
public List<ConfigPropertyRepresentation> getProperties() {
return properties;
}
public void setProperties(List<ConfigPropertyRepresentation> properties) {
this.properties = properties;
}
}
@@ -31,6 +31,7 @@ public class IdentityProviderRepresentation {
protected boolean enabled = true;
protected boolean updateProfileFirstLogin = true;
protected boolean storeToken;
protected boolean addReadTokenRoleOnCreate;
protected boolean authenticateByDefault;
protected Map<String, String> config = new HashMap<String, String>();
@@ -97,4 +98,12 @@ public class IdentityProviderRepresentation {
public void setStoreToken(boolean storeToken) {
this.storeToken = storeToken;
}
public boolean isAddReadTokenRoleOnCreate() {
return addReadTokenRoleOnCreate;
}
public void setAddReadTokenRoleOnCreate(boolean addReadTokenRoleOnCreate) {
this.addReadTokenRoleOnCreate = addReadTokenRoleOnCreate;
}
}
@@ -9,7 +9,7 @@ import java.util.Map;
*/
public class MappingsRepresentation {
protected List<RoleRepresentation> realmMappings;
protected Map<String, ApplicationMappingsRepresentation> applicationMappings;
protected Map<String, ClientMappingsRepresentation> clientMappings;
public List<RoleRepresentation> getRealmMappings() {
return realmMappings;
@@ -19,11 +19,11 @@ public class MappingsRepresentation {
this.realmMappings = realmMappings;
}
public Map<String, ApplicationMappingsRepresentation> getApplicationMappings() {
return applicationMappings;
public Map<String, ClientMappingsRepresentation> getClientMappings() {
return clientMappings;
}
public void setApplicationMappings(Map<String, ApplicationMappingsRepresentation> applicationMappings) {
this.applicationMappings = applicationMappings;
public void setClientMappings(Map<String, ClientMappingsRepresentation> clientMappings) {
this.clientMappings = clientMappings;
}
}
@@ -1,157 +1,9 @@
package org.keycloak.representations.idm;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class OAuthClientRepresentation {
protected String id;
protected String name;
protected List<String> redirectUris;
protected List<String> webOrigins;
protected Boolean enabled;
protected String secret;
@Deprecated
protected ClaimRepresentation claims;
protected Integer notBefore;
protected Boolean publicClient;
protected String protocol;
protected Map<String, String> attributes;
protected Boolean directGrantsOnly;
protected Boolean fullScopeAllowed;
protected Boolean frontchannelLogout;
protected List<ProtocolMapperRepresentation> protocolMappers;
private List<ClientIdentityProviderMappingRepresentation> identityProviders;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public List<String> getRedirectUris() {
return redirectUris;
}
public void setRedirectUris(List<String> redirectUris) {
this.redirectUris = redirectUris;
}
public List<String> getWebOrigins() {
return webOrigins;
}
public void setWebOrigins(List<String> webOrigins) {
this.webOrigins = webOrigins;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public ClaimRepresentation getClaims() {
return claims;
}
public void setClaims(ClaimRepresentation claims) {
this.claims = claims;
}
public Integer getNotBefore() {
return notBefore;
}
public void setNotBefore(Integer notBefore) {
this.notBefore = notBefore;
}
public Boolean isPublicClient() {
return publicClient;
}
public void setPublicClient(Boolean publicClient) {
this.publicClient = publicClient;
}
public Boolean isDirectGrantsOnly() {
return directGrantsOnly;
}
public void setDirectGrantsOnly(Boolean directGrantsOnly) {
this.directGrantsOnly = directGrantsOnly;
}
public Boolean isFullScopeAllowed() {
return fullScopeAllowed;
}
public void setFullScopeAllowed(Boolean fullScopeAllowed) {
this.fullScopeAllowed = fullScopeAllowed;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public Map<String, String> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes;
}
public Boolean isFrontchannelLogout() {
return frontchannelLogout;
}
public void setFrontchannelLogout(Boolean frontchannelLogout) {
this.frontchannelLogout = frontchannelLogout;
}
public List<ClientIdentityProviderMappingRepresentation> getIdentityProviders() {
return this.identityProviders;
}
public void setIdentityProviders(List<ClientIdentityProviderMappingRepresentation> identityProviders) {
this.identityProviders = identityProviders;
}
public List<ProtocolMapperRepresentation> getProtocolMappers() {
return protocolMappers;
}
public void setProtocolMappers(List<ProtocolMapperRepresentation> protocolMappers) {
this.protocolMappers = protocolMappers;
}
@Deprecated
public class OAuthClientRepresentation extends ApplicationRepresentation {
}
@@ -12,55 +12,7 @@ public class ProtocolMapperTypeRepresentation {
protected String category;
protected String helpText;
public static class ConfigProperty {
protected String name;
protected String label;
protected String helpText;
protected String type;
protected Object defaultValue;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Object getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(Object defaultValue) {
this.defaultValue = defaultValue;
}
public String getHelpText() {
return helpText;
}
public void setHelpText(String helpText) {
this.helpText = helpText;
}
}
protected List<ConfigProperty> properties;
protected List<ConfigPropertyRepresentation> properties;
public String getId() {
return id;
@@ -94,11 +46,11 @@ public class ProtocolMapperTypeRepresentation {
this.helpText = helpText;
}
public List<ConfigProperty> getProperties() {
public List<ConfigPropertyRepresentation> getProperties() {
return properties;
}
public void setProperties(List<ConfigProperty> properties) {
public void setProperties(List<ConfigPropertyRepresentation> properties) {
this.properties = properties;
}
}
@@ -25,13 +25,6 @@ public class RealmRepresentation {
protected Boolean verifyEmail;
protected Boolean resetPasswordAllowed;
@Deprecated
protected Boolean social;
@Deprecated
protected Boolean updateProfileOnInitialSocialLogin;
@Deprecated
protected Map<String, String> socialProviders;
protected Boolean userCacheEnabled;
protected Boolean realmCacheEnabled;
@@ -55,9 +48,8 @@ public class RealmRepresentation {
protected String passwordPolicy;
protected List<UserRepresentation> users;
protected List<ScopeMappingRepresentation> scopeMappings;
protected Map<String, List<ScopeMappingRepresentation>> applicationScopeMappings;
protected List<ApplicationRepresentation> applications;
protected List<OAuthClientRepresentation> oauthClients;
protected Map<String, List<ScopeMappingRepresentation>> clientScopeMappings;
protected List<ClientRepresentation> clients;
protected Map<String, String> browserSecurityHeaders;
protected Map<String, String> smtpServer;
protected List<UserFederationProviderRepresentation> userFederationProviders;
@@ -70,12 +62,25 @@ public class RealmRepresentation {
protected List<String> eventsListeners;
protected List<String> enabledEventTypes;
private List<IdentityProviderRepresentation> identityProviders;
private List<IdentityProviderMapperRepresentation> identityProviderMappers;
private List<ProtocolMapperRepresentation> protocolMappers;
private Boolean identityFederationEnabled;
protected Boolean internationalizationEnabled;
protected Set<String> supportedLocales;
protected String defaultLocale;
@Deprecated
protected Boolean social;
@Deprecated
protected Boolean updateProfileOnInitialSocialLogin;
@Deprecated
protected Map<String, String> socialProviders;
@Deprecated
protected Map<String, List<ScopeMappingRepresentation>> applicationScopeMappings;
@Deprecated
protected List<ApplicationRepresentation> applications;
@Deprecated
protected List<OAuthClientRepresentation> oauthClients;
public String getId() {
return id;
@@ -101,14 +106,6 @@ public class RealmRepresentation {
return applications;
}
public ApplicationRepresentation resource(String name) {
ApplicationRepresentation resource = new ApplicationRepresentation();
if (applications == null) applications = new ArrayList<ApplicationRepresentation>();
applications.add(resource);
resource.setName(name);
return resource;
}
public void setUsers(List<UserRepresentation> users) {
this.users = users;
}
@@ -121,8 +118,12 @@ public class RealmRepresentation {
return user;
}
public void setApplications(List<ApplicationRepresentation> applications) {
this.applications = applications;
public List<ClientRepresentation> getClients() {
return clients;
}
public void setClients(List<ClientRepresentation> clients) {
this.clients = clients;
}
public Boolean isEnabled() {
@@ -321,22 +322,16 @@ public class RealmRepresentation {
this.resetPasswordAllowed = resetPassword;
}
@Deprecated
public Boolean isSocial() {
return social;
}
public void setSocial(Boolean social) {
this.social = social;
}
@Deprecated
public Boolean isUpdateProfileOnInitialSocialLogin() {
return updateProfileOnInitialSocialLogin;
}
public void setUpdateProfileOnInitialSocialLogin(Boolean updateProfileOnInitialSocialLogin) {
this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin;
}
public Map<String, String> getBrowserSecurityHeaders() {
return browserSecurityHeaders;
}
@@ -345,14 +340,11 @@ public class RealmRepresentation {
this.browserSecurityHeaders = browserSecurityHeaders;
}
@Deprecated
public Map<String, String> getSocialProviders() {
return socialProviders;
}
public void setSocialProviders(Map<String, String> socialProviders) {
this.socialProviders = socialProviders;
}
public Map<String, String> getSmtpServer() {
return smtpServer;
}
@@ -361,22 +353,24 @@ public class RealmRepresentation {
this.smtpServer = smtpServer;
}
@Deprecated
public List<OAuthClientRepresentation> getOauthClients() {
return oauthClients;
}
public void setOauthClients(List<OAuthClientRepresentation> oauthClients) {
this.oauthClients = oauthClients;
public Map<String, List<ScopeMappingRepresentation>> getClientScopeMappings() {
return clientScopeMappings;
}
public void setClientScopeMappings(Map<String, List<ScopeMappingRepresentation>> clientScopeMappings) {
this.clientScopeMappings = clientScopeMappings;
}
@Deprecated
public Map<String, List<ScopeMappingRepresentation>> getApplicationScopeMappings() {
return applicationScopeMappings;
}
public void setApplicationScopeMappings(Map<String, List<ScopeMappingRepresentation>> applicationScopeMappings) {
this.applicationScopeMappings = applicationScopeMappings;
}
public RolesRepresentation getRoles() {
return roles;
}
@@ -577,4 +571,19 @@ public class RealmRepresentation {
public void setDefaultLocale(String defaultLocale) {
this.defaultLocale = defaultLocale;
}
public List<IdentityProviderMapperRepresentation> getIdentityProviderMappers() {
return identityProviderMappers;
}
public void setIdentityProviderMappers(List<IdentityProviderMapperRepresentation> identityProviderMappers) {
this.identityProviderMappers = identityProviderMappers;
}
public void addIdentityProviderMapper(IdentityProviderMapperRepresentation rep) {
if (identityProviderMappers == null) identityProviderMappers = new LinkedList<>();
identityProviderMappers.add(rep);
}
}
@@ -17,9 +17,10 @@ public class RoleRepresentation {
public static class Composites {
protected Set<String> realm;
protected Map<String, List<String>> client;
@Deprecated
protected Map<String, List<String>> application;
public Set<String> getRealm() {
return realm;
}
@@ -28,12 +29,17 @@ public class RoleRepresentation {
this.realm = realm;
}
public Map<String, List<String>> getApplication() {
return application;
public Map<String, List<String>> getClient() {
return client;
}
public void setApplication(Map<String, List<String>> application) {
this.application = application;
public void setClient(Map<String, List<String>> client) {
this.client = client;
}
@Deprecated
public Map<String, List<String>> getApplication() {
return application;
}
}
@@ -9,6 +9,8 @@ import java.util.Map;
*/
public class RolesRepresentation {
protected List<RoleRepresentation> realm;
protected Map<String, List<RoleRepresentation>> client;
@Deprecated
protected Map<String, List<RoleRepresentation>> application;
public List<RoleRepresentation> getRealm() {
@@ -19,11 +21,16 @@ public class RolesRepresentation {
this.realm = realm;
}
public Map<String, List<RoleRepresentation>> getClient() {
return client;
}
public void setClient(Map<String, List<RoleRepresentation>> client) {
this.client = client;
}
@Deprecated
public Map<String, List<RoleRepresentation>> getApplication() {
return application;
}
public void setApplication(Map<String, List<RoleRepresentation>> application) {
this.application = application;
}
}
@@ -0,0 +1,52 @@
package org.keycloak.representations.idm;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class UserConsentRepresentation {
protected String clientId;
// Key is protocol, Value is list of granted consents for this protocol
protected Map<String, List<String>> grantedProtocolMappers;
protected List<String> grantedRealmRoles;
// Key is clientId, Value is list of granted roles of this client
protected Map<String, List<String>> grantedClientRoles;
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public Map<String, List<String>> getGrantedProtocolMappers() {
return grantedProtocolMappers;
}
public void setGrantedProtocolMappers(Map<String, List<String>> grantedProtocolMappers) {
this.grantedProtocolMappers = grantedProtocolMappers;
}
public List<String> getGrantedRealmRoles() {
return grantedRealmRoles;
}
public void setGrantedRealmRoles(List<String> grantedRealmRoles) {
this.grantedRealmRoles = grantedRealmRoles;
}
public Map<String, List<String>> getGrantedClientRoles() {
return grantedClientRoles;
}
public void setGrantedClientRoles(Map<String, List<String>> grantedClientRoles) {
this.grantedClientRoles = grantedClientRoles;
}
}
@@ -25,10 +25,14 @@ public class UserRepresentation {
protected List<CredentialRepresentation> credentials;
protected List<String> requiredActions;
protected List<FederatedIdentityRepresentation> federatedIdentities;
protected List<String> realmRoles;
protected Map<String, List<String>> clientRoles;
protected List<UserConsentRepresentation> clientConsents;
@Deprecated
protected Map<String, List<String>> applicationRoles;
@Deprecated
protected List<SocialLinkRepresentation> socialLinks;
protected List<String> realmRoles;
protected Map<String, List<String>> applicationRoles;
public String getSelf() {
return self;
@@ -165,12 +169,25 @@ public class UserRepresentation {
this.realmRoles = realmRoles;
}
public Map<String, List<String>> getApplicationRoles() {
return applicationRoles;
public Map<String, List<String>> getClientRoles() {
return clientRoles;
}
public void setApplicationRoles(Map<String, List<String>> applicationRoles) {
this.applicationRoles = applicationRoles;
public void setClientRoles(Map<String, List<String>> clientRoles) {
this.clientRoles = clientRoles;
}
public List<UserConsentRepresentation> getClientConsents() {
return clientConsents;
}
public void setClientConsents(List<UserConsentRepresentation> clientConsents) {
this.clientConsents = clientConsents;
}
@Deprecated
public Map<String, List<String>> getApplicationRoles() {
return applicationRoles;
}
public String getFederationLink() {
@@ -15,7 +15,6 @@ public class UserSessionRepresentation {
private String ipAddress;
private long start;
private long lastAccess;
private Map<String, String> applications = new HashMap<String, String>();
private Map<String, String> clients = new HashMap<String, String>();
public String getId() {
@@ -58,14 +57,6 @@ public class UserSessionRepresentation {
this.lastAccess = lastAccess;
}
public Map<String, String> getApplications() {
return applications;
}
public void setApplications(Map<String, String> applications) {
this.applications = applications;
}
public Map<String, String> getClients() {
return clients;
}
@@ -1,4 +1,4 @@
package org.keycloak.adapters;
package org.keycloak.util;
import org.keycloak.constants.GenericConstants;
@@ -0,0 +1,25 @@
package org.keycloak.util;
import java.util.Properties;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class SystemEnvProperties extends Properties {
@Override
public String getProperty(String key) {
if (key.startsWith("env.")) {
return System.getenv().get(key.substring(4));
} else {
return System.getProperty(key);
}
}
@Override
public String getProperty(String key, String defaultValue) {
String value = getProperty(key);
return value != null ? value : defaultValue;
}
}
@@ -3,6 +3,8 @@ package org.keycloak.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Map;
import java.util.Properties;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.io.IOContext;
@@ -16,6 +18,8 @@ import org.codehaus.jackson.util.JsonParserDelegate;
*/
public class SystemPropertiesJsonParserFactory extends MappingJsonFactory {
private static final Properties properties = new SystemEnvProperties();
@Override
protected JsonParser _createJsonParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException {
JsonParser delegate = super._createJsonParser(data, offset, len, ctxt);
@@ -34,8 +38,6 @@ public class SystemPropertiesJsonParserFactory extends MappingJsonFactory {
return new SystemPropertiesAwareJsonParser(delegate);
}
public static class SystemPropertiesAwareJsonParser extends JsonParserDelegate {
public SystemPropertiesAwareJsonParser(JsonParser d) {
@@ -45,7 +47,7 @@ public class SystemPropertiesJsonParserFactory extends MappingJsonFactory {
@Override
public String getText() throws IOException {
String orig = super.getText();
return StringPropertyReplacer.replaceProperties(orig);
return StringPropertyReplacer.replaceProperties(orig, properties);
}
}
}
@@ -17,6 +17,10 @@ public class Time {
return new Date(((long) time ) * 1000);
}
public static long toMillis(int time) {
return ((long) time) * 1000;
}
public static void setOffset(int offset) {
Time.offset = offset;
}
@@ -18,7 +18,8 @@ public class UriUtils {
public static String getOrigin(String uri) {
String u = uri.toString();
return u.substring(0, u.indexOf('/', 8));
int e = u.indexOf('/', 8);
return e != -1 ? u.substring(0, u.indexOf('/', 8)) : u;
}
public static boolean isOrigin(String url) {
@@ -4,6 +4,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import org.codehaus.jackson.annotate.JsonAnyGetter;
import org.codehaus.jackson.annotate.JsonAnySetter;
@@ -12,6 +13,7 @@ import org.codehaus.jackson.annotate.JsonUnwrapped;
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.representations.IDToken;
import org.keycloak.representations.JsonWebToken;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.util.JsonSerialization;
@@ -20,6 +22,32 @@ import org.keycloak.util.JsonSerialization;
*/
public class JsonParserTest {
@Test
public void regex() throws Exception {
Pattern p = Pattern.compile(".*(?!\\.pdf)");
if (p.matcher("foo.pdf").matches()) {
System.out.println(".pdf no match");
}
if (p.matcher("foo.txt").matches()) {
System.out.println("foo.txt matches");
}
}
@Test
public void testOtherClaims() throws Exception {
String json = "{ \"floatData\" : 555.5," +
"\"boolData\": true, " +
"\"intData\": 1234," +
"\"array\": [ \"val\", \"val2\"] }";
JsonWebToken token = JsonSerialization.readValue(json, JsonWebToken.class);
System.out.println(token.getOtherClaims().get("floatData").getClass().getName());
System.out.println(token.getOtherClaims().get("boolData").getClass().getName());
System.out.println(token.getOtherClaims().get("intData").getClass().getName());
System.out.println(token.getOtherClaims().get("array").getClass().getName());
}
@Test
public void testUnwrap() throws Exception {
// just experimenting with unwrapped and any properties
Loaded 100 of 1622 files, more files were not shown because too many files have changed in this diff. Show more