Compare commits

..
342 Commits
Author SHA1 Message Date
Stian Thorgersen 5f348bb7d8 Version bump 2015-07-29 13:05:52 +02:00
Stian Thorgersen 630597746e Merge pull request #1514 from stianst/master
KEYCLOAK-1714 Add @JsonIgnoreProperties(ignoreUnknown = true) to JWSH…
2015-07-28 10:15:09 +02:00
Stian Thorgersen f98036296c KEYCLOAK-1716 NPE importing with empty database 2015-07-28 09:46:03 +02:00
Stian Thorgersen bf1f867f23 KEYCLOAK-1715 Cordova example doesn't work on Cordova 5.x 2015-07-28 08:36:01 +02:00
Stian Thorgersen 8215a85930 KEYCLOAK-1549
Migration for multiple attributes and LDAP mappers
2015-07-28 07:46:29 +02:00
Stian Thorgersen 10a5686955 KEYCLOAK-1714 Add @JsonIgnoreProperties(ignoreUnknown = true) to JWSHeader 2015-07-28 07:30:10 +02:00
Bill Burke 2e3c7a3acf Merge pull request #1512 from patriot1burke/master
canonicalization config
2015-07-27 15:33:27 -04:00
Bill Burke 06713da67b Merge remote-tracking branch 'upstream/master' 2015-07-27 11:57:21 -04:00
Bill Burke 46fb1b6582 canonicalization config 2015-07-27 11:57:08 -04:00
Stian Thorgersen 1b3f98db1e Merge pull request #1511 from stianst/master
UserInfo fix
2015-07-27 17:01:41 +02:00
Stian Thorgersen a190cdd8f2 KEYCLOAK-1712 Admin client example not working 2015-07-27 16:01:48 +02:00
Stian Thorgersen 8e25369001 KEYCLOAK-1711 Add get user info to js-console example 2015-07-27 15:43:52 +02:00
Stian Thorgersen 8d90ad816a KEYCLOAK-1710 UserInfoEndpoint throws NPE if user session is not found 2015-07-27 15:43:52 +02:00
Stian Thorgersen da9e42cb21 Merge pull request #1510 from stianst/master
KEYCLOAK-1709 Add standalone-keycloak-ha.xml to server overlays
2015-07-27 14:32:00 +02:00
Stian Thorgersen cbab3ff0d0 KEYCLOAK-1709 Add standalone-keycloak-ha.xml to server overlays 2015-07-27 14:31:40 +02:00
Stian Thorgersen 210b570c89 Merge pull request #1509 from stianst/master
Admin console fixes
2015-07-27 13:31:16 +02:00
Stian Thorgersen a2203df6a3 KEYCLOAK-1705 Logo not changed in admin console for logo example theme 2015-07-27 13:30:55 +02:00
Stian Thorgersen 6a2b369014 KEYCLOAK-1707 Can't export SAML keys 2015-07-27 13:00:22 +02:00
Stian Thorgersen fda6a6657f KEYCLOAK-1708 Authentication flows uses h2 in table 2015-07-27 12:32:55 +02:00
Stian Thorgersen 04565dad00 KEYCLOAK-1706 Service Account tab for clients displays title twice 2015-07-27 12:30:06 +02:00
Stian Thorgersen ac0ae06463 Merge pull request #1508 from stianst/master
KEYCLOAK-1694 Null Client Exception on OAuth Grant Page
2015-07-27 11:43:13 +02:00
Stian Thorgersen a8775f757f KEYCLOAK-1694 Null Client Exception on OAuth Grant Page 2015-07-27 09:59:50 +02:00
Stian Thorgersen e4f4ed923d Merge pull request #1507 from stianst/master
KEYCLOAK-1699 There are 3 failed tests when running testsuite with -P…
2015-07-27 09:57:52 +02:00
Stian Thorgersen c7915fa95d KEYCLOAK-1699 There are 3 failed tests when running testsuite with -Pinfinispan 2015-07-27 08:52:00 +02:00
Bill Burke 652b118634 Merge pull request #1506 from patriot1burke/master
timeout message
2015-07-25 15:27:51 -04:00
Bill Burke f122ff1471 Merge pull request #1505 from patriot1burke/master
login timeouts, verify email
2015-07-25 12:43:53 -04:00
Bill Burke f76b90fab9 timeout message 2015-07-25 12:43:46 -04:00
Bill Burke ccd8beae92 Merge remote-tracking branch 'upstream/master' 2015-07-25 12:16:05 -04:00
Bill Burke 33f01009d2 login timeouts, verify email 2015-07-25 12:13:41 -04:00
mposolda a0e3a71109 Fix infinispan - not finished 2015-07-25 12:05:16 +02:00
mposolda f35ecba801 Sybase - another attempt 2015-07-25 11:47:56 +02:00
Marek Posolda 0d7f26bd14 Merge pull request #1504 from mposolda/master
Migration
2015-07-25 11:36:04 +02:00
mposolda 2f7bdd2640 Fix migration version comparing bug 2015-07-25 09:00:50 +02:00
mposolda 3125e4ddd2 KEYCLOAK-1549 Mongo migration 2015-07-25 08:58:05 +02:00
Marek Posolda c624cb27da Merge pull request #1503 from mposolda/master
KEYCLOAK-1549 Migration for attributes and LDAP mappers - JPA
2015-07-24 23:29:28 +02:00
mposolda 06adf9a094 Sybase fix 2015-07-24 23:28:52 +02:00
mposolda 94ed55ee35 KEYCLOAK-1549 Migration for attributes and LDAP mappers - JPA 2015-07-24 23:09:48 +02:00
Marek Posolda 95c8603b69 Merge pull request #1502 from cwalker67/master
Added null email check in duplicate email check code
2015-07-24 23:04:46 +02:00
Charles Walker 9ad2a8272e quick return check for null email address, in reference to duplicate email check issue #1510 2015-07-24 16:08:26 -04:00
Charles Walker 6dc17da00a Merge pull request #1 from keycloak/master
merge updates
2015-07-24 15:55:34 -04:00
Marek Posolda 97aa1c6b2c Merge pull request #1501 from mposolda/master
Temporarily comment the test failing on Oracle and MSSQL as the funct…
2015-07-24 21:50:35 +02:00
mposolda 084fed8377 Temporarily comment the test failing on Oracle and MSSQL as the functionality is not used right now 2015-07-24 21:49:51 +02:00
Marek Posolda 68d86e4fa1 Merge pull request #1500 from mposolda/master
Fix JPA UserSession provider and MySQL
2015-07-24 20:46:46 +02:00
mposolda 5203373047 Fix JPA UserSession provider and MySQL 2015-07-24 20:39:39 +02:00
Stian Thorgersen 86de481ba1 Merge pull request #1499 from stianst/master
KEYCLOAK-1542
2015-07-24 17:30:29 +02:00
Stian Thorgersen 5d075a97dd Fixes to server-info 2015-07-24 16:45:58 +02:00
Stian Thorgersen e89499f5db Fix xmlns for modules in feature packs 2015-07-24 16:45:27 +02:00
Stian Thorgersen a1f0208767 Merge branch 'velias-KEYCLOAK-1542' 2015-07-24 14:48:10 +02:00
Stian Thorgersen d1964c20ab Refactored server-info and updated admin console 2015-07-24 14:47:37 +02:00
Marek Posolda 1e035bf2e5 Merge pull request #1498 from mposolda/master
Skip testDotInUsername for the MSAD configuration with sAMAccountName…
2015-07-24 13:03:24 +02:00
mposolda f3b87f9cf9 Skip testDotInUsername for the MSAD configuration with sAMAccountName as username 2015-07-24 13:01:54 +02:00
Stian Thorgersen d2ea45e1eb Merge pull request #1496 from stianst/master
KEYCLOAK-1691 and KEYCLOAK-1695
2015-07-24 12:52:19 +02:00
Marek Posolda 58ad6b1a40 Merge pull request #1497 from mposolda/master
Mongo model fix
2015-07-24 12:47:47 +02:00
mposolda 772ef3cd10 Mongo model fix 2015-07-24 12:46:47 +02:00
Stian Thorgersen 204dfd52c8 Merge branch 'KEYCLOAK-1542' of https://github.com/velias/keycloak into velias-KEYCLOAK-1542 2015-07-24 12:17:33 +02:00
Vlastimil Elias 244d3452d0 KEYCLOAK-1542 - removed unused code from the page template 2015-07-24 11:07:01 +02:00
Stian Thorgersen e65f8a7150 KEYCLOAK-1695 Upgrade to WildFly 9.0.1.Final 2015-07-24 10:44:15 +02:00
Stian Thorgersen 90d6991b9d KEYCLOAK-1691 Enable adapter subsystem to use with WildFly Swarm 2015-07-24 10:16:25 +02:00
Stian Thorgersen 87ca3f203a Merge pull request #1488 from stianst/master
KEYCLOAK-1506 Warning of Keycloak 1.3.1 on MySQL 5.6
2015-07-24 09:58:02 +02:00
Marek Posolda 3f3a20e178 Merge pull request #1494 from mposolda/master
KEYCLOAK-1693 Added test and possibility to create users with dot in …
2015-07-24 09:57:55 +02:00
mposolda ce1a19fdbe KEYCLOAK-1693 Added test and possibility to create users with dot in username 2015-07-24 09:33:16 +02:00
Marek Posolda 26ac92a6b1 Merge pull request #1493 from dylanplecki/issue-1693
KEYCLOAK-1693 LDAP DN parse error on escaped characters
2015-07-24 09:07:06 +02:00
Marek Posolda 1c7dd0a533 Merge pull request #1492 from mposolda/master
Simplify embedded server bootstrap in LDAP and Kerberos examples
2015-07-23 22:13:22 +02:00
Dylan Plecki ceb29d76b2 Fix regression in RegEx statements 2015-07-23 14:49:03 -05:00
Dylan Plecki 6efb354cce Fix LDAP DN component matching with negative lookbehind RegEx 2015-07-23 14:48:56 -05:00
Dylan Plecki 8ae242a6ed Fix LDAP RDN resolution based on unescaped comma split 2015-07-23 14:48:48 -05:00
mposolda 5f0f92fd15 Fix test to pass with MSAD 2015-07-23 21:39:40 +02:00
mposolda e521fa3554 Simplify embedded server bootstrap in LDAP and Kerberos examples 2015-07-23 21:17:40 +02:00
Marek Posolda b8f73cf21f Merge pull request #1491 from mposolda/master
Fix MSAD and OpenLDAP
2015-07-23 18:32:32 +02:00
mposolda d068cddbaf Fix MSAD and OpenLDAP 2015-07-23 18:03:12 +02:00
Bill Burke 99422835ab Merge pull request #1489 from patriot1burke/master
fix typo
2015-07-23 11:09:31 -04:00
Bill Burke 68263360c7 fix typo 2015-07-23 11:09:07 -04:00
Vlastimil Elias 34f5ddab0f Merge branch 'KEYCLOAK-1542' of https://github.com/velias/keycloak.git into KEYCLOAK-1542 2015-07-23 16:49:11 +02:00
Stian Thorgersen 0cc4630fc6 KEYCLOAK-1506 Warning of Keycloak 1.3.1 on MySQL 5.6 2015-07-23 16:46:06 +02:00
Vlastimil Elias ee57c79784 KEYCLOAK-1542 - rewrote showing info from providers to be generic 2015-07-23 16:40:57 +02:00
Vlastimil Elias 7e41b8df27 KEYCLOAK-1542 - code formatting cleanup 2015-07-23 16:40:56 +02:00
Vlastimil Elias 79c1d7bafa KEYCLOAK-1542 - code cleanup ahead of PR 2015-07-23 16:40:55 +02:00
Vlastimil Elias 652b2fee86 KEYCLOAK-1542 - Server Info page extended by info about DB and MongoDB.
Functional test for /serverinfo REST endpoint added.
2015-07-23 16:40:54 +02:00
Vlastimil Elias dfb871c26a KEYCLOAK-1542 - added system and memory info into "System Info" page 2015-07-23 16:40:53 +02:00
Vlastimil Elias 99f74acb94 KEYCLOAK-1542 - rewrote showing info from providers to be generic 2015-07-23 16:39:27 +02:00
Stian Thorgersen 83f7682606 Merge pull request #1487 from stianst/master
KEYCLOAK-1463
2015-07-23 15:53:50 +02:00
Stian Thorgersen c4a07f3098 KEYCLOAK-1463
Need better UI for Terms and Conditions
2015-07-23 14:35:57 +02:00
Stian Thorgersen c9451f4f35 Merge pull request #1486 from stianst/master
KEYCLOAK-1593 Move impersonate button on user details page
2015-07-23 14:24:49 +02:00
Stian Thorgersen dff4922783 KEYCLOAK-1593 Move impersonate button on user details page 2015-07-23 14:24:19 +02:00
Stian Thorgersen c05b666f3c Merge pull request #1483 from stianst/master
KEYCLOAK-1591 Issues with token settings page
2015-07-23 13:26:38 +02:00
Marek Posolda 9905024f08 Merge pull request #1485 from mposolda/service-acc
KEYCLOAK-401 Service account docs
2015-07-23 12:19:06 +02:00
mposolda b0cb457287 KEYCLOAK-401 Service account docs 2015-07-23 12:18:09 +02:00
Marek Posolda d4946c1c08 Merge pull request #1484 from mposolda/service-acc
KEYCLOAK-401 Service account refactoring and fixes
2015-07-23 12:09:33 +02:00
Stian Thorgersen 1d745b5cbd KEYCLOAK-1443 Add delete action to tables 2015-07-23 12:07:31 +02:00
mposolda c99785f266 KEYCLOAK-401 Service account refactoring and fixes 2015-07-23 11:29:52 +02:00
Stian Thorgersen 9ecdec3f45 KEYCLOAK-1591 Issues with token settings page 2015-07-23 11:23:53 +02:00
Stian Thorgersen 866f45bd95 Merge pull request #1479 from stianst/master
KEYCLOAK-1588 Strange version of slf4j not available in maven central
2015-07-23 09:17:21 +02:00
Bill Burke 320a79dbff Merge pull request #1482 from patriot1burke/master
better description
2015-07-22 14:47:16 -04:00
Bill Burke 1e0b6f1aad better description 2015-07-22 14:46:58 -04:00
Bill Burke 251ad6727a Merge pull request #1481 from patriot1burke/master
brute force fixes and enhancements
2015-07-22 14:46:29 -04:00
Bill Burke 1b1f56d183 ugh 2015-07-22 14:22:41 -04:00
Bill Burke 7dc05a45ac conflict 2015-07-22 14:20:52 -04:00
Bill Burke 7257bab289 brute force user ui 2015-07-22 14:16:09 -04:00
Bill Burke 48a76c2d0d test brute force 2015-07-22 12:30:52 -04:00
Stian Thorgersen d78c83b236 KEYCLOAK-1588 Strange version of slf4j not available in maven central 2015-07-22 16:27:34 +02:00
Bill Burke d9b0415047 brute force fixes, code cleanup, tests 2015-07-22 09:40:11 -04:00
Stian Thorgersen af2e3de96d Merge pull request #1474 from stianst/master
KEYCLOAK-1427
2015-07-22 14:30:45 +02:00
Vlastimil Elias 1c142cb170 KEYCLOAK-1542 - code formatting cleanup 2015-07-22 14:28:08 +02:00
Stian Thorgersen d5dae7d444 KEYCLOAK-1428 Disable buttons on forms instead of hiding 2015-07-22 14:05:47 +02:00
Vlastimil Elias 85816be967 KEYCLOAK-1542 - code cleanup ahead of PR 2015-07-22 13:35:56 +02:00
Stian Thorgersen e66d658955 KEYCLOAK-1428 Disable buttons on forms instead of hiding 2015-07-22 11:50:24 +02:00
Stian Thorgersen 1916fded95 Fix location of buttons on password-policy form 2015-07-22 11:50:23 +02:00
Stian Thorgersen 8cc8f4c49c Fix tests 2015-07-22 11:50:23 +02:00
Stian Thorgersen 1a3bf16729 KEYCLOAK-1434 Change edit action to buttons to make sure styles are correct 2015-07-22 11:50:23 +02:00
Stian Thorgersen 5304b533fc KEYCLOAK-1434 Add edit action to tables 2015-07-22 11:50:23 +02:00
Stian Thorgersen 0156d47d97 KEYCLOAK-1535 Set tooltip-placement correctly 2015-07-22 11:50:23 +02:00
Stian Thorgersen b04ac6cdba KEYCLOAK-1427 Add remove user button to all user tabs 2015-07-22 11:50:23 +02:00
Stian Thorgersen f43e4200da Removed title on password policy page 2015-07-22 11:50:23 +02:00
Stian Thorgersen 2ba7aef2b1 KEYCLOAK-1427 Add remove user federation button to all user federation tabs 2015-07-22 11:50:23 +02:00
Stian Thorgersen 7a1932cab4 KEYCLOAK-1427 Add remove identity provider button to all identity provider tabs 2015-07-22 11:50:23 +02:00
Stian Thorgersen 59f189229c KEYCLOAK-1427 Add remove client button to all client tabs 2015-07-22 11:50:23 +02:00
Stian Thorgersen 29af7a1d29 KEYCLOAK-1427 Added delete realm button to all realm settings tabs 2015-07-22 11:49:39 +02:00
Marek Posolda 3d9ddc53e6 Merge pull request #1477 from mposolda/service-acc
KEYCLOAK-401 Service accounts initial impl. Client credentials grant support. Demo
2015-07-22 11:37:55 +02:00
mposolda 1a34feda65 KEYCLOAK-401 Added service account demo 2015-07-22 11:07:57 +02:00
mposolda d8c1081578 KEYCLOAK-401 KEYCLOAK-941 Service accounts initial impl. Client credentials grant support 2015-07-22 10:55:50 +02:00
Bill Burke 1085a1da62 Merge pull request #1476 from patriot1burke/master
non-browser flow
2015-07-21 23:15:51 -04:00
Bill Burke 576db8e0e1 Merge remote-tracking branch 'upstream/master' 2015-07-21 20:56:22 -04:00
Bill Burke fe9dc4a28d non-browser flow 2015-07-21 20:56:05 -04:00
Stian Thorgersen d2aaae2340 Merge pull request #1475 from mstruk/KEYCLOAK-1496
KEYCLOAK-1496 Effective roles select element is not updated when addi…
2015-07-21 19:03:44 +02:00
Vlastimil Elias 84e40cbc2c KEYCLOAK-1542 - Server Info page extended by info about DB and MongoDB.
Functional test for /serverinfo REST endpoint added.
2015-07-21 16:09:47 +02:00
Marko Strukelj 42672a9ae2 KEYCLOAK-1496 Effective roles select element is not updated when adding composite client role 2015-07-21 15:59:21 +02:00
Stian Thorgersen 2dd2173553 Merge pull request #1473 from mstruk/KEYCLOAK-1560
KEYCLOAK-1560 IDENTITY_PROVIDER_LOGIN event is send twice
2015-07-21 10:28:32 +02:00
Marko Strukelj fcd5400cda KEYCLOAK-1560 IDENTITY_PROVIDER_LOGIN event is send twice 2015-07-21 09:49:37 +02:00
Marek Posolda 23b69bb7a7 Merge pull request #1467 from mposolda/master
LDAP and case-sensitivity fixes
2015-07-21 08:58:07 +02:00
mposolda c31b0945d8 Kerberos example README 2015-07-21 08:26:16 +02:00
mposolda c71a4ac4e8 KEYCLOAK-1545 KEYCLOAK-1551 Ensure that username and email are always saved to DB lowercased 2015-07-21 08:26:16 +02:00
mposolda 38c7ca64cb KEYCLOAK-1571 Error when the value of UUID LDAP attribute is the same of the Username LDAP attribute 2015-07-21 08:26:16 +02:00
Bill Burke bc187fe7b3 Merge pull request #1472 from patriot1burke/master
clean up client sessions
2015-07-20 19:17:31 -04:00
Bill Burke 548c0db0ae Merge remote-tracking branch 'upstream/master' 2015-07-20 12:21:57 -04:00
Bill Burke 3ecd1307b0 clean up client sessions 2015-07-20 12:21:48 -04:00
Vlastimil Elias b51661b790 KEYCLOAK-1542 - added system and memory info into "System Info" page 2015-07-20 17:11:04 +02:00
Stian Thorgersen 02d6e41ce3 Merge pull request #1471 from stianst/master
KEYCLOAK-1576
2015-07-20 15:21:51 +02:00
Stian Thorgersen f884aed25e KEYCLOAK-1576 2015-07-20 13:35:56 +02:00
Stian Thorgersen f2dee1a470 Add support for multi-part to MailServer util 2015-07-20 13:35:33 +02:00
Stian Thorgersen d5d806760f Merge branch 'ssilvert-KEYCLOAK-1414-cli-script-adapters' 2015-07-20 13:11:10 +02:00
Stian Thorgersen efe2a8f789 Merge branch 'KEYCLOAK-1414-cli-script-adapters' of https://github.com/ssilvert/keycloak into ssilvert-KEYCLOAK-1414-cli-script-adapters
Conflicts:
	docbook/reference/en/en-US/modules/jboss-adapter.xml
2015-07-20 13:10:51 +02:00
Stian Thorgersen 69bbed999f Merge pull request #1466 from ssilvert/KEYCLOAK-1415-server-cli-script
KEYCLOAK-1415 server cli script
2015-07-20 13:08:13 +02:00
Stian Thorgersen 5b7d4184f0 Merge pull request #1470 from stianst/master
Enabled testsuite/integration-arquillian on Travis
2015-07-20 11:52:13 +02:00
Stian Thorgersen 422b7ed5ef Enabled testsuite/integration-arquillian on Travis 2015-07-20 10:39:08 +02:00
Stian Thorgersen 66a2b916f2 Fix CRLF with LF 2015-07-20 07:48:02 +02:00
Stian Thorgersen 27ac253098 Merge pull request #1469 from Smartling/KEYCLOAK-1494-Fix
Support loading keycloak.json from the classpath
2015-07-20 06:48:14 +02:00
Scott Rossillo 1839b24b90 Support for loading keycloak.json from the classpath
Spring Boot and non-web based applications don't have a WEB-INF
directory. Support loading Spring Security adapter's keycloak.json
from the class path.
2015-07-19 12:10:33 -04:00
Bill Burke a60ebb6210 Merge pull request #1468 from patriot1burke/master
required action import/export
2015-07-18 09:06:39 -04:00
Bill Burke 4dba4f3c02 fix 2015-07-17 23:07:36 -04:00
Bill Burke 6727af2f35 required action import/export 2015-07-17 22:48:07 -04:00
Stan Silvert 5bcf334bc5 Don't add security-domain to server. 2015-07-17 12:48:32 -04:00
Stan Silvert ef49bd685e KEYCLOAK-1415 Add CLI scripts to WildFly and EAP overlay bundles. 2015-07-17 12:34:20 -04:00
Stan Silvert 092562fb94 KEYCLOAK-1414 Add CLI script for installing JBoss/WildFly/EAP adapters. 2015-07-17 12:23:10 -04:00
Bill Burke 44e2983803 Merge pull request #1462 from patriot1burke/master
migrate RequiredCredentials
2015-07-17 10:28:21 -04:00
Bill Burke 2e848da1a8 resolve conflicts 2015-07-17 09:45:53 -04:00
Stian Thorgersen 22ae4ecb23 Merge pull request #1464 from stianst/master
KEYCLOAK-1385 Introduce end-of-line normalization
2015-07-17 14:04:59 +02:00
Stian Thorgersen 1642ac2394 KEYCLOAK-1385 Introduce end-of-line normalization 2015-07-17 13:46:51 +02:00
Stian Thorgersen be8aa93cca Merge pull request #1463 from mstruk/remove-war
KEYCLOAK-1345 Remove distribution/subsystem-war
2015-07-17 13:26:34 +02:00
Marko Strukelj fc7ba85639 KEYCLOAK-1345 Remove distribution/subsystem-war 2015-07-17 12:41:00 +02:00
Stian Thorgersen 11d1dd6419 KEYCLOAK-1470 2015-07-17 11:46:02 +02:00
Stian Thorgersen e8bd7270e9 KEYCLOAK-1483 2015-07-17 11:22:04 +02:00
Stian Thorgersen 99e2afa81b KEYCLOAK-1498 2015-07-17 10:54:43 +02:00
Stian Thorgersen 676792d07d KEYCLOAK-1475 2015-07-17 10:33:18 +02:00
Stian Thorgersen 0bec2bcd1e KEYCLOAK-1504 2015-07-17 10:21:15 +02:00
Stian Thorgersen 25328048aa KEYCLOAK-1566 2015-07-17 10:09:28 +02:00
Bill Burke e51d2137cf migrate RequiredCredentials 2015-07-16 22:03:12 -04:00
Bill Burke 5695677873 Merge pull request #1458 from patriot1burke/master
idp initiated relaystate query param
2015-07-16 10:34:12 -04:00
Stian Thorgersen 9d86f582ac Merge branch 'girirajsharma-master' 2015-07-16 16:32:25 +02:00
Stian Thorgersen 97b0558f71 Merge branch 'master' of https://github.com/girirajsharma/keycloak into girirajsharma-master
Conflicts:
	forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
	forms/common-themes/src/main/resources/theme/base/admin/resources/partials/role-detail.html
	forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
2015-07-16 16:31:16 +02:00
Bill Burke e825be1c79 nonce in tokens 2015-07-16 10:17:44 -04:00
Stian Thorgersen 88252c3dfc Merge pull request #1457 from mstruk/keycloak-prod
KEYCLOAK-1331 Add product.conf and product module
2015-07-16 16:11:44 +02:00
Stian Thorgersen 02997b593f Merge pull request #1459 from stianst/master
Some fixes to integration-arquillian
2015-07-16 16:10:37 +02:00
giriraj sharma 9de81a0b14 Updated patternfly delete glyphicon css. 2015-07-16 18:28:56 +05:30
giriraj sharma ab13182091 [Keycloak-1427] Updated Delete icons with 'pointer' as hovering cursor. 2015-07-16 18:26:58 +05:30
Stian Thorgersen e75e4580ae Some fixes to integration-arquillian
Conflicts:
	testsuite/integration-arquillian/pom.xml
