Compare commits

..
685 Commits
Author SHA1 Message Date
Stian Thorgersen caeb710444 KEYCLOAK-1476 2015-06-17 19:34:41 +02:00
Marko Strukelj 0b0b101184 KEYCLOAK-1476 NotSerializableException: org.keycloak.models.cache.entities.CachedClientRole
Conflicts:
	model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java
2015-06-17 19:34:38 +02:00
Stian Thorgersen 9711ccd914 Merge pull request #1397 from stianst/master
Fixes
2015-06-17 15:11:33 +02:00
Stian Thorgersen fbc2b845ad KEYCLOAK-1476 NotSerializableException: org.keycloak.models.cache.entities.CachedClientRole 2015-06-17 14:05:55 +02:00
Stian Thorgersen 39ecca0989 KEYCLOAK-1479 2015-06-17 13:53:11 +02:00
Stian Thorgersen f19b6afd85 Fix demo-dist build prob 2015-06-17 13:53:11 +02:00
Stian Thorgersen dca347398d Make it possible to specify ldif file for LDAPEmbeddedServer 2015-06-17 13:53:11 +02:00
Stian Thorgersen de6341bd87 Fix demo-dist build prob 2015-06-17 13:53:11 +02:00
mposolda 5cb9dbb87a Reduce liquibase logging during test to debug 2015-06-17 12:15:30 +02:00
Stian Thorgersen 605f5decb3 Merge pull request #1392 from mstruk/KEYCLOAK-1448
KEYCLOAK-1448 Cannot configure an authenticator for method KEYCLOAK
2015-06-16 14:11:28 +01:00
Marko Strukelj d00e128920 KEYCLOAK-1448 Cannot configure an authenticator for method KEYCLOAK 2015-06-16 14:51:31 +02:00
Stian Thorgersen 226c1c53c0 Merge pull request #1390 from stianst/master
Add missing ExampleDS
2015-06-16 13:40:36 +01:00
Stian Thorgersen 001b874903 Add missing ExampleDS 2015-06-16 14:40:19 +02:00
Stian Thorgersen cc523ea356 Merge pull request #1388 from stianst/master
Fixes
2015-06-16 13:03:10 +01:00
Stian Thorgersen 5dc2b7b1d5 KEYCLOAK-1446
Field ... of subresource ... will not be injected according to spec
2015-06-16 14:02:21 +02:00
Stian Thorgersen dedaa24972 Updated clustering docs 2015-06-16 13:31:58 +02:00
Stian Thorgersen 1b0c4e5d1f KEYCLOAK-1468
Infinispan cache not available in JNDI
2015-06-16 13:31:51 +02:00
Stian Thorgersen b7e3fc5f73 Recover server-overlay name 2015-06-16 08:11:15 +02:00
Stian Thorgersen 9b9ec09788 Merge pull request #1384 from stianst/master
Fix event-store example
2015-06-16 06:21:30 +01:00
Bill Burke bd7ceb0b73 Merge pull request #1383 from patriot1burke/master
verify clientsession actions
2015-06-15 18:28:17 -04:00
Bill Burke 9638c0dd83 verify clientsession actions 2015-06-15 18:04:55 -04:00
Stian Thorgersen 60f40544bb Fix event-store example 2015-06-15 20:24:32 +02:00
Stian Thorgersen 066fb7178d Merge pull request #1382 from stianst/master
Added wildfly extension to dist
2015-06-15 19:14:05 +01:00
Stian Thorgersen cd762e5e8c Added wildfly extension to dist 2015-06-15 20:13:20 +02:00
Stian Thorgersen f990d68cee Merge pull request #1380 from stianst/master
Fixes
2015-06-15 15:04:31 +01:00
Bill Burke 88f14b39a7 Merge pull request #1378 from patriot1burke/master
auth spi
2015-06-15 09:50:05 -04:00
Stian Thorgersen eda64584d7 Update keycloak-server.json 2015-06-15 15:49:55 +02:00
Bill Burke 2f2a2dced6 Merge remote-tracking branch 'upstream/master' 2015-06-15 09:28:25 -04:00
Bill Burke 38a2ea2fdf fix merge conflict mistakes 2015-06-15 09:28:11 -04:00
Bill Burke 43bad9a608 merge 2015-06-15 09:02:15 -04:00
Stian Thorgersen be5f3086ce KEYCLOAK-1449 Demo bundle doesn't include subsystem configuration 2015-06-15 15:01:22 +02:00
Stian Thorgersen 663f12d5e6 Merge pull request #1376 from stianst/master
Fixes
2015-06-15 14:00:09 +01:00
Stian Thorgersen 6e758d721d Fix CDI examples 2015-06-15 14:21:27 +02:00
Stian Thorgersen 4c98b04ab7 KEYCLOAK-1447 LinkageError deploying oauth-client-cdi to WildFly 8.2.0.Final 2015-06-15 13:22:36 +02:00
Stian Thorgersen f846645989 Update adapter installation docs 2015-06-15 09:03:50 +02:00
Stian Thorgersen 6bc79b590c Renamed eap6 server overlay poms 2015-06-15 09:03:50 +02:00
Stian Thorgersen 2ad7e870d8 Merge pull request #1374 from stianst/master
Fix testsuite/docker-cluster deps
2015-06-15 06:41:11 +01:00
Stian Thorgersen 87636669ae Fix testsuite/docker-cluster deps 2015-06-15 07:40:19 +02:00
Stian Thorgersen 8d627f1329 Merge pull request #1369 from stianst/master
KEYCLOAK-1411
2015-06-15 06:32:49 +01:00
Bill Burke c51cc4703b kerberos fixes 2015-06-14 20:58:02 -04:00
mposolda a1124c9e1f KEYCLOAK-1260 Upgrade h2 version to fix locking issues in SAMLKeyCloakServerBrokerWithSignatureTest 2015-06-12 19:58:27 +02:00
mposolda 20e245cb05 KEYCLOAK-1298 Fix constraint names to be uppercased 2015-06-12 19:17:12 +02:00
mposolda 5c8e165080 Enable liquibase trace logging with JPA profile 2015-06-12 17:20:57 +02:00
mposolda afe088a9e8 Fix Oracle together with Sybase 2015-06-12 17:20:38 +02:00
Stian Thorgersen 162f31baa2 Update server and adapter install docs 2015-06-12 16:14:03 +02:00
Stian Thorgersen 28a50df746 KEYCLOAK-1411
DefaultCacheUserProvider addUser returns UserModel instance which is not cached/managed by the cache
2015-06-12 15:33:22 +02:00
Stian Thorgersen 5a0d600322 Merge pull request #1367 from stianst/master
Bump version
2015-06-12 13:36:42 +01:00
Stian Thorgersen 38c1945ce4 Bump version 2015-06-12 14:35:34 +02:00
Stian Thorgersen 939527bd98 Bump version 2015-06-12 14:32:06 +02:00
Stian Thorgersen cc2de52a1a Revert to old RestEasy until we can drop support for EAP 6.4 2015-06-12 14:20:08 +02:00
Stian Thorgersen 4ccc37abbb Add EAP6 standalone.xml to source instead of dl eap6 from techpreview repo 2015-06-12 14:20:08 +02:00
Stian Thorgersen 47d1681e90 Merge pull request #1365 from stianst/master
Error page is displayed if user rejects sign in with social provider
2015-06-12 13:03:08 +01:00
Stian Thorgersen c89a412661 KEYCLOAK-1321
Error page is displayed if user rejects sign in with social provider
2015-06-12 13:32:30 +02:00
Stian Thorgersen 5bd53804f9 Fix admin-api to show that users are retrieved by ip, not username 2015-06-12 13:32:30 +02:00
Stian Thorgersen 0d957311b4 Merge pull request #1364 from mstruk/wf9cr2
KEYCLOAK-1441 Upgrade to WildFly 9 CR2
2015-06-12 11:30:42 +01:00
Marko Strukelj c2495acca9 KEYCLOAK-1441 Upgrade to WildFly 9 CR2 2015-06-12 11:49:36 +02:00
Marek Posolda c297b29a13 Merge pull request #1363 from mposolda/master
KEYCLOAK-1403 Update liquibase to newest version. Fix Sybase
2015-06-12 11:26:09 +02:00
mposolda 112ebf77d5 KEYCLOAK-1403 Update liquibase to newest version. Fix Sybase 2015-06-12 11:11:28 +02:00
Stian Thorgersen 5993d40fab Merge pull request #1353 from Smartling/KEYCLOAK-1438
Fix Spring Security adapter logout handling
2015-06-12 07:50:00 +01:00
Stian Thorgersen 2d82d15e5b Merge pull request #1361 from ssilvert/KEYCLOAK-1083-unlock-user-account
KEYCLOAK-1083: Provide a way for admin to unlock user account
2015-06-12 07:44:07 +01:00
Stian Thorgersen 656b157103 Merge pull request #1360 from mstruk/fix-poms
Fixed pom issues that were generating tons of Maven warnings
2015-06-12 07:43:38 +01:00
Bill Burke 392fa21f1e finish reg 2015-06-11 21:15:53 -04:00
Bill Burke fa99b5415d flow alias lookup 2015-06-11 18:02:49 -04:00
Marek Posolda 514417ac2d Merge pull request #1362 from mposolda/master
KEYCLOAK-1405 Fix MSSQL Server
2015-06-11 22:37:16 +02:00
mposolda 938cfe2555 KEYCLOAK-1405 Fix MSSQL Server 2015-06-11 22:23:45 +02:00
Stan Silvert d6e64a2c5e KEYCLOAK-1083: Provide a way for admin to unlock user account 2015-06-11 15:32:03 -04:00
Bill Burke c52d16da6d default required actions in model 2015-06-11 15:18:09 -04:00
Bill Burke 3f62cd9271 terms and conditions 2015-06-11 14:39:08 -04:00
Marek Posolda df7cb4a9d0 Merge pull request #1359 from mposolda/master
Database fixes
2015-06-11 17:14:19 +02:00
Marko Strukelj 9a37696d29 Fixed pom issues that were generating tons of Maven warnings 2015-06-11 16:57:33 +02:00
mposolda 20fe7ec7b8 Database fixes 2015-06-11 16:56:38 +02:00
Stian Thorgersen 37cef49565 Merge pull request #1357 from mstruk/eap6
KEYCLOAK-1412 Server-overlay for EAP 6.4
2015-06-11 15:00:20 +01:00
Stian Thorgersen b5ee395aac Merge pull request #1358 from tkyjovsk/master
fixed some UI tests broken by KEYCLOAK-1431
2015-06-11 13:56:23 +01:00
fkiss 7b89b80f71 added ignore on role mappings test 2015-06-11 14:37:12 +02:00
Tomas Kyjovsky 08f2b0dea9 fixed UI tests broken by KEYCLOAK-1431 2015-06-11 14:15:05 +02:00
Marko Strukelj 92e17f4b40 KEYCLOAK-1412 Server-overlay for EAP 6.4
- Added as7-server-subsystem
 - Added eap6-server-overlay
 - Moved modules to eap6-server-modules
 - Renamed wildfly-server-subsystem to wf9-server-subsystem for consistency
2015-06-11 09:40:47 +02:00
Stian Thorgersen b8aedd5a3a Merge pull request #1356 from matzew/ImportFix
Applying suggested import change from mstruk 💄
2015-06-11 08:06:27 +01:00
Matthias Wessendorf d3e178d459 Applying suggested import change from mstruk 💄 2015-06-11 08:52:53 +02:00
Stian Thorgersen 996d8e8cd3 Merge pull request #1355 from stianst/master
KEYCLOAK-1439 Admin Console is not loaded when Keycloak is installed …
2015-06-11 07:38:29 +01:00
Stian Thorgersen 0e1a059d8d KEYCLOAK-1439 Admin Console is not loaded when Keycloak is installed as root context 2015-06-11 08:15:42 +02:00
Tomas Kyjovsky 30359b51fa Merge upstream-master into tkyjovsk-master 2015-06-10 23:27:36 +02:00
Marek Posolda d8880e5ff3 Merge pull request #1354 from mposolda/master
KEYCLOAK-1260 Fix saml backchannel logout with JPA UserSession provid…
2015-06-10 21:37:05 +02:00
Stian Thorgersen 6819b8e3a5 Merge pull request #1352 from girirajsharma/master
[KEYCLOAK-1431] Update page titles
2015-06-10 18:28:25 +01:00
mposolda 03b521926a KEYCLOAK-1260 Fix saml backchannel logout with JPA UserSession provider on MySQL + PostgreSQL 2015-06-10 18:59:07 +02:00
Scott Rossillo abfec23404 Fix Spring Security adapter logout handling
Stops KeycloakLogoutHandler from throwing an exception if the
authentication is not of type KeycloakAuthenticationToken.

Fixes KEYCLOAK-1438.
2015-06-10 12:22:18 -04:00
Bill Burke 3dd282e11b pluggable required actions backend 2015-06-10 11:38:01 -04:00
girirajsharma 92efe8441e [KEYCLOAK-1431] Update page titles 2015-06-10 20:23:39 +05:30
Bill Burke 95349e6e2e clientsession.action to String 2015-06-10 09:21:23 -04:00
fkiss 15f8e83d4a added initial tests and page for assigning user role mappings 2015-06-10 15:02:25 +02:00
Bill Burke 0095968a73 Merge pull request #1344 from velias/KEYCLOAK-1373
KEYCLOAK-1373 - Allow to map other informations provided by Social Identity providers into Keycloak user profile attributes
2015-06-10 09:02:03 -04:00
Stian Thorgersen ad01d64803 Merge pull request #1351 from girirajsharma/master
[KEYCLOAK-1425] Remove address options on registration screen
2015-06-10 13:32:18 +01:00
girirajsharma 30405804bc [KEYCLOAK-1425] Remove address options on registration screen 2015-06-10 17:42:22 +05:30
Stian Thorgersen 6093d00fdd Merge pull request #1350 from girirajsharma/master
[KEYCLOAK-1424] Login button on login screen should be on the left
2015-06-10 12:14:06 +01:00
girirajsharma 1ba5ea411b [KEYCLOAK-1424] Login button on login screen should be on the left 2015-06-10 16:14:19 +05:30
Tomas Kyjovsky 9dd7a947e9 Merge upstream-master into tkyjovsk-master 2015-06-10 10:40:32 +02:00
Stian Thorgersen c51e6a916c Merge pull request #1346 from mstruk/srv-overlay
Re-enabled server-overlay, and demo-dist for Wildfly 9
2015-06-10 07:30:49 +01:00
Marek Posolda e9b9da6d20 Merge pull request #1349 from mposolda/master
KEYCLOAK-1416 federation provider/mapper model fixes
2015-06-09 23:39:49 +02:00
mposolda 557edbec6d KEYCLOAK-1416 federation provider/mapper model fixes 2015-06-09 23:15:43 +02:00
Marek Posolda 93650c442a Merge pull request #1348 from mposolda/master
KEYCLOAK-1088 Audit of user grants during login
2015-06-09 20:55:26 +02:00
mposolda d3e9b29d78 KEYCLOAK-1088 Audit of user grants during login 2015-06-09 20:37:12 +02:00
Marek Posolda 7cf1b002d2 Merge pull request #1347 from mposolda/master
Fix AdminRecoveryTest + minor fixes in messages
2015-06-09 18:06:59 +02:00
mposolda e96ba202e9 Fix AdminRecoveryTest to not break admin tests 2015-06-09 17:44:07 +02:00
mposolda 9a5ca4d367 Minor improve in error messages and tests 2015-06-09 17:43:20 +02:00
Marko Strukelj e6928c6456 KEYCLOAK-1417 Re-enable demo-dist 2015-06-09 16:35:46 +02:00
Marko Strukelj 245edcb5b9 KEYCLOAK-1418 Re-enable server-overlay
- it's based on server-dist for Wildfly 9 in order to reuse server-feature-pack - no dependency on distribution/modules
2015-06-09 16:35:46 +02:00
Marek Posolda e4cb97aa49 Merge pull request #1345 from mposolda/master
KEYCLOAK-1401 Fix oracle and sybase - step 1
2015-06-09 15:51:51 +02:00
mposolda bc93066fe3 KEYCLOAK-1401 Fix oracle and sybase - step 1 2015-06-09 15:35:03 +02:00
Lukas Kubik bf686bd791 Revert "Add new .gitattributes file"
This reverts commit 01e232158b.
2015-06-09 15:19:36 +02:00
Lukas Kubik 01e232158b Add new .gitattributes file 2015-06-09 15:16:34 +02:00
Vlastimil Elias c6e0195a3c KEYCLOAK-1373 - added attribute importer for other social providers,
documented
2015-06-09 13:49:58 +02:00
Stian Thorgersen 966d939c12 Merge pull request #1340 from mstruk/srv-subsystem
Cleanup, and simplify keycloak-server-subsystem
2015-06-09 07:05:07 +01:00
Stian Thorgersen c61c05be51 Merge pull request #1335 from ssilvert/keycloak-config
KEYCLOAK-1404: Need recovery mechanism for master admin user
2015-06-09 07:02:47 +01:00
Stian Thorgersen 7b099e3d6b Merge pull request #1341 from mstruk/KEYCLOAK-1370
KEYCLOAK-1370 Empty module de.idyl.winzipaes in keycloak-server-dist
2015-06-09 06:45:39 +01:00
Marek Posolda 6d257a0d4c Merge pull request #1343 from mposolda/master
KEYCLOAK-1358 docs for LDAP mappers
2015-06-08 21:11:27 +02:00
mposolda b80a4a0aa9 KEYCLOAK-1358 docs for LDAP mappers 2015-06-08 21:10:38 +02:00
Marek Posolda aabb3428a8 Merge pull request #1342 from mposolda/master
JSON migration, LDAP logging reduced to trace/debug
2015-06-08 20:24:45 +02:00
mposolda 80ff7b92db KEYCLOAK-886 Reduce some LDAP info logging to trace and debug 2015-06-08 19:23:32 +02:00
mposolda 7badd3d5e5 JSON migration 2015-06-08 19:23:32 +02:00
Stan Silvert e977a363ef Require user to specify a temporary admin password to do admin recovery. 2015-06-08 13:04:52 -04:00
Bill Burke 91283878ac html encoding fix 2015-06-08 12:38:16 -04:00
Vlastimil Elias 74ace4006c Added base abstract implementation of Attribute mapper for Social
providers, used for GitHub provider
2015-06-08 17:04:53 +02:00
Marko Strukelj 7be957f4f9 KEYCLOAK-1370 Empty module de.idyl.winzipaes in keycloak-server-dist 2015-06-08 15:59:51 +02:00
Marko Strukelj 3643e76a06 Cleanup, and simplify keycloak-server-subsystem
- KEYCLOAK-1346 Remove support for overlays in server subsystem
 - KEYCLOAK-1347 Remove support for multiple auth-servers
 - KEYCLOAK-1348 Simplify server subsystem definition