2015-07-16 14:49:38 +02:00
Bill Burke b7f031d199 Merge remote-tracking branch 'upstream/master' 2015-07-16 08:49:23 -04:00
Bill Burke 808d27be2d idp initiated relaystate query param 2015-07-16 08:48:07 -04:00
Marko Strukelj dcf8751ff7 KEYCLOAK-1331 Add product.conf and product module 2015-07-16 14:16:10 +02:00
Bill Burke ec6e3d80ee Merge pull request #1456 from patriot1burke/master
SAML IDP Initiated Login
2015-07-15 20:35:01 -04:00
Bill Burke 5a9badf683 relay state 2015-07-15 20:22:30 -04:00
Bill Burke dd11dcef93 docs 2015-07-15 20:18:14 -04:00
Bill Burke e9851957f0 Merge remote-tracking branch 'upstream/master' 2015-07-15 20:09:13 -04:00
Bill Burke 57cfbb3770 SAML IDP Initiated login 2015-07-15 20:08:55 -04:00
Bill Burke da2590819d Merge pull request #1455 from patriot1burke/master
export/import flows
2015-07-15 15:08:09 -04:00
Bill Burke d74d93a522 fix problems 2015-07-15 14:54:14 -04:00
Bill Burke b3eadcc990 Merge remote-tracking branch 'upstream/master' 2015-07-15 14:17:54 -04:00
Bill Burke f4d4405c20 export/import flows 2015-07-15 14:17:23 -04:00
Stian Thorgersen 1ebec0e2e5 Merge pull request #1454 from brmeyer/KEYCLOAK-1563
KEYCLOAK-1563 update docs for server/adapter subsystem split & WF 9.0…
2015-07-15 09:37:00 +02:00
Brett Meyer 4433347ff3 KEYCLOAK-1563 update docs for server/adapter subsystem split & WF 9.0.0.Final 2015-07-14 15:55:14 -04:00
Stian Thorgersen 08c539ba51 Merge pull request #1452 from stianst/master
KEYCLOAK-1530 Ability to specify default database schema
2015-07-14 15:21:08 +02:00
Stian Thorgersen 4e538dd138 KEYCLOAK-1530 Ability to specify default database schema 2015-07-14 14:34:04 +02:00
Stian Thorgersen 66373ccab2 Merge pull request #1451 from stianst/master
KEYCLOAK-1426 and KEYCLOAK-1565
2015-07-14 12:12:06 +02:00
Stian Thorgersen 9c82c0700d Merge pull request #1425 from girirajsharma/master
[KEYCLOAK-1430] Buttons in form headers should not be primary buttons
2015-07-14 12:04:45 +02:00
Stian Thorgersen a480e50c16 KEYCLOAK-1426 Updated icons in menu 2015-07-14 11:53:02 +02:00
Stian Thorgersen 4391bf4f70 KEYCLOAK-1565 Updated to PatternFly 1.3.0 2015-07-14 11:52:58 +02:00
Stian Thorgersen 18f46cb527 Merge pull request #1450 from stianst/master
KEYCLOAK-1426
2015-07-14 11:32:19 +02:00
Stian Thorgersen 32b89adc04 Wire up new menu 2015-07-14 11:19:44 +02:00
Stian Thorgersen 5a213fcad9 Merge branch 'KEYCLOAK-1426' of https://github.com/andresgalante/keycloak 2015-07-14 10:09:34 +02:00
Stian Thorgersen d6f1d1bb3c Merge pull request #1449 from gtudan/KEYCLOAK-1564
KEYCLOAK-1564: Add CORS-Headers to token requests by password credentials
2015-07-14 09:10:22 +02:00
Gregor Tudan c8c0d0e804 KEYCLOAK-1564: Add CORS-Headers to token requests by password credentials 2015-07-14 08:22:08 +02:00
Stian Thorgersen aeddf2a860 Merge pull request #1448 from ssilvert/KEYCLOAK-1547-infinispan-session-timeout
Keycloak 1547 infinispan session timeout
2015-07-13 19:13:59 +02:00
giriraj sharma b876a5f7bd [KEYCLOAK-1430] Buttons in form headers should not be primary buttons 2015-07-13 22:41:54 +05:30
Stan Silvert af8949c291 Change to make removeUserSession() a protected method. 2015-07-13 11:21:35 -04:00
Stian Thorgersen e3da38df7c KEYCLOAK-1538 Deploy server feature pack to maven repo 2015-07-13 16:47:58 +02:00
Stian Thorgersen a16079e38d Remove unused feature-packs-builds 2015-07-13 14:08:25 +02:00
Stian Thorgersen e13b0fac63 Merge pull request #1447 from mstruk/KEYCLOAK-1370
KEYCLOAK-1370 Empty module de.idyl.winzipaes in keycloak-server-dist
2015-07-13 13:05:12 +02:00
Marko Strukelj 26190350c4 KEYCLOAK-1370 Empty module de.idyl.winzipaes in keycloak-server-dist 2015-07-13 12:42:06 +02:00
Bill Burke 7598cc62d3 Merge pull request #1446 from patriot1burke/master
test impersonation
2015-07-12 10:40:43 -04:00
Bill Burke b3211fa8c2 tooltip 2015-07-12 10:24:15 -04:00
Bill Burke 8dcc909b6e Merge remote-tracking branch 'upstream/master' 2015-07-12 10:12:31 -04:00
Bill Burke fafaf2c490 test impersonate 2015-07-12 10:12:19 -04:00
Marek Posolda 11e2f19503 Merge pull request #1444 from mposolda/master
Use kc-provider-config in LDAP mappers
2015-07-11 23:29:00 +02:00
mposolda d67bd0eff2 Use kc-provider-config in LDAP mappers 2015-07-11 23:11:01 +02:00
Marek Posolda 43bbea9a7c Merge pull request #1443 from mposolda/master
JSON import issue from older versions. Fix import at startup if master realm not present
2015-07-11 19:21:28 +02:00
mposolda dc366c53a7 KEYCLOAK-1175 Import at startup can fail if master realm not present 2015-07-11 18:56:52 +02:00
mposolda fe1ede26be KEYCLOAK-1558 Can't import the file exported in 1.2.0.Beta1 through admin console 2015-07-11 18:56:52 +02:00
Bill Burke aca799b28c Merge pull request #1442 from patriot1burke/master
impersonation in admin console
2015-07-11 11:36:01 -04:00
Bill Burke 1017a0af1d Merge remote-tracking branch 'upstream/master' 2015-07-11 11:09:48 -04:00
Bill Burke ff25484a34 viewUser fixes 2015-07-11 11:01:26 -04:00
Marek Posolda af922920ab Merge pull request #1441 from mposolda/master
KEYCLOAK-1233 Admin console support for add/remove federated identity
2015-07-11 16:48:29 +02:00
Bill Burke 919a3f1b91 fixes 2015-07-11 10:30:23 -04:00
Bill Burke 1ec8e63e9e impersonation redo in admin console 2015-07-11 10:12:55 -04:00
Bill Burke 96c76a059a Merge remote-tracking branch 'upstream/master' 2015-07-11 10:10:37 -04:00
Bill Burke 608701aea7 impersonation redo in admin console 2015-07-11 10:10:05 -04:00
mposolda 142d44a01f KEYCLOAK-1233 Admin console support for add/remove federated identity 2015-07-11 14:08:49 +02:00
mposolda 83c0f537ce Minor update in docs 2015-07-11 11:29:46 +02:00
Bill Burke aeab25b7fe Merge pull request #1440 from patriot1burke/master
user impersonation
2015-07-10 21:21:39 -04:00
Bill Burke ef255c6a5c fix build errors 2015-07-10 21:02:25 -04:00
Bill Burke 474ebed0ee Merge remote-tracking branch 'upstream/master' 2015-07-10 20:39:15 -04:00
Bill Burke 6e55604dc3 impersonation 2015-07-10 20:38:43 -04:00
Marek Posolda 2dfdfefb1a Merge pull request #1438 from mposolda/master
KEYCLOAK-1531 LDAP example and update Kerberos example to use example…
2015-07-09 22:45:56 +02:00
Stan Silvert 48f78cf80a KEYCLOAK-1547 Need to use overridable removeUserSession() method. 2015-07-09 14:46:54 -04:00
mposolda 69cc28dd4b KEYCLOAK-1531 LDAP example and update Kerberos example to use example embedded LDAP 2015-07-09 19:55:37 +02:00
Marek Posolda adb8bcf791 Merge pull request #1437 from mposolda/master
KEYCLOAK-1505 Fix JS error in admin console on old browsers
2015-07-09 10:33:27 +02:00
mposolda bd22d30674 KEYCLOAK-1505 Fix JS error in admin console on old browsers 2015-07-09 10:20:55 +02:00
mposolda afbf1cc33f Comment fix 2015-07-09 10:18:52 +02:00
Bill Burke 485df59c5b Merge pull request #1436 from patriot1burke/master
recaptcha docs
2015-07-08 19:47:45 -04:00
Bill Burke 43308b326d Merge pull request #1435 from patriot1burke/master
role selector
2015-07-08 19:47:22 -04:00
Bill Burke 6c9708c700 recaptcha docs 2015-07-08 19:47:15 -04:00
Bill Burke 4a962aa75a Merge remote-tracking branch 'upstream/master' 2015-07-08 19:18:23 -04:00
Bill Burke e786158071 role selector 2015-07-08 19:18:11 -04:00
Marek Posolda ea5a767948 Merge pull request #1434 from mposolda/master
KEYCLOAK-1533 KEYCLOAK-1534
2015-07-08 21:19:54 +02:00
mposolda bde65a6c57 KEYCLOAK-1533 Handle importing LDAP user with duplicate email during authentication 2015-07-08 20:41:32 +02:00
mposolda 94c0a436b5 KEYCLOAK-1534 handle account management update email or username to the already existing value 2015-07-08 15:27:05 +02:00
mposolda bddf1adeba Improve description for object classes 2015-07-08 12:27:15 +02:00
Bill Burke 9406a577c2 Merge pull request #1431 from mstruk/wf9-version
KEYCLOAK-1512 Upgrade to WildFly 9.0.0.Final
2015-07-07 11:34:34 -04:00
Bill Burke 40344726e1 Merge pull request #1432 from mstruk/ispn-activator
KEYCLOAK-1529 Drop InfinispanCacheActivator and make subsystem do it
2015-07-07 11:34:15 -04:00
Bill Burke 08ffea23b7 Merge pull request #1433 from patriot1burke/master
config directive
2015-07-07 11:32:45 -04:00
Bill Burke f1ddaa8051 config directive 2015-07-07 11:31:28 -04:00
Marko Strukelj 7ae62c1060 KEYCLOAK-1529 Drop InfinispanCacheActivator and make subsystem do it 2015-07-07 16:40:03 +02:00
Marko Strukelj 7c51046d8a KEYCLOAK-1512 Upgrade to WildFly 9.0.0.Final 2015-07-07 14:13:13 +02:00
Bill Burke df6d75e2c5 Merge pull request #1427 from grange74/KEYCLOAK-1486
KEYCLOAK-1486 fixed User link in Clients' Active Sessions page
2015-07-06 17:57:53 -04:00
Bill Burke 3c88d9666d Merge pull request #1430 from patriot1burke/master
auth config ui
2015-07-06 17:56:16 -04:00
Bill Burke ee74025f38 Merge pull request #1429 from mikeazo/patch-1
fixed typo in closing th tag
2015-07-06 17:55:51 -04:00
Bill Burke d49c7d47a2 Merge remote-tracking branch 'upstream/master' 2015-07-06 17:44:52 -04:00
Bill Burke 8f18e9bbbc auth config ui 2015-07-06 17:44:39 -04:00
mikeazo be96a3785b fixed typo in closing th tag 2015-07-06 13:41:10 -04:00
Marek Posolda f3aeb5ec9f Merge pull request #1428 from mposolda/master
Embedded LDAP for examples. LDAP sync bugfixing
2015-07-04 22:46:54 +02:00
mposolda 71ea61e7a6 KEYCLOAK-1532 LDAP sync fixes and other bugfixing 2015-07-04 22:25:47 +02:00
mposolda c6c73e5e59 KEYCLOAK-1531 Added embedded LDAP for examples. Added module keycloak-util-embedded-ldap with common code for testsuite and examples 2015-07-04 21:06:24 +02:00
nick.grange ae242f8480 KEYCLOAK-1486 fixed User link in Clients' Active Sessions page
Changed User link in Clients' Active Sessions page to use userId
instead of username to show User details instead of Resource not found.
Renamed UserSessionRepresentation’s user attribute to username as it
was ambiguous and only used in 1 place (i.e. client-sessions.html).
2015-07-04 22:38:34 +10:00
Stian Thorgersen 351a17f68d Merge pull request #1426 from stianst/master
KEYCLOAK-1530
2015-07-03 15:18:52 +02:00
Stian Thorgersen 1c4f7cb339 KEYCLOAK-1530
Ability to specify default database schema
2015-07-03 14:56:33 +02:00
Andres Galante 2713af189f fixes realm selector when there are too many realms on the list 2015-07-02 16:55:42 -03:00
Andres Galante be62d1a915 styles new menu 2015-07-02 16:45:48 -03:00
Bill Burke 68d71a6767 Merge pull request #1423 from patriot1burke/master
display flows
2015-07-02 11:44:51 -04:00
Bill Burke 46c88ad1bf Merge remote-tracking branch 'upstream/master' 2015-07-02 11:26:28 -04:00
Bill Burke 917151123f display flows 2015-07-02 11:25:51 -04:00
Stian Thorgersen b9e9d6a787 Merge branch 'velias-KEYCLOAK-1421' 2015-07-02 14:16:42 +02:00
Stian Thorgersen db20077c37 Merge branch 'KEYCLOAK-1421' of https://github.com/velias/keycloak into velias-KEYCLOAK-1421
Conflicts:
	testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