2015-06-08 15:33:34 +02:00
Marek Posolda 07bc756785 Merge pull request #1339 from mposolda/master
DB migration
2015-06-08 13:17:15 +02:00
mposolda 0af68d28f1 KEYCLOAK-1357 LDAP migration 2015-06-08 12:29:24 +02:00
mposolda 75e1f50faf KEYCLOAK-1390 mongo migration for identity providers 2015-06-08 12:29:18 +02:00
Bill Burke dcc40b0a63 error handling 2015-06-05 15:51:43 -04:00
Bill Burke dc0d9a8dce merge 2015-06-05 14:44:37 -04:00
Bill Burke 021b01f0bd passing tests 2015-06-05 13:49:24 -04:00
Marek Posolda b578122f19 Merge pull request #1337 from mposolda/ldap
KEYCLOAK-1067 Upgrade embedmongo plugin version. Speed-up mongo tests…
2015-06-05 18:53:36 +02:00
mposolda e014dcce76 KEYCLOAK-1067 Upgrade embedmongo plugin version. Speed-up mongo testsuite 2015-06-05 18:51:27 +02:00
Marek Posolda a1bdf2837e Merge pull request #1336 from mposolda/ldap
Mongo fixes & few other fixes
2015-06-05 18:29:45 +02:00
mposolda 0092d9f74d Mongo fixes & few other fixes 2015-06-05 17:52:56 +02:00
Stan Silvert e48aafd588 Fix spelling error. 2015-06-05 07:46:39 -04:00
Marek Posolda d958d85290 Merge pull request #1334 from mposolda/ldap
KEYCLOAK-1359 more Active Directory fixes
2015-06-05 11:59:08 +02:00
mposolda 5b40031251 KEYCLOAK-1359 more Active Directory fixes 2015-06-05 11:32:29 +02:00
Stian Thorgersen 399a4a0511 Merge pull request #1333 from stianst/master
KEYCLOAK-1407 Label for otp on login is 'Log in'
2015-06-05 08:52:48 +01:00
Stian Thorgersen bebe51c885 KEYCLOAK-1407 Label for otp on login is 'Log in' 2015-06-05 09:37:34 +02:00
Stan Silvert 6812514683 KEYCLOAK-1404: Need recovery mechanism for master admin user 2015-06-04 15:29:44 -04:00
Marek Posolda 62de481329 Merge pull request #1332 from mposolda/ldap
KEYCLOAK-1359 LDAP & Active directory fixes and improvements
2015-06-04 20:47:10 +02:00
mposolda c3eb6df220 KEYCLOAK-1359 LDAP & Active directory fixes and improvements 2015-06-04 20:15:44 +02:00
Stian Thorgersen d875b9885d Merge pull request #1331 from stianst/master
KEYCLOAK-1325 Public/private SPI
2015-06-04 15:26:46 +01:00
Bill Burke 9ab023cc6c more 2015-06-04 10:18:24 -04:00
Stian Thorgersen 3510e17dd3 Merge pull request #1330 from vramik/master
KEYCLOAK-1398 - Change type of 'REPRESENTATION' from BLOB to TEXT in …
2015-06-04 15:02:12 +01:00
Stian Thorgersen f50570e350 KEYCLOAK-1325
Public/private SPI
2015-06-04 15:59:47 +02:00
vramik a52cdae1f7 KEYCLOAK-1398 - Change type of 'REPRESENTATION' from BLOB to TEXT in jpa-changelog-1.3.0.Beta1.xml 2015-06-04 15:43:55 +02:00
Marek Posolda 826c60ae36 Merge pull request #1324 from velias/KEYCLOAK-1371
KEYCLOAK-1371 - Perform "Update Profile on First Login" only if some of mandatory user profile fields is missing from identity provider
2015-06-04 15:19:59 +02:00
Stian Thorgersen d2c9d1569c Merge pull request #1329 from mstruk/modules-cleanup
Clean up distribution/modules and adapter-feature-packs
2015-06-04 13:56:35 +01:00
Stian Thorgersen 15dca61c59 Merge pull request #1318 from cfsnyder/master
KEYCLOAK-681 multi-part emails
2015-06-04 13:55:52 +01:00
Stian Thorgersen 4224496611 Doc update 2015-06-04 14:41:24 +02:00
Stian Thorgersen 7467fbaa12 Merge pull request #1328 from girirajsharma/master
Minor fix: resolved a scope variable in services.js and a typo.
2015-06-04 11:52:17 +01:00
Marko Strukelj 580e4fb0b2 Clean up distribution/modules and adapter-feature-packs 2015-06-04 11:39:08 +02:00
girirajsharma 90b15a070c Minor fix: resolved a scope variable in services.js and a typo. 2015-06-04 14:35:48 +05:30
Bill Burke b54061fc3f more 2015-06-03 18:51:57 -04:00
Marek Posolda ce7de3b576 Merge pull request #1327 from mposolda/ldap
KEYCLOAK-1359 More LDAP fixes
2015-06-03 20:00:58 +02:00
mposolda 31d0365e85 KEYCLOAK-1359 More LDAP fixes 2015-06-03 19:57:11 +02:00
Stian Thorgersen 084da8a03d Merge pull request #1325 from stianst/master
KEYCLOAK-1386 Direct Grant API always on
2015-06-03 18:29:29 +01:00
Marek Posolda e395ecb749 Merge pull request #1326 from mposolda/ldap
KEYCLOAK-1359 Fix testsuite with OpenLDAP, Other LDAP fixes
2015-06-03 18:45:43 +02:00
mposolda a34cb92fc1 KEYCLOAK-1359 Fix testsuite with OpenLDAP, Other LDAP fixes 2015-06-03 18:44:23 +02:00
Bill Burke c12fe28b2d phased auth spi introduction 2015-06-03 10:55:03 -04:00
Cory Snyder 0ac000adca KEYCLOAK-681 multi-part emails 2015-06-03 10:33:18 -04:00
Stian Thorgersen 1dd0b563b6 KEYCLOAK-1386 Direct Grant API always on 2015-06-03 16:24:15 +02:00
Marek Posolda 26acf6fa83 Merge pull request #1323 from mposolda/ldap
Ldap: testsuite upgrade + per-request federation caching
2015-06-03 15:36:29 +02:00
Vlastimil Elias dcc19a39d7 KEYCLOAK-1371 - Perform "Update Profile on First Login" only if some of
mandatory user profile fields is missing from identity provider
2015-06-03 15:24:25 +02:00
mposolda 496062ef0c KEYCLOAK-1359 LDAP tests update 2015-06-03 14:54:14 +02:00
mposolda b27f18c380 KEYCLOAK-838 per-request cache in UserFederationManager 2015-06-03 12:48:19 +02:00
Marek Posolda 442d65bb5f Merge pull request #1322 from mposolda/master
KEYCLOAK-1368 allow forwarding to error page in bearer-only deploymen…
2015-06-03 12:47:04 +02:00
mposolda 35e4a5e56d KEYCLOAK-1368 allow forwarding to error page in bearer-only deployments. Adding DeploymentBuilder 2015-06-03 12:34:56 +02:00
Stian Thorgersen fb60ca115f Merge pull request #1321 from girirajsharma/master
[KEYCLOAK-1356] Order the roles in a alphabetical way
2015-06-03 11:00:57 +01:00
Stian Thorgersen 3d3871b0e6 Merge pull request #1291 from AOEpeople/KEYCLOAK-1305
KEYCLOAK-1305 Add possibility to change username
2015-06-03 10:51:34 +01:00
Stian Thorgersen 060a59f982 Merge pull request #1317 from velias/KEYCLOAK-1372
KEYCLOAK-1372 - do not perform email verification if email is provided by trusted Identity provider
2015-06-03 10:29:52 +01:00
Vlastimil Elias c6c0abbedf KEYCLOAK-1372 - typo patched 2015-06-03 11:00:02 +02:00
Vlastimil Elias df182c3301 KEYCLOAK-1372 - tests updated to test whole user flow including email
verification step
2015-06-03 10:54:33 +02:00
Marek Posolda 56c858d917 Merge pull request #1297 from AOEpeople/KEYCLOAK-1350
KEYCLOAK-1350 client_session_state should be updated when refreshing a token
2015-06-03 09:15:50 +02:00
Stian Thorgersen 00068c460a Merge pull request #1320 from bradthurber/missing-space-in-example-customer-app-string
minor text edit for an error message on customer example
2015-06-03 07:30:54 +01:00
girirajsharma 5d0b6400df [KEYCLOAK-1356] Order the roles in a alphabetical way 2015-06-03 02:02:20 +05:30
Brad Thurber 413295e15e minor text edit 2015-06-02 15:34:50 -04:00
Vlastimil Elias 1c3d165e5d KEYCLOAK-1372 - tests improved 2015-06-02 15:41:50 +02:00
Petr Mensik 5fe0406ec9 Fixed addDisabledUser test 2015-06-02 15:37:19 +02:00
Sebastian Rose 17affe576f KEYCLOAK-1350 client_session_state should be updated when refreshing a token 2015-06-02 15:01:47 +02:00
Vlastimil Elias b86d091ad1 KEYCLOAK-1372 - do not perform email verification if email is provided
by trusted Identity provider
2015-06-02 14:59:29 +02:00
Stian Thorgersen ea544c639e Merge pull request #1316 from girirajsharma/master
[KEYCLOAK-1367] - Enter doesn't work in user search box
2015-06-02 13:35:03 +01:00
Niko Köbler 492e6cd856 KEYCLOAK-1305 Add possibility to change username 2015-06-02 14:08:19 +02:00
Bill Burke 5680be743c Merge pull request #1298 from AOEpeople/KEYCLOAK-1103
KEYCLOAK-1103 proxy configuration for inject header if user is logged in, but do not enforce login
2015-06-02 08:04:05 -04:00
Bill Burke 1fd22ee672 Merge pull request #1288 from dbarentine/master
[KEYCLOAK-1354] Make destination optional
2015-06-02 07:59:50 -04:00
girirajsharma c3faea0428 [KEYCLOAK-1367] - Enter doesn't work in user search box 2015-06-02 17:25:05 +05:30
Stian Thorgersen 483ac69c47 Merge pull request #1315 from velias/KEYCLOAK-1362
KEYCLOAK-1362 - button CSS classes are defined by theme properties now to be customizable
2015-06-02 10:55:18 +01:00
Stian Thorgersen 805ad9f250 Merge pull request #1314 from mstruk/modules-cleanup
KEYCLOAK-1332 Remove adapter modules from server
2015-06-02 10:54:46 +01:00
Vlastimil Elias 43799fd315 KEYCLOAK-1362 - button CSS classes are defined by theme properties 2015-06-02 10:47:13 +02:00
Marko Strukelj 91ec4cb5c7 KEYCLOAK-1332 Remove adapter modules from server 2015-06-02 10:22:26 +02:00
Stian Thorgersen 634ce85f17 Merge pull request #1313 from stianst/master
Changes to Travis around caching
2015-06-02 09:15:44 +01:00
Stian Thorgersen b6c52e8859 Changes to Travis around caching 2015-06-02 09:59:06 +02:00
Stian Thorgersen d042f6e2c9 Merge pull request #1311 from tkyjovsk/master
Removed keycloak-wildfly-adapter-subsystem dependency from keycloak-jboss-modules.
2015-06-02 08:42:36 +01:00
Stian Thorgersen cfe8c2956f Merge pull request #1309 from velias/KEYCLOAK-1369
KEYCLOAK-1369 GUI order attribute of Social providers is reflected in Account management app
2015-06-02 08:41:58 +01:00
Stian Thorgersen 235a8eab02 Merge pull request #1308 from mstruk/srv-subsystem
KEYCLOAK-1304 WF9 server subsystem
2015-06-02 08:41:37 +01:00
Stian Thorgersen 6baefa7490 Merge pull request #1307 from velias/KEYCLOAK-1361
KEYCLOAK-1361 - ability to add custom fields (user.attributes) on login-update-profile.ftl page
2015-06-02 08:40:54 +01:00
Stian Thorgersen 319b29aa7d Merge pull request #1300 from girirajsharma/master
[KEYCLOAK-1200] From and To filter fields in Event viewer in admin app
2015-06-02 08:32:02 +01:00
Stian Thorgersen 02428e4f0f Merge pull request #1306 from vramik/master
KEYCLOAK-1366 - Change type of 'REPRESENTATION' from VARCHAR to BLOB …
2015-06-02 08:30:19 +01:00
Tomas Kyjovsky 3fe93eebb6 Removed keycloak-wildfly-adapter-subsystem dependency from keycloak-jboss-modules. 2015-06-01 19:18:56 +02:00
Marek Posolda f5c7c14ce0 Merge pull request #1310 from mposolda/ldap2
KEYCLOAK-886 LDAP: Fixes and improvements
2015-06-01 18:12:04 +02:00
mposolda f57ab78916 KEYCLOAK-886 LDAP: Fixes and improvements 2015-06-01 17:38:34 +02:00
Vlastimil Elias 087dab6f3d KEYCLOAK-1369 GUI order attribute of Social providers is reflected in
Account management app
2015-06-01 17:01:57 +02:00
Marko Strukelj 3df504b6b4 KEYCLOAK-1304 WF9 server subsystem
- remove unnecessary dependencies
2015-06-01 16:07:59 +02:00
Vlastimil Elias ea98b26766 KEYCLOAK-1361 - ability to add custom fields (user.attributes) on
login-update-profile.ftl page
2015-06-01 15:44:36 +02:00
Stian Thorgersen 6d6e598ce5 Merge pull request #1304 from lkubik/remove-multiple-mysql-versions
Remove duplicit mysql version declaration
2015-06-01 13:35:39 +01:00
Lukas Kubik 67ad7463f4 Remove duplicit mysql version declaration 2015-06-01 14:08:32 +02:00
vramik df6b481112 KEYCLOAK-1366 - Change type of 'REPRESENTATION' from VARCHAR to BLOB in jpa-changelog-1.3.0.Beta1.xml 2015-06-01 11:43:06 +02:00
Marek Posolda 5301b556f0 Merge pull request #1303 from mposolda/ldap2
Update links in kerberos docs and example README
2015-05-29 21:45:32 +02:00
mposolda 2ff7345b08 Update links in kerberos docs and example README 2015-05-29 21:44:28 +02:00
Stian Thorgersen bafcd3f036 Merge pull request #1302 from stianst/master
KEYCLOAK-1327 Updated for users
2015-05-29 15:59:07 +01:00
Stian Thorgersen 5f787028df KEYCLOAK-1327 Updated for users 2015-05-29 15:29:04 +01:00
Stian Thorgersen e1e65af8ea Merge pull request #1301 from mstruk/wf9
KEYCLOAK-1303 WF9 adapter subsystem
2015-05-29 15:23:28 +01:00
girirajsharma 5bb71d396a [KEYCLOAK-1200] From and To filter fields in Event viewer in admin app 2015-05-29 19:13:56 +05:30
Stian Thorgersen 058f44743c Merge pull request #1299 from stianst/master
Misc
2015-05-29 14:22:47 +01:00
Marko Strukelj 2077a2a3af KEYCLOAK-1303 WF9 adapter subsystem
- not dependent on distribution/modules any more
 - keycloak-adapter-subsystem moved to keycloak-wf9-subsystem
 - still installed as <extension module="org.keycloak.keycloak-adapter-subsystem"/>
2015-05-29 15:17:41 +02:00
Stian Thorgersen 5a3a0ae437 KEYCLOAK-1325 Log warning if internal SPI is used 2015-05-29 13:58:40 +01:00
Sebastian Rose b4204fc806 getOrDefault not in java 7 2015-05-29 14:33:52 +02:00
Sebastian Rose e8c90c1327 fixed typo in doc 2015-05-29 14:33:52 +02:00
Sebastian Rose 136f2c8bb7 documentation fixed 2015-05-29 14:33:52 +02:00
Sebastian Rose 2b5e0e238d constants 2015-05-29 14:33:52 +02:00
Sebastian Rose a6eface8bc configurable header names, documentation 2015-05-29 14:33:52 +02:00
Sebastian Rose 7659b1aa18 KEYCLOAK-1103 - First draft 2015-05-29 14:33:52 +02:00
Stian Thorgersen a953b52eac KEYCLOAK-1327 Updated for client stats 2015-05-29 13:15:35 +01:00
Stian Thorgersen 6f164f0ee9 KEYCLOAK-1327 Updated for clients 2015-05-29 13:15:34 +01:00
Stian Thorgersen f61a9d7a70 Merge pull request #1296 from mstruk/as7
KEYCLOAK-1301 AS7 / EAP6 adapter subsystem
2015-05-29 10:13:59 +01:00
Marko Strukelj 4f270bffd0 Refactor EAP 6 adapter distribution
- not dependent on distribution/modules any more
 - uses the same modules as AS7
 - again use <extension module="org.keycloak.keycloak-adapter-subsystem"/>
2015-05-28 23:56:11 +02:00
Marko Strukelj fa90602aa0 Refactor AS 7 adapter distribution
- not dependent on distribution/modules any more
 - again use <extension module="org.keycloak.keycloak-adapter-subsystem"/>
2015-05-28 23:56:11 +02:00
Marek Posolda eaf5034ec7 Merge pull request #1295 from mposolda/ldap2
Ldap fixes + added authType to UI to allow users specify LDAP auth type
2015-05-28 20:44:31 +02:00
mposolda e83de896c9 LDAP fixes + added authType to UI to allow users specify authType 2015-05-28 19:54:15 +02:00
mposolda bcd607a1ee KEYCLOAK-1360 ModelExceptionMapper logges the exception 2015-05-28 19:54:15 +02:00
Bill Burke e1adceeb85 Merge pull request #1294 from patriot1burke/master
javadoc
2015-05-28 09:31:38 -04:00
Bill Burke 106bdc3c52 javadoc 2015-05-28 09:30:57 -04:00
Stian Thorgersen 5229807020 Merge pull request #1293 from girirajsharma/master
[KEYCLOAK-1276] Update Zocial.css
2015-05-28 15:26:13 +02:00
girirajsharma 1e89467b31 [KEYCLOAK-1276] Update Zocial.css 2015-05-28 18:39:51 +05:30
Stian Thorgersen f65b502a96 Merge pull request #1283 from girirajsharma/master
[KEYCLOAK-1200]- From and To filter fields in Event viewer in admin app
2015-05-28 15:03:22 +02:00
girirajsharma efe8342965 [KEYCLOAK-1200]- From and To filter fields in Event viewer in admin app 2015-05-28 18:05:31 +05:30
Marek Posolda 90cb3acd2f Merge pull request #1292 from mposolda/ldap2
Ldap enhancements + Make admin requests to have RealmModel and ClientModel available
2015-05-28 12:40:11 +02:00
mposolda 98dd87efbc Added ProviderConfigProperty.CLIENT_LIST and refactor role federation mapper to use it 2015-05-28 12:28:26 +02:00
mposolda ce6576ce84 KEYCLOAK-1355 Make admin requests to have RealmModel and ClientModel available in KeycloakContext 2015-05-28 12:28:26 +02:00
mposolda 5d4869f915 KEYCLOAK-1300 Fullname test 2015-05-28 12:28:26 +02:00
Stian Thorgersen 92567a070c Merge pull request #1290 from mstruk/wf8
Reintroduce org.keycloak.keycloak-adapter-subsystem module to WF8
2015-05-28 10:08:30 +02:00
Marko Strukelj 30955c4e2c Reintroduce org.keycloak.keycloak-adapter-subsystem module to WF8
- again use <extension module="org.keycloak.keycloak-adapter-subsystem"/>
2015-05-28 09:49:34 +02:00
Stian Thorgersen f9d51db182 Merge pull request #1282 from behana/master
Add send-verify-email endpoint to admin-rest-api
2015-05-28 09:09:06 +02:00
Dane Barentine 852e799fea [KEYCLOAK-1354] Make destination optional and fix details for SAML login events 2015-05-27 15:36:10 -07:00
behana bbc3e99bb5 KEYCLOAK-1353 Option to pass client_id to resetPasswordMail 2015-05-28 00:10:36 +02:00
behana e8e22ee44c KEYCLOAK-1352 Add send-verify-email to admin-rest-api 2015-05-28 00:05:39 +02:00
Bill Burke 7a211beede Merge pull request #1287 from patriot1burke/master
unfinished working auth spi
2015-05-27 14:38:05 -04:00
Bill Burke 24279b138b Merge remote-tracking branch 'upstream/master' 2015-05-27 14:28:57 -04:00
Bill Burke a2718a889d unfinished working auth spi 2015-05-27 14:28:47 -04:00
Bill Burke 756de9f7f6 Merge pull request #1286 from patriot1burke/master
auth spi initialization
2015-05-27 11:27:10 -04:00
Bill Burke 143d176dcd Merge remote-tracking branch 'upstream/master' 2015-05-27 10:39:59 -04:00
Bill Burke a1f7cfaf3a auth spi initialization 2015-05-27 10:39:46 -04:00
Stian Thorgersen a549b3ac70 Merge pull request #1284 from pmensik/integration-arquillian
Prepare UI testing for PR
2015-05-27 10:25:01 +02:00
Marek Posolda 43c9179204 Merge pull request #1285 from mposolda/ldap
KEYCLOAK-886 User Federation Mappers - admin console
2015-05-26 18:28:43 +02:00
mposolda dfe232cf80 KEYCLOAK-886 User Federation Mappers - admin console 2015-05-26 17:48:52 +02:00
Petr Mensik ba88cdce70 Prepare UI testing for PR 2015-05-26 15:57:01 +02:00
Stian Thorgersen 269f9fe860 Merge pull request #1274 from mstruk/wf8
Wildfly 8.2.0.Final compatibility
2015-05-26 09:24:56 +02:00
Stian Thorgersen 4a670ab236 Merge pull request #1276 from mstruk/fix-demos
Fix HttpClient issues in example demos on AS7 / Wildfly
2015-05-26 09:02:06 +02:00
Stian Thorgersen ab934f3d35 Merge pull request #1273 from girirajsharma/master
[KEYCLOAK-1334] Forgot password page does not return validation error
2015-05-26 08:56:04 +02:00
Stian Thorgersen 24eca809f1 Merge pull request #1275 from mstruk/pom-repo-fix
Fix build issue with missing org.jboss.as version 7.5.0.Final-redhat-15
2015-05-26 08:55:34 +02:00
Bill Burke 57780b6520 Merge pull request #1279 from patriot1burke/master
auth spi data model
2015-05-22 17:03:45 -04:00
Bill Burke f5e301fe0d merge 2015-05-22 16:15:55 -04:00
Bill Burke 68976f5b0c auth spi datamodel 2015-05-22 16:03:26 -04:00
girirajsharma 8e7e208265 [KEYCLOAK-1334] Forgot password page does not return validation error
message when input field is empty
2015-05-23 01:18:23 +05:30
mposolda 0951c11aa6 Revert mistakenly edited file 2015-05-22 21:31:03 +02:00
Marek Posolda 3d4ca01242 Merge pull request #1277 from mposolda/ldap
Ldap enhancements - step 1
2015-05-22 21:28:07 +02:00
mposolda 319f86e91a KEYCLOAK-886 Admin console - step 1 2015-05-22 21:09:55 +02:00
mposolda 490b3e3603 KEYCLOAK-886 Added builtin federation mappers when creating new LDAP Federation model. Testsuite passing 2015-05-22 21:09:55 +02:00
mposolda 7476ee51e4 KEYCLOAK-886 Model and representations for UserFederationMappers 2015-05-22 21:09:50 +02:00
mposolda a9f1fda68a KEYCLOAK-630 Added Role Federation mapper. Fixes and refactoring 2015-05-22 21:08:33 +02:00
mposolda 975337f225 KEYCLOAK-1300 Added FullNameLDAPFederationMapper 2015-05-22 21:08:21 +02:00
mposolda 1490f106f2 KEYCLOAK-599 Added UserFederationMappers. Added UserAttributeLDAPFederationMapper 2015-05-22 21:07:32 +02:00
mposolda 61c35265a6 KEYCLOAK-826 Show count of created/updated users during federation sync 2015-05-22 21:06:38 +02:00
mposolda 0f4497e53e Remove user from KC after removing from federation 2015-05-22 21:06:38 +02:00
Marko Strukelj ca60c283fc Fix HttpClient issues in example demos on AS7 / Wildfly
- don't use org.keycloak.adapter.HttpClientBuilder - it's Keycloak internals
 - don't use HttpClient 4.2 or 4.3 APIs (not default in AS7, and Wildfly 8)
    so way we can use the same jboss-deployment-structure.xml
2015-05-22 19:16:34 +02:00
Marko Strukelj 37298bdbd6 Fix build issue with missing org.jboss.as version 7.5.0.Final-redhat-15
- activeByDefault profile is turned off when -P is used
2015-05-22 18:10:40 +02:00
Marko Strukelj d70fd86a82 Wildfly 8.2.0.Final compatibility
- added integration/keycloak/wf8-subsystem
 - added distribution/adapters/wf8-adapter containing wf8-modules, and wf8-adapter-zip
 - there's no dependency on distribution/modules
 - subsystem is installed by unzipping keycloak-wf8-adapter-dist.zip to Wildfly 8.2.0.Final, and adding <extension-module>org.keycloak.keycloak-wf8-subsystem</extension-module> to standalone.xml
2015-05-22 17:38:02 +02:00
Stian Thorgersen ad606cbce6 Merge pull request #1272 from stianst/master
Version bump
2015-05-22 09:05:30 +02:00
Stian Thorgersen 4e0a32a37a Version bump 2015-05-22 09:04:30 +02:00
Stian Thorgersen 7290040008 Merge pull request #1270 from mstruk/as7
AS 7.1.1.Final / EAP 6 compatibility
2015-05-22 09:04:06 +02:00
Marko Strukelj 91bf50eb6c AS 7.1.1.Final / EAP 6.4 compatibility 2015-05-21 11:30:26 +02:00
Stian Thorgersen 748ed30b47 Merge pull request #1268 from stianst/master
Fixes
2015-05-20 07:56:54 +02:00
Stian Thorgersen b88d2f792d NotSerializableException: org.keycloak.models.sessions.infinispan.entities.LoginFailureKey 2015-05-20 07:11:17 +02:00
Stian Thorgersen c8b19fa1ae Updated release instructions 2015-05-20 07:10:53 +02:00
Stian Thorgersen c0795ce2c7 Merge pull request #1267 from stianst/master
Updated admin events
2015-05-19 11:40:42 +02:00
Stian Thorgersen 12c3aa83dd Updated admin events 2015-05-19 11:40:17 +02:00
Stian Thorgersen c193ba0c81 Merge pull request #1261 from panga/master
KEYCLOAK-1306 - Better Admin API error handling (new)
2015-05-19 08:56:19 +02:00
Stian Thorgersen d8226c457f Merge pull request #1266 from stianst/master
Dist fixes
2015-05-19 08:30:44 +02:00
Stian Thorgersen a507771f7c Fix demo dist 2015-05-19 08:15:39 +02:00
Stian Thorgersen 2f8d577e95 Fix server-overlay 2015-05-19 08:15:39 +02:00
Stian Thorgersen 362babe5e2 Fixed server-dist 2015-05-19 08:15:39 +02:00
Bill Burke d9297cee16 Merge pull request #1265 from patriot1burke/master
phase 1 auth/required actions SPI
2015-05-18 16:57:15 -04:00
Bill Burke 3851a2fe72 merge 2015-05-18 16:41:51 -04:00
Bill Burke 338300df32 1st phase auth/required action spi 2015-05-18 16:32:38 -04:00
Stian Thorgersen 0c7f63b2f3 Merge branch 'master' of https://github.com/girirajsharma/keycloak into girirajsharma-master
Conflicts:
	services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
2015-05-18 06:56:12 +02:00
Stian Thorgersen 9a87b7f196 Merge pull request #1263 from panga/fix_response
Fix wrong response code
2015-05-15 09:13:50 +02:00
Leonardo Loch Zanivan abcd830af1 Fix response code 2015-05-13 15:39:51 -03:00
Leonardo Loch Zanivan f807c999b6 Initial messages SPI implementation for Admin REST Services. 2015-05-13 11:13:49 -03:00
girirajsharma ccd5deb85f [KEYCLOAK 392] Audit admin events 2015-05-13 16:47:30 +05:30
Stian Thorgersen dbf9525860 KEYCLOAK-1312 Add clearToken to keycloak.js 2015-05-13 13:13:36 +02:00
Stian Thorgersen 502efe42c9 KEYCLOAK-1288 2015-05-13 11:50:36 +02:00
Stian Thorgersen f0787d98d1 Merge pull request #1259 from stianst/master
Fix welcome-theme in test server
2015-05-13 11:31:19 +02:00
Stian Thorgersen d2ed8302a7 Fix welcome-theme in test server 2015-05-13 11:25:43 +02:00
Stian Thorgersen 15310d1e97 Merge pull request #1257 from stianst/master
KEYCLOAK-1310 Don't log migration model for fresh instal
2015-05-13 11:17:55 +02:00
Stian Thorgersen 38af401a16 KEYCLOAK-1310 Don't log migration model for fresh instal 2015-05-13 11:16:50 +02:00
Stian Thorgersen ab1d39dfb3 Merge pull request #1256 from stianst/master
KEYCLOAK-1277 Fix upgrading from 1.0.x to 1.2.0.Beta1
2015-05-13 10:55:26 +02:00
Stian Thorgersen d35786fe49 KEYCLOAK-1277 Fix upgrading from 1.0.x to 1.2.0.Beta1
Conflicts:
	connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
2015-05-13 10:54:48 +02:00
Stian Thorgersen b9db84edf3 Merge pull request #1254 from stianst/master
KEYCLOAK-1307 Fix docs for databaseSchema
2015-05-13 08:11:59 +02:00
Stian Thorgersen c3176cac6a KEYCLOAK-1307 Fix docs for databaseSchema 2015-05-13 08:09:29 +02:00
Stian Thorgersen 6c98d64874 Merge pull request #1238 from raehalme/KEYCLOAK-1294
Added support for GrantedAuthoritiesMapper in KeycloakAuthenticationProvider
2015-05-13 06:57:29 +02:00
Stian Thorgersen 269dc26029 Merge pull request #1250 from ssilvert/patch-1
Fix parent pom.
2015-05-12 14:55:20 +02:00
Stian Thorgersen 48d26fc0e9 Merge pull request #1249 from ssilvert/patch-2
Fix parent pom.
2015-05-12 14:55:11 +02:00
Stan Silvert 797bf49ea6 Update pom.xml 2015-05-12 08:39:57 -04:00
Stan Silvert fd96bacf70 Update pom.xml 2015-05-12 08:38:08 -04:00
Stian Thorgersen 7f9739cd4f Merge pull request #1248 from stianst/master
Added isPrivate to Spi
2015-05-12 08:25:56 +02:00
Stian Thorgersen 698660c92a Added isPrivate to Spi 2015-05-12 08:25:24 +02:00
Stian Thorgersen 5aa90243a0 Merge pull request #1247 from Smartling/KEYCLOAK-1299
Add Servlet 2.x support to the Spring Security adapter
2015-05-12 07:26:50 +02:00
Scott Rossillo 4d32ac8765 Add Servlet 2.x support to the Spring Security adapter
Cookie.setHttpOnly() was added in Servlet 3.0. Make setting a
cookie as HttpOnly dependent on servlet version.
2015-05-11 19:48:16 -04:00
Stian Thorgersen 224f85b981 Merge pull request #1246 from stianst/master
Moved adapter feature pack
2015-05-11 16:27:38 +02:00
Stian Thorgersen 29c72c0744 Moved adapter feature pack 2015-05-11 16:27:16 +02:00
Stian Thorgersen 36c162126b Merge pull request #1245 from stianst/master
Removed unused xslt from server-dist
2015-05-11 16:14:01 +02:00
Stian Thorgersen 252dfe006b Removed unused xslt from server-dist 2015-05-11 16:13:44 +02:00
Stian Thorgersen d1743f0608 Merge pull request #1244 from stianst/master
Build server dist with feature pack
2015-05-11 16:09:42 +02:00
Stian Thorgersen 9168cf811c Build server dist with feature pack 2015-05-11 16:09:16 +02:00
Stian Thorgersen 9e50ae6a01 Merge pull request #1243 from girirajsharma/patch-1
Minor issue: Fixed non-resolvable pom error while updating db schema.
2015-05-11 15:10:38 +02:00
Giriraj Sharma 371c798116 Minor issue: Fixed non-resolvabe pom error while updating schema via hibernate. 2015-05-11 18:38:02 +05:30
Stian Thorgersen d00e0021b6 Merge branch 'master' of https://github.com/girirajsharma/keycloak into girirajsharma-master
Conflicts:
	connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
	services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