2015-07-02 14:15:54 +02:00
Stian Thorgersen ec3d027f90 Merge pull request #1421 from stianst/master
KEYCLOAK-1507
2015-07-02 14:10:57 +02:00
Stian Thorgersen 50678085c9 Merge pull request #1403 from mstruk/KEYCLOAK-1484
KEYCLOAK-1484 Keycloak from server-dist and demo-dist doesn't start
2015-07-02 14:10:19 +02:00
Stian Thorgersen 5af4785ba1 KEYCLOAK-1507
Tomcat Adapter throws NPE under certain conditions
2015-07-02 09:08:40 +02:00
Stian Thorgersen f59d42d5d3 Merge pull request #1420 from girirajsharma/master
[KEYCLOAK-1435] Base URL on client table should open in new tab
2015-07-02 09:00:24 +02:00
giriraj sharma fc7ae1ee96 [KEYCLOAK-1435] Base URL on client table should open in new tab 2015-07-02 06:40:22 +05:30
Bill Burke 2699bb9a44 Merge pull request #1368 from girirajsharma/master
[KEYCLOAK-1427] Move delete buttons
2015-07-01 15:32:11 -04:00
Bill Burke 212f5b4082 Merge pull request #1410 from dbarentine/master
Spec compliance, bug fixes
2015-07-01 15:27:47 -04:00
Bill Burke 9591f48077 Merge pull request #1419 from patriot1burke/master
recapthca
2015-07-01 15:26:20 -04:00
Bill Burke 0a8494a0f2 Merge remote-tracking branch 'upstream/master' 2015-07-01 15:10:14 -04:00
Bill Burke 39f2a4ab71 repatcha 2015-07-01 15:10:04 -04:00
Bill Burke 39aa09ca36 form action refactor 2015-07-01 14:07:02 -04:00
Bill Burke 45c5a2ae78 Merge pull request #1418 from patriot1burke/master
registration flow
2015-06-30 13:19:18 -04:00
Bill Burke a1c612f833 remove old code 2015-06-30 12:47:34 -04:00
Bill Burke bc39bb32d4 Merge remote-tracking branch 'upstream/master' 2015-06-30 12:46:29 -04:00
Bill Burke 884308ecb5 reg flow 2015-06-30 12:46:10 -04:00
Marek Posolda 2d7f232c3d Merge pull request #1417 from mposolda/master
KEYCLOAK-1487 multivalued attributes support
2015-06-30 14:56:45 +02:00
Bill Burke afa65d9ead registration flow 2015-06-29 22:12:06 -04:00
mposolda 605c88a029 KEYCLOAK-1487 Multivalued support for UserAttribute protocol mapper. End-to-end LDAP example test including application 2015-06-29 21:45:47 +02:00
mposolda 09994d1730 KEYCLOAK-1487 Support for multiple values of one UserModel attribute. LDAP multivalued attribute support 2015-06-29 14:56:40 +02:00
girirajSharma bb9c99ebf6 [KEYCLOAK-1427] Move delete buttons 2015-06-28 14:31:40 +05:30
Marek Posolda d4819886a6 Merge pull request #1416 from mposolda/master
LDAP mappers fixes
2015-06-26 14:43:10 +02:00
mposolda 23445123a2 KEYCLOAK-1490 Possibility to always read user attribute values from LDAP 2015-06-26 14:06:31 +02:00
mposolda 773bb43b41 KEYCLOAK-1487 Fix LDAP case-sensitivity. Show warning in case of duplicate username 2015-06-25 19:27:22 +02:00
mposolda 493dddde02 Mongo fix 2015-06-25 19:24:50 +02:00
Bill Burke 2e5704c6b4 Merge pull request #1413 from patriot1burke/master
refactor auth spi into additional form spi
2015-06-24 21:29:26 -04:00
Bill Burke be6c843bba Merge remote-tracking branch 'upstream/master' 2015-06-24 21:07:51 -04:00
Bill Burke 064d677fdc form auth 2015-06-24 21:07:38 -04:00
Marek Posolda 084ab782da Merge pull request #1412 from mposolda/master
KEYCLOAK-1501 Fix osgi packaging. Improve fuse demo example
2015-06-24 16:43:09 +02:00
mposolda 1a10a1ef75 KEYCLOAK-1501 Fix osgi packaging and fuse demo. Improve example 2015-06-24 16:26:05 +02:00
mposolda acc77e4e6c Remove ClientConnectionFilter filter mappings from web.xml in distribution 2015-06-24 11:10:22 +02:00
Stian Thorgersen 2c0ff9be60 Merge pull request #1407 from ssilvert/enforcer
Add enforcer plugin to enforce maven version.
2015-06-23 18:42:18 +02:00
Dane Barentine b9f5946cf0 [KEYCLOAK-1493] Added explicit dependency on keycloak-saml-core 2015-06-22 14:50:04 -07:00
Dane Barentine c403968fa8 [KEYCLOAK-1493] Added explicit dependency on keycloak-saml-core 2015-06-22 11:07:29 -07:00
Dane Barentine 14baef425e [KEYCLOAK-1493] Added a couple of WSTrust constants 2015-06-22 11:07:23 -07:00
Dane Barentine 8da532da93 [KEYCLOAK-1492] Fix mapClaim when nested attribute already exists 2015-06-22 11:00:40 -07:00
Dane Barentine facf701897 [KEYCLOAK-1491] SAML Spec compliance. NameIDFormat is optional, AttributeStatement must contain one or more attribute or encryptedattribute statements 2015-06-22 10:58:13 -07:00
Vlastimil Elias f9fe9ea5d6 rebased to latest keycloak/master 2015-06-22 10:48:54 +02:00
Vlastimil Elias f5a80c3007 KEYCLOAK-1421 patched source formatting 2015-06-22 10:37:27 +02:00
Vlastimil Elias 7cff37e707 KEYCLOAK-1421 Added timestamp of keycloak user creation into user models
and persistence
2015-06-22 10:37:26 +02:00
Vlastimil Elias 4e8c6b3fc4 KEYCLOAK-1421 patched source formatting 2015-06-22 10:33:57 +02:00
Vlastimil Elias 82282c1c75 KEYCLOAK-1421 - user created date added to the admin console 2015-06-22 10:32:39 +02:00
Vlastimil Elias d7379f29da KEYCLOAK-1421 Added timestamp of keycloak user creation into user models
and persistence
2015-06-22 10:32:37 +02:00
Bill Burke 000159226d refactor 2015-06-21 12:09:22 -04:00
Bill Burke 4ebdb8d24b refactor 2015-06-21 11:52:30 -04:00
Bill Burke fc2df08e15 ClientSessionModel.getNotes() 2015-06-20 09:07:54 -04:00
Bill Burke aaa83dc19a Merge pull request #1408 from patriot1burke/master
make authenticator config optional
2015-06-19 21:17:23 -04:00
Bill Burke adff0d5da0 make authenticator config optional 2015-06-19 21:16:45 -04:00
Stan Silvert 285d6931bc Add enforcer plugin to enforce maven version. 2015-06-19 18:14:25 -04:00
Bill Burke 114b687c5c Merge pull request #1406 from patriot1burke/master
refactor registration and console pages
2015-06-19 18:06:12 -04:00
Bill Burke a26ade3988 refactor registration and console pages 2015-06-19 17:51:59 -04:00
Bill Burke 94e2983310 Merge pull request #1405 from patriot1burke/master
cleanup client sessions
2015-06-19 16:23:31 -04:00
Bill Burke fb2f5f6756 Merge remote-tracking branch 'upstream/master' 2015-06-19 14:44:00 -04:00
Bill Burke d7b818cba2 cleanup client sessions 2015-06-19 14:43:40 -04:00
Bill Burke 8d885d14ed Merge pull request #1404 from patriot1burke/master
handle page refresh better
2015-06-18 20:16:29 -04:00
Bill Burke 39e0023a5f Merge remote-tracking branch 'upstream/master' 2015-06-18 20:01:34 -04:00
Bill Burke 84faac0cd6 handle page refresh better 2015-06-18 20:01:12 -04:00
Bill Burke 1bce639d35 refactor flow first phase 2015-06-18 16:48:28 -04:00
Marko Strukelj f2061610b9 KEYCLOAK-1484 Keycloak from server-dist and demo-dist doesn't start 2015-06-18 20:36:32 +02:00
Bill Burke 8716f9b41a Merge pull request #1401 from mstruk/KEYCLOAK-1476
KEYCLOAK-1476 NotSerializableException: org.keycloak.models.cache.entities.CachedClientRole
2015-06-17 17:36:02 -04:00
Bill Burke a24c817bb7 Merge pull request #1402 from patriot1burke/master
cleanup authenticators
2015-06-17 17:34:57 -04:00
Bill Burke 47997e0056 Merge remote-tracking branch 'upstream/master' 2015-06-17 17:15:16 -04:00
Bill Burke 70fd7bea99 cleanup authenticators 2015-06-17 17:15:01 -04:00
Marko Strukelj 52565375b3 KEYCLOAK-1476 NotSerializableException: org.keycloak.models.cache.entities.CachedClientRole
- Remove noop code (Enum serialization 101 - http://docs.oracle.com/javase/6/docs/platform/serialization/spec/serial-arch.html#6469)
2015-06-17 22:05:32 +02:00
Bill Burke 2c327d9eee Merge pull request #1389 from patriot1burke/master
access code checks
2015-06-17 15:59:23 -04:00
Bill Burke f5b4a0788e timeout message 2015-06-17 10:16:21 -04:00
Bill Burke 1aa338fe3e Merge remote-tracking branch 'upstream/master' 2015-06-17 09:43:14 -04:00
Bill Burke 5615d8d870 required actions edit 2015-06-17 09:42:58 -04:00
Bill Burke dddc5181e7 required actions storage/display 2015-06-16 22:48:39 -04:00
Bill Burke 3b78fa2d5d required actions model 2015-06-16 18:17:38 -04:00
Bill Burke 2a64f5e3ce Merge remote-tracking branch 'upstream/master' 2015-06-16 09:30:07 -04:00
Bill Burke 8f5b53e3ce clientsession timeout checks 2015-06-16 09:29:36 -04:00
Bill Burke cd84e78b27 access code checks 2015-06-16 07:59:53 -04:00
1883 changed files with 219662 additions and 139390 deletions

No files matched your search

+20
View File
@@ -0,0 +1,20 @@
* text=auto
*.html text eol=lf
*.java text eol=lf
*.js text eol=lf
*.json text eol=lf
*.jsp text eol=lf
*.md text eol=lf
*.properties text eol=lf
*.svg text auto
*.xml text eol=lf
*.xsl text eol=lf
*.png binary
*.jpg binary
*.gif binary
*.ttf binary
*.eot binary
*.otf binary
*.woff binary
+9
View File
@@ -1,5 +1,8 @@
language: java
jdk:
- oraclejdk8
cache:
directories:
- $HOME/.m2
@@ -7,4 +10,10 @@ cache:
before_cache:
- rm -rf $HOME/.m2/repository/org/keycloak
install: mvn install -Pdistribution -DskipTests=true -B -V
script:
- mvn test -B
- mvn -file testsuite/integration-arquillian test -B
sudo: false
+50 -50
View File
@@ -1,50 +1,50 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-core</artifactId>
<name>Keycloak Broker Core</name>
<description/>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-events-api</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
<?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</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-core</artifactId>
<name>Keycloak Broker Core</name>
<description/>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-events-api</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
@@ -58,6 +58,11 @@ public abstract class AbstractIdentityProvider<C extends IdentityProviderModel>
return null;
}
@Override
public Response performLogin(AuthenticationRequest request) {
return null;
}
@Override
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
return null;
@@ -1,38 +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) {
}
}
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) {
}
}
@@ -1,110 +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.";
}
}
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. Click 'Select Role' button to browse roles, or just type it in the textbox. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
property.setType(ProviderConfigProperty.ROLE_TYPE);
configProperties.add(property);
}
public static String[] parseRole(String role) {
int scopeIndex = role.lastIndexOf('.');
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.";
}
}
@@ -1,56 +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);
}
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);
}
@@ -1,2 +1,2 @@
org.keycloak.broker.provider.IdentityProviderSpi
org.keycloak.broker.provider.IdentityProviderSpi
org.keycloak.broker.provider.IdentityProviderMapperSpi
+58 -58
View File
@@ -1,58 +1,58 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-oidc</artifactId>
<name>Keycloak Broker - OpenID Connect Identity Provider</name>
<description/>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-broker-core</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-events-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
<?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</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-oidc</artifactId>
<name>Keycloak Broker - OpenID Connect Identity Provider</name>
<description/>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-broker-core</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-events-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -1,114 +1,114 @@
package org.keycloak.broker.oidc;
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.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 javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.security.PublicKey;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
public static final String VALIDATED_ACCESS_TOKEN = "VALIDATED_ACCESS_TOKEN";
public KeycloakOIDCIdentityProvider(OIDCIdentityProviderConfig config) {
super(config);
}
@Override
public Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event) {
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);
}
@POST
@Path(AdapterConstants.K_LOGOUT)
public Response backchannelLogout(String input) {
JWSInput token = new JWSInput(input);
PublicKey key = getExternalIdpKey();
if (key != null) {
if (!verify(token, key)) {
logger.warn("Failed to verify logout request");
return Response.status(400).build();
}
}
LogoutAction action = null;
try {
action = JsonSerialization.readValue(token.getContent(), LogoutAction.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
if (!validateAction(action)) return Response.status(400).build();
if (action.getKeycloakSessionIds() != null) {
for (String sessionId : action.getKeycloakSessionIds()) {
String brokerSessionId = getConfig().getAlias() + "." + sessionId;
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId);
if (userSession != null
&& userSession.getState() != UserSessionModel.State.LOGGING_OUT
&& userSession.getState() != UserSessionModel.State.LOGGED_OUT
) {
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, false);
}
}
}
return Response.ok().build();
}
protected boolean validateAction(AdminAction action) {
if (!action.validate()) {
logger.warn("admin request failed, not validated" + action.getAction());
return false;
}
if (action.isExpired()) {
logger.warn("admin request failed, expired token");
return false;
}
if (!getConfig().getClientId().equals(action.getResource())) {
logger.warn("Resource name does not match");
return false;
}
return true;
}
@Override
public SimpleHttp generateTokenRequest(String authorizationCode) {
return super.generateTokenRequest(authorizationCode)
.param(AdapterConstants.CLIENT_SESSION_STATE, "n/a"); // hack to get backchannel logout to work
}
}
}
package org.keycloak.broker.oidc;
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.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 javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.security.PublicKey;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
public static final String VALIDATED_ACCESS_TOKEN = "VALIDATED_ACCESS_TOKEN";
public KeycloakOIDCIdentityProvider(OIDCIdentityProviderConfig config) {
super(config);
}
@Override
public Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event) {
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);
}
@POST
@Path(AdapterConstants.K_LOGOUT)
public Response backchannelLogout(String input) {
JWSInput token = new JWSInput(input);
PublicKey key = getExternalIdpKey();
if (key != null) {
if (!verify(token, key)) {
logger.warn("Failed to verify logout request");
return Response.status(400).build();
}
}
LogoutAction action = null;
try {
action = JsonSerialization.readValue(token.getContent(), LogoutAction.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
if (!validateAction(action)) return Response.status(400).build();
if (action.getKeycloakSessionIds() != null) {
for (String sessionId : action.getKeycloakSessionIds()) {
String brokerSessionId = getConfig().getAlias() + "." + sessionId;
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId);
if (userSession != null
&& userSession.getState() != UserSessionModel.State.LOGGING_OUT
&& userSession.getState() != UserSessionModel.State.LOGGED_OUT
) {
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, false);
}
}
}
return Response.ok().build();
}
protected boolean validateAction(AdminAction action) {
if (!action.validate()) {
logger.warn("admin request failed, not validated" + action.getAction());
return false;
}
if (action.isExpired()) {
logger.warn("admin request failed, expired token");
return false;
}
if (!getConfig().getClientId().equals(action.getResource())) {
logger.warn("Resource name does not match");
return false;
}
return true;
}
@Override
public SimpleHttp generateTokenRequest(String authorizationCode) {
return super.generateTokenRequest(authorizationCode)
.param(AdapterConstants.CLIENT_SESSION_STATE, "n/a"); // hack to get backchannel logout to work
}
}
}
@@ -1,97 +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;
}
}
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;
}
}
@@ -1,206 +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;
}
}
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.setSingleAttribute(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;
}
}
@@ -1,105 +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.";
}
}
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. Click 'Select Role' button to browse roles, or just type it in the textbox. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
property.setType(ProviderConfigProperty.ROLE_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.";
}
}
@@ -1,118 +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.";
}
}
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. Click 'Select Role' button to browse roles, or just type it in the textbox. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
property.setType(ProviderConfigProperty.ROLE_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.";
}
}
@@ -1,100 +1,98 @@
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.";
}
}
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.provider.ProviderConfigProperty;
import java.util.ArrayList;
import java.util.Collection;
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.setSingleAttribute(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.getFirstAttribute(attribute);
if (value != null && !value.equals(current)) {
user.setSingleAttribute(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.";
}
}
@@ -1,109 +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.";
}
}
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.";
}
}
@@ -1,32 +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());
}
}
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());
}
}
@@ -1,2 +1,2 @@
org.keycloak.broker.oidc.OIDCIdentityProviderFactory
org.keycloak.broker.oidc.OIDCIdentityProviderFactory
org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory
@@ -1,4 +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.ClaimToRoleMapper
org.keycloak.broker.oidc.mappers.ExternalKeycloakRoleToRoleMapper
org.keycloak.broker.oidc.mappers.UserAttributeMapper
org.keycloak.broker.oidc.mappers.UsernameTemplateMapper
+23 -23
View File
@@ -1,23 +1,23 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-parent</artifactId>
<name>Keycloak Broker Parent</name>
<description/>
<packaging>pom</packaging>
<modules>
<module>core</module>
<module>oidc</module>
<module>saml</module>
</modules>
</project>
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-parent</artifactId>
<name>Keycloak Broker Parent</name>
<description/>
<packaging>pom</packaging>
<modules>
<module>core</module>
<module>oidc</module>
<module>saml</module>
</modules>
</project>
+50 -50
View File
@@ -1,50 +1,50 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-saml</artifactId>
<name>Keycloak Broker - SAML Identity Provider</name>
<description/>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-broker-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-protocol</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-events-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
<?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</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-broker-saml</artifactId>
<name>Keycloak Broker - SAML Identity Provider</name>
<description/>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-broker-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-protocol</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-events-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -1,491 +1,492 @@
package org.keycloak.broker.saml;
import org.jboss.logging.Logger;
import org.keycloak.ClientConnection;
import org.keycloak.VerificationException;
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;
import org.keycloak.events.EventType;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
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.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
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 javax.xml.namespace.QName;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SAMLEndpoint {
protected static final Logger logger = Logger.getLogger(SAMLEndpoint.class);
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;
@Context
private KeycloakSession session;
@Context
private ClientConnection clientConnection;
@Context
private HttpHeaders headers;
public SAMLEndpoint(RealmModel realm, SAMLIdentityProvider provider, SAMLIdentityProviderConfig config, IdentityProvider.AuthenticationCallback callback) {
this.realm = realm;
this.config = config;
this.callback = callback;
this.provider = provider;
}
@GET
public Response redirectBinding(@QueryParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,
@QueryParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,
@QueryParam(GeneralConstants.RELAY_STATE) String relayState) {
return new RedirectBinding().execute(samlRequest, samlResponse, relayState);
}
/**
*/
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response postBinding(@FormParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,
@FormParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,
@FormParam(GeneralConstants.RELAY_STATE) String relayState) {
return new PostBinding().execute(samlRequest, samlResponse, relayState);
}
protected abstract class Binding {
private boolean checkSsl() {
if (uriInfo.getBaseUri().getScheme().equals("https")) {
return true;
} else {
return !realm.getSslRequired().isRequired(clientConnection);
}
}
protected Response basicChecks(String samlRequest, String samlResponse) {
if (!checkSsl()) {
event.event(EventType.LOGIN);
event.error(Errors.SSL_REQUIRED);
return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
}
if (!realm.isEnabled()) {
event.event(EventType.LOGIN_ERROR);
event.error(Errors.REALM_DISABLED);
return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
}
if (samlRequest == null && samlResponse == null) {
event.event(EventType.LOGIN);
event.error(Errors.INVALID_REQUEST);
return ErrorPage.error(session, Messages.INVALID_REQUEST);
}
return null;
}
protected abstract String getBindingType();
protected abstract void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException;
protected abstract SAMLDocumentHolder extractRequestDocument(String samlRequest);
protected abstract SAMLDocumentHolder extractResponseDocument(String response);
protected PublicKey getIDPKey() {
X509Certificate certificate = null;
try {
certificate = XMLSignatureUtil.getX509CertificateFromKeyInfoString(config.getSigningCertificate().replaceAll("\\s", ""));
} catch (ProcessingException e) {
throw new RuntimeException(e);
}
return certificate.getPublicKey();
}
public Response execute(String samlRequest, String samlResponse, String relayState) {
event = new EventBuilder(realm, session, clientConnection);
Response response = basicChecks(samlRequest, samlResponse);
if (response != null) return response;
if (samlRequest != null) return handleSamlRequest(samlRequest, relayState);
else return handleSamlResponse(samlResponse, relayState);
}
protected Response handleSamlRequest(String samlRequest, String relayState) {
SAMLDocumentHolder holder = extractRequestDocument(samlRequest);
RequestAbstractType requestAbstractType = (RequestAbstractType) holder.getSamlObject();
// validate destination
if (requestAbstractType.getDestination() != null && !uriInfo.getAbsolutePath().equals(requestAbstractType.getDestination())) {
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
event.detail(Details.REASON, "invalid_destination");
event.error(Errors.INVALID_SAML_RESPONSE);
return ErrorPage.error(session, Messages.INVALID_REQUEST);
}
if (config.isValidateSignature()) {
try {
verifySignature(holder);
} catch (VerificationException e) {
logger.error("validation failed", e);
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
event.error(Errors.INVALID_SIGNATURE);
return ErrorPage.error(session, Messages.INVALID_REQUESTER);
}
}
if (requestAbstractType instanceof LogoutRequestType) {
logger.debug("** logout request");
event.event(EventType.LOGOUT);
LogoutRequestType logout = (LogoutRequestType) requestAbstractType;
return logoutRequest(logout, relayState);
} else {
event.event(EventType.LOGIN);
event.error(Errors.INVALID_TOKEN);
return ErrorPage.error(session, Messages.INVALID_REQUEST);
}
}
protected Response logoutRequest(LogoutRequestType request, String relayState) {
String brokerUserId = config.getAlias() + "." + request.getNameID().getValue();
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, false);
} catch (Exception e) {
logger.warn("failed to do backchannel logout for userSession", e);
}
}
} else {
for (String sessionIndex : request.getSessionIndex()) {
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, false);
} catch (Exception e) {
logger.warn("failed to do backchannel logout for userSession", e);
}
}
}
}
String issuerURL = getEntityId(uriInfo, realm);
SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
builder.logoutRequestID(request.getID());
builder.destination(config.getSingleLogoutServiceUrl());
builder.issuer(issuerURL);
builder.relayState(relayState);
if (config.isWantAuthnRequestsSigned()) {
builder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signDocument();
}
try {
if (config.isPostBindingResponse()) {
return builder.postBinding().response();
} else {
return builder.redirectBinding().response();
}
} catch (ConfigurationException e) {
throw new RuntimeException(e);
} catch (ProcessingException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private String getEntityId(UriInfo uriInfo, RealmModel realm) {
return UriBuilder.fromUri(uriInfo.getBaseUri()).path("realms").path(realm.getName()).build().toString();
}
protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder holder, ResponseType responseType, String relayState) {
try {
AssertionType assertion = getAssertion(responseType);
SubjectType subject = assertion.getSubject();
SubjectType.STSubType subType = subject.getSubType();
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
//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());
if (subjectNameID.getFormat().toString().equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())) {
identity.setEmail(subjectNameID.getValue());
}
if (config.isStoreToken()) {
identity.setToken(samlResponse);
}
AuthnStatementType authn = null;
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());
}
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();
if (assertions.isEmpty()) {
throw new IdentityBrokerException("No assertion from response.");
}
ResponseType.RTChoiceType rtChoiceType = assertions.get(0);
EncryptedAssertionType encryptedAssertion = rtChoiceType.getEncryptedAssertion();
if (encryptedAssertion != null) {
decryptAssertion(responseType, realm.getPrivateKey());
}
return responseType.getAssertions().get(0).getAssertion();
}
public Response handleSamlResponse(String samlResponse, String relayState) {
SAMLDocumentHolder holder = extractResponseDocument(samlResponse);
StatusResponseType statusResponse = (StatusResponseType)holder.getSamlObject();
// validate destination
if (statusResponse.getDestination() != null && !uriInfo.getAbsolutePath().toString().equals(statusResponse.getDestination())) {
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
event.detail(Details.REASON, "invalid_destination");
event.error(Errors.INVALID_SAML_RESPONSE);
return ErrorPage.error(session, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
}
if (config.isValidateSignature()) {
try {
verifySignature(holder);
} catch (VerificationException e) {
logger.error("validation failed", e);
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
event.error(Errors.INVALID_SIGNATURE);
return ErrorPage.error(session, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
}
}
if (statusResponse instanceof ResponseType) {
return handleLoginResponse(samlResponse, holder, (ResponseType)statusResponse, relayState);
} else {
// todo need to check that it is actually a LogoutResponse
return handleLogoutResponse(holder, statusResponse, relayState);
}
//throw new RuntimeException("Unknown response type");
}
protected Response handleLogoutResponse(SAMLDocumentHolder holder, StatusResponseType responseType, String relayState) {
if (relayState == null) {
logger.error("no valid user session");
event.event(EventType.LOGOUT);
event.error(Errors.USER_SESSION_NOT_FOUND);
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 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 ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE);
}
return AuthenticationManager.finishBrowserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
}
protected ResponseType decryptAssertion(ResponseType responseType, PrivateKey privateKey) throws ProcessingException {
SAML2Response saml2Response = new SAML2Response();
try {
Document doc = saml2Response.convert(responseType);
Element enc = DocumentUtil.getElement(doc, new QName(JBossSAMLConstants.ENCRYPTED_ASSERTION.get()));
if (enc == null) {
throw new IdentityBrokerException("No encrypted assertion found.");
}
String oldID = enc.getAttribute(JBossSAMLConstants.ID.get());
Document newDoc = DocumentUtil.createDocument();
Node importedNode = newDoc.importNode(enc, true);
newDoc.appendChild(importedNode);
Element decryptedDocumentElement = XMLEncryptionUtil.decryptElementInDocument(newDoc, privateKey);
SAMLParser parser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(decryptedDocumentElement);
AssertionType assertion = (AssertionType) parser.parse(StaxParserUtil.getXMLEventReader(DocumentUtil
.getNodeAsStream(decryptedDocumentElement)));
responseType.replaceAssertion(oldID, new ResponseType.RTChoiceType(assertion));
return responseType;
} catch (Exception e) {
throw new IdentityBrokerException("Could not decrypt assertion.", e);
}
}
}
protected class PostBinding extends Binding {
@Override
protected void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException {
SamlProtocolUtils.verifyDocumentSignature(documentHolder.getSamlDocument(), getIDPKey());
}
@Override
protected SAMLDocumentHolder extractRequestDocument(String samlRequest) {
return SAMLRequestParser.parseRequestPostBinding(samlRequest);
}
@Override
protected SAMLDocumentHolder extractResponseDocument(String response) {
byte[] samlBytes = PostBindingUtil.base64Decode(response);
String xml = new String(samlBytes);
return SAMLRequestParser.parseResponseDocument(samlBytes);
}
@Override
protected String getBindingType() {
return SamlProtocol.SAML_POST_BINDING;
}
}
protected class RedirectBinding extends Binding {
@Override
protected void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException {
PublicKey publicKey = getIDPKey();
SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo);
}
@Override
protected SAMLDocumentHolder extractRequestDocument(String samlRequest) {
return SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
}
@Override
protected SAMLDocumentHolder extractResponseDocument(String response) {
return SAMLRequestParser.parseRequestRedirectBinding(response);
}
@Override
protected String getBindingType() {
return SamlProtocol.SAML_REDIRECT_BINDING;
}
}
}
package org.keycloak.broker.saml;
import org.jboss.logging.Logger;
import org.keycloak.ClientConnection;
import org.keycloak.VerificationException;
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;
import org.keycloak.events.EventType;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
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.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
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 javax.xml.namespace.QName;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SAMLEndpoint {
protected static final Logger logger = Logger.getLogger(SAMLEndpoint.class);
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;
@Context
private KeycloakSession session;
@Context
private ClientConnection clientConnection;
@Context
private HttpHeaders headers;
public SAMLEndpoint(RealmModel realm, SAMLIdentityProvider provider, SAMLIdentityProviderConfig config, IdentityProvider.AuthenticationCallback callback) {
this.realm = realm;
this.config = config;
this.callback = callback;
this.provider = provider;
}
@GET
public Response redirectBinding(@QueryParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,
@QueryParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,
@QueryParam(GeneralConstants.RELAY_STATE) String relayState) {
return new RedirectBinding().execute(samlRequest, samlResponse, relayState);
}
/**
*/
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response postBinding(@FormParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,
@FormParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,
@FormParam(GeneralConstants.RELAY_STATE) String relayState) {
return new PostBinding().execute(samlRequest, samlResponse, relayState);
}
protected abstract class Binding {
private boolean checkSsl() {
if (uriInfo.getBaseUri().getScheme().equals("https")) {
return true;
} else {
return !realm.getSslRequired().isRequired(clientConnection);
}
}
protected Response basicChecks(String samlRequest, String samlResponse) {
if (!checkSsl()) {
event.event(EventType.LOGIN);
event.error(Errors.SSL_REQUIRED);
return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
}
if (!realm.isEnabled()) {
event.event(EventType.LOGIN_ERROR);
event.error(Errors.REALM_DISABLED);
return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
}
if (samlRequest == null && samlResponse == null) {
event.event(EventType.LOGIN);
event.error(Errors.INVALID_REQUEST);
return ErrorPage.error(session, Messages.INVALID_REQUEST);
}
return null;
}
protected abstract String getBindingType();
protected abstract void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException;
protected abstract SAMLDocumentHolder extractRequestDocument(String samlRequest);
protected abstract SAMLDocumentHolder extractResponseDocument(String response);
protected PublicKey getIDPKey() {
X509Certificate certificate = null;
try {
certificate = XMLSignatureUtil.getX509CertificateFromKeyInfoString(config.getSigningCertificate().replaceAll("\\s", ""));
} catch (ProcessingException e) {
throw new RuntimeException(e);
}
return certificate.getPublicKey();
}
public Response execute(String samlRequest, String samlResponse, String relayState) {
event = new EventBuilder(realm, session, clientConnection);
Response response = basicChecks(samlRequest, samlResponse);
if (response != null) return response;
if (samlRequest != null) return handleSamlRequest(samlRequest, relayState);
else return handleSamlResponse(samlResponse, relayState);
}
protected Response handleSamlRequest(String samlRequest, String relayState) {
SAMLDocumentHolder holder = extractRequestDocument(samlRequest);
RequestAbstractType requestAbstractType = (RequestAbstractType) holder.getSamlObject();
// validate destination
if (requestAbstractType.getDestination() != null && !uriInfo.getAbsolutePath().equals(requestAbstractType.getDestination())) {
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
event.detail(Details.REASON, "invalid_destination");
event.error(Errors.INVALID_SAML_RESPONSE);
return ErrorPage.error(session, Messages.INVALID_REQUEST);
}
if (config.isValidateSignature()) {
try {
verifySignature(holder);
} catch (VerificationException e) {
logger.error("validation failed", e);
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
event.error(Errors.INVALID_SIGNATURE);
return ErrorPage.error(session, Messages.INVALID_REQUESTER);
}
}
if (requestAbstractType instanceof LogoutRequestType) {
logger.debug("** logout request");
event.event(EventType.LOGOUT);
LogoutRequestType logout = (LogoutRequestType) requestAbstractType;
return logoutRequest(logout, relayState);
} else {
event.event(EventType.LOGIN);
event.error(Errors.INVALID_TOKEN);
return ErrorPage.error(session, Messages.INVALID_REQUEST);
}
}
protected Response logoutRequest(LogoutRequestType request, String relayState) {
String brokerUserId = config.getAlias() + "." + request.getNameID().getValue();
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, false);
} catch (Exception e) {
logger.warn("failed to do backchannel logout for userSession", e);
}
}
} else {
for (String sessionIndex : request.getSessionIndex()) {
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, false);
} catch (Exception e) {
logger.warn("failed to do backchannel logout for userSession", e);
}
}
}
}
String issuerURL = getEntityId(uriInfo, realm);
SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
builder.logoutRequestID(request.getID());
builder.destination(config.getSingleLogoutServiceUrl());
builder.issuer(issuerURL);
builder.relayState(relayState);
if (config.isWantAuthnRequestsSigned()) {
builder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signDocument();
}
try {
if (config.isPostBindingResponse()) {
return builder.postBinding().response();
} else {
return builder.redirectBinding().response();
}
} catch (ConfigurationException e) {
throw new RuntimeException(e);
} catch (ProcessingException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private String getEntityId(UriInfo uriInfo, RealmModel realm) {
return UriBuilder.fromUri(uriInfo.getBaseUri()).path("realms").path(realm.getName()).build().toString();
}
protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder holder, ResponseType responseType, String relayState) {
try {
AssertionType assertion = getAssertion(responseType);
SubjectType subject = assertion.getSubject();
SubjectType.STSubType subType = subject.getSubType();
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
//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());
//SAML Spec 2.2.2 Format is optional
if (subjectNameID.getFormat() != null && subjectNameID.getFormat().toString().equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())) {
identity.setEmail(subjectNameID.getValue());
}
if (config.isStoreToken()) {
identity.setToken(samlResponse);
}
AuthnStatementType authn = null;
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());
}
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();
if (assertions.isEmpty()) {
throw new IdentityBrokerException("No assertion from response.");
}
ResponseType.RTChoiceType rtChoiceType = assertions.get(0);
EncryptedAssertionType encryptedAssertion = rtChoiceType.getEncryptedAssertion();
if (encryptedAssertion != null) {
decryptAssertion(responseType, realm.getPrivateKey());
}
return responseType.getAssertions().get(0).getAssertion();
}
public Response handleSamlResponse(String samlResponse, String relayState) {
SAMLDocumentHolder holder = extractResponseDocument(samlResponse);
StatusResponseType statusResponse = (StatusResponseType)holder.getSamlObject();
// validate destination
if (statusResponse.getDestination() != null && !uriInfo.getAbsolutePath().toString().equals(statusResponse.getDestination())) {
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
event.detail(Details.REASON, "invalid_destination");
event.error(Errors.INVALID_SAML_RESPONSE);
return ErrorPage.error(session, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
}
if (config.isValidateSignature()) {
try {
verifySignature(holder);
} catch (VerificationException e) {
logger.error("validation failed", e);
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
event.error(Errors.INVALID_SIGNATURE);
return ErrorPage.error(session, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
}
}
if (statusResponse instanceof ResponseType) {
return handleLoginResponse(samlResponse, holder, (ResponseType)statusResponse, relayState);
} else {
// todo need to check that it is actually a LogoutResponse
return handleLogoutResponse(holder, statusResponse, relayState);
}
//throw new RuntimeException("Unknown response type");
}
protected Response handleLogoutResponse(SAMLDocumentHolder holder, StatusResponseType responseType, String relayState) {
if (relayState == null) {
logger.error("no valid user session");
event.event(EventType.LOGOUT);
event.error(Errors.USER_SESSION_NOT_FOUND);
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 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 ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE);
}
return AuthenticationManager.finishBrowserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
}
protected ResponseType decryptAssertion(ResponseType responseType, PrivateKey privateKey) throws ProcessingException {
SAML2Response saml2Response = new SAML2Response();
try {
Document doc = saml2Response.convert(responseType);
Element enc = DocumentUtil.getElement(doc, new QName(JBossSAMLConstants.ENCRYPTED_ASSERTION.get()));
if (enc == null) {
throw new IdentityBrokerException("No encrypted assertion found.");
}
String oldID = enc.getAttribute(JBossSAMLConstants.ID.get());
Document newDoc = DocumentUtil.createDocument();
Node importedNode = newDoc.importNode(enc, true);
newDoc.appendChild(importedNode);
Element decryptedDocumentElement = XMLEncryptionUtil.decryptElementInDocument(newDoc, privateKey);
SAMLParser parser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(decryptedDocumentElement);
AssertionType assertion = (AssertionType) parser.parse(StaxParserUtil.getXMLEventReader(DocumentUtil
.getNodeAsStream(decryptedDocumentElement)));
responseType.replaceAssertion(oldID, new ResponseType.RTChoiceType(assertion));
return responseType;
} catch (Exception e) {
throw new IdentityBrokerException("Could not decrypt assertion.", e);
}
}
}
protected class PostBinding extends Binding {
@Override
protected void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException {
SamlProtocolUtils.verifyDocumentSignature(documentHolder.getSamlDocument(), getIDPKey());
}
@Override
protected SAMLDocumentHolder extractRequestDocument(String samlRequest) {
return SAMLRequestParser.parseRequestPostBinding(samlRequest);
}
@Override
protected SAMLDocumentHolder extractResponseDocument(String response) {
byte[] samlBytes = PostBindingUtil.base64Decode(response);
String xml = new String(samlBytes);
return SAMLRequestParser.parseResponseDocument(samlBytes);
}
@Override
protected String getBindingType() {
return SamlProtocol.SAML_POST_BINDING;
}
}
protected class RedirectBinding extends Binding {
@Override
protected void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException {
PublicKey publicKey = getIDPKey();
SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo);
}
@Override
protected SAMLDocumentHolder extractRequestDocument(String samlRequest) {
return SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
}
@Override
protected SAMLDocumentHolder extractResponseDocument(String response) {
return SAMLRequestParser.parseRequestRedirectBinding(response);
}
@Override
protected String getBindingType() {
return SamlProtocol.SAML_REDIRECT_BINDING;
}
}
}
@@ -1,140 +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.";
}
}
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. Click 'Select Role' button to browse roles, or just type it in the textbox. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
property.setType(ProviderConfigProperty.ROLE_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.";
}
}
@@ -1,132 +1,129 @@
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.";
}
}
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.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
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.setSingleAttribute(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.getFirstAttribute(attribute);
if (value != null && !value.equals(current)) {
user.setSingleAttribute(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.";
}
}
@@ -1,133 +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.";
}
}
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.";
}
}
@@ -1,3 +1,3 @@
org.keycloak.broker.saml.mappers.AttributeToRoleMapper
org.keycloak.broker.saml.mappers.UserAttributeMapper
org.keycloak.broker.saml.mappers.AttributeToRoleMapper
org.keycloak.broker.saml.mappers.UserAttributeMapper
org.keycloak.broker.saml.mappers.UsernameTemplateMapper
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
<version>1.4.0.Final</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -90,18 +90,7 @@ public class DefaultFileConnectionProviderFactory implements FileConnectionProvi
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);
}
ImportUtils.importRealms(session, model.getRealms(), Strategy.IGNORE_EXISTING);
} catch (IOException ioe) {
logger.error("Unable to read model file " + kcdata.getAbsolutePath(), ioe);
} finally {
@@ -1,30 +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;
}
}
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 -36
View File
@@ -1,36 +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>
<?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</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>
@@ -1,281 +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();
}
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();
}
}
+31 -31
View File
@@ -1,31 +1,31 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-infinispan</artifactId>
<name>Keycloak Connections Infinispan</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
<?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</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-infinispan</artifactId>
<name>Keycloak Connections Infinispan</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+76 -76
View File
@@ -1,76 +1,76 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-jpa-liquibase</artifactId>
<name>Keycloak Connections JPA Liquibase Updater</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-connections-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<exclusions>
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<configuration>
<changeLogFile>META-INF/jpa-changelog-master.xml</changeLogFile>
<url>${project.url}</url>
<driver>${driver}</driver>
<username>${username}</username>
<password>${password}</password>
<referenceUrl>${referenceUrl}</referenceUrl>
<referenceDriver>${driver}</referenceDriver>
<referenceUsername>${username}</referenceUsername>
<referencePassword>${password}</referencePassword>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<username>sa</username>
<password></password>
<driver>org.h2.Driver</driver>
</properties>
</project>
<?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</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-jpa-liquibase</artifactId>
<name>Keycloak Connections JPA Liquibase Updater</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-connections-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<exclusions>
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<configuration>
<changeLogFile>META-INF/jpa-changelog-master.xml</changeLogFile>
<url>${project.url}</url>
<driver>${driver}</driver>
<username>${username}</username>
<password>${password}</password>
<referenceUrl>${referenceUrl}</referenceUrl>
<referenceDriver>${driver}</referenceDriver>
<referenceUsername>${username}</referenceUsername>
<referencePassword>${password}</referencePassword>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<username>sa</username>
<password></password>
<driver>org.h2.Driver</driver>
</properties>
</project>
@@ -46,26 +46,26 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
private static final String CHANGELOG = "META-INF/jpa-changelog-master.xml";
@Override
public String getCurrentVersionSql() {
return "SELECT ID from DATABASECHANGELOG ORDER BY DATEEXECUTED DESC LIMIT 1";
public String getCurrentVersionSql(String defaultSchema) {
return "SELECT ID from " + getTable("DATABASECHANGELOG", defaultSchema) + " ORDER BY DATEEXECUTED DESC LIMIT 1";
}
@Override
public void update(KeycloakSession session, Connection connection) {
public void update(KeycloakSession session, Connection connection, String defaultSchema) {
logger.debug("Starting database update");
// Need ThreadLocal as liquibase doesn't seem to have API to inject custom objects into tasks
ThreadLocalSessionContext.setCurrentSession(session);
try {
Liquibase liquibase = getLiquibase(connection);
Liquibase liquibase = getLiquibase(connection, defaultSchema);
List<ChangeSet> changeSets = liquibase.listUnrunChangeSets((Contexts) null);
if (!changeSets.isEmpty()) {
if (changeSets.get(0).getId().equals(FIRST_VERSION)) {
Statement statement = connection.createStatement();
try {
statement.executeQuery("SELECT id FROM REALM");
statement.executeQuery("SELECT id FROM " + getTable("REALM", defaultSchema));
logger.infov("Updating database from {0} to {1}", FIRST_VERSION, changeSets.get(changeSets.size() - 1).getId());
liquibase.markNextChangeSetRan(null);
@@ -93,9 +93,9 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
}
@Override
public void validate(Connection connection) {
public void validate(Connection connection, String defaultSchema) {
try {
Liquibase liquibase = getLiquibase(connection);
Liquibase liquibase = getLiquibase(connection, defaultSchema);
liquibase.validate();
} catch (Exception e) {
@@ -103,7 +103,7 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
}
}
private Liquibase getLiquibase(Connection connection) throws Exception {
private Liquibase getLiquibase(Connection connection, String defaultSchema) throws Exception {
ServiceLocator sl = ServiceLocator.getInstance();
if (!System.getProperties().containsKey("liquibase.scan.packages")) {
@@ -125,6 +125,9 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
LogFactory.setInstance(new LogWrapper());
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
if (defaultSchema != null) {
database.setDefaultSchemaName(defaultSchema);
}
return new Liquibase(CHANGELOG, new ClassLoaderResourceAccessor(getClass().getClassLoader()), database);
}
@@ -228,4 +231,8 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
}
private String getTable(String table, String defaultSchema) {
return defaultSchema != null ? defaultSchema + "." + table : table;
}
}
@@ -0,0 +1,54 @@
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.statement.core.UpdateStatement;
import liquibase.structure.core.Table;
import org.keycloak.models.utils.KeycloakModelUtils;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class JpaUpdate1_4_0_Final extends CustomKeycloakTask {
@Override
protected void generateStatementsImpl() throws CustomChangeException {
String userAttributeTableName = database.correctObjectName("USER_ATTRIBUTE", Table.class);
try {
PreparedStatement statement = jdbcConnection.prepareStatement("select NAME, USER_ID from USER_ATTRIBUTE");
try {
ResultSet resultSet = statement.executeQuery();
try {
while (resultSet.next()) {
String name = resultSet.getString(1);
String userId = resultSet.getString(2);
UpdateStatement updateStatement = new UpdateStatement(null, null, userAttributeTableName)
.addNewColumnValue("ID", KeycloakModelUtils.generateId())
.setWhereClause("NAME='" + name + "' AND USER_ID='" + userId + "'");
statements.add(updateStatement);
}
} finally {
resultSet.close();
}
} finally {
statement.close();
}
confirmationMessage.append("Updated " + statements.size() + " attributes in USER_ATTRIBUTE table");
} catch (Exception e) {
throw new CustomChangeException(getTaskId() + ": Exception when updating data from previous version", e);
}
}
@Override
protected String getTaskId() {
return "Update 1.4.0.Final";
}
}
@@ -1,86 +1,156 @@
<?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>
<?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_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"/>
<addColumn tableName="CLIENT">
<column name="SERVICE_ACCOUNTS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
<addColumn tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" type="VARCHAR(36)">
<constraints nullable="true"/>
</column>
</addColumn>
<addColumn tableName="AUTHENTICATION_FLOW">
<column name="PROVIDER_ID" type="VARCHAR(36)" defaultValue="basic-flow">
<constraints nullable="false"/>
</column>
<column name="TOP_LEVEL" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="BUILT_IN" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
<addColumn tableName="AUTHENTICATION_EXECUTION">
<column name="AUTH_FLOW_ID" type="VARCHAR(36)">
<constraints nullable="true"/>
</column>
<column name="AUTH_CONFIG" type="VARCHAR(36)">
<constraints nullable="true"/>
</column>
</addColumn>
<addColumn tableName="USER_ATTRIBUTE">
<column name="ID" type="VARCHAR(36)" defaultValue="sybase-needs-something-here">
<constraints nullable="false"/>
</column>
</addColumn>
<dropColumn tableName="AUTHENTICATOR" columnName="PROVIDER_ID"/>
<renameTable oldTableName="AUTHENTICATOR_CONFIG" newTableName="AUTHENTICATOR_CONFIG_ENTRY"/>
<renameTable oldTableName="AUTHENTICATOR" newTableName="AUTHENTICATOR_CONFIG"/>
<!-- 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(2048)"/>
<column name="CLIENT_SESSION" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
</createTable>
<createTable tableName="REQUIRED_ACTION_PROVIDER">
<column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="ALIAS" type="VARCHAR(255)"/>
<column name="NAME" type="VARCHAR(255)"/>
<column name="REALM_ID" type="VARCHAR(36)"/>
<column name="ENABLED" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="DEFAULT_ACTION" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="PROVIDER_ID" type="VARCHAR(255)"/>
</createTable>
<createTable tableName="REQUIRED_ACTION_CONFIG">
<column name="REQUIRED_ACTION_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="VALUE" type="CLOB"/>
<column name="NAME" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
</createTable>
<customChange class="org.keycloak.connections.jpa.updater.liquibase.custom.JpaUpdate1_4_0_Final"/>
<dropPrimaryKey constraintName="CONSTRAINT_6" tableName="USER_ATTRIBUTE"/>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_USER_ATTRIBUTE_PK" tableName="USER_ATTRIBUTE"/>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_REQ_ACT_PRV_PK" tableName="REQUIRED_ACTION_PROVIDER"/>
<addPrimaryKey columnNames="REQUIRED_ACTION_ID, NAME" constraintName="CONSTRAINT_REQ_ACT_CFG_PK" tableName="REQUIRED_ACTION_CONFIG"/>
<addPrimaryKey columnNames="CLIENT_SESSION, NAME" constraintName="CONSTR_CL_USR_SES_NOTE" tableName="CLIENT_USER_SESSION_NOTE"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REQUIRED_ACTION_PROVIDER" constraintName="FK_REQ_ACT_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
<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"/>
<addColumn tableName="USER_ENTITY">
<column name="CREATED_TIMESTAMP" type="BIGINT"/>
<column name="SERVICE_ACCOUNT_CLIENT_LINK" type="VARCHAR(36)"/>
</addColumn>
<!-- Sybase specific hacks -->
<modifySql dbms="sybase">
<replace replace="[USER_ATTRIBUTE] DROP PRIMARY KEY" with="[USER_ATTRIBUTE] DROP CONSTRAINT CONSTRAINT_6" />
</modifySql>
</changeSet>
</databaseChangeLog>
+36 -36
View File
@@ -1,36 +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-jpa</artifactId>
<name>Keycloak Connections JPA</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
<?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</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-jpa</artifactId>
<name>Keycloak Connections JPA</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -13,10 +13,12 @@ import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
@@ -29,6 +31,8 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
private volatile EntityManagerFactory emf;
private Config.Scope config;
private Map<String,String> operationalInfo;
@Override
public JpaConnectionProvider create(KeycloakSession session) {
@@ -70,105 +74,123 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
Connection connection = null;
String unitName = config.get("unitName");
String databaseSchema = config.get("databaseSchema");
Map<String, Object> properties = new HashMap<String, Object>();
// Only load config from keycloak-server.json if unitName is not specified
if (unitName == null) {
unitName = "keycloak-default";
String unitName = "keycloak-default";
String dataSource = config.get("dataSource");
if (dataSource != null) {
if (config.getBoolean("jta", false)) {
properties.put(AvailableSettings.JTA_DATASOURCE, dataSource);
} else {
properties.put(AvailableSettings.NON_JTA_DATASOURCE, dataSource);
}
String dataSource = config.get("dataSource");
if (dataSource != null) {
if (config.getBoolean("jta", false)) {
properties.put(AvailableSettings.JTA_DATASOURCE, dataSource);
} else {
properties.put(AvailableSettings.JDBC_URL, config.get("url"));
properties.put(AvailableSettings.JDBC_DRIVER, config.get("driver"));
String user = config.get("user");
if (user != null) {
properties.put(AvailableSettings.JDBC_USER, user);
}
String password = config.get("password");
if (password != null) {
properties.put(AvailableSettings.JDBC_PASSWORD, password);
}
properties.put(AvailableSettings.NON_JTA_DATASOURCE, dataSource);
}
} else {
properties.put(AvailableSettings.JDBC_URL, config.get("url"));
properties.put(AvailableSettings.JDBC_DRIVER, config.get("driver"));
String driverDialect = config.get("driverDialect");
if (driverDialect != null && driverDialect.length() > 0) {
properties.put("hibernate.dialect", driverDialect);
String user = config.get("user");
if (user != null) {
properties.put(AvailableSettings.JDBC_USER, user);
}
if (databaseSchema != null) {
if (databaseSchema.equals("development-update")) {
properties.put("hibernate.hbm2ddl.auto", "update");
databaseSchema = null;
} else if (databaseSchema.equals("development-validate")) {
properties.put("hibernate.hbm2ddl.auto", "validate");
databaseSchema = null;
}
String password = config.get("password");
if (password != null) {
properties.put(AvailableSettings.JDBC_PASSWORD, password);
}
}
properties.put("hibernate.show_sql", config.getBoolean("showSql", false));
properties.put("hibernate.format_sql", config.getBoolean("formatSql", true));
String driverDialect = config.get("driverDialect");
if (driverDialect != null && driverDialect.length() > 0) {
properties.put("hibernate.dialect", driverDialect);
}
String schema = config.get("schema");
if (schema != null) {
properties.put("hibernate.default_schema", schema);
}
if (databaseSchema != null) {
logger.trace("Updating database");
JpaUpdaterProvider updater = session.getProvider(JpaUpdaterProvider.class);
if (updater == null) {
throw new RuntimeException("Can't update database: JPA updater provider not found");
if (databaseSchema.equals("development-update")) {
properties.put("hibernate.hbm2ddl.auto", "update");
databaseSchema = null;
} else if (databaseSchema.equals("development-validate")) {
properties.put("hibernate.hbm2ddl.auto", "validate");
databaseSchema = null;
}
connection = getConnection();
if (databaseSchema.equals("update")) {
String currentVersion = null;
try {
ResultSet resultSet = connection.createStatement().executeQuery(updater.getCurrentVersionSql());
if (resultSet.next()) {
currentVersion = resultSet.getString(1);
}
} catch (SQLException e) {
}
if (currentVersion == null || !JpaUpdaterProvider.LAST_VERSION.equals(currentVersion)) {
updater.update(session, connection);
} else {
logger.debug("Database is up to date");
}
} else if (databaseSchema.equals("validate")) {
updater.validate(connection);
} else {
throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
}
logger.trace("Database update completed");
}
logger.trace("Creating EntityManagerFactory");
emf = Persistence.createEntityManagerFactory(unitName, properties);
logger.trace("EntityManagerFactory created");
properties.put("hibernate.show_sql", config.getBoolean("showSql", false));
properties.put("hibernate.format_sql", config.getBoolean("formatSql", true));
// Close after creating EntityManagerFactory to prevent in-mem databases from closing
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
logger.warn(e);
}
connection = getConnection();
try{
prepareOperationalInfo(connection);
if (databaseSchema != null) {
logger.trace("Updating database");
JpaUpdaterProvider updater = session.getProvider(JpaUpdaterProvider.class);
if (updater == null) {
throw new RuntimeException("Can't update database: JPA updater provider not found");
}
if (databaseSchema.equals("update")) {
String currentVersion = null;
try {
ResultSet resultSet = connection.createStatement().executeQuery(updater.getCurrentVersionSql(schema));
if (resultSet.next()) {
currentVersion = resultSet.getString(1);
}
} catch (SQLException e) {
}
if (currentVersion == null || !JpaUpdaterProvider.LAST_VERSION.equals(currentVersion)) {
updater.update(session, connection, schema);
} else {
logger.debug("Database is up to date");
}
} else if (databaseSchema.equals("validate")) {
updater.validate(connection, schema);
} else {
throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
}
logger.trace("Database update completed");
}
logger.trace("Creating EntityManagerFactory");
emf = Persistence.createEntityManagerFactory(unitName, properties);
logger.trace("EntityManagerFactory created");
} finally {
// Close after creating EntityManagerFactory to prevent in-mem databases from closing
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
logger.warn(e);
}
}
}
}
}
}
}
protected void prepareOperationalInfo(Connection connection) {
try {
operationalInfo = new LinkedHashMap<>();
DatabaseMetaData md = connection.getMetaData();
operationalInfo.put("databaseUrl",md.getURL());
operationalInfo.put("databaseUser", md.getUserName());
operationalInfo.put("databaseProduct", md.getDatabaseProductName() + " " + md.getDatabaseProductVersion());
operationalInfo.put("databaseDriver", md.getDriverName() + " " + md.getDriverVersion());
} catch (SQLException e) {
logger.warn("Unable to prepare operational info due database exception: " + e.getMessage());
}
}
private Connection getConnection() {
try {
@@ -184,5 +206,10 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
throw new RuntimeException("Failed to connect to database", e);
}
}
@Override
public Map<String,String> getOperationalInfo() {
return operationalInfo;
}
}
@@ -1,9 +1,10 @@
package org.keycloak.connections.jpa;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.ServerInfoAwareProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface JpaConnectionProviderFactory extends ProviderFactory<JpaConnectionProvider> {
public interface JpaConnectionProviderFactory extends ServerInfoAwareProviderFactory<JpaConnectionProvider> {
}
@@ -1,53 +1,53 @@
package org.keycloak.connections.jpa;
import org.keycloak.models.KeycloakTransaction;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaKeycloakTransaction implements KeycloakTransaction {
protected EntityManager em;
public JpaKeycloakTransaction(EntityManager em) {
this.em = em;
}
@Override
public void begin() {
em.getTransaction().begin();
}
@Override
public void commit() {
try {
em.getTransaction().commit();
} catch (PersistenceException e) {
throw PersistenceExceptionConverter.convert(e.getCause() != null ? e.getCause() : e);
}
}
@Override
public void rollback() {
em.getTransaction().rollback();
}
@Override
public void setRollbackOnly() {
em.getTransaction().setRollbackOnly();
}
@Override
public boolean getRollbackOnly() {
return em.getTransaction().getRollbackOnly();
}
@Override
public boolean isActive() {
return em.getTransaction().isActive();
}
}
package org.keycloak.connections.jpa;
import org.keycloak.models.KeycloakTransaction;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaKeycloakTransaction implements KeycloakTransaction {
protected EntityManager em;
public JpaKeycloakTransaction(EntityManager em) {
this.em = em;
}
@Override
public void begin() {
em.getTransaction().begin();
}
@Override
public void commit() {
try {
em.getTransaction().commit();
} catch (PersistenceException e) {
throw PersistenceExceptionConverter.convert(e.getCause() != null ? e.getCause() : e);
}
}
@Override
public void rollback() {
em.getTransaction().rollback();
}
@Override
public void setRollbackOnly() {
em.getTransaction().setRollbackOnly();
}
@Override
public boolean getRollbackOnly() {
return em.getTransaction().getRollbackOnly();
}
@Override
public boolean isActive() {
return em.getTransaction().isActive();
}
}
@@ -14,10 +14,10 @@ public interface JpaUpdaterProvider extends Provider {
public String LAST_VERSION = "1.4.0";
public String getCurrentVersionSql();
public String getCurrentVersionSql(String defaultSchema);
public void update(KeycloakSession session, Connection connection);
public void update(KeycloakSession session, Connection connection, String defaultSchema);
public void validate(Connection connection);
public void validate(Connection connection, String defaultSchema);
}
@@ -1,53 +1,54 @@
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.ClientEntity</class>
<class>org.keycloak.models.jpa.entities.CredentialEntity</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>
<!-- JpaAuditProviders -->
<class>org.keycloak.events.jpa.EventEntity</class>
<class>org.keycloak.events.jpa.AdminEventEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="jboss.as.jpa.managed" value="false"/>
</properties>
</persistence-unit>
</persistence>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.ClientEntity</class>
<class>org.keycloak.models.jpa.entities.CredentialEntity</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.AuthenticatorConfigEntity</class>
<class>org.keycloak.models.jpa.entities.RequiredActionProviderEntity</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>
<!-- JpaAuditProviders -->
<class>org.keycloak.events.jpa.EventEntity</class>
<class>org.keycloak.events.jpa.AdminEventEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="jboss.as.jpa.managed" value="false"/>
</properties>
</persistence-unit>
</persistence>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
<version>1.4.0.Final</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -27,7 +27,8 @@ public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider {
Update1_1_0_Beta1.class,
Update1_2_0_Beta1.class,
Update1_2_0_CR1.class,
Update1_3_0.class
Update1_3_0.class,
Update1_4_0.class
};
@Override
@@ -249,7 +249,7 @@ public class Update1_2_0_Beta1 extends Update {
dbMapper.put("protocolMapper", protocolMapper.getProtocolMapper());
Map<String, String> config = protocolMapper.getConfig();
BasicDBObject dbConfig = MapMapper.convertMap(config);
BasicDBObject dbConfig = MapMapper.convertMap(config, null);
dbMapper.put("config", dbConfig);
dbProtocolMappers.add(dbMapper);
@@ -0,0 +1,63 @@
package org.keycloak.connections.mongo.updater.impl.updates;
import java.util.HashSet;
import java.util.Map;
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_4_0 extends Update {
@Override
public String getId() {
return "1.4.0";
}
@Override
public void update(KeycloakSession session) throws ClassNotFoundException {
deleteEntries("clientSessions");
deleteEntries("sessions");
// Remove warning
removeField("realms", "authenticators");
updateUserAttributes();
}
private void updateUserAttributes() {
DBCollection users = db.getCollection("users");
DBCursor usersCursor = users.find();
try {
while (usersCursor.hasNext()) {
BasicDBObject user = (BasicDBObject) usersCursor.next();
BasicDBObject attributes = (BasicDBObject) user.get("attributes");
if (attributes != null) {
for (Map.Entry<String, Object> attr : new HashSet<>(attributes.entrySet())) {
String attrName = attr.getKey();
Object attrValue = attr.getValue();
if (attrValue != null && attrValue instanceof String) {
BasicDBList asList = new BasicDBList();
asList.add(attrValue);
attributes.put(attrName, asList);
}
}
user.put("attributes", attributes);
users.save(user);
}
}
} finally {
usersCursor.close();
}
}
}
+35 -35
View File
@@ -1,35 +1,35 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-mongo</artifactId>
<name>Keycloak Connections Mongo</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
</dependencies>
</project>
<?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</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-mongo</artifactId>
<name>Keycloak Connections Mongo</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
</dependencies>
</project>
@@ -5,6 +5,7 @@ import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.connections.mongo.api.MongoStore;
@@ -18,6 +19,8 @@ import javax.net.ssl.SSLSocketFactory;
import java.lang.reflect.Method;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -46,7 +49,8 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
"org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity",
"org.keycloak.models.entities.AuthenticationExecutionEntity",
"org.keycloak.models.entities.AuthenticationFlowEntity",
"org.keycloak.models.entities.AuthenticatorEntity",
"org.keycloak.models.entities.AuthenticatorConfigEntity",
"org.keycloak.models.entities.RequiredActionProviderEntity",
};
private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class);
@@ -56,6 +60,8 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
private MongoStore mongoStore;
private DB db;
protected Config.Scope config;
private Map<String,String> operationalInfo;
@Override
public MongoConnectionProvider create(KeycloakSession session) {
@@ -158,7 +164,13 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
} else {
client = new MongoClient(new ServerAddress(host, port), clientOptions);
}
operationalInfo = new LinkedHashMap<>();
operationalInfo.put("mongoServerAddress", client.getAddress().toString());
operationalInfo.put("mongoDatabaseName", dbName);
operationalInfo.put("mongoUser", user);
operationalInfo.put("mongoDriverVersion", client.getVersion());
logger.debugv("Initialized mongo model. host: %s, port: %d, db: %s", host, port, dbName);
return client;
}
@@ -205,5 +217,10 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
}
}
}
@Override
public Map<String,String> getOperationalInfo() {
return operationalInfo;
}
}
@@ -1,9 +1,9 @@
package org.keycloak.connections.mongo;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.ServerInfoAwareProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface MongoConnectionProviderFactory extends ProviderFactory<MongoConnectionProvider> {
public interface MongoConnectionProviderFactory extends ServerInfoAwareProviderFactory<MongoConnectionProvider> {
}
@@ -1,79 +1,79 @@
package org.keycloak.connections.mongo.api;
import com.mongodb.DBObject;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface MongoStore {
/**
* Insert new entity
*
* @param entity to insert
*/
void insertEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context);
/**
* Update existing entity
*
* @param entity to update
*/
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);
boolean removeEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context);
boolean removeEntity(Class<? extends MongoIdentifiableEntity> type, String id, 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);
<S> boolean pullItemFromList(MongoIdentifiableEntity entity, String listPropertyName, S itemToPull, MongoStoreInvocationContext context);
}
package org.keycloak.connections.mongo.api;
import com.mongodb.DBObject;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface MongoStore {
/**
* Insert new entity
*
* @param entity to insert
*/
void insertEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context);
/**
* Update existing entity
*
* @param entity to update
*/
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);
boolean removeEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context);
boolean removeEntity(Class<? extends MongoIdentifiableEntity> type, String id, 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);
<S> boolean pullItemFromList(MongoIdentifiableEntity entity, String listPropertyName, S itemToPull, MongoStoreInvocationContext context);
}
@@ -1,5 +1,6 @@
package org.keycloak.connections.mongo.api.types;
import java.lang.reflect.Type;
import java.util.List;
/**
@@ -14,9 +15,9 @@ public class MapperContext<T, S> {
private final Class<? extends S> expectedReturnType;
// in case that expected return type is generic type (like "List<String>"), then genericTypes could contain list of expected generic arguments
private final List<Class<?>> genericTypes;
private final List<Type> genericTypes;
public MapperContext(T objectToConvert, Class<? extends S> expectedReturnType, List<Class<?>> genericTypes) {
public MapperContext(T objectToConvert, Class<? extends S> expectedReturnType, List<Type> genericTypes) {
this.objectToConvert = objectToConvert;
this.expectedReturnType = expectedReturnType;
this.genericTypes = genericTypes;
@@ -30,7 +31,7 @@ public class MapperContext<T, S> {
return expectedReturnType;
}
public List<Class<?>> getGenericTypes() {
public List<Type> getGenericTypes() {
return genericTypes;
}
}
@@ -1,111 +1,119 @@
package org.keycloak.connections.mongo.api.types;
import java.util.HashMap;
import java.util.Map;
/**
* Registry of mappers, which allow to convert application object to database objects. MapperRegistry is main entry point to be used by application.
* Application can create instance of MapperRegistry and then register required Mapper objects.
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class MapperRegistry {
// TODO: Thread-safety support (maybe...)
// Mappers of Application objects to DB objects
private Map<Class<?>, Mapper<?, ?>> appObjectMappers = new HashMap<Class<?>, Mapper<?, ?>>();
// Mappers of DB objects to Application objects
private Map<Class<?>, Map<Class<?>, Mapper<?, ?>>> dbObjectMappers = new HashMap<Class<?>, Map<Class<?>, Mapper<?,?>>>();
/**
* Add mapper for converting application objects to DB objects
*
* @param mapper
*/
public void addAppObjectMapper(Mapper<?, ?> mapper) {
appObjectMappers.put(mapper.getTypeOfObjectToConvert(), mapper);
}
/**
* Add mapper for converting DB objects to application objects
*
* @param mapper
*/
public void addDBObjectMapper(Mapper<?, ?> mapper) {
Class<?> dbObjectType = mapper.getTypeOfObjectToConvert();
Class<?> appObjectType = mapper.getExpectedReturnType();
Map<Class<?>, Mapper<?, ?>> appObjects = dbObjectMappers.get(dbObjectType);
if (appObjects == null) {
appObjects = new HashMap<Class<?>, Mapper<?, ?>>();
dbObjectMappers.put(dbObjectType, appObjects);
}
appObjects.put(appObjectType, mapper);
}
public <S> S convertDBObjectToApplicationObject(MapperContext<Object, S> context) {
Object dbObject = context.getObjectToConvert();
Class<?> expectedApplicationObjectType = context.getExpectedReturnType();
Class<?> dbObjectType = dbObject.getClass();
Mapper<Object, S> mapper;
Map<Class<?>, Mapper<?, ?>> appObjects = dbObjectMappers.get(dbObjectType);
if (appObjects == null) {
throw new IllegalArgumentException("Not found any mappers for type " + dbObjectType);
} else {
if (appObjects.size() == 1) {
mapper = (Mapper<Object, S>)appObjects.values().iterator().next();
} else {
// Try to find converter for requested application type
mapper = (Mapper<Object, S>)getAppConverterForType(context.getExpectedReturnType(), appObjects);
}
}
if (mapper == null) {
throw new IllegalArgumentException("Can't found mapper for type " + dbObjectType + " and expectedApplicationType " + expectedApplicationObjectType);
}
return mapper.convertObject(context);
}
public <S> S convertApplicationObjectToDBObject(Object applicationObject, Class<S> expectedDBObjectType) {
Class<?> appObjectType = applicationObject.getClass();
Mapper<Object, S> mapper = (Mapper<Object, S>)getAppConverterForType(appObjectType, appObjectMappers);
if (mapper == null) {
throw new IllegalArgumentException("Can't found converter for type " + appObjectType + " in registered appObjectMappers");
}
if (!expectedDBObjectType.isAssignableFrom(mapper.getExpectedReturnType())) {
throw new IllegalArgumentException("Converter " + mapper + " has return type " + mapper.getExpectedReturnType() +
" but we need type " + expectedDBObjectType);
}
return mapper.convertObject(new MapperContext<Object, S>(applicationObject, expectedDBObjectType, null));
}
// Try to find converter for given type or all it's supertypes
private static Mapper<Object, ?> getAppConverterForType(Class<?> appObjectType, Map<Class<?>, Mapper<?, ?>> appObjectConverters) {
Mapper<Object, ?> mapper = (Mapper<Object, ?>)appObjectConverters.get(appObjectType);
if (mapper != null) {
return mapper;
} else {
Class<?>[] interfaces = appObjectType.getInterfaces();
for (Class<?> interface1 : interfaces) {
mapper = getAppConverterForType(interface1, appObjectConverters);
if (mapper != null) {
return mapper;
}
}
Class<?> superType = appObjectType.getSuperclass();
if (superType != null) {
return getAppConverterForType(superType, appObjectConverters);
} else {
return null;
}
}
}
}
package org.keycloak.connections.mongo.api.types;
import java.util.HashMap;
import java.util.Map;
/**
* Registry of mappers, which allow to convert application object to database objects. MapperRegistry is main entry point to be used by application.
* Application can create instance of MapperRegistry and then register required Mapper objects.
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class MapperRegistry {
// TODO: Thread-safety support (maybe...)
// Mappers of Application objects to DB objects
private Map<Class<?>, Mapper<?, ?>> appObjectMappers = new HashMap<Class<?>, Mapper<?, ?>>();
// Mappers of DB objects to Application objects
private Map<Class<?>, Map<Class<?>, Mapper<?, ?>>> dbObjectMappers = new HashMap<Class<?>, Map<Class<?>, Mapper<?,?>>>();
/**
* Add mapper for converting application objects to DB objects
*
* @param mapper
*/
public void addAppObjectMapper(Mapper<?, ?> mapper) {
appObjectMappers.put(mapper.getTypeOfObjectToConvert(), mapper);
}
/**
* Add mapper for converting DB objects to application objects
*
* @param mapper
*/
public void addDBObjectMapper(Mapper<?, ?> mapper) {
Class<?> dbObjectType = mapper.getTypeOfObjectToConvert();
Class<?> appObjectType = mapper.getExpectedReturnType();
Map<Class<?>, Mapper<?, ?>> appObjects = dbObjectMappers.get(dbObjectType);
if (appObjects == null) {
appObjects = new HashMap<Class<?>, Mapper<?, ?>>();
dbObjectMappers.put(dbObjectType, appObjects);
}
appObjects.put(appObjectType, mapper);
}
public <S> S convertDBObjectToApplicationObject(MapperContext<Object, S> context) {
if (context.getObjectToConvert() == null) {
return null;
}
Object dbObject = context.getObjectToConvert();
Class<?> expectedApplicationObjectType = context.getExpectedReturnType();
Class<?> dbObjectType = dbObject.getClass();
Mapper<Object, S> mapper;
Map<Class<?>, Mapper<?, ?>> appObjects = dbObjectMappers.get(dbObjectType);
if (appObjects == null) {
throw new IllegalArgumentException("Not found any mappers for type " + dbObjectType);
} else {
if (appObjects.size() == 1) {
mapper = (Mapper<Object, S>)appObjects.values().iterator().next();
} else {
// Try to find converter for requested application type
mapper = (Mapper<Object, S>)getAppConverterForType(context.getExpectedReturnType(), appObjects);
}
}
if (mapper == null) {
throw new IllegalArgumentException("Can't found mapper for type " + dbObjectType + " and expectedApplicationType " + expectedApplicationObjectType);
}
return mapper.convertObject(context);
}
public <S> S convertApplicationObjectToDBObject(Object applicationObject, Class<S> expectedDBObjectType) {
if (applicationObject == null) {
return null;
}
Class<?> appObjectType = applicationObject.getClass();
Mapper<Object, S> mapper = (Mapper<Object, S>)getAppConverterForType(appObjectType, appObjectMappers);
if (mapper == null) {
throw new IllegalArgumentException("Can't found converter for type " + appObjectType + " in registered appObjectMappers");
}
if (!expectedDBObjectType.isAssignableFrom(mapper.getExpectedReturnType())) {
throw new IllegalArgumentException("Converter " + mapper + " has return type " + mapper.getExpectedReturnType() +
" but we need type " + expectedDBObjectType);
}
return mapper.convertObject(new MapperContext<Object, S>(applicationObject, expectedDBObjectType, null));
}
// Try to find converter for given type or all it's supertypes
private static Mapper<Object, ?> getAppConverterForType(Class<?> appObjectType, Map<Class<?>, Mapper<?, ?>> appObjectConverters) {
Mapper<Object, ?> mapper = (Mapper<Object, ?>)appObjectConverters.get(appObjectType);
if (mapper != null) {
return mapper;
} else {
Class<?>[] interfaces = appObjectType.getInterfaces();
for (Class<?> interface1 : interfaces) {
mapper = getAppConverterForType(interface1, appObjectConverters);
if (mapper != null) {
return mapper;
}
}
Class<?> superType = appObjectType.getSuperclass();
if (superType != null) {
return getAppConverterForType(superType, appObjectConverters);
} else {
return null;
}
}
}
}
@@ -1,40 +1,40 @@
package org.keycloak.connections.mongo.impl;
import org.keycloak.models.utils.reflection.Property;
import java.util.Collection;
import java.util.Map;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class EntityInfo {
private final Class<?> entityClass;
private final String dbCollectionName;
private final Map<String, Property<Object>> properties;
public EntityInfo(Class<?> entityClass, String dbCollectionName, Map<String, Property<Object>> properties) {
this.entityClass = entityClass;
this.dbCollectionName = dbCollectionName;
this.properties = properties;
}
public Class<?> getEntityClass() {
return entityClass;
}
public String getDbCollectionName() {
return dbCollectionName;
}
public Collection<Property<Object>> getProperties() {
return properties.values();
}
public Property<Object> getPropertyByName(String propertyName) {
return properties.get(propertyName);
}
}
package org.keycloak.connections.mongo.impl;
import org.keycloak.models.utils.reflection.Property;
import java.util.Collection;
import java.util.Map;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class EntityInfo {
private final Class<?> entityClass;
private final String dbCollectionName;
private final Map<String, Property<Object>> properties;
public EntityInfo(Class<?> entityClass, String dbCollectionName, Map<String, Property<Object>> properties) {
this.entityClass = entityClass;
this.dbCollectionName = dbCollectionName;
this.properties = properties;
}
public Class<?> getEntityClass() {
return entityClass;
}
public String getDbCollectionName() {
return dbCollectionName;
}
public Collection<Property<Object>> getProperties() {
return properties.values();
}
public Property<Object> getPropertyByName(String propertyName) {
return properties.get(propertyName);
}
}
@@ -1,473 +1,473 @@
package org.keycloak.connections.mongo.impl;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
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;
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
import org.keycloak.connections.mongo.api.MongoStore;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.connections.mongo.api.context.MongoTask;
import org.keycloak.connections.mongo.api.types.Mapper;
import org.keycloak.connections.mongo.api.types.MapperContext;
import org.keycloak.connections.mongo.api.types.MapperRegistry;
import org.keycloak.connections.mongo.impl.types.BasicDBListMapper;
import org.keycloak.connections.mongo.impl.types.BasicDBListToSetMapper;
import org.keycloak.connections.mongo.impl.types.BasicDBObjectMapper;
import org.keycloak.connections.mongo.impl.types.BasicDBObjectToMapMapper;
import org.keycloak.connections.mongo.impl.types.EnumToStringMapper;
import org.keycloak.connections.mongo.impl.types.ListMapper;
import org.keycloak.connections.mongo.impl.types.MapMapper;
import org.keycloak.connections.mongo.impl.types.MongoEntityMapper;
import org.keycloak.connections.mongo.impl.types.SimpleMapper;
import org.keycloak.connections.mongo.impl.types.StringToEnumMapper;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.ModelException;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.reflection.Property;
import org.keycloak.models.utils.reflection.PropertyQueries;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class MongoStoreImpl implements MongoStore {
private static final Class<?>[] SIMPLE_TYPES = { String.class, Integer.class, Boolean.class, Long.class, Double.class, Character.class, Date.class, byte[].class };
private final DB database;
private static final Logger logger = Logger.getLogger(MongoStoreImpl.class);
private final MapperRegistry mapperRegistry;
private ConcurrentMap<Class<?>, EntityInfo> entityInfoCache =
new ConcurrentHashMap<Class<?>, EntityInfo>();
public MongoStoreImpl(DB database, Class<?>[] managedEntityTypes) {
this.database = database;
mapperRegistry = new MapperRegistry();
for (Class<?> simpleMapperClass : SIMPLE_TYPES) {
SimpleMapper mapper = new SimpleMapper(simpleMapperClass);
mapperRegistry.addAppObjectMapper(mapper);
mapperRegistry.addDBObjectMapper(mapper);
}
// Specific converter for ArrayList is added just for performance purposes to avoid recursive converter lookup (most of list idm will be ArrayList)
mapperRegistry.addAppObjectMapper(new ListMapper(mapperRegistry, ArrayList.class));
mapperRegistry.addAppObjectMapper(new ListMapper(mapperRegistry, List.class));
mapperRegistry.addDBObjectMapper(new BasicDBListMapper(mapperRegistry));
mapperRegistry.addAppObjectMapper(new ListMapper(mapperRegistry, HashSet.class));
mapperRegistry.addAppObjectMapper(new ListMapper(mapperRegistry, Set.class));
mapperRegistry.addDBObjectMapper(new BasicDBListToSetMapper(mapperRegistry));
mapperRegistry.addAppObjectMapper(new MapMapper(HashMap.class));
mapperRegistry.addAppObjectMapper(new MapMapper(Map.class));
mapperRegistry.addDBObjectMapper(new BasicDBObjectToMapMapper());
// Enum converters
mapperRegistry.addAppObjectMapper(new EnumToStringMapper());
mapperRegistry.addDBObjectMapper(new StringToEnumMapper());
for (Class<?> type : managedEntityTypes) {
getEntityInfo(type);
mapperRegistry.addAppObjectMapper(new MongoEntityMapper(this, mapperRegistry, type));
mapperRegistry.addDBObjectMapper(new BasicDBObjectMapper(this, mapperRegistry, type));
}
}
protected void dropDatabase() {
this.database.dropDatabase();
logger.info("Database " + this.database.getName() + " dropped in MongoDB");
}
@Override
public void insertEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
Class<? extends MongoEntity> clazz = entity.getClass();
// Find annotations for ID, for all the properties and for the name of the collection.
EntityInfo entityInfo = getEntityInfo(clazz);
// Create instance of BasicDBObject and add all declared properties to it (properties with null value probably should be skipped)
BasicDBObject dbObject = mapperRegistry.convertApplicationObjectToDBObject(entity, BasicDBObject.class);
DBCollection dbCollection = database.getCollection(entityInfo.getDbCollectionName());
String currentId = entity.getId();
// Generate random ID if not set already
if (currentId == null) {
currentId = KeycloakModelUtils.generateId();
entity.setId(currentId);
}
// Adding "_id"
dbObject.put("_id", currentId);
try {
dbCollection.insert(dbObject);
} catch (MongoException e) {
throw convertException(e);
}
// Treat object as created in this transaction (It is already submitted to transaction)
context.addCreatedEntity(entity);
}
public static ModelException convertException(MongoException e) {
if (e instanceof MongoException.DuplicateKey) {
return new ModelDuplicateException(e);
} else {
return new ModelException(e);
}
}
@Override
public void updateEntity(final MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
MongoTask fullUpdateTask = new MongoTask() {
@Override
public void execute() {
Class<? extends MongoEntity> clazz = entity.getClass();
EntityInfo entityInfo = getEntityInfo(clazz);
BasicDBObject dbObject = mapperRegistry.convertApplicationObjectToDBObject(entity, BasicDBObject.class);
DBCollection dbCollection = database.getCollection(entityInfo.getDbCollectionName());
String currentId = entity.getId();
if (currentId == null) {
throw new IllegalStateException("Can't update entity without id: " + entity);
} else {
BasicDBObject query = new BasicDBObject("_id", currentId);
dbCollection.update(query, dbObject);
}
}
@Override
public boolean isFullUpdate() {
return true;
}
};
// update is just added to context and postponed
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) {
// First look if we already read the object with this oid and type during this transaction. If yes, use it instead of DB lookup
T cached = context.getLoadedEntity(type, id);
if (cached != null && type.isAssignableFrom(cached.getClass())) return cached;
DBCollection dbCollection = getDBCollectionForType(type);
BasicDBObject idQuery = new BasicDBObject("_id", id);
DBObject dbObject = dbCollection.findOne(idQuery);
if (dbObject == null) return null;
MapperContext<Object, T> mapperContext = new MapperContext<Object, T>(dbObject, type, null);
T converted = mapperRegistry.convertDBObjectToApplicationObject(mapperContext);
// Now add it to loaded objects
context.addLoadedEntity(converted);
return converted;
}
@Override
public <T extends MongoIdentifiableEntity> T loadSingleEntity(Class<T> type, DBObject query, MongoStoreInvocationContext context) {
// First we should execute all pending tasks before searching DB
context.beforeDBSearch(type);
DBCollection dbCollection = getDBCollectionForType(type);
DBObject dbObject = dbCollection.findOne(query);
if (dbObject == null) {
return null;
} else {
return convertDBObjectToEntity(type, dbObject, context);
}
}
@Override
public <T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context) {
// First we should execute all pending tasks before searching DB
context.beforeDBSearch(type);
DBCollection dbCollection = getDBCollectionForType(type);
DBCursor cursor = dbCollection.find(query);
return convertCursor(type, cursor, context);
}
@Override
public <T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, DBObject sort, int firstResult, int maxResults, MongoStoreInvocationContext context) {
// First we should execute all pending tasks before searching DB
context.beforeDBSearch(type);
DBCollection dbCollection = getDBCollectionForType(type);
DBCursor cursor = dbCollection.find(query);
if (firstResult != -1) {
cursor.skip(firstResult);
}
if (maxResults != -1) {
cursor.limit(maxResults);
}
if (sort != null) {
cursor.sort(sort);
}
return convertCursor(type, cursor, context);
}
public <T extends MongoIdentifiableEntity> int countEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context) {
context.beforeDBSearch(type);
DBCollection dbCollection = getDBCollectionForType(type);
Long count = dbCollection.count(query);
// For now, assume that int is sufficient
return count.intValue();
}
@Override
public boolean removeEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
return removeEntity(entity.getClass(), entity.getId(), context);
}
@Override
public boolean removeEntity(Class<? extends MongoIdentifiableEntity> type, String id, MongoStoreInvocationContext context) {
MongoIdentifiableEntity found = loadEntity(type, id, context);
if (found == null) {
return false;
} else {
DBCollection dbCollection = getDBCollectionForType(type);
BasicDBObject dbQuery = new BasicDBObject("_id", id);
dbCollection.remove(dbQuery);
//logger.debugf("Entity of type: %s , id: %s removed from MongoDB.", type, id);
context.addRemovedEntity(found);
return true;
}
}
@Override
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);
logger.debugf("Removed %d entities of type: %s, query: %s", foundObjects.size(), type, query);
for (MongoIdentifiableEntity found : foundObjects) {
context.addRemovedEntity(found);;
}
return foundObjects.size();
}
} 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;
}
}
@Override
public <S> boolean pushItemToList(final MongoIdentifiableEntity entity, final String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent, MongoStoreInvocationContext context) {
final Class<? extends MongoEntity> type = entity.getClass();
EntityInfo entityInfo = getEntityInfo(type);
// Add item to list directly in this object
Property<Object> listProperty = entityInfo.getPropertyByName(listPropertyName);
if (listProperty == null) {
throw new IllegalArgumentException("Property " + listPropertyName + " doesn't exist on object " + entity);
}
List<S> list = (List<S>)listProperty.getValue(entity);
if (list == null) {
list = new ArrayList<S>();
listProperty.setValue(entity, list);
}
// Skip if item is already in list
if (skipIfAlreadyPresent && list.contains(itemToPush)) {
return false;
}
// Update java object
list.add(itemToPush);
// Add update of list to pending tasks
final List<S> listt = list;
context.addUpdateTask(entity, new MongoTask() {
@Override
public void execute() {
// Now DB update of new list with usage of $set
BasicDBList dbList = mapperRegistry.convertApplicationObjectToDBObject(listt, BasicDBList.class);
BasicDBObject query = new BasicDBObject("_id", entity.getId());
BasicDBObject listObject = new BasicDBObject(listPropertyName, dbList);
BasicDBObject setCommand = new BasicDBObject("$set", listObject);
getDBCollectionForType(type).update(query, setCommand);
}
@Override
public boolean isFullUpdate() {
return false;
}
});
return true;
}
@Override
public <S> boolean pullItemFromList(final MongoIdentifiableEntity entity, final String listPropertyName, final S itemToPull, MongoStoreInvocationContext context) {
final Class<? extends MongoEntity> type = entity.getClass();
EntityInfo entityInfo = getEntityInfo(type);
// Remove item from list directly in this object
Property<Object> listProperty = entityInfo.getPropertyByName(listPropertyName);
if (listProperty == null) {
throw new IllegalArgumentException("Property " + listPropertyName + " doesn't exist on object " + entity);
}
List<S> list = (List<S>)listProperty.getValue(entity);
// If list is null, we skip both object and DB update
if (list == null || !list.contains(itemToPull)) {
return false;
} else {
// Update java object
list.remove(itemToPull);
// Add update of list to pending tasks
context.addUpdateTask(entity, new MongoTask() {
@Override
public void execute() {
// Pull item from DB
Object dbItemToPull = mapperRegistry.convertApplicationObjectToDBObject(itemToPull, Object.class);
BasicDBObject query = new BasicDBObject("_id", entity.getId());
BasicDBObject pullObject = new BasicDBObject(listPropertyName, dbItemToPull);
BasicDBObject pullCommand = new BasicDBObject("$pull", pullObject);
getDBCollectionForType(type).update(query, pullCommand);
}
@Override
public boolean isFullUpdate() {
return false;
}
});
return true;
}
}
// Possibility to add user-defined mappers
public void addAppObjectConverter(Mapper<?, ?> mapper) {
mapperRegistry.addAppObjectMapper(mapper);
}
public void addDBObjectConverter(Mapper<?, ?> mapper) {
mapperRegistry.addDBObjectMapper(mapper);
}
public EntityInfo getEntityInfo(Class<?> entityClass) {
EntityInfo entityInfo = entityInfoCache.get(entityClass);
if (entityInfo == null) {
Map<String, Property<Object>> properties = PropertyQueries.createQuery(entityClass).getWritableResultList();
MongoCollection classAnnotation = entityClass.getAnnotation(MongoCollection.class);
String dbCollectionName = classAnnotation==null ? null : classAnnotation.collectionName();
entityInfo = new EntityInfo(entityClass, dbCollectionName, properties);
EntityInfo existing = entityInfoCache.putIfAbsent(entityClass, entityInfo);
if (existing != null) {
entityInfo = existing;
}
}
return entityInfo;
}
protected <T extends MongoIdentifiableEntity> List<T> convertCursor(Class<T> type, DBCursor cursor, MongoStoreInvocationContext context) {
List<T> result = new ArrayList<T>();
try {
for (DBObject dbObject : cursor) {
T entity = convertDBObjectToEntity(type, dbObject, context);
result.add(entity);
}
} finally {
cursor.close();
}
return result;
}
protected <T extends MongoIdentifiableEntity> T convertDBObjectToEntity(Class<T> type, DBObject dbObject, MongoStoreInvocationContext context) {
// First look if we already have loaded object cached. If yes, we will use cached instance
String id = dbObject.get("_id").toString();
T object = context.getLoadedEntity(type, id);
if (object == null) {
// So convert and use fresh instance from DB
MapperContext<Object, T> mapperContext = new MapperContext<Object, T>(dbObject, type, null);
object = mapperRegistry.convertDBObjectToApplicationObject(mapperContext);
context.addLoadedEntity(object);
}
return object;
}
protected DBCollection getDBCollectionForType(Class<?> type) {
EntityInfo entityInfo = getEntityInfo(type);
String dbCollectionName = entityInfo.getDbCollectionName();
return dbCollectionName==null ? null : database.getCollection(dbCollectionName);
}
}
package org.keycloak.connections.mongo.impl;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
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;
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
import org.keycloak.connections.mongo.api.MongoStore;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.connections.mongo.api.context.MongoTask;
import org.keycloak.connections.mongo.api.types.Mapper;
import org.keycloak.connections.mongo.api.types.MapperContext;
import org.keycloak.connections.mongo.api.types.MapperRegistry;
import org.keycloak.connections.mongo.impl.types.BasicDBListMapper;
import org.keycloak.connections.mongo.impl.types.BasicDBListToSetMapper;
import org.keycloak.connections.mongo.impl.types.BasicDBObjectMapper;
import org.keycloak.connections.mongo.impl.types.BasicDBObjectToMapMapper;
import org.keycloak.connections.mongo.impl.types.EnumToStringMapper;
import org.keycloak.connections.mongo.impl.types.ListMapper;
import org.keycloak.connections.mongo.impl.types.MapMapper;
import org.keycloak.connections.mongo.impl.types.MongoEntityMapper;
import org.keycloak.connections.mongo.impl.types.SimpleMapper;
import org.keycloak.connections.mongo.impl.types.StringToEnumMapper;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.ModelException;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.reflection.Property;
import org.keycloak.models.utils.reflection.PropertyQueries;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class MongoStoreImpl implements MongoStore {
private static final Class<?>[] SIMPLE_TYPES = { String.class, Integer.class, Boolean.class, Long.class, Double.class, Character.class, Date.class, byte[].class };
private final DB database;
private static final Logger logger = Logger.getLogger(MongoStoreImpl.class);
private final MapperRegistry mapperRegistry;
private ConcurrentMap<Class<?>, EntityInfo> entityInfoCache =
new ConcurrentHashMap<Class<?>, EntityInfo>();
public MongoStoreImpl(DB database, Class<?>[] managedEntityTypes) {
this.database = database;
mapperRegistry = new MapperRegistry();
for (Class<?> simpleMapperClass : SIMPLE_TYPES) {
SimpleMapper mapper = new SimpleMapper(simpleMapperClass);
mapperRegistry.addAppObjectMapper(mapper);
mapperRegistry.addDBObjectMapper(mapper);
}
// Specific converter for ArrayList is added just for performance purposes to avoid recursive converter lookup (most of list idm will be ArrayList)
mapperRegistry.addAppObjectMapper(new ListMapper(mapperRegistry, ArrayList.class));
mapperRegistry.addAppObjectMapper(new ListMapper(mapperRegistry, List.class));
mapperRegistry.addDBObjectMapper(new BasicDBListMapper(mapperRegistry));
mapperRegistry.addAppObjectMapper(new ListMapper(mapperRegistry, HashSet.class));
mapperRegistry.addAppObjectMapper(new ListMapper(mapperRegistry, Set.class));
mapperRegistry.addDBObjectMapper(new BasicDBListToSetMapper(mapperRegistry));
mapperRegistry.addAppObjectMapper(new MapMapper(mapperRegistry, HashMap.class));
mapperRegistry.addAppObjectMapper(new MapMapper(mapperRegistry, Map.class));
mapperRegistry.addDBObjectMapper(new BasicDBObjectToMapMapper(mapperRegistry));
// Enum converters
mapperRegistry.addAppObjectMapper(new EnumToStringMapper());
mapperRegistry.addDBObjectMapper(new StringToEnumMapper());
for (Class<?> type : managedEntityTypes) {
getEntityInfo(type);
mapperRegistry.addAppObjectMapper(new MongoEntityMapper(this, mapperRegistry, type));
mapperRegistry.addDBObjectMapper(new BasicDBObjectMapper(this, mapperRegistry, type));
}
}
protected void dropDatabase() {
this.database.dropDatabase();
logger.info("Database " + this.database.getName() + " dropped in MongoDB");
}
@Override
public void insertEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
Class<? extends MongoEntity> clazz = entity.getClass();
// Find annotations for ID, for all the properties and for the name of the collection.
EntityInfo entityInfo = getEntityInfo(clazz);
// Create instance of BasicDBObject and add all declared properties to it (properties with null value probably should be skipped)
BasicDBObject dbObject = mapperRegistry.convertApplicationObjectToDBObject(entity, BasicDBObject.class);
DBCollection dbCollection = database.getCollection(entityInfo.getDbCollectionName());
String currentId = entity.getId();
// Generate random ID if not set already
if (currentId == null) {
currentId = KeycloakModelUtils.generateId();
entity.setId(currentId);
}
// Adding "_id"
dbObject.put("_id", currentId);
try {
dbCollection.insert(dbObject);
} catch (MongoException e) {
throw convertException(e);
}
// Treat object as created in this transaction (It is already submitted to transaction)
context.addCreatedEntity(entity);
}
public static ModelException convertException(MongoException e) {
if (e instanceof MongoException.DuplicateKey) {
return new ModelDuplicateException(e);
} else {
return new ModelException(e);
}
}
@Override
public void updateEntity(final MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
MongoTask fullUpdateTask = new MongoTask() {
@Override
public void execute() {
Class<? extends MongoEntity> clazz = entity.getClass();
EntityInfo entityInfo = getEntityInfo(clazz);
BasicDBObject dbObject = mapperRegistry.convertApplicationObjectToDBObject(entity, BasicDBObject.class);
DBCollection dbCollection = database.getCollection(entityInfo.getDbCollectionName());
String currentId = entity.getId();
if (currentId == null) {
throw new IllegalStateException("Can't update entity without id: " + entity);
} else {
BasicDBObject query = new BasicDBObject("_id", currentId);
dbCollection.update(query, dbObject);
}
}
@Override
public boolean isFullUpdate() {
return true;
}
};
// update is just added to context and postponed
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) {
// First look if we already read the object with this oid and type during this transaction. If yes, use it instead of DB lookup
T cached = context.getLoadedEntity(type, id);
if (cached != null && type.isAssignableFrom(cached.getClass())) return cached;
DBCollection dbCollection = getDBCollectionForType(type);
BasicDBObject idQuery = new BasicDBObject("_id", id);
DBObject dbObject = dbCollection.findOne(idQuery);
if (dbObject == null) return null;
MapperContext<Object, T> mapperContext = new MapperContext<Object, T>(dbObject, type, null);
T converted = mapperRegistry.convertDBObjectToApplicationObject(mapperContext);
// Now add it to loaded objects
context.addLoadedEntity(converted);
return converted;
}
@Override
public <T extends MongoIdentifiableEntity> T loadSingleEntity(Class<T> type, DBObject query, MongoStoreInvocationContext context) {
// First we should execute all pending tasks before searching DB
context.beforeDBSearch(type);
DBCollection dbCollection = getDBCollectionForType(type);
DBObject dbObject = dbCollection.findOne(query);
if (dbObject == null) {
return null;
} else {
return convertDBObjectToEntity(type, dbObject, context);
}
}
@Override
public <T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context) {
// First we should execute all pending tasks before searching DB
context.beforeDBSearch(type);
DBCollection dbCollection = getDBCollectionForType(type);
DBCursor cursor = dbCollection.find(query);
return convertCursor(type, cursor, context);
}
@Override
public <T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, DBObject sort, int firstResult, int maxResults, MongoStoreInvocationContext context) {
// First we should execute all pending tasks before searching DB
context.beforeDBSearch(type);
DBCollection dbCollection = getDBCollectionForType(type);
DBCursor cursor = dbCollection.find(query);
if (firstResult != -1) {
cursor.skip(firstResult);
}
if (maxResults != -1) {
cursor.limit(maxResults);
}
if (sort != null) {
cursor.sort(sort);
}
return convertCursor(type, cursor, context);
}
public <T extends MongoIdentifiableEntity> int countEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context) {
context.beforeDBSearch(type);
DBCollection dbCollection = getDBCollectionForType(type);
Long count = dbCollection.count(query);
// For now, assume that int is sufficient
return count.intValue();
}
@Override
public boolean removeEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
return removeEntity(entity.getClass(), entity.getId(), context);
}
@Override
public boolean removeEntity(Class<? extends MongoIdentifiableEntity> type, String id, MongoStoreInvocationContext context) {
MongoIdentifiableEntity found = loadEntity(type, id, context);
if (found == null) {
return false;
} else {
DBCollection dbCollection = getDBCollectionForType(type);
BasicDBObject dbQuery = new BasicDBObject("_id", id);
dbCollection.remove(dbQuery);
//logger.debugf("Entity of type: %s , id: %s removed from MongoDB.", type, id);
context.addRemovedEntity(found);
return true;
}
}
@Override
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);
logger.debugf("Removed %d entities of type: %s, query: %s", foundObjects.size(), type, query);
for (MongoIdentifiableEntity found : foundObjects) {
context.addRemovedEntity(found);;
}
return foundObjects.size();
}
} 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;
}
}
@Override
public <S> boolean pushItemToList(final MongoIdentifiableEntity entity, final String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent, MongoStoreInvocationContext context) {
final Class<? extends MongoEntity> type = entity.getClass();
EntityInfo entityInfo = getEntityInfo(type);
// Add item to list directly in this object
Property<Object> listProperty = entityInfo.getPropertyByName(listPropertyName);
if (listProperty == null) {
throw new IllegalArgumentException("Property " + listPropertyName + " doesn't exist on object " + entity);
}
List<S> list = (List<S>)listProperty.getValue(entity);
if (list == null) {
list = new ArrayList<S>();
listProperty.setValue(entity, list);
}
// Skip if item is already in list
if (skipIfAlreadyPresent && list.contains(itemToPush)) {
return false;
}
// Update java object
list.add(itemToPush);
// Add update of list to pending tasks
final List<S> listt = list;
context.addUpdateTask(entity, new MongoTask() {
@Override
public void execute() {
// Now DB update of new list with usage of $set
BasicDBList dbList = mapperRegistry.convertApplicationObjectToDBObject(listt, BasicDBList.class);
BasicDBObject query = new BasicDBObject("_id", entity.getId());
BasicDBObject listObject = new BasicDBObject(listPropertyName, dbList);
BasicDBObject setCommand = new BasicDBObject("$set", listObject);
getDBCollectionForType(type).update(query, setCommand);
}
@Override
public boolean isFullUpdate() {
return false;
}
});
return true;
}
@Override
public <S> boolean pullItemFromList(final MongoIdentifiableEntity entity, final String listPropertyName, final S itemToPull, MongoStoreInvocationContext context) {
final Class<? extends MongoEntity> type = entity.getClass();
EntityInfo entityInfo = getEntityInfo(type);
// Remove item from list directly in this object
Property<Object> listProperty = entityInfo.getPropertyByName(listPropertyName);
if (listProperty == null) {
throw new IllegalArgumentException("Property " + listPropertyName + " doesn't exist on object " + entity);
}
List<S> list = (List<S>)listProperty.getValue(entity);
// If list is null, we skip both object and DB update
if (list == null || !list.contains(itemToPull)) {
return false;
} else {
// Update java object
list.remove(itemToPull);
// Add update of list to pending tasks
context.addUpdateTask(entity, new MongoTask() {
@Override
public void execute() {
// Pull item from DB
Object dbItemToPull = mapperRegistry.convertApplicationObjectToDBObject(itemToPull, Object.class);
BasicDBObject query = new BasicDBObject("_id", entity.getId());
BasicDBObject pullObject = new BasicDBObject(listPropertyName, dbItemToPull);
BasicDBObject pullCommand = new BasicDBObject("$pull", pullObject);
getDBCollectionForType(type).update(query, pullCommand);
}
@Override
public boolean isFullUpdate() {
return false;
}
});
return true;
}
}
// Possibility to add user-defined mappers
public void addAppObjectConverter(Mapper<?, ?> mapper) {
mapperRegistry.addAppObjectMapper(mapper);
}
public void addDBObjectConverter(Mapper<?, ?> mapper) {
mapperRegistry.addDBObjectMapper(mapper);
}
public EntityInfo getEntityInfo(Class<?> entityClass) {
EntityInfo entityInfo = entityInfoCache.get(entityClass);
if (entityInfo == null) {
Map<String, Property<Object>> properties = PropertyQueries.createQuery(entityClass).getWritableResultList();
MongoCollection classAnnotation = entityClass.getAnnotation(MongoCollection.class);
String dbCollectionName = classAnnotation==null ? null : classAnnotation.collectionName();
entityInfo = new EntityInfo(entityClass, dbCollectionName, properties);
EntityInfo existing = entityInfoCache.putIfAbsent(entityClass, entityInfo);
if (existing != null) {
entityInfo = existing;
}
}
return entityInfo;
}
protected <T extends MongoIdentifiableEntity> List<T> convertCursor(Class<T> type, DBCursor cursor, MongoStoreInvocationContext context) {
List<T> result = new ArrayList<T>();
try {
for (DBObject dbObject : cursor) {
T entity = convertDBObjectToEntity(type, dbObject, context);
result.add(entity);
}
} finally {
cursor.close();
}
return result;
}
protected <T extends MongoIdentifiableEntity> T convertDBObjectToEntity(Class<T> type, DBObject dbObject, MongoStoreInvocationContext context) {
// First look if we already have loaded object cached. If yes, we will use cached instance
String id = dbObject.get("_id").toString();
T object = context.getLoadedEntity(type, id);
if (object == null) {
// So convert and use fresh instance from DB
MapperContext<Object, T> mapperContext = new MapperContext<Object, T>(dbObject, type, null);
object = mapperRegistry.convertDBObjectToApplicationObject(mapperContext);
context.addLoadedEntity(object);
}
return object;
}
protected DBCollection getDBCollectionForType(Class<?> type) {
EntityInfo entityInfo = getEntityInfo(type);
String dbCollectionName = entityInfo.getDbCollectionName();
return dbCollectionName==null ? null : database.getCollection(dbCollectionName);
}
}
@@ -1,45 +1,45 @@
package org.keycloak.connections.mongo.impl.types;
import com.mongodb.BasicDBList;
import org.keycloak.connections.mongo.api.types.Mapper;
import org.keycloak.connections.mongo.api.types.MapperContext;
import org.keycloak.connections.mongo.api.types.MapperRegistry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class BasicDBListMapper implements Mapper<BasicDBList, List> {
private final MapperRegistry mapperRegistry;
public BasicDBListMapper(MapperRegistry mapperRegistry) {
this.mapperRegistry = mapperRegistry;
}
@Override
public List convertObject(MapperContext<BasicDBList, List> context) {
BasicDBList dbList = context.getObjectToConvert();
ArrayList<Object> appObjects = new ArrayList<Object>();
Class<?> expectedListElementType = context.getGenericTypes().get(0);
for (Object dbObject : dbList) {
MapperContext<Object, Object> newContext = new MapperContext<Object, Object>(dbObject, expectedListElementType, null);
appObjects.add(mapperRegistry.convertDBObjectToApplicationObject(newContext));
}
return appObjects;
}
@Override
public Class<? extends BasicDBList> getTypeOfObjectToConvert() {
return BasicDBList.class;
}
@Override
public Class<List> getExpectedReturnType() {
return List.class;
}
}
package org.keycloak.connections.mongo.impl.types;
import com.mongodb.BasicDBList;
import org.keycloak.connections.mongo.api.types.Mapper;
import org.keycloak.connections.mongo.api.types.MapperContext;
import org.keycloak.connections.mongo.api.types.MapperRegistry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class BasicDBListMapper implements Mapper<BasicDBList, List> {
private final MapperRegistry mapperRegistry;
public BasicDBListMapper(MapperRegistry mapperRegistry) {
this.mapperRegistry = mapperRegistry;
}
@Override
public List convertObject(MapperContext<BasicDBList, List> context) {
BasicDBList dbList = context.getObjectToConvert();
ArrayList<Object> appObjects = new ArrayList<Object>();
Class<?> expectedListElementType = (Class<?>) context.getGenericTypes().get(0);
for (Object dbObject : dbList) {
MapperContext<Object, Object> newContext = new MapperContext<Object, Object>(dbObject, expectedListElementType, null);
appObjects.add(mapperRegistry.convertDBObjectToApplicationObject(newContext));
}
return appObjects;
}
@Override
public Class<? extends BasicDBList> getTypeOfObjectToConvert() {
return BasicDBList.class;
}
@Override
public Class<List> getExpectedReturnType() {
return List.class;
}
}
@@ -23,7 +23,7 @@ public class BasicDBListToSetMapper implements Mapper<BasicDBList, Set> {
public Set convertObject(MapperContext<BasicDBList, Set> context) {
BasicDBList dbList = context.getObjectToConvert();
Set<Object> appObjects = new HashSet<Object>();
Class<?> expectedListElementType = context.getGenericTypes().get(0);
Class<?> expectedListElementType = (Class<?>) context.getGenericTypes().get(0);
for (Object dbObject : dbList) {
MapperContext<Object, Object> newContext = new MapperContext<Object, Object>(dbObject, expectedListElementType, null);
@@ -14,6 +14,7 @@ import org.keycloak.util.reflections.Types;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
@@ -87,10 +88,7 @@ public class BasicDBObjectMapper<S> implements Mapper<BasicDBObject, S> {
ParameterizedType parameterized = (ParameterizedType) type;
Type[] genericTypeArguments = parameterized.getActualTypeArguments();
List<Class<?>> genericTypes = new ArrayList<Class<?>>();
for (Type genericType : genericTypeArguments) {
genericTypes.add((Class<?>)genericType);
}
List<Type> genericTypes = Arrays.asList(genericTypeArguments);
Class<?> expectedReturnType = (Class<?>)parameterized.getRawType();
context = new MapperContext<Object, Object>(valueFromDB, expectedReturnType, genericTypes);
@@ -3,31 +3,44 @@ package org.keycloak.connections.mongo.impl.types;
import com.mongodb.BasicDBObject;
import org.keycloak.connections.mongo.api.types.Mapper;
import org.keycloak.connections.mongo.api.types.MapperContext;
import org.keycloak.connections.mongo.api.types.MapperRegistry;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* For now, there is support just for convert to Map<String, simpleType>
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class BasicDBObjectToMapMapper implements Mapper<BasicDBObject, Map> {
private final MapperRegistry mapperRegistry;
public BasicDBObjectToMapMapper(MapperRegistry mapperRegistry) {
this.mapperRegistry = mapperRegistry;
}
@Override
public Map convertObject(MapperContext<BasicDBObject, Map> context) {
BasicDBObject dbObjectToConvert = context.getObjectToConvert();
Type expectedElementValueType = context.getGenericTypes().get(1);
HashMap<String, Object> result = new HashMap<String, Object>();
for (Map.Entry<String, Object> entry : dbObjectToConvert.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Object dbValue = entry.getValue();
// Workaround as manually inserted numbers into mongo may be treated as "Double"
if (value instanceof Double && context.getGenericTypes().get(1) == Integer.class) {
value = ((Double)value).intValue();
if (dbValue instanceof Double && expectedElementValueType == Integer.class) {
dbValue = ((Double)dbValue).intValue();
}
MapperContext<Object, Object> newContext = getMapperContext(dbValue, expectedElementValueType);
Object value = mapperRegistry.convertDBObjectToApplicationObject(newContext);
if (key.contains(MapMapper.DOT_PLACEHOLDER)) {
key = key.replaceAll(MapMapper.DOT_PLACEHOLDER, ".");
}
@@ -46,4 +59,19 @@ public class BasicDBObjectToMapMapper implements Mapper<BasicDBObject, Map> {
public Class<Map> getExpectedReturnType() {
return Map.class;
}
private MapperContext<Object, Object> getMapperContext(Object dbValue, Type expectedElementValueType) {
if (expectedElementValueType instanceof Class) {
Class<?> clazz = (Class<?>) expectedElementValueType;
return new MapperContext<>(dbValue, clazz, null);
} else if (expectedElementValueType instanceof ParameterizedType) {
ParameterizedType parameterized = (ParameterizedType) expectedElementValueType;
Class<?> expectedClazz = (Class<?>) parameterized.getRawType();
Type[] generics = parameterized.getActualTypeArguments();
return new MapperContext<>(dbValue, expectedClazz, Arrays.asList(generics));
} else {
throw new IllegalArgumentException("Unexpected type: '" + expectedElementValueType + "' for converting " + dbValue);
}
}
}
@@ -1,45 +1,45 @@
package org.keycloak.connections.mongo.impl.types;
import com.mongodb.BasicDBList;
import org.keycloak.connections.mongo.api.types.Mapper;
import org.keycloak.connections.mongo.api.types.MapperContext;
import org.keycloak.connections.mongo.api.types.MapperRegistry;
import java.util.Collection;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ListMapper<T extends Collection> implements Mapper<T, BasicDBList> {
private final MapperRegistry mapperRegistry;
private final Class<T> listType;
public ListMapper(MapperRegistry mapperRegistry, Class<T> listType) {
this.mapperRegistry = mapperRegistry;
this.listType = listType;
}
@Override
public BasicDBList convertObject(MapperContext<T, BasicDBList> context) {
T appObjectsList = context.getObjectToConvert();
BasicDBList dbObjects = new BasicDBList();
for (Object appObject : appObjectsList) {
Object dbObject = mapperRegistry.convertApplicationObjectToDBObject(appObject, Object.class);
dbObjects.add(dbObject);
}
return dbObjects;
}
@Override
public Class<? extends T> getTypeOfObjectToConvert() {
return listType;
}
@Override
public Class<BasicDBList> getExpectedReturnType() {
return BasicDBList.class;
}
}
package org.keycloak.connections.mongo.impl.types;
import com.mongodb.BasicDBList;
import org.keycloak.connections.mongo.api.types.Mapper;
import org.keycloak.connections.mongo.api.types.MapperContext;
import org.keycloak.connections.mongo.api.types.MapperRegistry;
import java.util.Collection;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ListMapper<T extends Collection> implements Mapper<T, BasicDBList> {
private final MapperRegistry mapperRegistry;
private final Class<T> listType;
public ListMapper(MapperRegistry mapperRegistry, Class<T> listType) {
this.mapperRegistry = mapperRegistry;
this.listType = listType;
}
@Override
public BasicDBList convertObject(MapperContext<T, BasicDBList> context) {
T appObjectsList = context.getObjectToConvert();
BasicDBList dbObjects = new BasicDBList();
for (Object appObject : appObjectsList) {
Object dbObject = mapperRegistry.convertApplicationObjectToDBObject(appObject, Object.class);
dbObjects.add(dbObject);
}
return dbObjects;
}
@Override
public Class<? extends T> getTypeOfObjectToConvert() {
return listType;
}
@Override
public Class<BasicDBList> getExpectedReturnType() {
return BasicDBList.class;
}
}
@@ -3,6 +3,7 @@ package org.keycloak.connections.mongo.impl.types;
import com.mongodb.BasicDBObject;
import org.keycloak.connections.mongo.api.types.Mapper;
import org.keycloak.connections.mongo.api.types.MapperContext;
import org.keycloak.connections.mongo.api.types.MapperRegistry;
import java.util.Map;
import java.util.Set;
@@ -17,30 +18,34 @@ public class MapMapper<T extends Map> implements Mapper<T, BasicDBObject> {
// Just some dummy way of encoding . character as it's not allowed by mongo in key fields
static final String DOT_PLACEHOLDER = "###";
private final MapperRegistry mapperRegistry;
private final Class<T> mapType;
public MapMapper(Class<T> mapType) {
public MapMapper(MapperRegistry mapperRegistry, Class<T> mapType) {
this.mapperRegistry = mapperRegistry;
this.mapType = mapType;
}
@Override
public BasicDBObject convertObject(MapperContext<T, BasicDBObject> context) {
T mapToConvert = context.getObjectToConvert();
return convertMap(mapToConvert);
return convertMap(mapToConvert, mapperRegistry);
}
public static BasicDBObject convertMap(Map mapToConvert) {
public static BasicDBObject convertMap(Map mapToConvert, MapperRegistry mapperRegistry) {
BasicDBObject dbObject = new BasicDBObject();
Set<Map.Entry> entries = mapToConvert.entrySet();
for (Map.Entry entry : entries) {
String key = (String)entry.getKey();
Object value = entry.getValue();
Object dbValue = mapperRegistry==null ? entry.getValue() : mapperRegistry.convertApplicationObjectToDBObject(value, Object.class);
if (key.contains(".")) {
key = key.replaceAll("\\.", DOT_PLACEHOLDER);
}
dbObject.put(key, value);
dbObject.put(key, dbValue);
}
return dbObject;
}
@@ -1,63 +1,63 @@
package org.keycloak.connections.mongo.impl.types;
import com.mongodb.BasicDBObject;
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
import org.keycloak.connections.mongo.api.types.Mapper;
import org.keycloak.connections.mongo.api.types.MapperContext;
import org.keycloak.connections.mongo.api.types.MapperRegistry;
import org.keycloak.connections.mongo.impl.EntityInfo;
import org.keycloak.connections.mongo.impl.MongoStoreImpl;
import org.keycloak.models.utils.reflection.Property;
import java.util.Collection;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class MongoEntityMapper<T> implements Mapper<T, BasicDBObject> {
private final MongoStoreImpl mongoStoreImpl;
private final MapperRegistry mapperRegistry;
private final Class<T> expectedMongoEntityType;
public MongoEntityMapper(MongoStoreImpl mongoStoreImpl, MapperRegistry mapperRegistry, Class<T> expectedMongoEntityType) {
this.mongoStoreImpl = mongoStoreImpl;
this.mapperRegistry = mapperRegistry;
this.expectedMongoEntityType = expectedMongoEntityType;
}
@Override
public BasicDBObject convertObject(MapperContext<T, BasicDBObject> context) {
T applicationObject = context.getObjectToConvert();
EntityInfo entityInfo = mongoStoreImpl.getEntityInfo(applicationObject.getClass());
// Create instance of BasicDBObject and add all declared properties to it
BasicDBObject dbObject = new BasicDBObject();
Collection<Property<Object>> props = entityInfo.getProperties();
for (Property<Object> property : props) {
String propName = property.getName();
// Ignore "id" property
if (!"id".equals(propName) || !(applicationObject instanceof MongoIdentifiableEntity)) {
Object propValue = property.getValue(applicationObject);
if (propValue != null) {
Object dbValue = propValue == null ? null : mapperRegistry.convertApplicationObjectToDBObject(propValue, Object.class);
dbObject.put(propName, dbValue);
}
}
}
return dbObject;
}
@Override
public Class<? extends T> getTypeOfObjectToConvert() {
return expectedMongoEntityType;
}
@Override
public Class<BasicDBObject> getExpectedReturnType() {
return BasicDBObject.class;
}
}
package org.keycloak.connections.mongo.impl.types;
import com.mongodb.BasicDBObject;
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
import org.keycloak.connections.mongo.api.types.Mapper;
import org.keycloak.connections.mongo.api.types.MapperContext;
import org.keycloak.connections.mongo.api.types.MapperRegistry;
import org.keycloak.connections.mongo.impl.EntityInfo;
import org.keycloak.connections.mongo.impl.MongoStoreImpl;
import org.keycloak.models.utils.reflection.Property;
import java.util.Collection;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class MongoEntityMapper<T> implements Mapper<T, BasicDBObject> {
private final MongoStoreImpl mongoStoreImpl;
private final MapperRegistry mapperRegistry;
private final Class<T> expectedMongoEntityType;
public MongoEntityMapper(MongoStoreImpl mongoStoreImpl, MapperRegistry mapperRegistry, Class<T> expectedMongoEntityType) {
this.mongoStoreImpl = mongoStoreImpl;
this.mapperRegistry = mapperRegistry;
this.expectedMongoEntityType = expectedMongoEntityType;
}
@Override
public BasicDBObject convertObject(MapperContext<T, BasicDBObject> context) {
T applicationObject = context.getObjectToConvert();
EntityInfo entityInfo = mongoStoreImpl.getEntityInfo(applicationObject.getClass());
// Create instance of BasicDBObject and add all declared properties to it
BasicDBObject dbObject = new BasicDBObject();
Collection<Property<Object>> props = entityInfo.getProperties();
for (Property<Object> property : props) {
String propName = property.getName();
// Ignore "id" property
if (!"id".equals(propName) || !(applicationObject instanceof MongoIdentifiableEntity)) {
Object propValue = property.getValue(applicationObject);
if (propValue != null) {
Object dbValue = propValue == null ? null : mapperRegistry.convertApplicationObjectToDBObject(propValue, Object.class);
dbObject.put(propName, dbValue);
}
}
}
return dbObject;
}
@Override
public Class<? extends T> getTypeOfObjectToConvert() {
return expectedMongoEntityType;
}
@Override
public Class<BasicDBObject> getExpectedReturnType() {
return BasicDBObject.class;
}
}
+36 -36
View File
@@ -1,36 +1,36 @@
<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>
</parent>
<name>Connections Parent</name>
<description/>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-pom</artifactId>
<packaging>pom</packaging>
<modules>
<module>jpa</module>
<module>jpa-liquibase</module>
<module>infinispan</module>
<module>mongo</module>
<module>file</module>
<module>mongo-update</module>
<module>http-client</module>
</modules>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final</version>
</parent>
<name>Connections Parent</name>
<description/>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-connections-pom</artifactId>
<packaging>pom</packaging>
<modules>
<module>jpa</module>
<module>jpa-liquibase</module>
<module>infinispan</module>
<module>mongo</module>
<module>file</module>
<module>mongo-update</module>
<module>http-client</module>
</modules>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
+66 -66
View File
@@ -1,66 +1,66 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-core-jaxrs</artifactId>
<name>Keycloak Core JAX-RS</name>
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
<?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</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-core-jaxrs</artifactId>
<name>Keycloak Core JAX-RS</name>
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -1,37 +1,37 @@
package org.keycloak;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
/**
* Any class with package org.jboss.resteasy.skeleton.key will use NON_DEFAULT inclusion
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Provider
public class SkeletonKeyContextResolver implements ContextResolver<ObjectMapper> {
protected ObjectMapper mapper = new ObjectMapper();
public SkeletonKeyContextResolver() {
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
}
public SkeletonKeyContextResolver(boolean indent) {
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
if (indent) {
mapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);
}
}
@Override
public ObjectMapper getContext(Class<?> type) {
if (type.getPackage() != null && type.getPackage().getName().startsWith(getClass().getPackage().getName())) return mapper;
return null;
}
}
package org.keycloak;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
/**
* Any class with package org.jboss.resteasy.skeleton.key will use NON_DEFAULT inclusion
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Provider
public class SkeletonKeyContextResolver implements ContextResolver<ObjectMapper> {
protected ObjectMapper mapper = new ObjectMapper();
public SkeletonKeyContextResolver() {
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
}
public SkeletonKeyContextResolver(boolean indent) {
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
if (indent) {
mapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);
}
}
@Override
public ObjectMapper getContext(Class<?> type) {
if (type.getPackage() != null && type.getPackage().getName().startsWith(getClass().getPackage().getName())) return mapper;
return null;
}
}
+112 -112
View File
@@ -1,112 +1,112 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-core</artifactId>
<name>Keycloak Core</name>
<packaging>jar</packaging>
<description/>
<properties>
<timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
<keycloak.osgi.export>
org.keycloak.*
</keycloak.osgi.export>
<keycloak.osgi.import>
net.iharder;version=${base64.version},
*;resolution:=optional
</keycloak.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.iharder</groupId>
<artifactId>base64</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-ClassPath>.</Bundle-ClassPath>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
<?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</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-core</artifactId>
<name>Keycloak Core</name>
<packaging>jar</packaging>
<description/>
<properties>
<timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
<keycloak.osgi.export>
org.keycloak.*
</keycloak.osgi.export>
<keycloak.osgi.import>
net.iharder;version=${base64.version},
*;resolution:=optional
</keycloak.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.iharder</groupId>
<artifactId>base64</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-ClassPath>.</Bundle-ClassPath>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -1,111 +1,111 @@
package org.keycloak;
import org.keycloak.enums.RelativeUrlsUsed;
import org.keycloak.util.KeycloakUriBuilder;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AbstractOAuthClient {
private static final String OAUTH_TOKEN_REQUEST_STATE = "OAuth_Token_Request_State";
private final AtomicLong counter = new AtomicLong();
protected String clientId;
protected Map<String, String> credentials;
protected String authUrl;
protected String tokenUrl;
protected RelativeUrlsUsed relativeUrlsUsed;
protected String scope;
protected String stateCookieName = OAUTH_TOKEN_REQUEST_STATE;
protected String stateCookiePath;
protected boolean isSecure;
protected boolean publicClient;
protected String getStateCode() {
return counter.getAndIncrement() + "/" + UUID.randomUUID().toString();
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public Map<String, String> getCredentials() {
return credentials;
}
public void setCredentials(Map<String, String> credentials) {
this.credentials = credentials;
}
public String getAuthUrl() {
return authUrl;
}
public void setAuthUrl(String authUrl) {
this.authUrl = authUrl;
}
public String getTokenUrl() {
return tokenUrl;
}
public void setTokenUrl(String tokenUrl) {
this.tokenUrl = tokenUrl;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getStateCookieName() {
return stateCookieName;
}
public void setStateCookieName(String stateCookieName) {
this.stateCookieName = stateCookieName;
}
public String getStateCookiePath() {
return stateCookiePath;
}
public void setStateCookiePath(String stateCookiePath) {
this.stateCookiePath = stateCookiePath;
}
public boolean isPublicClient() {
return publicClient;
}
public void setPublicClient(boolean publicClient) {
this.publicClient = publicClient;
}
public RelativeUrlsUsed getRelativeUrlsUsed() {
return relativeUrlsUsed;
}
public void setRelativeUrlsUsed(RelativeUrlsUsed relativeUrlsUsed) {
this.relativeUrlsUsed = relativeUrlsUsed;
}
protected String stripOauthParametersFromRedirect(String uri) {
KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(uri)
.replaceQueryParam(OAuth2Constants.CODE, null)
.replaceQueryParam(OAuth2Constants.STATE, null);
return builder.build().toString();
}
}
package org.keycloak;
import org.keycloak.enums.RelativeUrlsUsed;
import org.keycloak.util.KeycloakUriBuilder;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AbstractOAuthClient {
private static final String OAUTH_TOKEN_REQUEST_STATE = "OAuth_Token_Request_State";
private final AtomicLong counter = new AtomicLong();
protected String clientId;
protected Map<String, String> credentials;
protected String authUrl;
protected String tokenUrl;
protected RelativeUrlsUsed relativeUrlsUsed;
protected String scope;
protected String stateCookieName = OAUTH_TOKEN_REQUEST_STATE;
protected String stateCookiePath;
protected boolean isSecure;
protected boolean publicClient;
protected String getStateCode() {
return counter.getAndIncrement() + "/" + UUID.randomUUID().toString();
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public Map<String, String> getCredentials() {
return credentials;
}
public void setCredentials(Map<String, String> credentials) {
this.credentials = credentials;
}
public String getAuthUrl() {
return authUrl;
}
public void setAuthUrl(String authUrl) {
this.authUrl = authUrl;
}
public String getTokenUrl() {
return tokenUrl;
}
public void setTokenUrl(String tokenUrl) {
this.tokenUrl = tokenUrl;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getStateCookieName() {
return stateCookieName;
}
public void setStateCookieName(String stateCookieName) {
this.stateCookieName = stateCookieName;
}
public String getStateCookiePath() {
return stateCookiePath;
}
public void setStateCookiePath(String stateCookiePath) {
this.stateCookiePath = stateCookiePath;
}
public boolean isPublicClient() {
return publicClient;
}
public void setPublicClient(boolean publicClient) {
this.publicClient = publicClient;
}
public RelativeUrlsUsed getRelativeUrlsUsed() {
return relativeUrlsUsed;
}
public void setRelativeUrlsUsed(RelativeUrlsUsed relativeUrlsUsed) {
this.relativeUrlsUsed = relativeUrlsUsed;
}
protected String stripOauthParametersFromRedirect(String uri) {
KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(uri)
.replaceQueryParam(OAuth2Constants.CODE, null)
.replaceQueryParam(OAuth2Constants.STATE, null);
return builder.build().toString();
}
}
@@ -1,13 +1,13 @@
package org.keycloak;
/**
* Information about the client connection
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface ClientConnection {
String getRemoteAddr();
String getRemoteHost();
int getReportPort();
}
package org.keycloak;
/**
* Information about the client connection
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface ClientConnection {
String getRemoteAddr();
String getRemoteHost();
int getReportPort();
}
+163 -163
View File
@@ -1,163 +1,163 @@
package org.keycloak;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Config {
private static ConfigProvider configProvider = new SystemPropertiesConfigProvider();
public static void init(ConfigProvider configProvider) {
Config.configProvider = configProvider;
}
public static String getAdminRealm() {
return configProvider.scope("admin").get("realm", "master");
}
public static String getProvider(String spi) {
String provider = configProvider.getProvider(spi);
if (provider == null || provider.trim().equals("")) {
return null;
} else {
return provider;
}
}
public static Scope scope(String... scope) {
return configProvider.scope(scope);
}
public static interface ConfigProvider {
String getProvider(String spi);
Scope scope(String... scope);
}
public static class SystemPropertiesConfigProvider implements ConfigProvider {
@Override
public String getProvider(String spi) {
return System.getProperties().getProperty("keycloak." + spi + ".provider");
}
@Override
public Scope scope(String... scope) {
StringBuilder sb = new StringBuilder();
sb.append("keycloak.");
for (String s : scope) {
sb.append(s);
sb.append(".");
}
return new SystemPropertiesScope(sb.toString());
}
}
public static class SystemPropertiesScope implements Scope {
private String prefix;
public SystemPropertiesScope(String prefix) {
this.prefix = prefix;
}
@Override
public String get(String key) {
return get(key, null);
}
@Override
public String get(String key, String defaultValue) {
return System.getProperty(prefix + key, defaultValue);
}
@Override
public String[] getArray(String key) {
String value = get(key);
if (value != null) {
String[] a = value.split(",");
for (int i = 0; i < a.length; i++) {
a[i] = a[i].trim();
}
return a;
} else {
return null;
}
}
@Override
public Integer getInt(String key) {
return getInt(key, null);
}
@Override
public Integer getInt(String key, Integer defaultValue) {
String v = get(key, null);
return v != null ? Integer.parseInt(v) : defaultValue;
}
@Override
public Long getLong(String key) {
return getLong(key, null);
}
@Override
public Long getLong(String key, Long defaultValue) {
String v = get(key, null);
return v != null ? Long.parseLong(v) : defaultValue;
}
@Override
public Boolean getBoolean(String key) {
return getBoolean(key, null);
}
@Override
public Boolean getBoolean(String key, Boolean defaultValue) {
String v = get(key, null);
return v != null ? Boolean.parseBoolean(v) : defaultValue;
}
@Override
public Scope scope(String... scope) {
StringBuilder sb = new StringBuilder();
sb.append(prefix + ".");
for (String s : scope) {
sb.append(s);
sb.append(".");
}
return new SystemPropertiesScope(sb.toString());
}
}
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public static interface Scope {
String get(String key);
String get(String key, String defaultValue);
String[] getArray(String key);
Integer getInt(String key);
Integer getInt(String key, Integer defaultValue);
Long getLong(String key);
Long getLong(String key, Long defaultValue);
Boolean getBoolean(String key);
Boolean getBoolean(String key, Boolean defaultValue);
Scope scope(String... scope);
}
}
package org.keycloak;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Config {
private static ConfigProvider configProvider = new SystemPropertiesConfigProvider();
public static void init(ConfigProvider configProvider) {
Config.configProvider = configProvider;
}
public static String getAdminRealm() {
return configProvider.scope("admin").get("realm", "master");
}
public static String getProvider(String spi) {
String provider = configProvider.getProvider(spi);
if (provider == null || provider.trim().equals("")) {
return null;
} else {
return provider;
}
}
public static Scope scope(String... scope) {
return configProvider.scope(scope);
}
public static interface ConfigProvider {
String getProvider(String spi);
Scope scope(String... scope);
}
public static class SystemPropertiesConfigProvider implements ConfigProvider {
@Override
public String getProvider(String spi) {
return System.getProperties().getProperty("keycloak." + spi + ".provider");
}
@Override
public Scope scope(String... scope) {
StringBuilder sb = new StringBuilder();
sb.append("keycloak.");
for (String s : scope) {
sb.append(s);
sb.append(".");
}
return new SystemPropertiesScope(sb.toString());
}
}
public static class SystemPropertiesScope implements Scope {
private String prefix;
public SystemPropertiesScope(String prefix) {
this.prefix = prefix;
}
@Override
public String get(String key) {
return get(key, null);
}
@Override
public String get(String key, String defaultValue) {
return System.getProperty(prefix + key, defaultValue);
}
@Override
public String[] getArray(String key) {
String value = get(key);
if (value != null) {
String[] a = value.split(",");
for (int i = 0; i < a.length; i++) {
a[i] = a[i].trim();
}
return a;
} else {
return null;
}
}
@Override
public Integer getInt(String key) {
return getInt(key, null);
}
@Override
public Integer getInt(String key, Integer defaultValue) {
String v = get(key, null);
return v != null ? Integer.parseInt(v) : defaultValue;
}
@Override
public Long getLong(String key) {
return getLong(key, null);
}
@Override
public Long getLong(String key, Long defaultValue) {
String v = get(key, null);
return v != null ? Long.parseLong(v) : defaultValue;
}
@Override
public Boolean getBoolean(String key) {
return getBoolean(key, null);
}
@Override
public Boolean getBoolean(String key, Boolean defaultValue) {
String v = get(key, null);
return v != null ? Boolean.parseBoolean(v) : defaultValue;
}
@Override
public Scope scope(String... scope) {
StringBuilder sb = new StringBuilder();
sb.append(prefix + ".");
for (String s : scope) {
sb.append(s);
sb.append(".");
}
return new SystemPropertiesScope(sb.toString());
}
}
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public static interface Scope {
String get(String key);
String get(String key, String defaultValue);
String[] getArray(String key);
Integer getInt(String key);
Integer getInt(String key, Integer defaultValue);
Long getLong(String key);
Long getLong(String key, Long defaultValue);
Boolean getBoolean(String key);
Boolean getBoolean(String key, Boolean defaultValue);
Scope scope(String... scope);
}
}
@@ -1,49 +1,49 @@
package org.keycloak;
import java.io.Serializable;
import java.security.Principal;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakPrincipal<T extends KeycloakSecurityContext> implements Principal, Serializable {
protected final String name;
protected final T context;
public KeycloakPrincipal(String name, T context) {
this.name = name;
this.context = context;
}
public T getKeycloakSecurityContext() {
return context;
}
@Override
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KeycloakPrincipal that = (KeycloakPrincipal) o;
if (!name.equals(that.name)) return false;
return true;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public String toString() {
return name;
}
}
package org.keycloak;
import java.io.Serializable;
import java.security.Principal;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakPrincipal<T extends KeycloakSecurityContext> implements Principal, Serializable {
protected final String name;
protected final T context;
public KeycloakPrincipal(String name, T context) {
this.name = name;
this.context = context;
}
public T getKeycloakSecurityContext() {
return context;
}
@Override
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KeycloakPrincipal that = (KeycloakPrincipal) o;
if (!name.equals(that.name)) return false;
return true;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public String toString() {
return name;
}
}
@@ -1,82 +1,82 @@
package org.keycloak;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import org.keycloak.util.Base64Url;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakSecurityContext implements Serializable {
protected String tokenString;
protected String idTokenString;
// Don't store parsed tokens into HTTP session
protected transient AccessToken token;
protected transient IDToken idToken;
public KeycloakSecurityContext() {
}
public KeycloakSecurityContext(String tokenString, AccessToken token, String idTokenString, IDToken idToken) {
this.tokenString = tokenString;
this.token = token;
this.idToken = idToken;
this.idTokenString = idTokenString;
}
public AccessToken getToken() {
return token;
}
public String getTokenString() {
return tokenString;
}
public IDToken getIdToken() {
return idToken;
}
public String getIdTokenString() {
return idTokenString;
}
public String getRealm() {
// Assumption that issuer contains realm name
return token.getIssuer().substring(token.getIssuer().lastIndexOf('/') + 1);
}
// SERIALIZATION
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
token = parseToken(tokenString, AccessToken.class);
idToken = parseToken(idTokenString, IDToken.class);
}
// Just decode token without any verifications
private <T> T parseToken(String encoded, Class<T> clazz) throws IOException {
if (encoded == null)
return null;
String[] parts = encoded.split("\\.");
if (parts.length < 2 || parts.length > 3) throw new IllegalArgumentException("Parsing error");
byte[] bytes = Base64Url.decode(parts[1]);
return JsonSerialization.readValue(bytes, clazz);
}
}
package org.keycloak;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import org.keycloak.util.Base64Url;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakSecurityContext implements Serializable {
protected String tokenString;
protected String idTokenString;
// Don't store parsed tokens into HTTP session
protected transient AccessToken token;
protected transient IDToken idToken;
public KeycloakSecurityContext() {
}
public KeycloakSecurityContext(String tokenString, AccessToken token, String idTokenString, IDToken idToken) {
this.tokenString = tokenString;
this.token = token;
this.idToken = idToken;
this.idTokenString = idTokenString;
}
public AccessToken getToken() {
return token;
}
public String getTokenString() {
return tokenString;
}
public IDToken getIdToken() {
return idToken;
}
public String getIdTokenString() {
return idTokenString;
}
public String getRealm() {
// Assumption that issuer contains realm name
return token.getIssuer().substring(token.getIssuer().lastIndexOf('/') + 1);
}
// SERIALIZATION
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
token = parseToken(tokenString, AccessToken.class);
idToken = parseToken(idTokenString, IDToken.class);
}
// Just decode token without any verifications
private <T> T parseToken(String encoded, Class<T> clazz) throws IOException {
if (encoded == null)
return null;
String[] parts = encoded.split("\\.");
if (parts.length < 2 || parts.length > 3) throw new IllegalArgumentException("Parsing error");
byte[] bytes = Base64Url.decode(parts[1]);
return JsonSerialization.readValue(bytes, clazz);
}
}
@@ -29,6 +29,8 @@ public interface OAuth2Constants {
String PASSWORD = "password";
String CLIENT_CREDENTIALS = "client_credentials";
}
@@ -1,64 +1,64 @@
package org.keycloak;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class OAuthErrorException extends Exception {
public static final String INVALID_REQUEST = "invalid_request";
public static final String INVALID_CLIENT = "invalid_client";
public static final String INVALID_GRANT = "invalid_grant";
public static final String INVALID_SCOPE = "invalid_grant";
public static final String UNAUTHORIZED_CLIENT = "unauthorized_client";
public static final String UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type";
public OAuthErrorException(String error, String description, String message, Throwable cause) {
super(message, cause);
this.error = error;
this.description = description;
}
public OAuthErrorException(String error, String description, String message) {
super(message);
this.error = error;
this.description = description;
}
public OAuthErrorException(String error, String description) {
super(description);
this.error = error;
this.description = description;
}
public OAuthErrorException(String error, String description, Throwable cause) {
super(description, cause);
this.error = error;
this.description = description;
}
public OAuthErrorException(String error) {
super(error);
this.error = error;
}
public OAuthErrorException(String error, Throwable cause) {
super(error, cause);
this.error = error;
}
protected String error;
protected String description;
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
package org.keycloak;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class OAuthErrorException extends Exception {
public static final String INVALID_REQUEST = "invalid_request";
public static final String INVALID_CLIENT = "invalid_client";
public static final String INVALID_GRANT = "invalid_grant";
public static final String INVALID_SCOPE = "invalid_grant";
public static final String UNAUTHORIZED_CLIENT = "unauthorized_client";
public static final String UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type";
public OAuthErrorException(String error, String description, String message, Throwable cause) {
super(message, cause);
this.error = error;
this.description = description;
}
public OAuthErrorException(String error, String description, String message) {
super(message);
this.error = error;
this.description = description;
}
public OAuthErrorException(String error, String description) {
super(description);
this.error = error;
this.description = description;
}
public OAuthErrorException(String error, String description, Throwable cause) {
super(description, cause);
this.error = error;
this.description = description;
}
public OAuthErrorException(String error) {
super(error);
this.error = error;
}
public OAuthErrorException(String error, Throwable cause) {
super(error, cause);
this.error = error;
}
protected String error;
protected String description;
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
@@ -1,59 +1,59 @@
package org.keycloak;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.representations.AccessToken;
import java.io.IOException;
import java.security.PublicKey;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RSATokenVerifier {
public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realmUrl) throws VerificationException {
return verifyToken(tokenString, realmKey, realmUrl, true);
}
public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realmUrl, boolean checkActive) throws VerificationException {
JWSInput input = null;
try {
input = new JWSInput(tokenString);
} catch (Exception e) {
throw new VerificationException("Couldn't parse token", e);
}
if (!isPublicKeyValid(input, realmKey)) throw new VerificationException("Invalid token signature.");
AccessToken token;
try {
token = input.readJsonContent(AccessToken.class);
} catch (IOException e) {
throw new VerificationException("Couldn't parse token signature", e);
}
String user = token.getSubject();
if (user == null) {
throw new VerificationException("Token user was null.");
}
if (realmUrl == null) {
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. Token issuer is " + token.getIssuer() + ", but URL from configuration is " + realmUrl);
}
if (checkActive && !token.isActive()) {
throw new VerificationException("Token is not active.");
}
return token;
}
private static boolean isPublicKeyValid(JWSInput input, PublicKey realmKey) throws VerificationException {
try {
return RSAProvider.verify(input, realmKey);
} catch (Exception e) {
throw new VerificationException("Token signature not validated.", e);
}
}
}
package org.keycloak;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.representations.AccessToken;
import java.io.IOException;
import java.security.PublicKey;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RSATokenVerifier {
public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realmUrl) throws VerificationException {
return verifyToken(tokenString, realmKey, realmUrl, true);
}
public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realmUrl, boolean checkActive) throws VerificationException {
JWSInput input = null;
try {
input = new JWSInput(tokenString);
} catch (Exception e) {
throw new VerificationException("Couldn't parse token", e);
}
if (!isPublicKeyValid(input, realmKey)) throw new VerificationException("Invalid token signature.");
AccessToken token;
try {
token = input.readJsonContent(AccessToken.class);
} catch (IOException e) {
throw new VerificationException("Couldn't parse token signature", e);
}
String user = token.getSubject();
if (user == null) {
throw new VerificationException("Token user was null.");
}
if (realmUrl == null) {
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. Token issuer is " + token.getIssuer() + ", but URL from configuration is " + realmUrl);
}
if (checkActive && !token.isActive()) {
throw new VerificationException("Token is not active.");
}
return token;
}
private static boolean isPublicKeyValid(JWSInput input, PublicKey realmKey) throws VerificationException {
try {
return RSAProvider.verify(input, realmKey);
} catch (Exception e) {
throw new VerificationException("Token signature not validated.", e);
}
}
}
@@ -1,16 +1,16 @@
package org.keycloak;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class TokenIdGenerator {
private static final AtomicLong counter = new AtomicLong();
public static String generateId() {
return UUID.randomUUID().toString() + "-" + System.currentTimeMillis();
}
}
package org.keycloak;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class TokenIdGenerator {
private static final AtomicLong counter = new AtomicLong();
public static String generateId() {
return UUID.randomUUID().toString() + "-" + System.currentTimeMillis();
}
}
+48 -48
View File
@@ -1,48 +1,48 @@
package org.keycloak;
import org.codehaus.jackson.annotate.JsonProperty;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class Version {
public static String VERSION;
public static String RESOURCES_VERSION;
public static String BUILD_TIME;
public static final String UNKNOWN = "UNKNOWN";
public static final Version SINGLETON;
private final String version = VERSION;
private final String buildTime = BUILD_TIME;
static {
Properties props = new Properties();
InputStream is = Version.class.getResourceAsStream("/keycloak-version.properties");
try {
props.load(is);
VERSION = props.getProperty("version");
BUILD_TIME = props.getProperty("build-time");
RESOURCES_VERSION = VERSION.toLowerCase();
} catch (IOException e) {
VERSION=UNKNOWN;
BUILD_TIME=UNKNOWN;
}
SINGLETON = new Version();
}
@JsonProperty("version")
public String getVersion() {
return version;
}
@JsonProperty("build-time")
public String getBuildTime() {
return buildTime;
}
}
package org.keycloak;
import org.codehaus.jackson.annotate.JsonProperty;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class Version {
public static String VERSION;
public static String RESOURCES_VERSION;
public static String BUILD_TIME;
public static final String UNKNOWN = "UNKNOWN";
public static final Version SINGLETON;
private final String version = VERSION;
private final String buildTime = BUILD_TIME;
static {
Properties props = new Properties();
InputStream is = Version.class.getResourceAsStream("/keycloak-version.properties");
try {
props.load(is);
VERSION = props.getProperty("version");
BUILD_TIME = props.getProperty("build-time");
RESOURCES_VERSION = VERSION.toLowerCase();
} catch (IOException e) {
VERSION=UNKNOWN;
BUILD_TIME=UNKNOWN;
}
SINGLETON = new Version();
}
@JsonProperty("version")
public String getVersion() {
return version;
}
@JsonProperty("build-time")
public String getBuildTime() {
return buildTime;
}
}
@@ -1,35 +1,35 @@
package org.keycloak.constants;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface AdapterConstants {
// URL endpoints
public static final String K_LOGOUT = "k_logout";
public static final String K_VERSION = "k_version";
public static final String K_PUSH_NOT_BEFORE = "k_push_not_before";
public static final String K_TEST_AVAILABLE = "k_test_available";
public static final String K_QUERY_BEARER_TOKEN = "k_query_bearer_token";
// This param name is defined again in Keycloak Subsystem class
// org.keycloak.subsystem.extensionKeycloakAdapterConfigDeploymentProcessor. We have this value in
// two places to avoid dependency between Keycloak Subsystem and Keyclaok Undertow Integration.
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 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 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 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";
// Request parameter used to specify the identifier of the identity provider that should be used to authenticate an user
String KC_IDP_HINT = "kc_idp_hint";
}
package org.keycloak.constants;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface AdapterConstants {
// URL endpoints
public static final String K_LOGOUT = "k_logout";
public static final String K_VERSION = "k_version";
public static final String K_PUSH_NOT_BEFORE = "k_push_not_before";
public static final String K_TEST_AVAILABLE = "k_test_available";
public static final String K_QUERY_BEARER_TOKEN = "k_query_bearer_token";
// This param name is defined again in Keycloak Subsystem class
// org.keycloak.subsystem.extensionKeycloakAdapterConfigDeploymentProcessor. We have this value in
// two places to avoid dependency between Keycloak Subsystem and Keyclaok Undertow Integration.
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 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 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 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";
// Request parameter used to specify the identifier of the identity provider that should be used to authenticate an user
String KC_IDP_HINT = "kc_idp_hint";
}
@@ -0,0 +1,19 @@
package org.keycloak.constants;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface ServiceAccountConstants {
String CLIENT_AUTH = "client_auth";
String SERVICE_ACCOUNT_USER_PREFIX = "service-account-";
String CLIENT_ID_PROTOCOL_MAPPER = "Client ID";
String CLIENT_HOST_PROTOCOL_MAPPER = "Client Host";
String CLIENT_ADDRESS_PROTOCOL_MAPPER = "Client IP Address";
String CLIENT_ID = "clientId";
String CLIENT_HOST = "clientHost";
String CLIENT_ADDRESS = "clientAddress";
}
@@ -1,17 +1,17 @@
package org.keycloak.constants;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface ServiceUrlConstants {
public static final String AUTH_PATH = "/realms/{realm-name}/protocol/openid-connect/auth";
public static final String TOKEN_PATH = "/realms/{realm-name}/protocol/openid-connect/token";
public static final String TOKEN_SERVICE_LOGOUT_PATH = "/realms/{realm-name}/protocol/openid-connect/logout";
public static final String ACCOUNT_SERVICE_PATH = "/realms/{realm-name}/account";
public static final String REALM_INFO_PATH = "/realms/{realm-name}";
public static final String CLIENTS_MANAGEMENT_REGISTER_NODE_PATH = "/realms/{realm-name}/clients-managements/register-node";
public static final String CLIENTS_MANAGEMENT_UNREGISTER_NODE_PATH = "/realms/{realm-name}/clients-managements/unregister-node";
}
package org.keycloak.constants;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface ServiceUrlConstants {
public static final String AUTH_PATH = "/realms/{realm-name}/protocol/openid-connect/auth";
public static final String TOKEN_PATH = "/realms/{realm-name}/protocol/openid-connect/token";
public static final String TOKEN_SERVICE_LOGOUT_PATH = "/realms/{realm-name}/protocol/openid-connect/logout";
public static final String ACCOUNT_SERVICE_PATH = "/realms/{realm-name}/account";
public static final String REALM_INFO_PATH = "/realms/{realm-name}";
public static final String CLIENTS_MANAGEMENT_REGISTER_NODE_PATH = "/realms/{realm-name}/clients-managements/register-node";
public static final String CLIENTS_MANAGEMENT_UNREGISTER_NODE_PATH = "/realms/{realm-name}/clients-managements/unregister-node";
}
@@ -14,8 +14,6 @@ public enum SslRequired {
EXTERNAL,
NONE;
private static final long serialVersionUID = 1L;
public boolean isRequired(ClientConnection connection) {
return isRequired(connection.getRemoteAddr());
}
@@ -1,32 +1,32 @@
package org.keycloak.jose.jws;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.jose.jws.crypto.SignatureProvider;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public enum Algorithm {
none(null),
HS256(null),
HS384(null),
HS512(null),
RS256(new RSAProvider()),
RS384(new RSAProvider()),
RS512(new RSAProvider()),
ES256(null),
ES384(null),
ES512(null)
;
private SignatureProvider provider;
Algorithm(SignatureProvider provider) {
this.provider = provider;
}
public SignatureProvider getProvider() {
return provider;
}
}
package org.keycloak.jose.jws;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.jose.jws.crypto.SignatureProvider;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public enum Algorithm {
none(null),
HS256(null),
HS384(null),
HS512(null),
RS256(new RSAProvider()),
RS384(new RSAProvider()),
RS512(new RSAProvider()),
ES256(null),
ES384(null),
ES512(null)
;
private SignatureProvider provider;
Algorithm(SignatureProvider provider) {
this.provider = provider;
}
public SignatureProvider getProvider() {
return provider;
}
}
@@ -1,205 +1,205 @@
package org.keycloak.jose.jws;
import org.keycloak.jose.jws.crypto.HMACProvider;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.util.Base64Url;
import org.keycloak.util.JsonSerialization;
import javax.crypto.SecretKey;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.PrivateKey;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JWSBuilder {
String type;
String contentType;
byte[] contentBytes;
public JWSBuilder type(String type) {
this.type = type;
return this;
}
public JWSBuilder contentType(String type) {
this.contentType = type;
return this;
}
public EncodingBuilder content(byte[] bytes) {
this.contentBytes = bytes;
return new EncodingBuilder();
}
public EncodingBuilder jsonContent(Object object) {
try {
this.contentBytes = JsonSerialization.writeValueAsBytes(object);
} catch (IOException e) {
throw new RuntimeException(e);
}
return new EncodingBuilder();
}
protected String encodeHeader(Algorithm alg) {
StringBuilder builder = new StringBuilder("{");
builder.append("\"alg\":\"").append(alg.toString()).append("\"");
if (type != null) builder.append(",\"typ\" : \"").append(type).append("\"");
if (contentType != null) builder.append(",\"cty\":\"").append(contentType).append("\"");
builder.append("}");
try {
return Base64Url.encode(builder.toString().getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
protected String encodeAll(StringBuffer encoding, byte[] signature) {
encoding.append('.');
if (signature != null) {
encoding.append(Base64Url.encode(signature));
}
return encoding.toString();
}
protected void encode(Algorithm alg, byte[] data, StringBuffer encoding) {
encoding.append(encodeHeader(alg));
encoding.append('.');
encoding.append(Base64Url.encode(data));
}
protected byte[] marshalContent() {
return contentBytes;
}
public class EncodingBuilder {
public String none() {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.none, data, buffer);
return encodeAll(buffer, null);
}
public String rsa256(PrivateKey privateKey) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.RS256, data, buffer);
byte[] signature = null;
try {
signature = RSAProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.RS256, privateKey);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String rsa384(PrivateKey privateKey) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.RS384, data, buffer);
byte[] signature = null;
try {
signature = RSAProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.RS384, privateKey);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String rsa512(PrivateKey privateKey) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.RS512, data, buffer);
byte[] signature = null;
try {
signature = RSAProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.RS512, privateKey);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String hmac256(byte[] sharedSecret) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.HS256, data, buffer);
byte[] signature = null;
try {
signature = HMACProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.HS256, sharedSecret);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String hmac384(byte[] sharedSecret) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.HS384, data, buffer);
byte[] signature = null;
try {
signature = HMACProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.HS384, sharedSecret);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String hmac512(byte[] sharedSecret) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.HS512, data, buffer);
byte[] signature = null;
try {
signature = HMACProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.HS512, sharedSecret);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String hmac256(SecretKey sharedSecret) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.HS256, data, buffer);
byte[] signature = null;
try {
signature = HMACProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.HS256, sharedSecret);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String hmac384(SecretKey sharedSecret) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.HS384, data, buffer);
byte[] signature = null;
try {
signature = HMACProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.HS384, sharedSecret);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String hmac512(SecretKey sharedSecret) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.HS512, data, buffer);
byte[] signature = null;
try {
signature = HMACProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.HS512, sharedSecret);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
}
}
package org.keycloak.jose.jws;
import org.keycloak.jose.jws.crypto.HMACProvider;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.util.Base64Url;
import org.keycloak.util.JsonSerialization;
import javax.crypto.SecretKey;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.PrivateKey;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JWSBuilder {
String type;
String contentType;
byte[] contentBytes;
public JWSBuilder type(String type) {
this.type = type;
return this;
}
public JWSBuilder contentType(String type) {
this.contentType = type;
return this;
}
public EncodingBuilder content(byte[] bytes) {
this.contentBytes = bytes;
return new EncodingBuilder();
}
public EncodingBuilder jsonContent(Object object) {
try {
this.contentBytes = JsonSerialization.writeValueAsBytes(object);
} catch (IOException e) {
throw new RuntimeException(e);
}
return new EncodingBuilder();
}
protected String encodeHeader(Algorithm alg) {
StringBuilder builder = new StringBuilder("{");
builder.append("\"alg\":\"").append(alg.toString()).append("\"");
if (type != null) builder.append(",\"typ\" : \"").append(type).append("\"");
if (contentType != null) builder.append(",\"cty\":\"").append(contentType).append("\"");
builder.append("}");
try {
return Base64Url.encode(builder.toString().getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
protected String encodeAll(StringBuffer encoding, byte[] signature) {
encoding.append('.');
if (signature != null) {
encoding.append(Base64Url.encode(signature));
}
return encoding.toString();
}
protected void encode(Algorithm alg, byte[] data, StringBuffer encoding) {
encoding.append(encodeHeader(alg));
encoding.append('.');
encoding.append(Base64Url.encode(data));
}
protected byte[] marshalContent() {
return contentBytes;
}
public class EncodingBuilder {
public String none() {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.none, data, buffer);
return encodeAll(buffer, null);
}
public String rsa256(PrivateKey privateKey) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.RS256, data, buffer);
byte[] signature = null;
try {
signature = RSAProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.RS256, privateKey);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String rsa384(PrivateKey privateKey) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.RS384, data, buffer);
byte[] signature = null;
try {
signature = RSAProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.RS384, privateKey);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String rsa512(PrivateKey privateKey) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.RS512, data, buffer);
byte[] signature = null;
try {
signature = RSAProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.RS512, privateKey);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String hmac256(byte[] sharedSecret) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.HS256, data, buffer);
byte[] signature = null;
try {
signature = HMACProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.HS256, sharedSecret);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String hmac384(byte[] sharedSecret) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.HS384, data, buffer);
byte[] signature = null;
try {
signature = HMACProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.HS384, sharedSecret);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String hmac512(byte[] sharedSecret) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.HS512, data, buffer);
byte[] signature = null;
try {
signature = HMACProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.HS512, sharedSecret);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String hmac256(SecretKey sharedSecret) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.HS256, data, buffer);
byte[] signature = null;
try {
signature = HMACProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.HS256, sharedSecret);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String hmac384(SecretKey sharedSecret) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.HS384, data, buffer);
byte[] signature = null;
try {
signature = HMACProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.HS384, sharedSecret);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String hmac512(SecretKey sharedSecret) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(Algorithm.HS512, data, buffer);
byte[] signature = null;
try {
signature = HMACProvider.sign(buffer.toString().getBytes("UTF-8"), Algorithm.HS512, sharedSecret);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
}
}
@@ -1,69 +1,71 @@
package org.keycloak.jose.jws;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import java.io.IOException;
import java.io.Serializable;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JWSHeader implements Serializable {
@JsonProperty("alg")
private Algorithm algorithm;
@JsonProperty("typ")
private String type;
@JsonProperty("cty")
private String contentType;
@JsonProperty("kid")
private String keyId;
public JWSHeader() {
}
public JWSHeader(Algorithm algorithm, String type, String contentType) {
this.algorithm = algorithm;
this.type = type;
this.contentType = contentType;
}
public Algorithm getAlgorithm() {
return algorithm;
}
public String getType() {
return type;
}
public String getContentType() {
return contentType;
}
public String getKeyId() {
return keyId;
}
private static final ObjectMapper mapper = new ObjectMapper();
static {
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
}
public String toString() {
try {
return mapper.writeValueAsString(this);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
package org.keycloak.jose.jws;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import java.io.IOException;
import java.io.Serializable;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class JWSHeader implements Serializable {
@JsonProperty("alg")
private Algorithm algorithm;
@JsonProperty("typ")
private String type;
@JsonProperty("cty")
private String contentType;
@JsonProperty("kid")
private String keyId;
public JWSHeader() {
}
public JWSHeader(Algorithm algorithm, String type, String contentType) {
this.algorithm = algorithm;
this.type = type;
this.contentType = contentType;
}
public Algorithm getAlgorithm() {
return algorithm;
}
public String getType() {
return type;
}
public String getContentType() {
return contentType;
}
public String getKeyId() {
return keyId;
}
private static final ObjectMapper mapper = new ObjectMapper();
static {
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
}
public String toString() {
try {
return mapper.writeValueAsString(this);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@@ -1,89 +1,89 @@
package org.keycloak.jose.jws.crypto;
import org.keycloak.jose.jws.Algorithm;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.util.Base64Url;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class HMACProvider implements SignatureProvider {
private static String getJavaAlgorithm(Algorithm alg) {
switch (alg) {
case HS256:
return "HMACSHA256";
case HS384:
return "HMACSHA384";
case HS512:
return "HMACSHA512";
default:
throw new IllegalArgumentException("Not a MAC Algorithm");
}
}
private static Mac getMAC(final Algorithm alg) {
try {
return Mac.getInstance(getJavaAlgorithm(alg));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Unsupported HMAC algorithm: " + e.getMessage(), e);
}
}
public static byte[] sign(byte[] data, Algorithm algorithm, byte[] sharedSecret) {
try {
Mac mac = getMAC(algorithm);
mac.init(new SecretKeySpec(sharedSecret, mac.getAlgorithm()));
mac.update(data);
return mac.doFinal();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static byte[] sign(byte[] data, Algorithm algorithm, SecretKey key) {
try {
Mac mac = getMAC(algorithm);
mac.init(key);
mac.update(data);
return mac.doFinal();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static boolean verify(JWSInput input, SecretKey key) {
try {
byte[] signature = sign(input.getContent(), input.getHeader().getAlgorithm(), key);
String x = Base64Url.encode(signature);
return x.equals(input.getEncodedSignature());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static boolean verify(JWSInput input, byte[] sharedSecret) {
try {
byte[] signature = sign(input.getEncodedSignatureInput().getBytes("UTF-8"), input.getHeader().getAlgorithm(), sharedSecret);
String x = Base64Url.encode(signature);
return x.equals(input.getEncodedSignature());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public boolean verify(JWSInput input, String key) {
return false;
}
}
package org.keycloak.jose.jws.crypto;
import org.keycloak.jose.jws.Algorithm;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.util.Base64Url;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class HMACProvider implements SignatureProvider {
private static String getJavaAlgorithm(Algorithm alg) {
switch (alg) {
case HS256:
return "HMACSHA256";
case HS384:
return "HMACSHA384";
case HS512:
return "HMACSHA512";
default:
throw new IllegalArgumentException("Not a MAC Algorithm");
}
}
private static Mac getMAC(final Algorithm alg) {
try {
return Mac.getInstance(getJavaAlgorithm(alg));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Unsupported HMAC algorithm: " + e.getMessage(), e);
}
}
public static byte[] sign(byte[] data, Algorithm algorithm, byte[] sharedSecret) {
try {
Mac mac = getMAC(algorithm);
mac.init(new SecretKeySpec(sharedSecret, mac.getAlgorithm()));
mac.update(data);
return mac.doFinal();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static byte[] sign(byte[] data, Algorithm algorithm, SecretKey key) {
try {
Mac mac = getMAC(algorithm);
mac.init(key);
mac.update(data);
return mac.doFinal();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static boolean verify(JWSInput input, SecretKey key) {
try {
byte[] signature = sign(input.getEncodedSignatureInput().getBytes("UTF-8"), input.getHeader().getAlgorithm(), key);
String x = Base64Url.encode(signature);
return x.equals(input.getEncodedSignature());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static boolean verify(JWSInput input, byte[] sharedSecret) {
try {
byte[] signature = sign(input.getEncodedSignatureInput().getBytes("UTF-8"), input.getHeader().getAlgorithm(), sharedSecret);
String x = Base64Url.encode(signature);
return x.equals(input.getEncodedSignature());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public boolean verify(JWSInput input, String key) {
return false;
}
}
@@ -1,11 +1,11 @@
package org.keycloak.jose.jws.crypto;
import org.keycloak.jose.jws.JWSInput;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface SignatureProvider {
boolean verify(JWSInput input, String key);
}
package org.keycloak.jose.jws.crypto;
import org.keycloak.jose.jws.JWSInput;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface SignatureProvider {
boolean verify(JWSInput input, String key);
}
@@ -1,211 +1,211 @@
package org.keycloak.representations;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonProperty;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AccessToken extends IDToken {
public static class Access implements Serializable {
@JsonProperty("roles")
protected Set<String> roles;
@JsonProperty("verify_caller")
protected Boolean verifyCaller;
public Access() {
}
public Access clone() {
Access access = new Access();
access.verifyCaller = verifyCaller;
if (roles != null) {
access.roles = new HashSet<String>();
access.roles.addAll(roles);
}
return access;
}
public Set<String> getRoles() {
return roles;
}
public Access roles(Set<String> roles) {
this.roles = roles;
return this;
}
@JsonIgnore
public boolean isUserInRole(String role) {
if (roles == null) return false;
return roles.contains(role);
}
public Access addRole(String role) {
if (roles == null) roles = new HashSet<String>();
roles.add(role);
return this;
}
public Boolean getVerifyCaller() {
return verifyCaller;
}
public Access verifyCaller(Boolean required) {
this.verifyCaller = required;
return this;
}
}
@JsonProperty("client_session")
protected String clientSession;
@JsonProperty("trusted-certs")
protected Set<String> trustedCertificates;
@JsonProperty("allowed-origins")
protected Set<String> allowedOrigins;
@JsonProperty("realm_access")
protected Access realmAccess;
@JsonProperty("resource_access")
protected Map<String, Access> resourceAccess = new HashMap<String, Access>();
public Map<String, Access> getResourceAccess() {
return resourceAccess;
}
public void setResourceAccess(Map<String, Access> resourceAccess) {
this.resourceAccess = resourceAccess;
}
/**
* Does the realm require verifying the caller?
*
* @return
*/
@JsonIgnore
public boolean isVerifyCaller() {
if (getRealmAccess() != null && getRealmAccess().getVerifyCaller() != null)
return getRealmAccess().getVerifyCaller().booleanValue();
return false;
}
/**
* Does the resource override the requirement of verifying the caller?
*
* @param resource
* @return
*/
@JsonIgnore
public boolean isVerifyCaller(String resource) {
Access access = getResourceAccess(resource);
if (access != null && access.getVerifyCaller() != null) return access.getVerifyCaller().booleanValue();
return false;
}
@JsonIgnore
public Access getResourceAccess(String resource) {
return resourceAccess.get(resource);
}
public String getClientSession() {
return clientSession;
}
public Access addAccess(String service) {
Access access = resourceAccess.get(service);
if (access != null) return access;
access = new Access();
resourceAccess.put(service, access);
return access;
}
public AccessToken clientSession(String session) {
this.clientSession = session;
return this;
}
@Override
public AccessToken id(String id) {
return (AccessToken) super.id(id);
}
@Override
public AccessToken expiration(int expiration) {
return (AccessToken) super.expiration(expiration);
}
@Override
public AccessToken notBefore(int notBefore) {
return (AccessToken) super.notBefore(notBefore);
}
@Override
public AccessToken issuedAt(int issuedAt) {
return (AccessToken) super.issuedAt(issuedAt);
}
@Override
public AccessToken issuer(String issuer) {
return (AccessToken) super.issuer(issuer);
}
@Override
public AccessToken audience(String audience) {
return (AccessToken) super.audience(audience);
}
@Override
public AccessToken subject(String subject) {
return (AccessToken) super.subject(subject);
}
@Override
public AccessToken type(String type) {
return (AccessToken) super.type(type);
}
public Set<String> getAllowedOrigins() {
return allowedOrigins;
}
public void setAllowedOrigins(Set<String> allowedOrigins) {
this.allowedOrigins = allowedOrigins;
}
public Access getRealmAccess() {
return realmAccess;
}
public void setRealmAccess(Access realmAccess) {
this.realmAccess = realmAccess;
}
public Set<String> getTrustedCertificates() {
return trustedCertificates;
}
public void setTrustedCertificates(Set<String> trustedCertificates) {
this.trustedCertificates = trustedCertificates;
}
@Override
public AccessToken issuedFor(String issuedFor) {
return (AccessToken)super.issuedFor(issuedFor);
}
}
package org.keycloak.representations;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonProperty;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AccessToken extends IDToken {
public static class Access implements Serializable {
@JsonProperty("roles")
protected Set<String> roles;
@JsonProperty("verify_caller")
protected Boolean verifyCaller;
public Access() {
}
public Access clone() {
Access access = new Access();
access.verifyCaller = verifyCaller;
if (roles != null) {
access.roles = new HashSet<String>();
access.roles.addAll(roles);
}
return access;
}
public Set<String> getRoles() {
return roles;
}
public Access roles(Set<String> roles) {
this.roles = roles;
return this;
}
@JsonIgnore
public boolean isUserInRole(String role) {
if (roles == null) return false;
return roles.contains(role);
}
public Access addRole(String role) {
if (roles == null) roles = new HashSet<String>();
roles.add(role);
return this;
}
public Boolean getVerifyCaller() {
return verifyCaller;
}
public Access verifyCaller(Boolean required) {
this.verifyCaller = required;
return this;
}
}
@JsonProperty("client_session")
protected String clientSession;
@JsonProperty("trusted-certs")
protected Set<String> trustedCertificates;
@JsonProperty("allowed-origins")
protected Set<String> allowedOrigins;
@JsonProperty("realm_access")
protected Access realmAccess;
@JsonProperty("resource_access")
protected Map<String, Access> resourceAccess = new HashMap<String, Access>();
public Map<String, Access> getResourceAccess() {
return resourceAccess;
}
public void setResourceAccess(Map<String, Access> resourceAccess) {
this.resourceAccess = resourceAccess;
}
/**
* Does the realm require verifying the caller?
*
* @return
*/
@JsonIgnore
public boolean isVerifyCaller() {
if (getRealmAccess() != null && getRealmAccess().getVerifyCaller() != null)
return getRealmAccess().getVerifyCaller().booleanValue();
return false;
}
/**
* Does the resource override the requirement of verifying the caller?
*
* @param resource
* @return
*/
@JsonIgnore
public boolean isVerifyCaller(String resource) {
Access access = getResourceAccess(resource);
if (access != null && access.getVerifyCaller() != null) return access.getVerifyCaller().booleanValue();
return false;
}
@JsonIgnore
public Access getResourceAccess(String resource) {
return resourceAccess.get(resource);
}
public String getClientSession() {
return clientSession;
}
public Access addAccess(String service) {
Access access = resourceAccess.get(service);
if (access != null) return access;
access = new Access();
resourceAccess.put(service, access);
return access;
}
public AccessToken clientSession(String session) {
this.clientSession = session;
return this;
}
@Override
public AccessToken id(String id) {
return (AccessToken) super.id(id);
}
@Override
public AccessToken expiration(int expiration) {
return (AccessToken) super.expiration(expiration);
}
@Override
public AccessToken notBefore(int notBefore) {
return (AccessToken) super.notBefore(notBefore);
}
@Override
public AccessToken issuedAt(int issuedAt) {
return (AccessToken) super.issuedAt(issuedAt);
}
@Override
public AccessToken issuer(String issuer) {
return (AccessToken) super.issuer(issuer);
}
@Override
public AccessToken audience(String audience) {
return (AccessToken) super.audience(audience);
}
@Override
public AccessToken subject(String subject) {
return (AccessToken) super.subject(subject);
}
@Override
public AccessToken type(String type) {
return (AccessToken) super.type(type);
}
public Set<String> getAllowedOrigins() {
return allowedOrigins;
}
public void setAllowedOrigins(Set<String> allowedOrigins) {
this.allowedOrigins = allowedOrigins;
}
public Access getRealmAccess() {
return realmAccess;
}
public void setRealmAccess(Access realmAccess) {
this.realmAccess = realmAccess;
}
public Set<String> getTrustedCertificates() {
return trustedCertificates;
}
public void setTrustedCertificates(Set<String> trustedCertificates) {
this.trustedCertificates = trustedCertificates;
}
@Override
public AccessToken issuedFor(String issuedFor) {
return (AccessToken)super.issuedFor(issuedFor);
}
}
@@ -1,83 +1,83 @@
package org.keycloak.representations;
import org.codehaus.jackson.annotate.JsonProperty;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AddressClaimSet {
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)
protected String streetAddress;
@JsonProperty(LOCALITY)
protected String locality;
@JsonProperty(REGION)
protected String region;
@JsonProperty(POSTAL_CODE)
protected String postalCode;
@JsonProperty(COUNTRY)
protected String country;
public String getFormattedAddress() {
return this.formattedAddress;
}
public void setFormattedAddress(String formattedAddress) {
this.formattedAddress = formattedAddress;
}
public String getStreetAddress() {
return this.streetAddress;
}
public void setStreetAddress(String streetAddress) {
this.streetAddress = streetAddress;
}
public String getLocality() {
return this.locality;
}
public void setLocality(String locality) {
this.locality = locality;
}
public String getRegion() {
return this.region;
}
public void setRegion(String region) {
this.region = region;
}
public String getPostalCode() {
return this.postalCode;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public String getCountry() {
return this.country;
}
public void setCountry(String country) {
this.country = country;
}
}
package org.keycloak.representations;
import org.codehaus.jackson.annotate.JsonProperty;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AddressClaimSet {
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)
protected String streetAddress;
@JsonProperty(LOCALITY)
protected String locality;
@JsonProperty(REGION)
protected String region;
@JsonProperty(POSTAL_CODE)
protected String postalCode;
@JsonProperty(COUNTRY)
protected String country;
public String getFormattedAddress() {
return this.formattedAddress;
}
public void setFormattedAddress(String formattedAddress) {
this.formattedAddress = formattedAddress;
}
public String getStreetAddress() {
return this.streetAddress;
}
public void setStreetAddress(String streetAddress) {
this.streetAddress = streetAddress;
}
public String getLocality() {
return this.locality;
}
public void setLocality(String locality) {
this.locality = locality;
}
public String getRegion() {
return this.region;
}
public void setRegion(String region) {
this.region = region;
}
public String getPostalCode() {
return this.postalCode;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public String getCountry() {
return this.country;
}
public void setCountry(String country) {
this.country = country;
}
}
@@ -1,282 +1,282 @@
package org.keycloak.representations;
import org.codehaus.jackson.annotate.JsonAnyGetter;
import org.codehaus.jackson.annotate.JsonAnySetter;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonUnwrapped;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @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)
protected String nonce;
@JsonProperty(SESSION_STATE)
protected String sessionState;
@JsonProperty(NAME)
protected String name;
@JsonProperty(GIVEN_NAME)
protected String givenName;
@JsonProperty(FAMILY_NAME)
protected String familyName;
@JsonProperty(MIDDLE_NAME)
protected String middleName;
@JsonProperty(NICKNAME)
protected String nickName;
@JsonProperty(PREFERRED_USERNAME)
protected String preferredUsername;
@JsonProperty(PROFILE)
protected String profile;
@JsonProperty(PICTURE)
protected String picture;
@JsonProperty(WEBSITE)
protected String website;
@JsonProperty(EMAIL)
protected String email;
@JsonProperty(EMAIL_VERIFIED)
protected Boolean emailVerified;
@JsonProperty(GENDER)
protected String gender;
@JsonProperty(BIRTHDATE)
protected String birthdate;
@JsonProperty(ZONEINFO)
protected String zoneinfo;
@JsonProperty(LOCALE)
protected String locale;
@JsonProperty(PHONE_NUMBER)
protected String phoneNumber;
@JsonProperty(PHONE_NUMBER_VERIFIED)
protected Boolean phoneNumberVerified;
@JsonProperty(ADDRESS)
protected AddressClaimSet address;
@JsonProperty(UPDATED_AT)
protected Long updatedAt;
@JsonProperty(CLAIMS_LOCALES)
protected String claimsLocales;
public String getNonce() {
return nonce;
}
public void setNonce(String nonce) {
this.nonce = nonce;
}
public String getSessionState() {
return sessionState;
}
public void setSessionState(String sessionState) {
this.sessionState = sessionState;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getGivenName() {
return this.givenName;
}
public void setGivenName(String givenName) {
this.givenName = givenName;
}
public String getFamilyName() {
return this.familyName;
}
public void setFamilyName(String familyName) {
this.familyName = familyName;
}
public String getMiddleName() {
return this.middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getNickName() {
return this.nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getPreferredUsername() {
return this.preferredUsername;
}
public void setPreferredUsername(String preferredUsername) {
this.preferredUsername = preferredUsername;
}
public String getProfile() {
return this.profile;
}
public void setProfile(String profile) {
this.profile = profile;
}
public String getPicture() {
return this.picture;
}
public void setPicture(String picture) {
this.picture = picture;
}
public String getWebsite() {
return this.website;
}
public void setWebsite(String website) {
this.website = website;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public Boolean getEmailVerified() {
return this.emailVerified;
}
public void setEmailVerified(Boolean emailVerified) {
this.emailVerified = emailVerified;
}
public String getGender() {
return this.gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getBirthdate() {
return this.birthdate;
}
public void setBirthdate(String birthdate) {
this.birthdate = birthdate;
}
public String getZoneinfo() {
return this.zoneinfo;
}
public void setZoneinfo(String zoneinfo) {
this.zoneinfo = zoneinfo;
}
public String getLocale() {
return this.locale;
}
public void setLocale(String locale) {
this.locale = locale;
}
public String getPhoneNumber() {
return this.phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public Boolean getPhoneNumberVerified() {
return this.phoneNumberVerified;
}
public void setPhoneNumberVerified(Boolean phoneNumberVerified) {
this.phoneNumberVerified = phoneNumberVerified;
}
public AddressClaimSet getAddress() {
return address;
}
public void setAddress(AddressClaimSet address) {
this.address = address;
}
public Long getUpdatedAt() {
return this.updatedAt;
}
public void setUpdatedAt(Long updatedAt) {
this.updatedAt = updatedAt;
}
public String getClaimsLocales() {
return this.claimsLocales;
}
public void setClaimsLocales(String claimsLocales) {
this.claimsLocales = claimsLocales;
}
}
package org.keycloak.representations;
import org.codehaus.jackson.annotate.JsonAnyGetter;
import org.codehaus.jackson.annotate.JsonAnySetter;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonUnwrapped;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @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)
protected String nonce;
@JsonProperty(SESSION_STATE)
protected String sessionState;
@JsonProperty(NAME)
protected String name;
@JsonProperty(GIVEN_NAME)
protected String givenName;
@JsonProperty(FAMILY_NAME)
protected String familyName;
@JsonProperty(MIDDLE_NAME)
protected String middleName;
@JsonProperty(NICKNAME)
protected String nickName;
@JsonProperty(PREFERRED_USERNAME)
protected String preferredUsername;
@JsonProperty(PROFILE)
protected String profile;
@JsonProperty(PICTURE)
protected String picture;
@JsonProperty(WEBSITE)
protected String website;
@JsonProperty(EMAIL)
protected String email;
@JsonProperty(EMAIL_VERIFIED)
protected Boolean emailVerified;
@JsonProperty(GENDER)
protected String gender;
@JsonProperty(BIRTHDATE)
protected String birthdate;
@JsonProperty(ZONEINFO)
protected String zoneinfo;
@JsonProperty(LOCALE)
protected String locale;
@JsonProperty(PHONE_NUMBER)
protected String phoneNumber;
@JsonProperty(PHONE_NUMBER_VERIFIED)
protected Boolean phoneNumberVerified;
@JsonProperty(ADDRESS)
protected AddressClaimSet address;
@JsonProperty(UPDATED_AT)
protected Long updatedAt;
@JsonProperty(CLAIMS_LOCALES)
protected String claimsLocales;
public String getNonce() {
return nonce;
}
public void setNonce(String nonce) {
this.nonce = nonce;
}
public String getSessionState() {
return sessionState;
}
public void setSessionState(String sessionState) {
this.sessionState = sessionState;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getGivenName() {
return this.givenName;
}
public void setGivenName(String givenName) {
this.givenName = givenName;
}
public String getFamilyName() {
return this.familyName;
}
public void setFamilyName(String familyName) {
this.familyName = familyName;
}
public String getMiddleName() {
return this.middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getNickName() {
return this.nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getPreferredUsername() {
return this.preferredUsername;
}
public void setPreferredUsername(String preferredUsername) {
this.preferredUsername = preferredUsername;
}
public String getProfile() {
return this.profile;
}
public void setProfile(String profile) {
this.profile = profile;
}
public String getPicture() {
return this.picture;
}
public void setPicture(String picture) {
this.picture = picture;
}
public String getWebsite() {
return this.website;
}
public void setWebsite(String website) {
this.website = website;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public Boolean getEmailVerified() {
return this.emailVerified;
}
public void setEmailVerified(Boolean emailVerified) {
this.emailVerified = emailVerified;
}
public String getGender() {
return this.gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getBirthdate() {
return this.birthdate;
}
public void setBirthdate(String birthdate) {
this.birthdate = birthdate;
}
public String getZoneinfo() {
return this.zoneinfo;
}
public void setZoneinfo(String zoneinfo) {
this.zoneinfo = zoneinfo;
}
public String getLocale() {
return this.locale;
}
public void setLocale(String locale) {
this.locale = locale;
}
public String getPhoneNumber() {
return this.phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public Boolean getPhoneNumberVerified() {
return this.phoneNumberVerified;
}
public void setPhoneNumberVerified(Boolean phoneNumberVerified) {
this.phoneNumberVerified = phoneNumberVerified;
}
public AddressClaimSet getAddress() {
return address;
}
public void setAddress(AddressClaimSet address) {
this.address = address;
}
public Long getUpdatedAt() {
return this.updatedAt;
}
public void setUpdatedAt(Long updatedAt) {
this.updatedAt = updatedAt;
}
public String getClaimsLocales() {
return this.claimsLocales;
}
public void setClaimsLocales(String claimsLocales) {
this.claimsLocales = claimsLocales;
}
}
@@ -1,176 +1,176 @@
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>
* @version $Revision: 1 $
*/
public class JsonWebToken implements Serializable {
@JsonProperty("jti")
protected String id;
@JsonProperty("exp")
protected int expiration;
@JsonProperty("nbf")
protected int notBefore;
@JsonProperty("iat")
protected int issuedAt;
@JsonProperty("iss")
protected String issuer;
@JsonProperty("aud")
protected String audience;
@JsonProperty("sub")
protected String subject;
@JsonProperty("typ")
protected String type;
@JsonProperty("azp")
public String issuedFor;
protected Map<String, Object> otherClaims = new HashMap<String, Object>();
public String getId() {
return id;
}
public JsonWebToken id(String id) {
this.id = id;
return this;
}
public int getExpiration() {
return expiration;
}
public JsonWebToken expiration(int expiration) {
this.expiration = expiration;
return this;
}
@JsonIgnore
public boolean isExpired() {
return Time.currentTime() > expiration;
}
public int getNotBefore() {
return notBefore;
}
public JsonWebToken notBefore(int notBefore) {
this.notBefore = notBefore;
return this;
}
@JsonIgnore
public boolean isNotBefore() {
return Time.currentTime() >= notBefore;
}
/**
* Tests that the token is not expired and is not-before.
*
* @return
*/
@JsonIgnore
public boolean isActive() {
return (!isExpired() || expiration == 0) && (isNotBefore() || notBefore == 0);
}
public int getIssuedAt() {
return issuedAt;
}
/**
* Set issuedAt to the current time
*/
@JsonIgnore
public JsonWebToken issuedNow() {
issuedAt = Time.currentTime();
return this;
}
public JsonWebToken issuedAt(int issuedAt) {
this.issuedAt = issuedAt;
return this;
}
public String getIssuer() {
return issuer;
}
public JsonWebToken issuer(String issuer) {
this.issuer = issuer;
return this;
}
public String getAudience() {
return audience;
}
public JsonWebToken audience(String audience) {
this.audience = audience;
return this;
}
public String getSubject() {
return subject;
}
public JsonWebToken subject(String subject) {
this.subject = subject;
return this;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getType() {
return type;
}
public JsonWebToken type(String type) {
this.type = type;
return this;
}
/**
* OAuth client the token was issued for.
*
* @return
*/
public String getIssuedFor() {
return issuedFor;
}
public JsonWebToken issuedFor(String issuedFor) {
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);
}
}
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>
* @version $Revision: 1 $
*/
public class JsonWebToken implements Serializable {
@JsonProperty("jti")
protected String id;
@JsonProperty("exp")
protected int expiration;
@JsonProperty("nbf")
protected int notBefore;
@JsonProperty("iat")
protected int issuedAt;
@JsonProperty("iss")
protected String issuer;
@JsonProperty("aud")
protected String audience;
@JsonProperty("sub")
protected String subject;
@JsonProperty("typ")
protected String type;
@JsonProperty("azp")
public String issuedFor;
protected Map<String, Object> otherClaims = new HashMap<String, Object>();
public String getId() {
return id;
}
public JsonWebToken id(String id) {
this.id = id;
return this;
}
public int getExpiration() {
return expiration;
}
public JsonWebToken expiration(int expiration) {
this.expiration = expiration;
return this;
}
@JsonIgnore
public boolean isExpired() {
return Time.currentTime() > expiration;
}
public int getNotBefore() {
return notBefore;
}
public JsonWebToken notBefore(int notBefore) {
this.notBefore = notBefore;
return this;
}
@JsonIgnore
public boolean isNotBefore() {
return Time.currentTime() >= notBefore;
}
/**
* Tests that the token is not expired and is not-before.
*
* @return
*/
@JsonIgnore
public boolean isActive() {
return (!isExpired() || expiration == 0) && (isNotBefore() || notBefore == 0);
}
public int getIssuedAt() {
return issuedAt;
}
/**
* Set issuedAt to the current time
*/
@JsonIgnore
public JsonWebToken issuedNow() {
issuedAt = Time.currentTime();
return this;
}
public JsonWebToken issuedAt(int issuedAt) {
this.issuedAt = issuedAt;
return this;
}
public String getIssuer() {
return issuer;
}
public JsonWebToken issuer(String issuer) {
this.issuer = issuer;
return this;
}
public String getAudience() {
return audience;
}
public JsonWebToken audience(String audience) {
this.audience = audience;
return this;
}
public String getSubject() {
return subject;
}
public JsonWebToken subject(String subject) {
this.subject = subject;
return this;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getType() {
return type;
}
public JsonWebToken type(String type) {
this.type = type;
return this;
}
/**
* OAuth client the token was issued for.
*
* @return
*/
public String getIssuedFor() {
return issuedFor;
}
public JsonWebToken issuedFor(String issuedFor) {
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,42 +1,42 @@
package org.keycloak.representations;
import org.codehaus.jackson.annotate.JsonProperty;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RefreshToken extends AccessToken {
private RefreshToken() {
type("REFRESH");
}
/**
* Deep copies issuer, subject, issuedFor, sessionState, realmAccess, and resourceAccess
* from AccessToken.
*
* @param token
*/
public RefreshToken(AccessToken token) {
this();
this.clientSession = token.getClientSession();
this.issuer = token.issuer;
this.subject = token.subject;
this.issuedFor = token.issuedFor;
this.sessionState = token.sessionState;
if (token.realmAccess != null) {
realmAccess = token.realmAccess.clone();
}
if (token.resourceAccess != null) {
resourceAccess = new HashMap<String, Access>();
for (Map.Entry<String, Access> entry : token.resourceAccess.entrySet()) {
resourceAccess.put(entry.getKey(), entry.getValue().clone());
}
}
}
}
package org.keycloak.representations;
import org.codehaus.jackson.annotate.JsonProperty;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RefreshToken extends AccessToken {
private RefreshToken() {
type("REFRESH");
}
/**
* Deep copies issuer, subject, issuedFor, sessionState, realmAccess, and resourceAccess
* from AccessToken.
*
* @param token
*/
public RefreshToken(AccessToken token) {
this();
this.clientSession = token.getClientSession();
this.issuer = token.issuer;
this.subject = token.subject;
this.issuedFor = token.issuedFor;
this.sessionState = token.sessionState;
if (token.realmAccess != null) {
realmAccess = token.realmAccess.clone();
}
if (token.resourceAccess != null) {
resourceAccess = new HashMap<String, Access>();
for (Map.Entry<String, Access> entry : token.resourceAccess.entrySet()) {
resourceAccess.put(entry.getKey(), entry.getValue().clone());
}
}
}
}
@@ -1,71 +1,71 @@
package org.keycloak.representations.adapters.action;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.keycloak.util.Time;
/**
* Posted to managed client from admin server.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public abstract class AdminAction {
protected String id;
protected int expiration;
protected String resource;
protected String action;
public AdminAction() {
}
public AdminAction(String id, int expiration, String resource, String action) {
this.id = id;
this.expiration = expiration;
this.resource = resource;
this.action = action;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@JsonIgnore
public boolean isExpired() {
return Time.currentTime() > expiration;
}
/**
* Time in seconds since epoc
*
* @return
*/
public int getExpiration() {
return expiration;
}
public void setExpiration(int expiration) {
this.expiration = expiration;
}
public String getResource() {
return resource;
}
public void setResource(String resource) {
this.resource = resource;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public abstract boolean validate();
}
package org.keycloak.representations.adapters.action;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.keycloak.util.Time;
/**
* Posted to managed client from admin server.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public abstract class AdminAction {
protected String id;
protected int expiration;
protected String resource;
protected String action;
public AdminAction() {
}
public AdminAction(String id, int expiration, String resource, String action) {
this.id = id;
this.expiration = expiration;
this.resource = resource;
this.action = action;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@JsonIgnore
public boolean isExpired() {
return Time.currentTime() > expiration;
}
/**
* Time in seconds since epoc
*
* @return
*/
public int getExpiration() {
return expiration;
}
public void setExpiration(int expiration) {
this.expiration = expiration;
}
public String getResource() {
return resource;
}
public void setResource(String resource) {
this.resource = resource;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public abstract boolean validate();
}
@@ -1,50 +1,50 @@
package org.keycloak.representations.adapters.action;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class LogoutAction extends AdminAction {
public static final String LOGOUT = "LOGOUT";
protected List<String> adapterSessionIds;
protected int notBefore;
protected List<String> keycloakSessionIds;
public LogoutAction() {
}
public LogoutAction(String id, int expiration, String resource, List<String> adapterSessionIds, int notBefore, List<String> keycloakSessionIds) {
super(id, expiration, resource, LOGOUT);
this.adapterSessionIds = adapterSessionIds;
this.notBefore = notBefore;
this.keycloakSessionIds = keycloakSessionIds;
}
public int getNotBefore() {
return notBefore;
}
public void setNotBefore(int notBefore) {
this.notBefore = notBefore;
}
public List<String> getAdapterSessionIds() {
return adapterSessionIds;
}
public List<String> getKeycloakSessionIds() {
return keycloakSessionIds;
}
public void setKeycloakSessionIds(List<String> keycloakSessionIds) {
this.keycloakSessionIds = keycloakSessionIds;
}
@Override
public boolean validate() {
return LOGOUT.equals(action);
}
}
package org.keycloak.representations.adapters.action;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class LogoutAction extends AdminAction {
public static final String LOGOUT = "LOGOUT";
protected List<String> adapterSessionIds;
protected int notBefore;
protected List<String> keycloakSessionIds;
public LogoutAction() {
}
public LogoutAction(String id, int expiration, String resource, List<String> adapterSessionIds, int notBefore, List<String> keycloakSessionIds) {
super(id, expiration, resource, LOGOUT);
this.adapterSessionIds = adapterSessionIds;
this.notBefore = notBefore;
this.keycloakSessionIds = keycloakSessionIds;
}
public int getNotBefore() {
return notBefore;
}
public void setNotBefore(int notBefore) {
this.notBefore = notBefore;
}
public List<String> getAdapterSessionIds() {
return adapterSessionIds;
}
public List<String> getKeycloakSessionIds() {
return keycloakSessionIds;
}
public void setKeycloakSessionIds(List<String> keycloakSessionIds) {
this.keycloakSessionIds = keycloakSessionIds;
}
@Override
public boolean validate() {
return LOGOUT.equals(action);
}
}
@@ -1,33 +1,33 @@
package org.keycloak.representations.adapters.action;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class PushNotBeforeAction extends AdminAction {
public static final String PUSH_NOT_BEFORE = "PUSH_NOT_BEFORE";
protected int notBefore;
public PushNotBeforeAction() {
}
public PushNotBeforeAction(String id, int expiration, String resource, int notBefore) {
super(id, expiration, resource, PUSH_NOT_BEFORE);
this.notBefore = notBefore;
}
public int getNotBefore() {
return notBefore;
}
public void setNotBefore(int notBefore) {
this.notBefore = notBefore;
}
@Override
public boolean validate() {
return PUSH_NOT_BEFORE.equals(action);
}
}
package org.keycloak.representations.adapters.action;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class PushNotBeforeAction extends AdminAction {
public static final String PUSH_NOT_BEFORE = "PUSH_NOT_BEFORE";
protected int notBefore;
public PushNotBeforeAction() {
}
public PushNotBeforeAction(String id, int expiration, String resource, int notBefore) {
super(id, expiration, resource, PUSH_NOT_BEFORE);
this.notBefore = notBefore;
}
public int getNotBefore() {
return notBefore;
}
public void setNotBefore(int notBefore) {
this.notBefore = notBefore;
}
@Override
public boolean validate() {
return PUSH_NOT_BEFORE.equals(action);
}
}
@@ -1,165 +1,165 @@
package org.keycloak.representations.adapters.config;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
/**
* Configuration for Java based adapters
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-required",
"resource", "public-client", "credentials",
"use-resource-role-mappings",
"enable-cors", "cors-max-age", "cors-allowed-methods",
"expose-token", "bearer-only",
"connection-pool-size",
"allow-any-hostname", "disable-trust-manager", "truststore", "truststore-password",
"client-keystore", "client-keystore-password", "client-key-password",
"auth-server-url-for-backend-requests", "always-refresh-token",
"register-node-at-startup", "register-node-period", "token-store", "principal-attribute"
})
public class AdapterConfig extends BaseAdapterConfig {
@JsonProperty("allow-any-hostname")
protected boolean allowAnyHostname;
@JsonProperty("disable-trust-manager")
protected boolean disableTrustManager;
@JsonProperty("truststore")
protected String truststore;
@JsonProperty("truststore-password")
protected String truststorePassword;
@JsonProperty("client-keystore")
protected String clientKeystore;
@JsonProperty("client-keystore-password")
protected String clientKeystorePassword;
@JsonProperty("client-key-password")
protected String clientKeyPassword;
@JsonProperty("connection-pool-size")
protected int connectionPoolSize = 20;
@JsonProperty("auth-server-url-for-backend-requests")
protected String authServerUrlForBackendRequests;
@JsonProperty("always-refresh-token")
protected boolean alwaysRefreshToken = false;
@JsonProperty("register-node-at-startup")
protected boolean registerNodeAtStartup = false;
@JsonProperty("register-node-period")
protected int registerNodePeriod = -1;
@JsonProperty("token-store")
protected String tokenStore;
@JsonProperty("principal-attribute")
protected String principalAttribute;
public boolean isAllowAnyHostname() {
return allowAnyHostname;
}
public void setAllowAnyHostname(boolean allowAnyHostname) {
this.allowAnyHostname = allowAnyHostname;
}
public boolean isDisableTrustManager() {
return disableTrustManager;
}
public void setDisableTrustManager(boolean disableTrustManager) {
this.disableTrustManager = disableTrustManager;
}
public String getTruststore() {
return truststore;
}
public void setTruststore(String truststore) {
this.truststore = truststore;
}
public String getTruststorePassword() {
return truststorePassword;
}
public void setTruststorePassword(String truststorePassword) {
this.truststorePassword = truststorePassword;
}
public String getClientKeystore() {
return clientKeystore;
}
public void setClientKeystore(String clientKeystore) {
this.clientKeystore = clientKeystore;
}
public String getClientKeystorePassword() {
return clientKeystorePassword;
}
public void setClientKeystorePassword(String clientKeystorePassword) {
this.clientKeystorePassword = clientKeystorePassword;
}
public String getClientKeyPassword() {
return clientKeyPassword;
}
public void setClientKeyPassword(String clientKeyPassword) {
this.clientKeyPassword = clientKeyPassword;
}
public int getConnectionPoolSize() {
return connectionPoolSize;
}
public void setConnectionPoolSize(int connectionPoolSize) {
this.connectionPoolSize = connectionPoolSize;
}
public String getAuthServerUrlForBackendRequests() {
return authServerUrlForBackendRequests;
}
public void setAuthServerUrlForBackendRequests(String authServerUrlForBackendRequests) {
this.authServerUrlForBackendRequests = authServerUrlForBackendRequests;
}
public boolean isAlwaysRefreshToken() {
return alwaysRefreshToken;
}
public void setAlwaysRefreshToken(boolean alwaysRefreshToken) {
this.alwaysRefreshToken = alwaysRefreshToken;
}
public boolean isRegisterNodeAtStartup() {
return registerNodeAtStartup;
}
public void setRegisterNodeAtStartup(boolean registerNodeAtStartup) {
this.registerNodeAtStartup = registerNodeAtStartup;
}
public int getRegisterNodePeriod() {
return registerNodePeriod;
}
public void setRegisterNodePeriod(int registerNodePeriod) {
this.registerNodePeriod = registerNodePeriod;
}
public String getTokenStore() {
return tokenStore;
}
public void setTokenStore(String tokenStore) {
this.tokenStore = tokenStore;
}
public String getPrincipalAttribute() {
return principalAttribute;
}
public void setPrincipalAttribute(String principalAttribute) {
this.principalAttribute = principalAttribute;
}
}
package org.keycloak.representations.adapters.config;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
/**
* Configuration for Java based adapters
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-required",
"resource", "public-client", "credentials",
"use-resource-role-mappings",
"enable-cors", "cors-max-age", "cors-allowed-methods",
"expose-token", "bearer-only",
"connection-pool-size",
"allow-any-hostname", "disable-trust-manager", "truststore", "truststore-password",
"client-keystore", "client-keystore-password", "client-key-password",
"auth-server-url-for-backend-requests", "always-refresh-token",
"register-node-at-startup", "register-node-period", "token-store", "principal-attribute"
})
public class AdapterConfig extends BaseAdapterConfig {
@JsonProperty("allow-any-hostname")
protected boolean allowAnyHostname;
@JsonProperty("disable-trust-manager")
protected boolean disableTrustManager;
@JsonProperty("truststore")
protected String truststore;
@JsonProperty("truststore-password")
protected String truststorePassword;
@JsonProperty("client-keystore")
protected String clientKeystore;
@JsonProperty("client-keystore-password")
protected String clientKeystorePassword;
@JsonProperty("client-key-password")
protected String clientKeyPassword;
@JsonProperty("connection-pool-size")
protected int connectionPoolSize = 20;
@JsonProperty("auth-server-url-for-backend-requests")
protected String authServerUrlForBackendRequests;
@JsonProperty("always-refresh-token")
protected boolean alwaysRefreshToken = false;
@JsonProperty("register-node-at-startup")
protected boolean registerNodeAtStartup = false;
@JsonProperty("register-node-period")
protected int registerNodePeriod = -1;
@JsonProperty("token-store")
protected String tokenStore;
@JsonProperty("principal-attribute")
protected String principalAttribute;
public boolean isAllowAnyHostname() {
return allowAnyHostname;
}
public void setAllowAnyHostname(boolean allowAnyHostname) {
this.allowAnyHostname = allowAnyHostname;
}
public boolean isDisableTrustManager() {
return disableTrustManager;
}
public void setDisableTrustManager(boolean disableTrustManager) {
this.disableTrustManager = disableTrustManager;
}
public String getTruststore() {
return truststore;
}
public void setTruststore(String truststore) {
this.truststore = truststore;
}
public String getTruststorePassword() {
return truststorePassword;
}
public void setTruststorePassword(String truststorePassword) {
this.truststorePassword = truststorePassword;
}
public String getClientKeystore() {
return clientKeystore;
}
public void setClientKeystore(String clientKeystore) {
this.clientKeystore = clientKeystore;
}
public String getClientKeystorePassword() {
return clientKeystorePassword;
}
public void setClientKeystorePassword(String clientKeystorePassword) {
this.clientKeystorePassword = clientKeystorePassword;
}
public String getClientKeyPassword() {
return clientKeyPassword;
}
public void setClientKeyPassword(String clientKeyPassword) {
this.clientKeyPassword = clientKeyPassword;
}
public int getConnectionPoolSize() {
return connectionPoolSize;
}
public void setConnectionPoolSize(int connectionPoolSize) {
this.connectionPoolSize = connectionPoolSize;
}
public String getAuthServerUrlForBackendRequests() {
return authServerUrlForBackendRequests;
}
public void setAuthServerUrlForBackendRequests(String authServerUrlForBackendRequests) {
this.authServerUrlForBackendRequests = authServerUrlForBackendRequests;
}
public boolean isAlwaysRefreshToken() {
return alwaysRefreshToken;
}
public void setAlwaysRefreshToken(boolean alwaysRefreshToken) {
this.alwaysRefreshToken = alwaysRefreshToken;
}
public boolean isRegisterNodeAtStartup() {
return registerNodeAtStartup;
}
public void setRegisterNodeAtStartup(boolean registerNodeAtStartup) {
this.registerNodeAtStartup = registerNodeAtStartup;
}
public int getRegisterNodePeriod() {
return registerNodePeriod;
}
public void setRegisterNodePeriod(int registerNodePeriod) {
this.registerNodePeriod = registerNodePeriod;
}
public String getTokenStore() {
return tokenStore;
}
public void setTokenStore(String tokenStore) {
this.tokenStore = tokenStore;
}
public String getPrincipalAttribute() {
return principalAttribute;
}
public void setPrincipalAttribute(String principalAttribute) {
this.principalAttribute = principalAttribute;
}
}
@@ -1,132 +1,132 @@
package org.keycloak.representations.adapters.config;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
import java.util.HashMap;
import java.util.Map;
/**
* Common Adapter configuration
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-required",
"resource", "public-client", "credentials",
"use-resource-role-mappings",
"enable-cors", "cors-max-age", "cors-allowed-methods",
"expose-token", "bearer-only", "enable-basic-auth"})
public class BaseAdapterConfig extends BaseRealmConfig {
@JsonProperty("resource")
protected String resource;
@JsonProperty("use-resource-role-mappings")
protected boolean useResourceRoleMappings;
@JsonProperty("enable-cors")
protected boolean cors;
@JsonProperty("cors-max-age")
protected int corsMaxAge = -1;
@JsonProperty("cors-allowed-headers")
protected String corsAllowedHeaders;
@JsonProperty("cors-allowed-methods")
protected String corsAllowedMethods;
@JsonProperty("expose-token")
protected boolean exposeToken;
@JsonProperty("bearer-only")
protected boolean bearerOnly;
@JsonProperty("enable-basic-auth")
protected boolean enableBasicAuth;
@JsonProperty("public-client")
protected boolean publicClient;
@JsonProperty("credentials")
protected Map<String, String> credentials = new HashMap<String, String>();
public boolean isUseResourceRoleMappings() {
return useResourceRoleMappings;
}
public void setUseResourceRoleMappings(boolean useResourceRoleMappings) {
this.useResourceRoleMappings = useResourceRoleMappings;
}
public String getResource() {
return resource;
}
public void setResource(String resource) {
this.resource = resource;
}
public boolean isCors() {
return cors;
}
public void setCors(boolean cors) {
this.cors = cors;
}
public int getCorsMaxAge() {
return corsMaxAge;
}
public void setCorsMaxAge(int corsMaxAge) {
this.corsMaxAge = corsMaxAge;
}
public String getCorsAllowedHeaders() {
return corsAllowedHeaders;
}
public void setCorsAllowedHeaders(String corsAllowedHeaders) {
this.corsAllowedHeaders = corsAllowedHeaders;
}
public String getCorsAllowedMethods() {
return corsAllowedMethods;
}
public void setCorsAllowedMethods(String corsAllowedMethods) {
this.corsAllowedMethods = corsAllowedMethods;
}
public boolean isExposeToken() {
return exposeToken;
}
public void setExposeToken(boolean exposeToken) {
this.exposeToken = exposeToken;
}
public boolean isBearerOnly() {
return bearerOnly;
}
public void setBearerOnly(boolean bearerOnly) {
this.bearerOnly = bearerOnly;
}
public boolean isEnableBasicAuth() {
return enableBasicAuth;
}
public void setEnableBasicAuth(boolean enableBasicAuth) {
this.enableBasicAuth = enableBasicAuth;
}
public Map<String, String> getCredentials() {
return credentials;
}
public void setCredentials(Map<String, String> credentials) {
this.credentials = credentials;
}
public boolean isPublicClient() {
return publicClient;
}
public void setPublicClient(boolean publicClient) {
this.publicClient = publicClient;
}
}
package org.keycloak.representations.adapters.config;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
import java.util.HashMap;
import java.util.Map;
/**
* Common Adapter configuration
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-required",
"resource", "public-client", "credentials",
"use-resource-role-mappings",
"enable-cors", "cors-max-age", "cors-allowed-methods",
"expose-token", "bearer-only", "enable-basic-auth"})
public class BaseAdapterConfig extends BaseRealmConfig {
@JsonProperty("resource")
protected String resource;
@JsonProperty("use-resource-role-mappings")
protected boolean useResourceRoleMappings;
@JsonProperty("enable-cors")
protected boolean cors;
@JsonProperty("cors-max-age")
protected int corsMaxAge = -1;
@JsonProperty("cors-allowed-headers")
protected String corsAllowedHeaders;
@JsonProperty("cors-allowed-methods")
protected String corsAllowedMethods;
@JsonProperty("expose-token")
protected boolean exposeToken;
@JsonProperty("bearer-only")
protected boolean bearerOnly;
@JsonProperty("enable-basic-auth")
protected boolean enableBasicAuth;
@JsonProperty("public-client")
protected boolean publicClient;
@JsonProperty("credentials")
protected Map<String, String> credentials = new HashMap<String, String>();
public boolean isUseResourceRoleMappings() {
return useResourceRoleMappings;
}
public void setUseResourceRoleMappings(boolean useResourceRoleMappings) {
this.useResourceRoleMappings = useResourceRoleMappings;
}
public String getResource() {
return resource;
}
public void setResource(String resource) {
this.resource = resource;
}
public boolean isCors() {
return cors;
}
public void setCors(boolean cors) {
this.cors = cors;
}
public int getCorsMaxAge() {
return corsMaxAge;
}
public void setCorsMaxAge(int corsMaxAge) {
this.corsMaxAge = corsMaxAge;
}
public String getCorsAllowedHeaders() {
return corsAllowedHeaders;
}
public void setCorsAllowedHeaders(String corsAllowedHeaders) {
this.corsAllowedHeaders = corsAllowedHeaders;
}
public String getCorsAllowedMethods() {
return corsAllowedMethods;
}
public void setCorsAllowedMethods(String corsAllowedMethods) {
this.corsAllowedMethods = corsAllowedMethods;
}
public boolean isExposeToken() {
return exposeToken;
}
public void setExposeToken(boolean exposeToken) {
this.exposeToken = exposeToken;
}
public boolean isBearerOnly() {
return bearerOnly;
}
public void setBearerOnly(boolean bearerOnly) {
this.bearerOnly = bearerOnly;
}
public boolean isEnableBasicAuth() {
return enableBasicAuth;
}
public void setEnableBasicAuth(boolean enableBasicAuth) {
this.enableBasicAuth = enableBasicAuth;
}
public Map<String, String> getCredentials() {
return credentials;
}
public void setCredentials(Map<String, String> credentials) {
this.credentials = credentials;
}
public boolean isPublicClient() {
return publicClient;
}
public void setPublicClient(boolean publicClient) {
this.publicClient = publicClient;
}
}
@@ -1,54 +1,54 @@
package org.keycloak.representations.adapters.config;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
/**
* Common Realm Configuration
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-required"})
public class BaseRealmConfig {
@JsonProperty("realm")
protected String realm;
@JsonProperty("realm-public-key")
protected String realmKey;
@JsonProperty("auth-server-url")
protected String authServerUrl;
@JsonProperty("ssl-required")
protected String sslRequired;
public String getSslRequired() {
return sslRequired;
}
public void setSslRequired(String sslRequired) {
this.sslRequired = sslRequired;
}
public String getRealm() {
return realm;
}
public void setRealm(String realm) {
this.realm = realm;
}
public String getRealmKey() {
return realmKey;
}
public void setRealmKey(String realmKey) {
this.realmKey = realmKey;
}
public String getAuthServerUrl() {
return authServerUrl;
}
public void setAuthServerUrl(String authServerUrl) {
this.authServerUrl = authServerUrl;
}
}
package org.keycloak.representations.adapters.config;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
/**
* Common Realm Configuration
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-required"})
public class BaseRealmConfig {
@JsonProperty("realm")
protected String realm;
@JsonProperty("realm-public-key")
protected String realmKey;
@JsonProperty("auth-server-url")
protected String authServerUrl;
@JsonProperty("ssl-required")
protected String sslRequired;
public String getSslRequired() {
return sslRequired;
}
public void setSslRequired(String sslRequired) {
this.sslRequired = sslRequired;
}
public String getRealm() {
return realm;
}
public void setRealm(String realm) {
this.realm = realm;
}
public String getRealmKey() {
return realmKey;
}
public void setRealmKey(String realmKey) {
this.realmKey = realmKey;
}
public String getAuthServerUrl() {
return authServerUrl;
}
public void setAuthServerUrl(String authServerUrl) {
this.authServerUrl = authServerUrl;
}
}
@@ -0,0 +1,87 @@
package org.keycloak.representations.idm;
import java.io.Serializable;
import java.util.Comparator;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AuthenticationExecutionRepresentation implements Serializable {
private static final long serialVersionUID = 1L;
private String authenticatorConfig;
private String authenticator;
private String flowAlias;
private boolean autheticatorFlow;
private String requirement;
private boolean userSetupAllowed;
private int priority;
public String getAuthenticatorConfig() {
return authenticatorConfig;
}
public void setAuthenticatorConfig(String authenticatorConfig) {
this.authenticatorConfig = authenticatorConfig;
}
public String getAuthenticator() {
return authenticator;
}
public void setAuthenticator(String authenticator) {
this.authenticator = authenticator;
}
public String getRequirement() {
return requirement;
}
public void setRequirement(String requirement) {
this.requirement = requirement;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
public boolean isUserSetupAllowed() {
return userSetupAllowed;
}
public void setUserSetupAllowed(boolean userSetupAllowed) {
this.userSetupAllowed = userSetupAllowed;
}
/**
* If this execution is a flow, this is the flowId pointing to an AuthenticationFlowModel
*
* @return
*/
public String getFlowAlias() {
return flowAlias;
}
public void setFlowAlias(String flowId) {
this.flowAlias = flowId;
}
/**
* Is the referenced authenticator a flow?
*
* @return
*/
public boolean isAutheticatorFlow() {
return autheticatorFlow;
}
public void setAutheticatorFlow(boolean autheticatorFlow) {
this.autheticatorFlow = autheticatorFlow;
}
}
@@ -0,0 +1,67 @@
package org.keycloak.representations.idm;
import java.io.Serializable;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AuthenticationFlowRepresentation implements Serializable {
private static final long serialVersionUID = 1L;
private String alias;
private String description;
private String providerId;
private boolean topLevel;
private boolean builtIn;
protected List<AuthenticationExecutionRepresentation> authenticationExecutions;
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getProviderId() {
return providerId;
}
public void setProviderId(String providerId) {
this.providerId = providerId;
}
public boolean isTopLevel() {
return topLevel;
}
public void setTopLevel(boolean topLevel) {
this.topLevel = topLevel;
}
public boolean isBuiltIn() {
return builtIn;
}
public void setBuiltIn(boolean builtIn) {
this.builtIn = builtIn;
}
public List<AuthenticationExecutionRepresentation> getAuthenticationExecutions() {
return authenticationExecutions;
}
public void setAuthenticationExecutions(List<AuthenticationExecutionRepresentation> authenticationExecutions) {
this.authenticationExecutions = authenticationExecutions;
}
}
@@ -0,0 +1,35 @@
package org.keycloak.representations.idm;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AuthenticatorConfigRepresentation implements Serializable {
private static final long serialVersionUID = 1L;
private String alias;
private Map<String, String> config = new HashMap<String, String>();
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public Map<String, String> getConfig() {
return config;
}
public void setConfig(Map<String, String> config) {
this.config = config;
}
}
@@ -1,134 +1,134 @@
package org.keycloak.representations.idm;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ClaimRepresentation {
protected boolean name;
protected boolean username;
protected boolean profile;
protected boolean picture;
protected boolean website;
protected boolean email;
protected boolean gender;
protected boolean locale;
protected boolean address;
protected boolean phone;
public boolean getName() {
return name;
}
public void setName(boolean name) {
this.name = name;
}
public boolean getUsername() {
return username;
}
public void setUsername(boolean username) {
this.username = username;
}
public boolean getProfile() {
return profile;
}
public void setProfile(boolean profile) {
this.profile = profile;
}
public boolean getPicture() {
return picture;
}
public void setPicture(boolean picture) {
this.picture = picture;
}
public boolean getWebsite() {
return website;
}
public void setWebsite(boolean website) {
this.website = website;
}
public boolean getEmail() {
return email;
}
public void setEmail(boolean email) {
this.email = email;
}
public boolean getGender() {
return gender;
}
public void setGender(boolean gender) {
this.gender = gender;
}
public boolean getLocale() {
return locale;
}
public void setLocale(boolean locale) {
this.locale = locale;
}
public boolean getAddress() {
return address;
}
public void setAddress(boolean address) {
this.address = address;
}
public boolean getPhone() {
return phone;
}
public void setPhone(boolean phone) {
this.phone = phone;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ClaimRepresentation that = (ClaimRepresentation) o;
if (address != that.address) return false;
if (email != that.email) return false;
if (gender != that.gender) return false;
if (locale != that.locale) return false;
if (name != that.name) return false;
if (phone != that.phone) return false;
if (picture != that.picture) return false;
if (profile != that.profile) return false;
if (username != that.username) return false;
if (website != that.website) return false;
return true;
}
@Override
public int hashCode() {
int result = (name ? 1 : 0);
result = 31 * result + (username ? 1 : 0);
result = 31 * result + (profile ? 1 : 0);
result = 31 * result + (picture ? 1 : 0);
result = 31 * result + (website ? 1 : 0);
result = 31 * result + (email ? 1 : 0);
result = 31 * result + (gender ? 1 : 0);
result = 31 * result + (locale ? 1 : 0);
result = 31 * result + (address ? 1 : 0);
result = 31 * result + (phone ? 1 : 0);
return result;
}
}
package org.keycloak.representations.idm;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ClaimRepresentation {
protected boolean name;
protected boolean username;
protected boolean profile;
protected boolean picture;
protected boolean website;
protected boolean email;
protected boolean gender;
protected boolean locale;
protected boolean address;
protected boolean phone;
public boolean getName() {
return name;
}
public void setName(boolean name) {
this.name = name;
}
public boolean getUsername() {
return username;
}
public void setUsername(boolean username) {
this.username = username;
}
public boolean getProfile() {
return profile;
}
public void setProfile(boolean profile) {
this.profile = profile;
}
public boolean getPicture() {
return picture;
}
public void setPicture(boolean picture) {
this.picture = picture;
}
public boolean getWebsite() {
return website;
}
public void setWebsite(boolean website) {
this.website = website;
}
public boolean getEmail() {
return email;
}
public void setEmail(boolean email) {
this.email = email;
}
public boolean getGender() {
return gender;
}
public void setGender(boolean gender) {
this.gender = gender;
}
public boolean getLocale() {
return locale;
}
public void setLocale(boolean locale) {
this.locale = locale;
}
public boolean getAddress() {
return address;
}
public void setAddress(boolean address) {
this.address = address;
}
public boolean getPhone() {
return phone;
}
public void setPhone(boolean phone) {
this.phone = phone;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ClaimRepresentation that = (ClaimRepresentation) o;
if (address != that.address) return false;
if (email != that.email) return false;
if (gender != that.gender) return false;
if (locale != that.locale) return false;
if (name != that.name) return false;
if (phone != that.phone) return false;
if (picture != that.picture) return false;
if (profile != that.profile) return false;
if (username != that.username) return false;
if (website != that.website) return false;
return true;
}
@Override
public int hashCode() {
int result = (name ? 1 : 0);
result = 31 * result + (username ? 1 : 0);
result = 31 * result + (profile ? 1 : 0);
result = 31 * result + (picture ? 1 : 0);
result = 31 * result + (website ? 1 : 0);
result = 31 * result + (email ? 1 : 0);
result = 31 * result + (gender ? 1 : 0);
result = 31 * result + (locale ? 1 : 0);
result = 31 * result + (address ? 1 : 0);
result = 31 * result + (phone ? 1 : 0);
return result;
}
}
@@ -1,38 +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;
}
}
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;
}
}
Loaded 100 of 1883 files, more files were not shown because too many files have changed in this diff. Show more