2015-05-11 13:36:30 +02:00
Stian Thorgersen bce3e61b7d Merge pull request #1242 from stianst/master
KEYCLOAK-1289 Support environment variables in keycloak.json
2015-05-11 11:03:50 +02:00
Stian Thorgersen ccbd4bace1 KEYCLOAK-1289 Support environment variables in keycloak.json 2015-05-11 10:17:25 +02:00
Thomas Raehalme 78999537f0 Added support for GrantedAuthoritiesMapper in KeycloakAuthenticationProvider. 2015-05-09 13:42:09 +03:00
Bill Burke e3b0cc7093 Merge pull request #1236 from Smartling/KEYCLOAK-1291
Improve Spring Security adapter default configuration
2015-05-08 20:53:36 -04:00
Bill Burke 50014f4398 Merge pull request #1235 from Smartling/KEYCLOAK-1290
Use backchannel logout for Spring Security SSO
2015-05-08 20:53:26 -04:00
Bill Burke 833c276424 Merge pull request #1234 from mstruk/wfly-subs-split-master
Wildfly 9 - subsystem split + feature packs
2015-05-08 20:53:17 -04:00
Bill Burke 770525a385 Merge pull request #1237 from patriot1burke/master
username mapper
2015-05-08 20:53:05 -04:00
Bill Burke e4204a56f5 username mapper 2015-05-08 20:41:31 -04:00
Scott Rossillo d37a9eada3 Improve Spring Security adapter default configuration 2015-05-08 18:24:49 -04:00
Scott Rossillo a7bfae2f56 Use backchannel logout for Spring Security SSO
Use backchannel logout for Keycloak's Spring Security adapter
single sign-out to allow Spring Security's logout complete handler
to fire.
2015-05-08 18:20:17 -04:00
Stian Thorgersen aaebcd074e Merge pull request #1233 from Smartling/KEYCLOAK-1274
Add Spring Security adapter documentation
2015-05-08 07:10:03 +02:00
Stian Thorgersen 58fc4520c9 Merge pull request #1232 from Smartling/KEYCLOAK-1287
Enable Spring Security adapter to register nodes
2015-05-08 07:09:42 +02:00
Stian Thorgersen 53716697ca Merge pull request #1222 from Smartling/KEYCLOAK-1273
Improve Spring Security adapter client to client authorization
2015-05-08 07:01:14 +02:00
Stian Thorgersen 96668daefa Merge pull request #1231 from Smartling/KEYCLOAK-1286
Permit Spring Security adapter to process admin tasks with CSRF enabled
2015-05-08 07:00:15 +02:00
Scott Rossillo 4b033d3d93 Add Spring Security adapter documentation 2015-05-08 00:23:25 -04:00
Scott Rossillo 8ca9a6a64a Enable Spring Security adapter to register nodes
Enable dynamic application node registration and unregistration
from Spring Security protected applications.
2015-05-07 20:09:16 -04:00
Scott Rossillo 2ce3925ba9 Permit Spring Security adapter to process admin tasks with CSRF enabled
Spring Security's CSRF protection blocks Keycloak administrative
actions when configured with the default request matcher.

This provides a CSRF request matcher that permits Keycloak
administrative actions without the CSRF token.
2015-05-07 19:58:27 -04:00
Marek Posolda 3d9adbe991 Merge pull request #1230 from mposolda/master
KEYCLOAK-1284 Allow users to easily inject or configure MongoClient
2015-05-07 20:56:48 +02:00
mposolda f16828e129 KEYCLOAK-1284 Allow users to easily inject or configure MongoClient for MongoConnectionProvider 2015-05-07 20:40:17 +02:00
mposolda 91eb80beb0 Minor improvements 2015-05-07 20:07:30 +02:00
Marko Strukelj 30682968ff Fix invalid module references that should point to keycloak-wildfly-adapter 2015-05-07 17:21:23 +02:00
Bill Burke 620e3b67e9 Merge pull request #1229 from patriot1burke/master
fix nameid policy
2015-05-07 10:53:34 -04:00
Bill Burke afe1ddd719 Merge remote-tracking branch 'upstream/master' 2015-05-07 10:52:55 -04:00
Bill Burke 0aefacc88e fix name id policy 2015-05-07 10:52:12 -04:00
Stian Thorgersen 366c707afa Merge pull request #1227 from stianst/master
KEYCLOAK-1282
2015-05-07 09:39:54 +02:00
Stian Thorgersen 707e29adef KEYCLOAK-1282 2015-05-07 09:39:28 +02:00
Stian Thorgersen 66f2735224 Merge pull request #1225 from stianst/master
KEYCLOAK-1279
2015-05-07 06:46:25 +02:00
Stian Thorgersen 86e19c274f KEYCLOAK-1279 2015-05-07 06:46:12 +02:00
Stian Thorgersen 46bb59e348 Merge pull request #1224 from stianst/master
Fix doc for installing onto EAP
2015-05-07 06:29:53 +02:00
Stian Thorgersen 37cbba57e6 Fix doc for installing onto EAP 2015-05-07 06:27:32 +02:00
Scott Rossillo b05da425b9 Improve Spring Security adapter client to client authorization 2015-05-06 16:54:55 -04:00
Marko Strukelj c28f5cef7a Fix WildFly org.apache.httpcomponents issue 2015-05-06 18:57:40 +02:00
Marko Strukelj f65e804836 WildFly testsuite work 2015-05-06 16:51:49 +02:00
Marko Strukelj 01e9b3bcff Upgrade version on overlooked poms to 1.3.0.Beta1-SNAPSHOT 2015-05-05 21:30:42 +02:00
Marko Strukelj 4e58bed39e Move keycloak modules under integration/keycloak 2015-05-05 20:48:13 +02:00
Marko Strukelj 7d2942e1be KEYCLOAK-1183 Split subsystem into separate server and adapter subsystems
- Rename keycloak-server-subsystem dir to wildfly-server-subsystem
 - Rename keycloak-agent-subsystem dir to keycloak-wildfly-agent-subsystem
 - Rename artifacts accordingly (keycloak- prefix)
 Only maven artifacts are renamed, jboss-modules modules are still called keycloak-server-subsystem, and keycloak-agent-subsystem
2015-05-05 20:43:23 +02:00
Marko Strukelj 943404689e KEYCLOAK-1183 Split subsystem into separate server and adapter subsystems
- Use WildFly 9.0.0.CR1
2015-05-05 20:40:05 +02:00
Marko Strukelj 89875aea02 KEYCLOAK-1183 Split subsystem into separate server and adapter subsystems
- Fix deprecated uses, and more code cleanup
2015-05-05 20:40:05 +02:00
Marko Strukelj 70ce96caa0 KEYCLOAK-1183 Split subsystem into separate server and adapter subsystems
- Fix overlooked file renames causing subsystem tests to fail
2015-05-05 20:40:05 +02:00
Marko Strukelj 625fb02dfe Fix typo preventing adapter-only-dist feature pack to build 2015-05-05 20:40:04 +02:00
Stan Silvert 18966334ab Update README.md 2015-05-05 20:40:04 +02:00
Stan Silvert a8f3756bbc Rename feature pack artifacts. Fix empty content directory. 2015-05-05 20:40:04 +02:00
Stan Silvert f0dab97b80 Add README file. 2015-05-05 20:40:04 +02:00
Stan Silvert 3a7bba041f Create builds based on WildFly 9 feature packs. 2015-05-05 20:40:04 +02:00
Marko Strukelj a50c0e61e4 Fix missing wildfly-undertow version 2015-05-05 20:40:04 +02:00
Marko Strukelj 87cb28eaab KEYCLOAK-1183 Split subsystem into separate server and adapter subsystems
- Fix server-subsystem tests
2015-05-05 20:40:04 +02:00
Marko Strukelj 03fa58a271 KEYCLOAK-1183 Split subsystem into separate server and adapter subsystems
- POM dependencies cleanup
2015-05-05 20:40:03 +02:00
Marko Strukelj 666e610334 Upgrade WildFly to 9.0.0.RC1-SNAPSHOT 2015-05-05 20:40:03 +02:00
Marko Strukelj 3c76a85674 KEYCLOAK-1183 Split subsystem into separate server and adapter subsystems
- More code and pom cleanup
2015-05-05 20:40:03 +02:00
Marko Strukelj c571ad9902 KEYCLOAK-1183 Split subsystem into separate server and adapter subsystems
- Renamed keycloak-subsystem into keycloak-server-subsystem
 - Added keycloak-adapter-subsystem without auth-server support
 - Removed adapter config from keycloak-server subsystem
 - Added proper .xsd for server subsystem
2015-05-05 20:40:03 +02:00
Tomaz Cerar 355c440c3d Update subsystem code to reflect latest standards 2015-05-05 20:38:52 +02:00
Tomaz Cerar 9a17658f84 remove eap6 dup 2015-05-05 20:38:52 +02:00
Bill Burke 69347aa7f6 Merge pull request #1220 from panga/master
KEYCLOAK-1269 and KEYCLOAK-1270
2015-05-05 14:31:48 -04:00
Bill Burke bf259f4c98 Merge pull request #1221 from patriot1burke/master
no backchannel if state
2015-05-05 14:18:52 -04:00
Bill Burke 082ebe8043 no backchannel if state 2015-05-05 13:58:07 -04:00
Leonardo Zanivan a13a02c995 KEYCLOAK-1270 fix 2015-05-05 11:17:27 -03:00
Leonardo Zanivan 3a9ac80786 Merge branch 'master' of github.com:panga/keycloak 2015-05-05 10:30:32 -03:00
Leonardo Zanivan d3cf177ebc Fix user.emailVerified on realm import 2015-05-05 10:29:44 -03:00
Leonardo Zanivan 7b49dbb74e Merge pull request #1 from keycloak/master
Merge
2015-05-05 10:27:59 -03:00
Stian Thorgersen 4cc249bcd4 Merge pull request #1219 from stianst/master
Fixes
2015-05-05 11:56:19 +02:00
Stian Thorgersen 035529d7a2 Version bump 2015-05-05 11:45:21 +02:00
Stian Thorgersen fa91c0ed03 Added missing dependencies to testsuite/docker-cluster£ 2015-05-05 10:40:47 +02:00
Stian Thorgersen adbde56c1a Removed tar.gz dist of docs 2015-05-05 10:28:04 +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
girirajsharma 70f53c6c06 [KEYCLOAK-392] - Admin audit events 2015-05-03 01:46:52 +05:30
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
2362 changed files with 174015 additions and 43374 deletions

No files matched your search

+4 -2
View File
@@ -3,6 +3,8 @@ language: java
cache:
directories:
- $HOME/.m2
sudo: false
after_script: 'misc/.travis/after.sh'
before_cache:
- rm -rf $HOME/.m2/repository/org/keycloak
sudo: false
+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)
+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.4.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -18,17 +18,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-events-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
@@ -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,29 @@ 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 preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, 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) {
}
}
@@ -18,8 +18,10 @@
package org.keycloak.broker.provider;
import org.keycloak.Config;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import java.io.InputStream;
import java.util.HashMap;
@@ -0,0 +1,38 @@
package org.keycloak.broker.provider;
import org.keycloak.broker.provider.IdentityProviderMapper;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
/**
* @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) {
}
@Override
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
}
}
@@ -17,25 +17,34 @@
*/
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 modelUsername;
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.");
}
@@ -51,6 +60,11 @@ public class FederatedIdentity {
this.id = id;
}
/**
* Username in remote idp
*
* @return
*/
public String getUsername() {
return username;
}
@@ -59,24 +73,17 @@ public class FederatedIdentity {
this.username = username;
}
public String getFirstName() {
return firstName;
/**
* username to store in UserModel
*
* @return
*/
public String getModelUsername() {
return modelUsername;
}
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 void setModelUsername(String modelUsername) {
this.modelUsername = modelUsername;
}
public String getEmail() {
@@ -87,23 +94,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 +110,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,35 +18,45 @@
package org.keycloak.broker.provider;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.IdentityProviderMapperModel;
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
*/
public interface IdentityProvider<C extends IdentityProviderModel> extends Provider {
public interface AuthenticationCallback {
interface AuthenticationCallback {
/**
* 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);
Response authenticated(BrokeredIdentityContext context);
Response cancelled(String code);
Response error(String code, String message);
}
void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, 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 +68,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 +83,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
@@ -18,6 +18,8 @@
package org.keycloak.broker.provider;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.ProviderFactory;
import java.io.InputStream;
@@ -0,0 +1,56 @@
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();
/**
* Called to determine what keycloak username and email to use to process the login request from the external IDP
* Usually used to map BrokeredIdentityContet.username or email.
*
* @param session
* @param realm
* @param mapperModel
* @param context
*/
void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
/**
* Called after UserModel is created for first time for this user.
*
* @param session
* @param realm
* @param user
* @param mapperModel
* @param context
*/
void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
/**
* Called when this user has logged in before and has already been imported.
*
* @param session
* @param realm
* @param user
* @param mapperModel
* @param context
*/
void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
}
@@ -0,0 +1,32 @@
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 boolean isInternal() {
return true;
}
@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;
}
}
@@ -28,6 +28,11 @@ public class IdentityProviderSpi implements Spi {
public static final String IDENTITY_PROVIDER_SPI_NAME = "identity_provider";
@Override
public boolean isInternal() {
return false;
}
@Override
public String getName() {
return IDENTITY_PROVIDER_SPI_NAME;
@@ -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
+6 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -18,12 +18,10 @@
<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>
@@ -38,13 +36,11 @@
<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>
@@ -52,6 +48,11 @@
<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;
@@ -60,6 +58,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
public static final String FEDERATED_ACCESS_TOKEN = "FEDERATED_ACCESS_TOKEN";
public static final String FEDERATED_REFRESH_TOKEN = "FEDERATED_REFRESH_TOKEN";
public static final String FEDERATED_TOKEN_EXPIRATION = "FEDERATED_TOKEN_EXPIRATION";
public static final String ACCESS_DENIED = "access_denied";
protected static ObjectMapper mapper = new ObjectMapper();
public static final String OAUTH2_PARAMETER_ACCESS_TOKEN = "access_token";
@@ -87,7 +86,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 +107,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 +135,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 +160,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;
@@ -194,9 +214,11 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
@QueryParam(OAuth2Constants.ERROR) String error) {
if (error != null) {
//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);
if (error.equals(ACCESS_DENIED)) {
return callback.cancelled(state);
} else {
return callback.error(state, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
}
}
try {
@@ -204,21 +226,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
}
}
}
@@ -17,13 +17,15 @@
*/
package org.keycloak.broker.oidc;
import org.keycloak.broker.oidc.mappers.UsernameTemplateMapper;
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
import org.keycloak.util.JsonSerialization;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
@@ -53,4 +55,5 @@ public class KeycloakOIDCIdentityProviderFactory extends AbstractIdentityProvide
return OIDCIdentityProviderFactory.parseOIDCConfig(inputStream);
}
}
}
@@ -19,25 +19,28 @@ 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.mappers.AbstractJsonUserAttributeMapper;
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;
@@ -48,9 +51,9 @@ import javax.ws.rs.core.Context;
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.PublicKey;
import java.util.Map;
/**
* @author Pedro Igor
@@ -61,6 +64,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 +98,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 +115,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 +189,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 +206,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");
AbstractJsonUserAttributeMapper.storeUserProfileForMapper(identity, userInfo, getConfig().getAlias());
}
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 +270,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 +307,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,21 +17,24 @@
*/
package org.keycloak.broker.oidc;
import org.codehaus.jackson.annotate.JsonProperty;
import org.keycloak.broker.oidc.util.SimpleHttp;
import org.keycloak.broker.oidc.mappers.UsernameTemplateMapper;
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;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
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;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
/**
@@ -82,7 +85,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);
@@ -91,10 +94,15 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
}
} catch (IOException e) {
throw new RuntimeException("F ailed to query JWKSet from: " + uri, e);
throw new RuntimeException("Failed to query JWKSet from: " + uri, e);
}
}
return config.getConfig();
}
protected static boolean keyTypeSupported(String type) {
return type != null && type.equals("RSA");
}
}
@@ -0,0 +1,97 @@
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;
}
public static Object getClaimValue(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String claim = mapperModel.getConfig().get(CLAIM);
return getClaimValue(context, claim);
}
public static Object getClaimValue(BrokeredIdentityContext context, String 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,206 @@
package org.keycloak.broker.oidc.mappers;
import java.util.ArrayList;
import java.util.List;
import org.codehaus.jackson.JsonNode;
import org.jboss.logging.Logger;
import org.keycloak.broker.oidc.OIDCIdentityProvider;
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderConfigProperty;
/**
* Abstract class for Social Provider mappers which allow mapping of JSON user profile field into Keycloak user
* attribute. Concrete mapper classes with own ID and provider mapping must be implemented for each social provider who
* uses {@link JsonNode} user profile.
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityProviderMapper {
protected static final Logger logger = Logger.getLogger(AbstractJsonUserAttributeMapper.class);
protected static final Logger LOGGER_DUMP_USER_PROFILE = Logger.getLogger("org.keycloak.social.user_profile_dump");
private static final String JSON_PATH_DELIMITER = ".";
/**
* Config param where name of mapping source JSON User Profile field is stored.
*/
public static final String CONF_JSON_FIELD = "jsonField";
/**
* Config param where name of mapping target USer attribute is stored.
*/
public static final String CONF_USER_ATTRIBUTE = "userAttribute";
/**
* Key in {@link BrokeredIdentityContext#getContextData()} where {@link JsonNode} with user profile is stored.
*/
public static final String CONTEXT_JSON_NODE = OIDCIdentityProvider.USER_INFO;
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
ProviderConfigProperty property;
ProviderConfigProperty property1;
property1 = new ProviderConfigProperty();
property1.setName(CONF_JSON_FIELD);
property1.setLabel("Social Profile JSON Field Path");
property1.setHelpText("Path of field in Social provider User Profile JSON data to get value from. You can use dot notation for nesting and square brackets for array index. Eg. 'contact.address[0].country'.");
property1.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property1);
property = new ProviderConfigProperty();
property.setName(CONF_USER_ATTRIBUTE);
property.setLabel("User Attribute Name");
property.setHelpText("User attribute name to store information into.");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
}
/**
* Store used profile JsonNode into user context for later use by this mapper. Profile data are dumped into special logger if enabled also to allow investigation of the structure.
*
* @param user context to store profile data into
* @param profile to store into context
* @param provider identification of social provider to be used in log dump
*
* @see #importNewUser(KeycloakSession, RealmModel, UserModel, IdentityProviderMapperModel, BrokeredIdentityContext)
* @see BrokeredIdentityContext#getContextData()
*/
public static void storeUserProfileForMapper(BrokeredIdentityContext user, JsonNode profile, String provider) {
user.getContextData().put(AbstractJsonUserAttributeMapper.CONTEXT_JSON_NODE, profile);
if (LOGGER_DUMP_USER_PROFILE.isDebugEnabled())
LOGGER_DUMP_USER_PROFILE.debug("User Profile JSON Data for provider "+provider+": " + profile);
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getDisplayCategory() {
return "Attribute Importer";
}
@Override
public String getDisplayType() {
return "Attribute Importer";
}
@Override
public String getHelpText() {
return "Import user profile information if it exists in Social provider JSON data into the specified user attribute.";
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(CONF_USER_ATTRIBUTE);
if (attribute == null || attribute.trim().isEmpty()) {
logger.debug("Attribute is not configured");
return;
}
attribute = attribute.trim();
String value = getJsonValue(mapperModel, context);
if (value != null) {
user.setAttribute(attribute, value);
}
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
// we do not update user profile from social provider
}
protected static String getJsonValue(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String jsonField = mapperModel.getConfig().get(CONF_JSON_FIELD);
if (jsonField == null || jsonField.trim().isEmpty()) {
logger.debug("JSON field path is not configured");
return null;
}
jsonField = jsonField.trim();
if (jsonField.startsWith(JSON_PATH_DELIMITER) || jsonField.endsWith(JSON_PATH_DELIMITER) || jsonField.startsWith("[")) {
logger.debug("JSON field path is invalid " + jsonField);
return null;
}
JsonNode profileJsonNode = (JsonNode) context.getContextData().get(CONTEXT_JSON_NODE);
String value = getJsonValue(profileJsonNode, jsonField);
if (value == null) {
logger.debug("User profile JSON value '" + jsonField + "' is not available.");
}
return value;
}
protected static String getJsonValue(JsonNode baseNode, String fieldPath) {
logger.debug("Going to process JsonNode path " + fieldPath + " on data " + baseNode);
if (baseNode != null) {
int idx = fieldPath.indexOf(JSON_PATH_DELIMITER);
String currentFieldName = fieldPath;
if (idx > 0) {
currentFieldName = fieldPath.substring(0, idx).trim();
if (currentFieldName.isEmpty()) {
logger.debug("JSON path is invalid " + fieldPath);
return null;
}
}
String currentNodeName = currentFieldName;
int arrayIndex = -1;
if (currentFieldName.endsWith("]")) {
int bi = currentFieldName.indexOf("[");
if (bi == -1) {
logger.debug("Invalid array index construct in " + currentFieldName);
return null;
}
try {
String is = currentFieldName.substring(bi+1, currentFieldName.length() - 1).trim();
arrayIndex = Integer.parseInt(is);
} catch (Exception e) {
logger.debug("Invalid array index construct in " + currentFieldName);
return null;
}
currentNodeName = currentFieldName.substring(0,bi).trim();
}
JsonNode currentNode = baseNode.get(currentNodeName);
if (arrayIndex > -1 && currentNode.isArray()) {
logger.debug("Going to take array node at index " + arrayIndex);
currentNode = currentNode.get(arrayIndex);
}
if (currentNode == null) {
logger.debug("JsonNode not found for name " + currentFieldName);
return null;
}
if (idx < 0) {
if (!currentNode.isValueNode()) {
logger.debug("JsonNode is not value node for name " + currentFieldName);
return null;
}
String ret = currentNode.asText();
if (ret != null && !ret.trim().isEmpty())
return ret.trim();
} else {
return getJsonValue(currentNode, fieldPath.substring(idx + 1));
}
}
return null;
}
}
@@ -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,109 @@
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.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.provider.ProviderConfigProperty;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UsernameTemplateMapper 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 TEMPLATE = "template";
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty();
property.setName(TEMPLATE);
property.setLabel("Template");
property.setHelpText("Template to use to format the username to import. Substitutions are enclosed in ${}. For example: '${ALIAS}.${CLAIM.sub}'. ALIAS is the provider alias. CLAIM.<NAME> references an ID or Access token claim.");
property.setType(ProviderConfigProperty.STRING_TYPE);
property.setDefaultValue("${ALIAS}.${CLAIM.preferred_username}");
configProperties.add(property);
}
public static final String PROVIDER_ID = "oidc-username-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 "Preprocessor";
}
@Override
public String getDisplayType() {
return "Username Template Importer";
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
}
static Pattern substitution = Pattern.compile("\\$\\{([^}]+)\\}");
@Override
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String template = mapperModel.getConfig().get(TEMPLATE);
Matcher m = substitution.matcher(template);
StringBuffer sb = new StringBuffer();
while (m.find()) {
String variable = m.group(1);
if (variable.equals("ALIAS")) {
m.appendReplacement(sb, context.getIdpConfig().getAlias());
} else if (variable.equals("UUID")) {
m.appendReplacement(sb, KeycloakModelUtils.generateId());
} else if (variable.startsWith("CLAIM.")) {
String name = variable.substring("CLAIM.".length());
Object value = AbstractClaimMapper.getClaimValue(context, name);
if (value == null) value = "";
m.appendReplacement(sb, value.toString());
} else {
m.appendReplacement(sb, m.group(1));
}
}
m.appendTail(sb);
String username = sb.toString();
context.setModelUsername(username);
}
@Override
public String getHelpText() {
return "Format the username to import.";
}
}
@@ -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,4 @@
org.keycloak.broker.oidc.mappers.ClaimToRoleMapper
org.keycloak.broker.oidc.mappers.ExternalKeycloakRoleToRoleMapper
org.keycloak.broker.oidc.mappers.UserAttributeMapper
org.keycloak.broker.oidc.mappers.UsernameTemplateMapper
@@ -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);
};
};
}
@@ -0,0 +1,120 @@
/*
* 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.mappers;
import java.io.IOException;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;
/**
* Unit test for {@link AbstractJsonUserAttributeMapper}
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public class AbstractJsonUserAttributeMapperTest {
private static ObjectMapper mapper = new ObjectMapper();
private static JsonNode baseNode;
private JsonNode getJsonNode() throws JsonProcessingException, IOException {
if (baseNode == null)
baseNode = mapper.readTree("{ \"value1\" : \"v1 \",\"value_empty\" : \"\", \"value_b\" : true, \"value_i\" : 454, " + " \"value_array\":[\"a1\",\"a2\"], " +" \"nest1\": {\"value1\": \" fgh \",\"value_empty\" : \"\", \"nest2\":{\"value_b\" : false, \"value_i\" : 43}}, "+ " \"nesta\": { \"a\":[{\"av1\": \"vala1\"},{\"av1\": \"vala2\"}]}"+" }");
return baseNode;
}
@Test
public void getJsonValue_invalidPath() throws JsonProcessingException, IOException {
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "."));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), ".."));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "...value1"));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), ".value1"));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value1."));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "[]"));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "[value1"));
}
@Test
public void getJsonValue_simpleValues() throws JsonProcessingException, IOException {
//unknown field returns null
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_unknown"));
// we check value is trimmed also!
Assert.assertEquals("v1", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value1"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_empty"));
Assert.assertEquals("true", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_b"));
Assert.assertEquals("454", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_i"));
}
@Test
public void getJsonValue_nestedSimpleValues() throws JsonProcessingException, IOException {
// null if path points to JSON object
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.nest2"));
//unknown field returns null
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.value_unknown"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.nest2.value_unknown"));
// we check value is trimmed also!
Assert.assertEquals("fgh", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.value1"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.value_empty"));
Assert.assertEquals("false", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.nest2.value_b"));
Assert.assertEquals("43", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.nest2.value_i"));
// null if invalid nested path
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1."));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.nest2."));
}
@Test
public void getJsonValue_simpleArray() throws JsonProcessingException, IOException {
// array field itself returns null if no index is provided
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array"));
// outside index returns null
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[2]"));
//corect index
Assert.assertEquals("a1", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[0]"));
Assert.assertEquals("a2", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[1]"));
//incorrect array constructs
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[]"));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array]"));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array["));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[a]"));
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[-2]"));
}
@Test
public void getJsonValue_nestedArrayWithObjects() throws JsonProcessingException, IOException {
Assert.assertEquals("vala1", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[0].av1"));
Assert.assertEquals("vala2", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[1].av1"));
//different path erros or nonexisting indexes or fields return null
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[2].av1"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[0]"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[0].av_unknown"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[].av1"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a.av1"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a].av1"));
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[.av1"));
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+4 -7
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -18,29 +18,26 @@
<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>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-core</artifactId>
<scope>provided</scope>
</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>
@@ -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;
@@ -173,11 +180,11 @@ public class SAMLEndpoint {
SAMLDocumentHolder holder = extractRequestDocument(samlRequest);
RequestAbstractType requestAbstractType = (RequestAbstractType) holder.getSamlObject();
// validate destination
if (!uriInfo.getAbsolutePath().equals(requestAbstractType.getDestination())) {
if (requestAbstractType.getDestination() != null && !uriInfo.getAbsolutePath().equals(requestAbstractType.getDestination())) {
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);
event.error(Errors.INVALID_SAML_RESPONSE);
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);
}
}
@@ -208,8 +215,11 @@ public class SAMLEndpoint {
if (request.getSessionIndex() == null || request.getSessionIndex().isEmpty()) {
List<UserSessionModel> userSessions = session.sessions().getUserSessionByBrokerUserId(realm, brokerUserId);
for (UserSessionModel userSession : userSessions) {
if (userSession.getState() == UserSessionModel.State.LOGGING_OUT || userSession.getState() == UserSessionModel.State.LOGGED_OUT) {
continue;
}
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);
}
@@ -220,8 +230,11 @@ public class SAMLEndpoint {
String brokerSessionId = brokerUserId + "." + sessionIndex;
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId);
if (userSession != null) {
if (userSession.getState() == UserSessionModel.State.LOGGING_OUT || userSession.getState() == UserSessionModel.State.LOGGED_OUT) {
continue;
}
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 +278,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 +298,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();
@@ -323,11 +354,11 @@ public class SAMLEndpoint {
SAMLDocumentHolder holder = extractResponseDocument(samlResponse);
StatusResponseType statusResponse = (StatusResponseType)holder.getSamlObject();
// validate destination
if (!uriInfo.getAbsolutePath().toString().equals(statusResponse.getDestination())) {
if (statusResponse.getDestination() != null && !uriInfo.getAbsolutePath().toString().equals(statusResponse.getDestination())) {
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);
event.error(Errors.INVALID_SAML_RESPONSE);
return ErrorPage.error(session, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
}
if (config.isValidateSignature()) {
try {
@@ -336,7 +367,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 +386,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 +451,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,21 @@
package org.keycloak.broker.saml;
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
import org.keycloak.broker.saml.mappers.UsernameTemplateMapper;
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.IdentityProviderMapperModel;
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.models.KeycloakSession;
import org.keycloak.models.RealmModel;
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;
@@ -150,4 +154,5 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
public String getId() {
return PROVIDER_ID;
}
}
@@ -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,133 @@
package org.keycloak.broker.saml.mappers;
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
import org.keycloak.broker.provider.BrokeredIdentityContext;
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.dom.saml.v2.assertion.NameIDType;
import org.keycloak.dom.saml.v2.assertion.SubjectType;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.provider.ProviderConfigProperty;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UsernameTemplateMapper extends AbstractIdentityProviderMapper {
public static final String[] COMPATIBLE_PROVIDERS = {SAMLIdentityProviderFactory.PROVIDER_ID};
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
public static final String TEMPLATE = "template";
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty();
property.setName(TEMPLATE);
property.setLabel("Template");
property.setHelpText("Template to use to format the username to import. Substitutions are enclosed in ${}. For example: '${ALIAS}.${NAMEID}'. ALIAS is the provider alias. NAMEID is that SAML name id assertion. ATTRIBUTE.<NAME> references a SAML attribute where name is the attribute name or friendly name.");
property.setType(ProviderConfigProperty.STRING_TYPE);
property.setDefaultValue("${ALIAS}.${NAMEID}");
configProperties.add(property);
}
public static final String PROVIDER_ID = "saml-username-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 "Preprocessor";
}
@Override
public String getDisplayType() {
return "Username Template Importer";
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
}
static Pattern substitution = Pattern.compile("\\$\\{([^}]+)\\}");
@Override
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
AssertionType assertion = (AssertionType)context.getContextData().get(SAMLEndpoint.SAML_ASSERTION);
String template = mapperModel.getConfig().get(TEMPLATE);
Matcher m = substitution.matcher(template);
StringBuffer sb = new StringBuffer();
while (m.find()) {
String variable = m.group(1);
if (variable.equals("ALIAS")) {
m.appendReplacement(sb, context.getIdpConfig().getAlias());
} else if (variable.equals("UUID")) {
m.appendReplacement(sb, KeycloakModelUtils.generateId());
} else if (variable.equals("NAMEID")) {
SubjectType subject = assertion.getSubject();
SubjectType.STSubType subType = subject.getSubType();
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
m.appendReplacement(sb, subjectNameID.getValue());
} else if (variable.startsWith("ATTRIBUTE.")) {
String name = variable.substring("ATTRIBUTE.".length());
String value = "";
for (AttributeStatementType statement : assertion.getAttributeStatements()) {
for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) {
AttributeType attr = choice.getAttribute();
if (name.equals(attr.getName()) || name.equals(attr.getFriendlyName())) {
List<Object> attributeValue = attr.getAttributeValue();
if (attributeValue != null && !attributeValue.isEmpty()) {
value = attributeValue.get(0).toString();
}
break;
}
}
}
m.appendReplacement(sb, value);
} else {
m.appendReplacement(sb, m.group(1));
}
}
m.appendTail(sb);
context.setModelUsername(sb.toString());
}
@Override
public String getHelpText() {
return "Format the username to import.";
}
}
@@ -0,0 +1,3 @@
org.keycloak.broker.saml.mappers.AttributeToRoleMapper
org.keycloak.broker.saml.mappers.UserAttributeMapper
org.keycloak.broker.saml.mappers.UsernameTemplateMapper
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.4.0.Final-SNAPSHOT</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
@@ -9,6 +9,11 @@ import org.keycloak.provider.Spi;
*/
public class FileConnectionSpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return "connectionsFile";
@@ -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.4.0.Final-SNAPSHOT</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;
}
@@ -0,0 +1,32 @@
package org.keycloak.connections.httpclient;
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 HttpClientSpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return "connectionsHttpClient";
}
@Override
public Class<? extends Provider> getProviderClass() {
return HttpClientProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return HttpClientFactory.class;
}
}
@@ -0,0 +1 @@
org.keycloak.connections.httpclient.DefaultHttpClientFactory
@@ -0,0 +1 @@
org.keycloak.connections.httpclient.HttpClientSpi
+1 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -17,12 +17,10 @@
<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>
@@ -9,6 +9,11 @@ import org.keycloak.provider.Spi;
*/
public class InfinispanConnectionSpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return "connectionsInfinispan";
+10 -12
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -17,12 +17,10 @@
<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>
@@ -38,7 +36,14 @@
<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>
@@ -49,7 +54,7 @@
<configuration>
<changeLogFile>META-INF/jpa-changelog-master.xml</changeLogFile>
<url>${url}</url>
<url>${project.url}</url>
<driver>${driver}</driver>
<username>${username}</username>
<password>${password}</password>
@@ -59,13 +64,6 @@
<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>
@@ -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,53 @@
package org.keycloak.connections.jpa.updater.liquibase.custom;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import liquibase.datatype.DataTypeFactory;
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 {
String trueValue = DataTypeFactory.getInstance().getTrueBooleanValue(database);
PreparedStatement statement = jdbcConnection.prepareStatement("select CLIENT.REALM_ID, CLIENT.ID CLIENT_ID from CLIENT where CLIENT.CONSENT_REQUIRED = " + trueValue);
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>
@@ -0,0 +1,200 @@
<?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.3.0">
<delete tableName="CLIENT_SESSION_ROLE"/>
<delete tableName="CLIENT_SESSION_PROT_MAPPER"/>
<delete tableName="CLIENT_SESSION_NOTE"/>
<delete tableName="CLIENT_SESSION"/>
<delete tableName="USER_SESSION_NOTE"/>
<delete tableName="USER_SESSION"/>
<createTable tableName="ADMIN_EVENT_ENTITY">
<column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="ADMIN_EVENT_TIME" type="BIGINT"/>
<column name="REALM_ID" type="VARCHAR(255)"/>
<column name="OPERATION_TYPE" type="VARCHAR(255)"/>
<column name="AUTH_REALM_ID" type="VARCHAR(255)"/>
<column name="AUTH_CLIENT_ID" type="VARCHAR(255)"/>
<column name="AUTH_USER_ID" type="VARCHAR(255)"/>
<column name="IP_ADDRESS" type="VARCHAR(255)"/>
<column name="RESOURCE_PATH" type="VARCHAR(2550)"/>
<column name="REPRESENTATION" type="TEXT(25500)"/>
<column name="ERROR" type="VARCHAR(255)"/>
</createTable>
<createTable tableName="AUTHENTICATOR">
<column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="ALIAS" type="VARCHAR(255)"/>
<column name="REALM_ID" type="VARCHAR(36)"/>
<column name="PROVIDER_ID" type="VARCHAR(255)"/>
</createTable>
<createTable tableName="AUTHENTICATION_FLOW">
<column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="ALIAS" type="VARCHAR(255)"/>
<column name="DESCRIPTION" type="VARCHAR(255)"/>
<column name="REALM_ID" type="VARCHAR(36)"/>
</createTable>
<createTable tableName="AUTHENTICATION_EXECUTION">
<column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="ALIAS" type="VARCHAR(255)"/>
<column name="AUTHENTICATOR" type="VARCHAR(36)"/>
<column name="REALM_ID" type="VARCHAR(36)"/>
<column name="FLOW_ID" type="VARCHAR(36)"/>
<column name="REQUIREMENT" type="INT"/>
<column name="PRIORITY" type="INT"/>
<column name="USER_SETUP_ALLOWED" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="AUTHENTICATOR_FLOW" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</createTable>
<createTable tableName="AUTHENTICATOR_CONFIG">
<column name="AUTHENTICATOR_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="VALUE" type="CLOB"/>
<column name="NAME" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
</createTable>
<createTable tableName="USER_FEDERATION_MAPPER">
<column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="NAME" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="FEDERATION_PROVIDER_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="FEDERATION_MAPPER_TYPE" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="REALM_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
</createTable>
<createTable tableName="USER_FEDERATION_MAPPER_CONFIG">
<column name="USER_FEDERATION_MAPPER_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="VALUE" type="VARCHAR(255)"/>
<column name="NAME" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
</createTable>
<addColumn tableName="REALM">
<column name="ADMIN_EVENTS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="ADMIN_EVENTS_DETAILS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="EDIT_USERNAME_ALLOWED" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
<createTable tableName="CLIENT_SESSION_AUTH_STATUS">
<column name="AUTHENTICATOR" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="STATUS" type="INT"/>
<column name="CLIENT_SESSION" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
</createTable>
<addColumn tableName="CLIENT_SESSION">
<column name="AUTH_USER_ID" type="VARCHAR(36)"/>
</addColumn>
<addColumn tableName="IDENTITY_PROVIDER">
<column name="TRUST_EMAIL" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="UPDATE_PROFILE_FIRST_LGN_MD" type="VARCHAR(255)" defaultValue="on">
<constraints nullable="false"/>
</column>
</addColumn>
<!-- migrate value from UPDATE_PROFILE_FIRST_LOGIN to UPDATE_PROFILE_FIRST_LGN_MD then drop it -->
<update tableName="IDENTITY_PROVIDER">
<column name="UPDATE_PROFILE_FIRST_LGN_MD" value="off"/>
<where>UPDATE_PROFILE_FIRST_LOGIN = :value</where>
<whereParams>
<param valueBoolean="false" />
</whereParams>
</update>
<dropDefaultValue tableName="IDENTITY_PROVIDER" columnName="UPDATE_PROFILE_FIRST_LOGIN" />
<dropColumn tableName="IDENTITY_PROVIDER" columnName="UPDATE_PROFILE_FIRST_LOGIN"/>
<addColumn tableName="USER_REQUIRED_ACTION">
<column name="REQUIRED_ACTION" type="VARCHAR(255)" defaultValue=" ">
<constraints nullable="false"/>
</column>
</addColumn>
<!-- VERIFY_EMAIL, UPDATE_PROFILE, CONFIGURE_TOTP, UPDATE_PASSWORD -->
<update tableName="USER_REQUIRED_ACTION">
<column name="REQUIRED_ACTION" value="VERIFY_EMAIL"/>
<where>ACTION = 0</where>
</update>
<update tableName="USER_REQUIRED_ACTION">
<column name="REQUIRED_ACTION" value="UPDATE_PROFILE"/>
<where>ACTION = 1</where>
</update>
<update tableName="USER_REQUIRED_ACTION">
<column name="REQUIRED_ACTION" value="CONFIGURE_TOTP"/>
<where>ACTION = 2</where>
</update>
<update tableName="USER_REQUIRED_ACTION">
<column name="REQUIRED_ACTION" value="UPDATE_PASSWORD"/>
<where>ACTION = 3</where>
</update>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_AUTH_PK" tableName="AUTHENTICATOR"/>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_AUTH_FLOW_PK" tableName="AUTHENTICATION_FLOW"/>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_AUTH_EXEC_PK" tableName="AUTHENTICATION_EXECUTION"/>
<addPrimaryKey columnNames="AUTHENTICATOR_ID, NAME" constraintName="CONSTRAINT_AUTH_CFG_PK" tableName="AUTHENTICATOR_CONFIG"/>
<dropPrimaryKey constraintName="CONSTRAINT_2" tableName="USER_REQUIRED_ACTION"/>
<dropColumn tableName="USER_REQUIRED_ACTION" columnName="ACTION"/>
<addPrimaryKey columnNames="REQUIRED_ACTION, USER_ID" constraintName="CONSTRAINT_REQUIRED_ACTION" tableName="USER_REQUIRED_ACTION"/>
<addPrimaryKey columnNames="CLIENT_SESSION, AUTHENTICATOR" constraintName="CONSTRAINT_AUTH_STATUS_PK" tableName="CLIENT_SESSION_AUTH_STATUS"/>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_FEDMAPPERPM" tableName="USER_FEDERATION_MAPPER"/>
<addPrimaryKey columnNames="USER_FEDERATION_MAPPER_ID, NAME" constraintName="CONSTRAINT_FEDMAPPER_CFG_PM" tableName="USER_FEDERATION_MAPPER_CONFIG"/>
<addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_AUTH_STATUS" constraintName="AUTH_STATUS_CONSTRAINT" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATOR" constraintName="FK_AUTH_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATION_FLOW" constraintName="FK_AUTH_FLOW_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATION_EXECUTION" constraintName="FK_AUTH_EXEC_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
<addForeignKeyConstraint baseColumnNames="FLOW_ID" baseTableName="AUTHENTICATION_EXECUTION" constraintName="FK_AUTH_EXEC_FLOW" referencedColumnNames="ID" referencedTableName="AUTHENTICATION_FLOW"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="USER_FEDERATION_MAPPER" constraintName="FK_FEDMAPPERPM_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
<addForeignKeyConstraint baseColumnNames="FEDERATION_PROVIDER_ID" baseTableName="USER_FEDERATION_MAPPER" constraintName="FK_FEDMAPPERPM_FEDPRV" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_PROVIDER"/>
<addForeignKeyConstraint baseColumnNames="USER_FEDERATION_MAPPER_ID" baseTableName="USER_FEDERATION_MAPPER_CONFIG" constraintName="FK_FEDMAPPER_CFG" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_MAPPER"/>
<dropDefaultValue tableName="REALM" columnName="PASSWORD_CRED_GRANT_ALLOWED" />
<dropColumn tableName="REALM" columnName="PASSWORD_CRED_GRANT_ALLOWED"/>
<!-- KEYCLOAK-1298 Change constraint names to be upper-cased -->
<dropForeignKeyConstraint baseTableName="PROTOCOL_MAPPER_CONFIG" constraintName="FK_PMConfig" />
<dropPrimaryKey constraintName="CONSTRAINT_PMConfig" tableName="PROTOCOL_MAPPER_CONFIG"/>
<addPrimaryKey columnNames="PROTOCOL_MAPPER_ID, NAME" constraintName="CONSTRAINT_PMCONFIG" tableName="PROTOCOL_MAPPER_CONFIG"/>
<addForeignKeyConstraint baseColumnNames="PROTOCOL_MAPPER_ID" baseTableName="PROTOCOL_MAPPER_CONFIG" constraintName="FK_PMCONFIG" referencedColumnNames="ID" referencedTableName="PROTOCOL_MAPPER"/>
<dropForeignKeyConstraint baseTableName="IDP_MAPPER_CONFIG" constraintName="FK_IDPMConfig" />
<dropPrimaryKey constraintName="CONSTRAINT_IDPMConfig" tableName="IDP_MAPPER_CONFIG"/>
<addPrimaryKey columnNames="IDP_MAPPER_ID, NAME" constraintName="CONSTRAINT_IDPMCONFIG" tableName="IDP_MAPPER_CONFIG"/>
<addForeignKeyConstraint baseColumnNames="IDP_MAPPER_ID" baseTableName="IDP_MAPPER_CONFIG" constraintName="FK_IDPMCONFIG" referencedColumnNames="ID" referencedTableName="IDENTITY_PROVIDER_MAPPER"/>
<!-- Sybase specific hacks -->
<modifySql dbms="sybase">
<replace replace="[USER_REQUIRED_ACTION] DROP PRIMARY KEY" with="[USER_REQUIRED_ACTION] DROP CONSTRAINT CONSTRAINT_2" />
<replace replace="[PROTOCOL_MAPPER_CONFIG] DROP PRIMARY KEY" with="[PROTOCOL_MAPPER_CONFIG] DROP CONSTRAINT CONSTRAINT_PMConfig" />
<replace replace="[IDP_MAPPER_CONFIG] DROP PRIMARY KEY" with="[IDP_MAPPER_CONFIG] DROP CONSTRAINT CONSTRAINT_IDPMConfig" />
<regExpReplace replace=".*(SET DEFAULT NULL)" with="SELECT 1" />
</modifySql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,86 @@
<?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.4.0">
<delete tableName="CLIENT_SESSION_AUTH_STATUS"/>
<delete tableName="CLIENT_SESSION_PROT_MAPPER"/>
<delete tableName="CLIENT_SESSION_NOTE"/>
<delete tableName="CLIENT_SESSION"/>
<delete tableName="USER_SESSION_NOTE"/>
<delete tableName="USER_SESSION"/>
<createTable tableName="DEFAULT_REQUIRED_ACTIONS">
<column name="REALM_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="VALUE" type="VARCHAR(36)"/>
</createTable>
<addColumn tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
</addColumn>
<!-- OAUTH_GRANT,
CODE_TO_TOKEN,
VERIFY_EMAIL,
UPDATE_PROFILE,
CONFIGURE_TOTP,
UPDATE_PASSWORD,
RECOVER_PASSWORD,
AUTHENTICATE,
SOCIAL_CALLBACK,
LOGGED_OUT -->
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="OAUTH_GRANT"/>
<where>ACTION = 0</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="CODE_TO_TOKEN"/>
<where>ACTION = 1</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="VERIFY_EMAIL"/>
<where>ACTION = 2</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="UPDATE_PROFILE"/>
<where>ACTION = 3</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="CONFIGURE_TOTP"/>
<where>ACTION = 4</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="UPDATE_PASSWORD"/>
<where>ACTION = 5</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="RECOVER_PASSWORD"/>
<where>ACTION = 6</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="AUTHENTICATE"/>
<where>ACTION = 7</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="SOCIAL_CALLBACK"/>
<where>ACTION = 8</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="LOGGED_OUT"/>
<where>ACTION = 9</where>
</update>
<createTable tableName="CLIENT_USER_SESSION_NOTE">
<column name="NAME" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="VALUE" type="VARCHAR(255)"/>
<column name="CLIENT_SESSION" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
</createTable>
<addPrimaryKey columnNames="CLIENT_SESSION, NAME" constraintName="CONSTR_CL_USR_SES_NOTE" tableName="CLIENT_USER_SESSION_NOTE"/>
<addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_USER_SESSION_NOTE" constraintName="FK_CL_USR_SES_NOTE" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
<dropColumn tableName="CLIENT_SESSION" columnName="ACTION"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="DEFAULT_REQUIRED_ACTIONS" constraintName="FK_DEF_REQ_ACTS_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
</changeSet>
</databaseChangeLog>
@@ -4,4 +4,8 @@
<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"/>
<include file="META-INF/jpa-changelog-1.3.0.xml"/>
<include file="META-INF/jpa-changelog-1.4.0.xml"/>
</databaseChangeLog>
+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.4.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -17,12 +17,10 @@
<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>
@@ -32,7 +30,6 @@
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.entitymanager.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -9,6 +9,11 @@ import org.keycloak.provider.Spi;
*/
public class JpaConnectionSpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return "connectionsJpa";
@@ -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.4.0";
public String getCurrentVersionSql();
@@ -9,6 +9,11 @@ import org.keycloak.provider.Spi;
*/
public class JpaUpdaterSpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return "connectionsJpaUpdater";
@@ -3,35 +3,47 @@
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.UserFederationMapperEntity</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>
<class>org.keycloak.models.jpa.entities.AuthenticationFlowEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticationExecutionEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticatorEntity</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.ClientSessionAuthStatusEntity</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.ClientUserSessionNoteEntity</class>
<class>org.keycloak.models.sessions.jpa.entities.UserSessionNoteEntity</class>
<class>org.keycloak.models.sessions.jpa.entities.UserSessionEntity</class>
<class>org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity</class>
<!-- JpaAuditProvider -->
<!-- JpaAuditProviders -->
<class>org.keycloak.events.jpa.EventEntity</class>
<class>org.keycloak.events.jpa.AdminEventEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
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.4.0.Final-SNAPSHOT</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>
@@ -6,10 +6,7 @@ import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import org.jboss.logging.Logger;
import org.keycloak.connections.mongo.updater.MongoUpdaterProvider;
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.*;
import org.keycloak.models.KeycloakSession;
import java.util.Date;
@@ -28,7 +25,9 @@ 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,
Update1_3_0.class
};
@Override
@@ -53,15 +53,12 @@ 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 removeField(String collection, String field) {
db.getCollection(collection).update(new BasicDBObject(), new BasicDBObject("$unset" , new BasicDBObject(field, 1)), false, true);
}
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");
}
}
@@ -0,0 +1,59 @@
package org.keycloak.connections.mongo.updater.impl.updates;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class Update1_3_0 extends Update {
@Override
public String getId() {
return "1.3.0";
}
@Override
public void update(KeycloakSession session) {
deleteEntries("clientSessions");
deleteEntries("sessions");
removeField("realms", "passwordCredentialGrantAllowed");
updateIdentityProviders();
}
private void updateIdentityProviders() {
DBCollection realms = db.getCollection("realms");
DBCursor realmsCursor = realms.find();
try {
while (realmsCursor.hasNext()) {
BasicDBObject realm = (BasicDBObject) realmsCursor.next();
BasicDBList identityProviders = (BasicDBList) realm.get("identityProviders");
if (identityProviders != null) {
for (Object ipObj : identityProviders) {
BasicDBObject identityProvider = (BasicDBObject) ipObj;
boolean updateProfileFirstLogin = identityProvider.getBoolean("updateProfileFirstLogin");
String upflMode = updateProfileFirstLogin ? IdentityProviderRepresentation.UPFLM_ON : IdentityProviderRepresentation.UPFLM_OFF;
identityProvider.put("updateProfileFirstLoginMode", upflMode);
identityProvider.removeField("updateProfileFirstLogin");
identityProvider.put("trustEmail", false);
}
}
realms.save(realm);
}
} finally {
realmsCursor.close();
}
}
}
+1 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -17,12 +17,10 @@
<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>
@@ -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,19 @@ 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.UserFederationMapperEntity",
"org.keycloak.models.entities.ProtocolMapperEntity",
"org.keycloak.models.entities.IdentityProviderMapperEntity",
"org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity",
"org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity",
"org.keycloak.models.entities.AuthenticationExecutionEntity",
"org.keycloak.models.entities.AuthenticationFlowEntity",
"org.keycloak.models.entities.AuthenticatorEntity",
};
private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class);
@@ -48,7 +55,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 +82,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 +103,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 +131,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);
@@ -9,6 +9,11 @@ import org.keycloak.provider.Spi;
*/
public class MongoConnectionSpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return "connectionsMongo";
@@ -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();
@@ -9,6 +9,11 @@ import org.keycloak.provider.Spi;
*/
public class MongoUpdaterSpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return "connectionsMongoUpdater";
+2 -1
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<version>1.4.0.Final-SNAPSHOT</version>
</parent>
<name>Connections Parent</name>
<description/>
@@ -19,6 +19,7 @@
<module>mongo</module>
<module>file</module>
<module>mongo-update</module>
<module>http-client</module>
</modules>
<build>
+1 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -22,7 +22,6 @@
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
+1 -7
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.2.0.Beta1</version>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -56,12 +56,6 @@
<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>
@@ -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";
@@ -14,6 +14,8 @@ public enum SslRequired {
EXTERNAL,
NONE;
private static final long serialVersionUID = 1L;
public boolean isRequired(ClientConnection connection) {
return isRequired(connection.getRemoteAddr());
}
@@ -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;
}
}
Loaded 100 of 2362 files, more files were not shown because too many files have changed in this diff. Show more