Compare commits

..
229 Commits
Author SHA1 Message Date
keycloak-bot aa4b34d1aa Set version to 4.0.0.Beta1 2018-03-22 15:13:47 +00:00
stianst ffd084f414 Removed authz from examples-dist 2018-03-22 14:57:15 +01:00
Hynek Mlnarik bb3c76a9d9 KEYCLOAK-6929 Externalizers for AuthenticationSessionEntity 2018-03-21 12:46:50 +01:00
June Zhang ca15db81bb KEYCLOAK-6262 Incorporate new visual design from PatternFly (#4983)
* KEYCLOAK-6262 Incorporate new visual design from PatternFly

Update the username or email

Fix narrow/wide in template.ftl

minor style update

Add the Realm HTML name and image

Config OTP and Update Password
Not display the locale selector if there is less than 1 locale.

Fix margins/paddings on config otp screens

Fix title

Upgraded to PatternFly 3.41.6

Added RCUE and updated RH-SSO login theme

Refine the RCUE padding issue

Fix tests

Fix Keycloak background

Fix

* fix the overflowing issue

* Fix Console UI Tests to reflect the new login page

* Fix the different style of the IdP buttons
Fix the IE placeholder issue - add label

* Removed placeholder on login and reset pass. Fixed Keycloak background on wide screens.

* fixed the stackoverflow issues
fixed the width in the tablets
2018-03-21 10:47:33 +01:00
Pedro Igor 66507cbf91 Merge pull request #5081 from sguilhen/KC6543
[KEYCLOAK-6543] Remove the authz examples from the Keycloak codebase.
2018-03-20 09:09:04 -03:00
Jérôme Blanchard f11c24e359 [KEYCLOAK-6147] Include Nonce in OIDC authentication 2018-03-20 10:51:44 +01:00
Alex Szczuczko 3ade41a76d KEYCLOAK-6299 Upstream keycloak-javadocs-pom into the product profile of keycloak-api-docs-dist
A product profile has been added to keycloak-api-docs-dist, to replace the
downstream product javadocs POM. I've merged in any misc. changes from that
pom, but I've kept the upstream zip layout:

```
index.html
rest-api/index.html
javadocs/index.html
javadocs/index-all.html
javadocs/*
```

instead of the current product deliverable layout of:

```
META-INF/*
index.html
index-all.html
*
```

The community layout includes the rest-api that's distributed as a separate
product deliverable in 7.2.0. I've kept this layout for better artifact
consistency, but it could easily be changed to keep the product artifacts
consistent for the next product minor version.
2018-03-20 04:41:31 +01:00
Stefan Guilhen 35b9fe043c [KEYCLOAK-6543] Remove the authz examples from the Keycloak codebase. 2018-03-19 17:00:02 -03:00
Áron Bustya 82ba2b1b0d remove changes from standard OIDC client registration, move constants 2018-03-19 19:31:22 +01:00
Áron Bustya 57f57f5c75 set request object mandatory for client, restrict delivery mode
handle new attribute in client representation


add to UI
2018-03-19 19:31:22 +01:00
Pedro Igor 917ba90f2c Merge pull request #5077 from pedroigor/KEYCLOAK-6628
[KEYCLOAK-6628] - Expose methods to query roles, groups, and attributes of users in Evaluation API
2018-03-19 08:54:12 -03:00
mhajas 3cf104580e KEYCLOAK-6642 Stabilize Cluster tests 2018-03-19 07:45:35 +01:00
Pedro Igor 7cfe6addf0 Merge pull request #5078 from sguilhen/KC5886
[KEYCLOAK-5886] Improvements to Photoz example
2018-03-16 10:30:22 -03:00
Alex Szczuczko e4781b8aa3 KEYCLOAK-6828 Drop jcenter repository from services/pom.xml
swagger2markup-maven-plugin depends transitively on markdown_to_asciidoc, which
is inexplicably not in Central. This causes issues during productisation, as
it's reasonably assumed that all third party artifacts will be in Central.

Stian has already asked the community project to get their artifacts in Central
( bodiam/markdown-to-asciidoc#26 ), and they haven't done anything in almost a
year. So, I've added the artifacts under my own namespace, and changed the pom
to use those instead. The artifacts are unchanged from the ones on jcenter,
except the pom was expanded slightly to meet the minimum requirements of
Central.

I'm making this change now, as I hit the problem when trying to set up
continuous productization builds from master.
2018-03-16 08:36:04 +01:00
Johannes Knutsen 9de8e79577 KEYCLOAK-6824: Remove negation of isEmpty list check 2018-03-15 18:43:22 +01:00
mhajas 3826f6fae2 KEYCLOAK-3161 KEYCLOAK-3165 Migrate SAML tests from old testsuite to integration-arquillian 2018-03-15 18:15:44 +01:00
pedroigor 711bf244ed [KEYCLOAK-6628] - Expose methods to query roles, groups, and attributes of users in Evaluation API 2018-03-15 14:02:15 -03:00
Stefan Guilhen 99d0811412 [KEYCLOAK-5886] Improvements to Photoz example
- Fixed location of the authz json file in the README
 - Added links to allow easy navigation back to the main page
 - requestEntitlements link now properly display the RPT
2018-03-15 09:56:12 -03:00
sebastienblanc ee2d28d589 KEYCLOAK-6775 : fix french typos 2018-03-14 21:13:46 +01:00
Inki Hwang 22e32117e0 [KEYCLOAK-6789] show client id when client name is empty 2018-03-14 15:20:20 +01:00
Douglas Palmer fed1b62c5d [KEYCLOAK-6301] Remove service account when it is disabled from the client 2018-03-14 15:09:42 +01:00
Takashi Norimatsu 5b1e65c23e KEYCLOAK-6700 Financial API Read and Write API Security Profile : state
hash value (s_hash) to protect state parameter
2018-03-13 16:40:34 +01:00
Takashi Norimatsu e72756d01a KEYCLOAK-6700 Financial API Read and Write API Security Profile : state hash value (s_hash) to protect state parameter 2018-03-13 16:40:34 +01:00
Pedro Igor 871ecf83fb Merge pull request #5071 from vramik/KEYCLOAK-6644-stabilize-PhotozExampleAdapterTest
KEYCLOAK-6644 PhotozExampleAdapterTest is not stable
2018-03-13 09:03:57 -03:00
vmuzikar daaa35bc37 KEYCLOAK-6831 Fix Microsoft Social Login test 2018-03-13 10:19:27 +01:00
vramik 9d10ccef70 KEYCLOAK-6644 PhotozExampleAdapterTest is not stable 2018-03-13 09:53:17 +01:00
Pedro Igor 2aa71d1737 Merge pull request #5051 from pedroigor/KEYCLOAK-6787
[KEYCLOAK-6787] - Wrong validation of resources with same name and different owners
2018-03-12 11:41:49 -03:00
Pedro Igor b9b1102b74 Merge pull request #5004 from pedroigor/KEYCLOAK-6623
[KEYCLOAK-6623] - Policy enforcer gets confused with similar paths ending with wildcards
2018-03-12 09:59:05 -03:00
Pedro Igor f824582aac Merge pull request #5009 from pedroigor/KEYCLOAK-6116
[KEYCLOAK-6116] - Get email attribute from 'subject alternative name' using X509 certificate
2018-03-12 09:58:02 -03:00
pedroigor 199f289ee3 [KEYCLOAK-6623] - Adding test 2018-03-09 16:39:33 -03:00
pedroigor 668b67dcdb [KEYCLOAK-6623] - Policy enforcer gets confused with similar paths ending with wildcards 2018-03-09 16:38:57 -03:00
Hynek Mlnarik 190771ddf1 KEYCLOAK-6783 Add authentication into cross-dc testing 2018-03-09 15:08:55 +01:00
Hynek Mlnarik 05dcc6e3af KEYCLOAK-6783 JDG-auth optimizations 2018-03-09 15:08:55 +01:00
mposolda 6f7200868b Secured RemoteCache availability 2018-03-09 15:08:55 +01:00
pedroigor 62b70b561e [KEYCLOAK-6116] - Removing references to phantomjs.cli.args 2018-03-09 10:56:35 -03:00
pedroigor 1f13427dee [KEYCLOAK-6116] - Enabling tests for both jboss servers 2018-03-09 10:56:35 -03:00
pedroigor 6aee573e2e [KEYCLOAK-6116] - Tests for X509 Subject Alternative Name Extension 2018-03-09 10:56:35 -03:00
pedroigor c5c285abc3 [KEYCLOAK-6116] - Adding a OpenSSL CA Authority for testing 2018-03-09 10:56:35 -03:00
pedroigor 0a4fd79b22 [KEYCLOAK-6116] - Get email attribute from 'subject alternative name' using X509 certificate 2018-03-09 10:56:35 -03:00
Marko Strukelj b29edc7056 Merge pull request #5049 from tkyjovsk/KEYCLOAK-6165
KEYCLOAK-6165 Ability to load performance provisioning and test parameters from a properties file
2018-03-07 15:20:57 +01:00
Marko Strukelj 97aa3f5f36 Merge pull request #5064 from tkyjovsk/KEYCLOAK-6683
KEYCLOAK-6683 Ability to archive logs of provisioned docker instances
2018-03-07 15:16:16 +01:00
vmuzikar d66c33a8b9 KEYCLOAK-6793 Support custom Chrome binary in Arquillian testsuite 2018-03-07 10:38:12 +01:00
Tomas Kyjovsky 63383f8032 KEYCLOAK-6683 Ability to archive logs of provisioned docker instances 2018-03-06 23:49:08 +01:00
Pedro Igor a63a01c9ea Merge pull request #5060 from vmuzikar/fix-authz-KEYCLOAK-6802
KEYCLOAK-6802 Fix ScopePermissionManagement Console UI test
2018-03-06 15:47:12 -03:00
Tomas Kyjovsky dabf4e8bbd KEYCLOAK-6165 Ability to load performance provisioning and test parameters from a properties file 2018-03-06 19:19:24 +01:00
Marko Strukelj 35ffdb4745 Merge pull request #5030 from tkyjovsk/KEYCLOAK-6512
Keycloak 6512 Open workload injection model
2018-03-06 15:39:37 +01:00
Tomas Kyjovsky b0d63980e3 KEYCLOAK-6512 Open workload injection model + LogProcessor fixes 2018-03-06 13:20:20 +01:00
wyvie c27ffbda8b [KEYCLOAK-6643] server version now retreived from server info web page 2018-03-06 10:49:09 +01:00
vmuzikar 502fc62967 KEYCLOAK-6797 Fix Social Login test 2018-03-06 10:19:10 +01:00
Bill Burke c06ac70633 Merge pull request #5059 from patriot1burke/keycloak-6026
KEYCLOAK-6026
2018-03-05 16:54:45 -05:00
vmuzikar 414d51b02b KEYCLOAK-6802 Fix ScopePermissionManagement Console UI test 2018-03-05 18:19:54 +01:00
Bill Burke 4b6b45cf43 KEYCLOAK-6026 2018-03-05 11:57:05 -05:00
Bill Burke d90affa47d Merge remote-tracking branch 'upstream/master' into keycloak-6026 2018-03-02 18:54:10 -05:00
Bill Burke d5a075da30 Merge pull request #5057 from patriot1burke/client-flow-console
KEYCLOAK-6336
2018-03-02 18:52:34 -05:00
Pedro Igor 1b06194455 Merge pull request #5050 from TeliaSoneraNorge/KEYCLOAK-6659
Add pairwise sub support to authorization services
2018-03-02 14:44:28 -03:00
Bill Burke e2bec73011 KEYCLOAK-6336 2018-03-02 12:29:07 -05:00
Bill Burke 07613bcca0 Merge remote-tracking branch 'upstream/master' into client-flow-console 2018-03-02 11:30:19 -05:00
Bill Burke cc24484eaf Merge remote-tracking branch 'upstream/master' 2018-03-02 11:29:13 -05:00
Martin Hardselius 8549bd70b7 Add pairwise sub support to authorization services
Identity token verification will now fetch the user from the session
state instead of relying on the sub provided in the token. Also done in
KeycloakIdentity.

Resolves: KEYCLOAK-6659
2018-03-02 13:08:27 +01:00
vramik 9bd2e70376 KEYCLOAK-6790 Identifier for RESOURCE_SERVER_PERMISSION_TICKET table is too long for Oracle databases 2018-03-02 12:46:19 +01:00
vramik 569f26776e KEYCLOAK-5060 KEYCLOAK-3157 migrated Adapter package from old testsuite 2018-03-02 10:56:26 +01:00
Bill Burke 7f21cdd1f4 KEYCLOAK-6551 2018-03-02 10:41:05 +01:00
Bill Burke 7b5a0c6dcf KEYCLOAK-6551 2018-03-01 21:29:58 -05:00
pedroigor 1e1de85685 [KEYCLOAK-6787] - Wrong validation of resources with same name and different owners 2018-03-01 16:50:05 -03:00
Pedro Igor 9b1275f182 Merge pull request #5021 from pedroigor/KEYCLOAK-6621
[KEYCLOAK-6621] - Removing unnecessary code to process scopes from typed resources
2018-03-01 09:40:02 -03:00
pedroigor cb531056a6 [KEYCLOAK-6621] - Fixing cache and queries of policies with type scope 2018-02-28 16:33:45 -03:00
pedroigor b0200d462d [KEYCLOAK-6621] - Removing unnecessary code to process scopes from typed resources 2018-02-28 16:33:45 -03:00
vmuzikar 028e78f46b KEYCLOAK-6772 Fix SessionsPreloadCrossDCTest 2018-02-28 20:14:50 +01:00
Hynek Mlnarik 9ca7b22cec KEYCLOAK-6777 Fix AccountPageTest 2018-02-28 16:28:48 +01:00
Hynek Mlnarik 1b45ab2601 KEYCLOAK-6773 XML vulnerability test 2018-02-28 15:05:07 +01:00
Bill Burke 215bbaac6c hide tab when no client storage providers 2018-02-28 08:47:13 -05:00
Bill Burke 53f96099fb review fixes 2018-02-28 08:47:13 -05:00
Bill Burke 681256a079 KEYCLOAK-6622 2018-02-28 08:47:13 -05:00
mhajas 2a4663c940 KEYCLOAK-6471 Refactor SAML metadata parsers 2018-02-28 14:08:06 +01:00
Gaétan Collaud 1d88d061e6 French translation for KEYCLOAK-2120
* Added translation for KEYCLOAK-2120
* Removed link in translation since apps are now in a list (and html was escaped)
2018-02-28 08:55:26 +01:00
Gaétan Collaud 123c6dffe0 Improved french translation
In french, the "forgot password" email displays the full link instead of having a message like other languages.

`Lien pour réinitialiser votre mot de passe` = `Link to reset your password`.
2018-02-28 08:55:03 +01:00
Pedro Igor 91bdc4bde2 [KEYCLOAK-3169] - UMA 2.0 (#4368)
* [KEYCLOAK-3169] - UMA 2.0 Support

* [KEYCLOAK-3169] - Changes to account service and more tests

* [KEYCLOAK-3169] - Code cleanup and tests

* [KEYCLOAK-3169] - Changes to account service and tests

* [KEYCLOAK-3169] - Changes to account service and tests

* [KEYCLOAK-3169] - More tests

* [KEYCLOAK-3169] - Changes to adapter configuration

* [KEYCLOAK-3169] - Reviewing UMA specs and more tests

* [KEYCLOAK-3169] - Reviewing UMA specs and more tests

* [KEYCLOAK-3169] - Changes to UMA Grant Type and refactoring

* [KEYCLOAK-3169] - Refresh tokens for RPT responses and tests

* [KEYCLOAK-3169] - Changes to account my resources and policy enforcers

* [KEYCLOAK-3169] - Realm settings flag to enable/disable user-managed access in account mgmt console

* [KEYCLOAK-3169] - More changes to my resource pages in account mgmt console

* [KEYCLOAK-3169] - Need to enable user-managed on realm to run tests

* [KEYCLOAK-3169] - Removing more UMA 1.0 related code

* [KEYCLOAK-3169] - Only submit requests if ticket exists

* [KEYCLOAK-3169] - Returning UMA 401 response when not authenticated

* [KEYCLOAK-3169] - Removing unused code

* [KEYCLOAK-3169] - Removing unused code

* [KEYCLOAK-3169] - 403 response in case ticket is not created

* [KEYCLOAK-3169] - Fixing AbstractPhotozExampleAdapterTest#testClientRoleRepresentingUserConsent

* [KEYCLOAK-3169] - 403 status code only returned for non-bearer clients
2018-02-28 08:53:10 +01:00
Ferdinand Hübner 190ad06f1a KEYCLOAK-6618 Update German translations (#5002)
* KEYCLOAK-6618 Update German translations

Add missing translations for OTP authenticator settings and update
outdated translations for OTP authenticator

Fix minor issue for the username property (plural -> singular)

Add missing translations

* KEYCLOAK-6618: Include review feedback into German translations

* KEYCLOAK-6618: Reword translation for multi-factor authentication and fix minor translation issues

* KEYCLOAK-6618: Update German translation for the login theme

Message bundle keys have been reordered to be in sync with the english
version to improve scanning through the message bundles side-by-side.

The updated German translations from the account theme were applied to
the login theme as well (where applicable).
2018-02-28 05:10:31 +01:00
wyvie f8022a5c2f [KEYCLOAK-6585] hybrid flow: removed token_type and expires_in paramters from oidc auth response 2018-02-27 15:31:12 +01:00
mhajas e52380915b KEYCLOAK-4817 Fix instability 2018-02-27 14:35:52 +01:00
vmuzikar 08bf19d4aa KEYCLOAK-6694 Revamp OpenShift Social Login test 2018-02-27 12:28:42 +01:00
vmuzikar a2cc7bd4b9 KEYCLOAK-6709 Fix OpenShift IdP doesn't fetch user's full name 2018-02-27 12:28:42 +01:00
Marko Strukelj b8637bf1fa Merge pull request #5031 from tkyjovsk/KEYCLOAK-5973
KEYCLOAK-5973 Filtering of Gatling results
2018-02-27 12:00:27 +01:00
Tomas Kyjovsky 417c31bc0b KEYCLOAK-5973 Filtering of Gatling results 2018-02-27 10:33:24 +01:00
Hynek Mlnarik 1f20c03afa KEYCLOAK-6470 Refactor SAML adapter parsers 2018-02-27 09:37:29 +01:00
vmuzikar d70e4740fc KEYCLOAK-6693 Support external truststore in testsuite 2018-02-27 07:45:21 +01:00
Marko Strukelj b1d42381cd Merge pull request #5025 from tkyjovsk/KEYCLOAK-5372
KEYCLOAK-5372 Add warm-up, test-time, ramp-down times
2018-02-26 22:20:50 +01:00
Bill Burke aa089980ce Merge pull request #4942 from mstruk/KEYCLOAK-5807
KEYCLOAK-5807 Intermittent failures in UserStorageTest
2018-02-26 12:14:38 -05:00
wyvie 52acd959e0 [KEYCLOAK-6584] removed not-before-policy parameter from authorization response 2018-02-26 17:41:18 +01:00
Josh Cain 8cf65bc37d add getExecution method to admin resource 2018-02-26 17:00:17 +01:00
Josh Cain 24132c8f5b Return location for execution and flow creation in admin interface. Also allow for retrieval of execution by ID 2018-02-26 17:00:17 +01:00
mhajas fe1c447d9a KEYCLOAK-6546 Run filter test on WebLogic and WebSphere 2018-02-26 15:47:13 +01:00
Hynek Mlnarik bde9210fa3 KEYCLOAK-6692 Fix LogoutTest on Oracle 2018-02-26 15:45:55 +01:00
Václav Muzikář 10e31c397c KEYCLOAK-6592 Fix TokensTest#testButtonDisabledForEmptyAttributes in FF (#4996) 2018-02-26 11:37:58 +01:00
mhajas e2ad59a74d KEYCLOAK-4816 KEYCLOAK-4817 Move javascript tests to base testsuite and (#4964)
* KEYCLOAK-4816 KEYCLOAK-4817 Move javascript tests to base testsuite and
use JavascriptExecutor

* Use PhantomJS 2.1.1 instead of 1.9.8 in Travis CI
2018-02-26 10:49:05 +01:00
Ismael Jimenez f604449188 Removed duplicated tabindex
The tabindex="3" was at the beginning and at the end of the tag, kept the one at the beginning for consistency.
2018-02-26 10:47:32 +01:00
Tomas Kyjovsky f3b965d466 KEYCLOAK-5372 Add warm-up, test-time, ramp-down times 2018-02-23 20:24:19 +01:00
Hynek Mlnarik e7cdb8ad54 KEYCLOAK-6473 KEYCLOAK-6472 SAML parser refactor + protocol parsers 2018-02-23 08:16:14 +01:00
gonzalad 898347366d KEYCLOAK-6589: Optimize jpql in User search API
This commit removes the 6 n+1 select
that are issued when calling GET /users api.

We now have 4 select queries.
2018-02-22 14:21:42 +01:00
Stian Thorgersen 9ef1f1b73c KEYCLOAK-3482 2018-02-22 09:42:45 -03:00
Marko Strukelj eea6eb6263 KEYCLOAK-6664 Fix performance testsuite shell scripts to run on macOS 2018-02-22 12:40:23 +01:00
Marko Strukelj 548ab4f78c KEYCLOAK-6514 Common approach to writing performance tests 2018-02-22 12:40:04 +01:00
pedroigor b464dc15f2 [KEYCLOAK-6492] - Removing resources files 2018-02-22 09:05:40 +01:00
pedroigor 8112c5d3f2 [KEYCLOAK-6492] - Migrate authorization package from old testsuite 2018-02-22 09:05:40 +01:00
stianst eb326cd1bb KEYCLOAK-6534 Check for string in receiveMessage in session iframe 2018-02-22 07:02:16 +01:00
Erlend Hamnaberg 208ecbc3f7 KEYCLOAK-6676: Fix NPE if the redirect_uri parameter is missing 2018-02-21 19:44:22 +01:00
pedroigor 61d5425fdf [KEYCLOAK-6321] - Tests 2018-02-21 19:41:44 +01:00
pedroigor d590600c12 [KEYCLOAK-6321] - NPE when deleting a resource with admin events enabled 2018-02-21 19:41:44 +01:00
Hynek Mlnarik 12a2f23101 KEYCLOAK-6651 Fix JDK7 compilation issue 2018-02-21 09:43:04 +01:00
Bruno Oliveira f351db608e [KEYCLOAK-6334] Minor typo: "read only" should be "read-only" 2018-02-20 20:18:16 +01:00
Christian Katzorke (cig393a) 2c59d948fc in addition to KEYCLOAK-6065 no automplete 2018-02-20 20:14:28 +01:00
Tomas Kyjovsky 72199cc0b3 KEYCLOAK-6582 Specify exact version of MariaDB server in the performance tests 2018-02-19 11:53:45 +01:00
mposolda fc463ae50b KEYCLOAK-6617 Offline token logout did not invalidate user session 2018-02-19 08:49:05 +01:00
cgol 86a8addf49 KEYCLOAK-6615 Remove offline session from database on offline token logout
remove offline token from database on offline session logout
2018-02-19 08:49:05 +01:00
Hynek Mlnarik b367e94261 KEYCLOAK-6522 Align versions 2018-02-16 10:45:36 +01:00
Tomas Kyjovsky df68ca8dcf KEYCLOAK-5968 Move configuration of Infinispan server from Dockerfile to maven 2018-02-14 16:48:22 +01:00
stianst 9b63cd35f0 KEYCLOAK-6431 2018-02-13 19:38:46 +01:00
Hynek Mlnarik 84ea3f8cb1 KEYCLOAK-4315 Remove some dead/duplicate classes 2018-02-13 15:41:36 +01:00
Bill Burke 5d5373454c Merge pull request #4991 from patriot1burke/challenge-support
KEYCLOAK-6355
2018-02-13 09:38:45 -05:00
Bill Burke a3d6917f20 disable clisso experimental feature 2018-02-12 17:57:05 -05:00
Bill Burke 87ee15a081 fix 2018-02-12 16:52:55 -05:00
David Festal f44cda2621 Make the keycloak.js capable of working with alternate OIDC providers (#4978)
* Make the `keycloak.js` capable of working with alternate OIDC providers

(provided that they create access_tokens as JWT tokens with `exp` and
`iat` claims).

Also add a `useNonce` option, to allow disabling the `nonce` check
since, in the OIDC specification, `nonce` is optional.

Signed-off-by: David Festal <dfestal@redhat.com>

* Update the `keycloak.ts` with the `useNonce` additional init option. 

Signed-off-by: David Festal <dfestal@redhat.com>

* Fix 2 errors in the case `checkSessionIframe` is used

Signed-off-by: David Festal <dfestal@redhat.com>
2018-02-12 11:00:02 +01:00
stianst dd1e5b5c0f Fix JS console update profile 2018-02-12 10:25:43 +01:00
Stian Thorgersen adb6c7789c Merge pull request #4967 from k-tamura/fix-incorrect-japanese-translations
KEYCLOAK-6469 Fix incorrect Japanese translations
2018-02-12 09:58:58 +01:00
Bill Burke d6788a0839 finish 2018-02-10 13:38:39 -05:00
mhajas 2b65adc15f KEYCLOAK-6309 Fix tests
Add trustore to war even if ssl is not enabled because HttpClient is configured with truststore
2018-02-09 10:24:58 +01:00
Bruno Oliveira b91998a0d8 [KEYCLOAK-6111] 'Override User-Initiated Action Lifespan' admin GUI can break realm configuration 2018-02-09 06:36:23 -02:00
stianst 505cf5b251 KEYCLOAK-6519 Theme resource provider 2018-02-09 08:28:59 +01:00
Bill Burke 5ea4ef9e55 change code query params to session_code 2018-02-08 17:37:27 -05:00
Douglas Palmer e8de4655ac KEYCLOAK-6344 Use POST instead of GET for LDAP connection tests 2018-02-08 21:18:03 +01:00
pedroigor 76657d5239 [KEYCLOAK-6528] - Fixing mysql error. Probably a consequence of KEYCLOAK-6228 changes. 2018-02-08 21:17:33 +01:00
Stian Thorgersen d63040283f Merge pull request #4958 from iilei/missing_accept_language_country_code_fallback
Fix Locale Negotiation for omitted Country Code
2018-02-08 20:55:19 +01:00
Josh Cain 04c2ad9430 give more useful failure message when event.error is called without first setting event.type 2018-02-08 16:20:16 +01:00
Stian Thorgersen 75f040f38e Merge pull request #4979 from stianst/KEYCLOAK-1874
KEYCLOAK-1874 Add PatternFly 3.38.1
2018-02-07 15:29:01 +01:00
stianst 0e69dc6885 KEYCLOAK-1874 Add PatternFly 3.38.1 2018-02-07 14:26:10 +01:00
stianst f1e7d3d6da KEYCLOAK-6378 Clean themes common directory 2018-02-07 12:07:40 +01:00
Hynek Mlnarik c07b60d527 KEYCLOAK-6474 Fix NPE on SAML logout 2018-02-07 08:05:36 +01:00
David 2743e4a182 Update changes on sssd.conf file to make script idempotent 2018-02-06 09:04:37 -02:00
Hynek Mlnarik 972c3dd36b KEYCLOAK-6521 Update licenses 2018-02-06 11:58:11 +01:00
Martin Kanis 9ccef223f6 KEYCLOAK-6522 Align versions to match EAP 7.1.1 2018-02-06 11:58:11 +01:00
Douglas Palmer fc52ff65bd [KEYCLOAK-6518] Added explicit guava dependency instead of relying on transitive dependency. 2018-02-06 10:52:48 +01:00
Jochen Preusche 8325151e16 Extract findLocale to LocaleNegotiator, add tests
* Improve Testability of Locale Negotiation
 * Add test for Locale Negotiation
 * Fix Locale Negotiation for omitted Country Code
2018-02-06 09:50:04 +01:00
Hynek Mlnarik b3766576d7 KEYCLOAK-6146 Simplify test via RealmCreator 2018-02-06 09:28:07 +01:00
Pedro Igor e3153295a9 Merge pull request #4969 from vmuzikar/fix-authz-tests
KEYCLOAK-6509 Stabilize authz rules UI tests
2018-02-05 17:06:43 -02:00
vmuzikar 3420293f36 KEYCLOAK-6509 Set waiting period in RulePolicyForm to longer value 2018-02-05 15:47:44 +01:00
Kohei Tamura 0370cb64e7 Fix incorrect Japanese translations 2018-02-05 19:11:03 +09:00
Kohei Tamura a50b94be0c KEYCLOAK-6457 Fix typos in properties 2018-02-05 05:58:08 +01:00
Marko Strukelj 62a9d4ea91 KEYCLOAK-5807 Under daily eviction policy user entries not returned from cache when they should 2018-02-02 19:27:23 +01:00
Serhii Shymkiv c2fe500eb8 [KEYCLOAK-4721] Consider Session Language of Realm Also In ReCaptcha 2018-02-02 13:57:03 +01:00
vmuzikar 340afb2a50 KEYCLOAK-6450 Stabilize WelcomePageTest 2018-02-02 13:08:52 +01:00
Tomas Kyjovsky 3873ab811a KEYCLOAK-6288 Add support for "sar" metrics for docker-based performance testing 2018-02-02 12:44:33 +01:00
Tomas Kyjovsky 4ab856a0be KEYCLOAK-6140 Allow to customize max_connections parameter of MariaDB server 2018-02-02 12:43:01 +01:00
vmuzikar 46ebff2163 KEYCLOAK-6331 Fix and stabilize Console UI tests 2018-02-02 11:58:47 +01:00
Hynek Mlnarik afa26f7d3c KEYCLOAK-6339 Display SAML client IdP-initiated SSO URL 2018-02-02 11:17:10 +01:00
Pavel Drozd 9382439a05 Merge pull request #4944 from mhajas/KEYCLOAK-4751
KEYCLOAK-4751 Fix tests on EAP6 and add test for EAP7
2018-02-02 11:13:07 +01:00
o.pakers 8495a7c05a [master]: fix type for checkLoginIframeInterval 2018-02-02 08:37:07 +01:00
vramik 019c3c9ef9 KEYCLOAK-6146 realm import fails when password policy is specified 2018-02-02 08:30:06 +01:00
Thomas Darimont 77334af34e KEYCLOAK-6222 Check syntax for errors on ScriptBasedOIDCProtocolMapper validation
We now explicitly check for syntax errors
during validation of ScriptBasedOIDCProtocolMappers.
2018-02-02 08:28:27 +01:00
Bill Burke 8f09efab9d Merge pull request #4949 from patriot1burke/client-storage-spi
KEYCLOAK-6228
2018-02-01 08:59:02 -05:00
mhajas c34db4cf01 KEYCLOAK-6309 Configure HTTPClient in keycloak-saml.xml 2018-02-01 09:59:08 +01:00
Bill Burke 6054b69eec fix serializable exceptions 2018-01-31 20:26:50 -05:00
Martin Kanis 6b8ec0bb82 KEYCLOAK-6154 Exclude common-logging/codec from keycloak-osgi-thirdparty 2018-01-31 21:26:41 +01:00
Bill Burke a0d275c850 whoops, fix db script 2018-01-31 14:28:40 -05:00
Bruno Oliveira 179e371d0e [KEYCLOAK-6298] SAML adapter script should support offline installation of adapter 2018-01-31 17:10:31 -02:00
Bill Burke 26411a123e migrate fix 2018-01-31 13:11:46 -05:00
Bill Burke 126dd70efc client stat improvement 2018-01-31 13:05:13 -05:00
Pedro Igor 461c62ea10 Merge pull request #4953 from pedroigor/KEYCLOAK-6164
[KEYCLOAK-6164] - Authorization services client doesn't work with JDK7
2018-01-31 15:18:50 -02:00
pedroigor bda57d00df [KEYCLOAK-6164] - Authorization services client doesn't work with JDK7 2018-01-31 13:42:30 -02:00
Bill Burke bd3eb9d662 more hynek db changes 2018-01-30 19:46:42 -05:00
Bill Burke a571781240 hynek db changes 2018-01-30 17:00:55 -05:00
Vlastimil Elias a5f675d693 KEYCLOAK-4937 - convert time units in emails into human-friendly format 2018-01-30 06:38:57 +01:00
Bill Burke 4a044fe867 add ofline token test 2018-01-29 17:08:13 -05:00
Bill Burke 0fc7fa557d fix caching 2018-01-29 16:26:51 -05:00
Bill Burke 79f9de9de4 Merge remote-tracking branch 'upstream/master' into client-storage-spi 2018-01-29 12:28:26 -05:00
Bill Burke 4bf23cc83a caching 2018-01-29 12:28:17 -05:00
Pedro Igor 82815ff614 Merge pull request #4943 from mhajas/KEYCLOAK-6392
KEYCLOAK-6392 Install elytron adapter on windows
2018-01-29 09:35:12 -02:00
Pedro Igor bdfab04c10 Merge pull request #4948 from mhajas/KEYCLOAK-4793
KEYCLOAK-4793 Fix authorization services maven scanner properties in …
2018-01-29 09:33:58 -02:00
mhajas 2a2f255640 KEYCLOAK-4793 Fix authorization services maven scanner properties in base testsuite for product 2018-01-29 09:18:20 +01:00
Bill Burke 1d8e38f0c6 admin console 2018-01-27 13:05:02 -05:00
Bill Burke dd4c0d448c Merge remote-tracking branch 'upstream/master' into client-storage-spi 2018-01-27 09:47:41 -05:00
Bill Burke 6b84b9b4b6 done 1st iteration 2018-01-27 09:47:16 -05:00
mhajas 8ac7d1deca KEYCLOAK-4751 Fix tests on EAP6 and add test for EAP7 2018-01-26 11:25:33 +01:00
mhajas f0511c5f51 KEYCLOAK-6392 Install elytron adapter on windows 2018-01-26 11:21:53 +01:00
Takashi Norimatsu 502627f590 KEYCLOAK-5811 Client Authentication by JWS Client Assertion in client secret 2018-01-26 10:59:40 +01:00
vmuzikar 806b554fb2 Social login test for GitHub with private email 2018-01-25 20:56:24 +01:00
gregoirew 13261b52db Use the github /user/emails api endpoint if the github user did not set any public email.
Github can send a null email on the user info endpoint if there is no public email on the user profile.
This commit look for email on the /user/emails endpoint, selecting the primary email.
2018-01-25 20:56:24 +01:00
Ray DeCampo a21a94078b KEYCLOAK-5578: Keycloak JS adapter returns native Promise instances when available.
Promise instances are adorned with success() and error() functions to retain backwards compatibility.
2018-01-25 20:42:31 +01:00
vramik b0fbe5c8ba KEYCLOAK-6300 List of group members is not sorted alphabetically 2018-01-25 20:21:03 +01:00
Bill Burke ddad1cb8af Merge remote-tracking branch 'upstream/master' into client-storage-spi 2018-01-25 10:08:37 -05:00
Bill Burke 8a17b61f4e initial work 2018-01-25 10:08:26 -05:00
Bill Burke 7c66f76858 Merge pull request #4932 from patriot1burke/per-client-flow
KEYCLOAK-6335
2018-01-25 09:55:11 -05:00
Thomas Darimont 3d12bf7d14 KEYCLOAK-4743 Revise proxy support for HttpClient SPI
Polishing & more tests.
2018-01-25 09:31:32 +01:00
Thomas Darimont 851d0192ad KEYCLOAK-4743 Add proxy support to HttpClient SPI
We now provide a configurable way for dynamic proxy route selection
for the default HttpClient based on regex based targetHostname patterns.

Introduced `ProxyMapping` to describe a regex based mapping
between target hosts and the proxy URL to use.

A `ProxyMapping` can be build from an ordered list of string based
mapping representations, e.g:
```
   ^.*.(google.com|googleapis.com)$;http://localhost:8080
```
If the targetHost does not match a configured proxy mapping,
no proxy is used.

This can be configured via standalone.xml / jboss-cli, e.g.:
```
   echo SETUP: Configure proxy routes for HttpClient SPI
   /subsystem=keycloak-server/spi=connectionsHttpClient/provider=default:add(enabled=true)
   /subsystem=keycloak-server/spi=connectionsHttpClient/provider=default:write-attribute(name=properties.proxy-mappings,value=["^.*.(google.com|googleapis.com)$;http://www-proxy1:8080","^.*.facebook.com$;http://www-proxy2:8080"])
```
The new `ProxyMappingWareRoutePlanner` uses a configured `ProxyMapping`
to decide which proxy to use for a given request based on the target host
denoted by the HTTP request to execute.

I verified this manually with the BurpProxy Suite.
2018-01-25 09:31:32 +01:00
Douglas Palmer 42759be6ff [KEYCLOAK-6143] Remove Hmac prefix from algorithms in the OTP manual config pages 2018-01-25 07:10:30 +01:00
Douglas Palmer 0f1644e612 [KEYCLOAK-6142] Updated OTP manual config pages to reflect HOTP 2018-01-25 07:09:24 +01:00
Thorsten Boock 64b75d2806 Fix Authenticator Page HTML
Closes `<thead>` and disables escaping for the `totpStep1` message as it includes HTML links.
2018-01-24 13:44:57 -05:00
Bill Burke 4bfb62d7f4 marek suggested fixes 2018-01-24 09:32:38 -05:00
mposolda 6369c26671 KEYCLOAK-6286 Adding 'Exclude Session State From Authentication Response' switch to fix backwards compatibility with Keycloak 2.X adapters 2018-01-24 11:35:13 +01:00
Thomas Recloux 71e0b00600 KEYCLOAK-5857 Supports PBKDF2 hashes with different key size
The original use case is to support imported credentials with a different key size without
implementing a totally new PasswordHashProvider
2018-01-24 09:02:37 +01:00
Bill Burke be65c14a6a fix provider test 2018-01-23 13:03:45 -05:00
Bill Burke 7b2e72d395 Merge remote-tracking branch 'upstream/master' into per-client-flow 2018-01-23 12:10:11 -05:00
Bill Burke a9297df89c KEYCLOAK-6335 2018-01-23 12:09:49 -05:00
Hynek Mlnarik 4ba72e2d2d KEYCLOAK-5976 Fix client setting in brokered IdP-initiated scenario 2018-01-23 09:34:11 +01:00
Tomas Kyjovsky 5f49db6c7f KEYCLOAK-6311 Remote-mode adapter tests don't work 2018-01-19 15:09:57 +01:00
Douglas Palmer fc3c07f6de [KEYCLOAK-6236] Use MessageDigest.isEquals in place of String.equals 2018-01-18 13:04:54 +01:00
Hynek Mlnarik c7cba6d5ad KEYCLOAK-6109 Skip Scoping element in AuthnRequest 2018-01-18 11:40:13 +01:00
Martin Kanis 6f624b29e4 KEYCLOAK-6240 Exclude events doesn't work in examples 2018-01-18 11:10:13 +01:00
stianst f762173eb0 KEYCLOAK-3370 Add option to override theme in client template and client 2018-01-18 09:14:13 +01:00
stianst 35ada9d636 KEYCLOAK-6289 Add ThemeSelectorSPI 2018-01-18 09:14:13 +01:00
stianst 81a4ba854c KEYCLOAK-6136 Set target/source 2018-01-16 15:12:28 +01:00
Tomas Kyjovsky 5758c7e175 KEYCLOAK-6138 Allow to customize version of the KC server artifact used in performance tests 2018-01-12 14:11:41 +01:00
Tomas Kyjovsky ba2f8cdeef KEYCLOAK-6085 Allow to customize DB dump download location through a Maven property 2018-01-12 14:11:10 +01:00
Pavel Drozd c2fa2de290 Merge pull request #4911 from wyvie/springboot-jetty
[KEYCLOAK-3837] jetty version can be passed as argument for springboot tests
2018-01-12 11:58:14 +01:00
Pedro Igor 932aef887c Merge pull request #4913 from pedroigor/KEYCLOAK-6159
[KEYCLOAK-6159] - Resource Permission UI not enabling save button when no policies are set
2018-01-11 10:41:20 -02:00
Thomas Darimont bae4d4c673 KEYCLOAK-5791 Allow multi-valued ScriptBasedOIDCProtocolMapper
We now support multi-valued attribute values for the
`ScriptBasedOIDCProtocolMapper`.
Previously the `ScriptBasedOIDCProtocolMapper` only supported
single valued output. If a script returned a list of
output values then only the first value was emitted to the token.

By default multi-valued is set to `false` / `off`.
2018-01-11 08:52:24 +01:00
Pedro Igor 6bf050e05c Merge pull request #4912 from mhajas/KEYCLOAK-5503
KEYCLOAK-5503 Remove redirecting to error page for AutodetectBearerOn…
2018-01-10 15:28:52 -02:00
pedroigor 79349e6307 [KEYCLOAK-6159] - Resource Permission UI not enabling save button when no policies are set 2018-01-09 16:28:53 -02:00
mhajas a77be7eb7b KEYCLOAK-5503 Remove redirecting to error page for AutodetectBearerOnly client 2018-01-09 16:34:18 +01:00
wyvie 082714a436 [KEYCLOAK-3837] jetty version can be passed as argument for springboot tests 2018-01-09 16:15:31 +01:00
stianst 06bb6f00e5 Include Jetty 9.1 in product profile 2018-01-04 09:14:11 +01:00
Hynek Mlnarik e4c875eb41 KEYCLOAK-6108 Remove DROP INDEX in postgres (handled automatically) 2018-01-04 09:03:52 +01:00
Hynek Mlnarik f0c1e65b2d KEYCLOAK-6095 Include schema in custom SQL 2018-01-04 09:03:52 +01:00
Hynek Mlnarik b5fc6045fd KEYCLOAK-6106 Put dotless ids first in identity broker state 2018-01-02 21:31:49 +01:00
Pedro Igor d99dcc0d4b Merge pull request #4898 from pedroigor/KEYCLOAK-6113
[KEYCLOAK-6113] - Remove red asterisk sign next to 'Apply Policy' while creating a new permission
2018-01-02 13:40:53 -02:00
pedroigor 56750cba83 [KEYCLOAK-6113] - Remove red asterisk sign next to 'Apply Policy' while creating a new permission 2018-01-02 12:40:10 -02:00
stianst 6e3303145f KEYCLOAK-6079 2018-01-02 15:15:44 +01:00
stianst d8c0cc447f KEYCLOAK-6090 Add missing cors headers with invalid username/password and resource owner grant 2018-01-02 15:15:15 +01:00
Ramon Spahr 3ee999ae2a fix wrong file path and extension for welcome-theme example 2018-01-02 12:11:44 +01:00
stianst 0bedbb4dd3 Bump version to 4.0.0.CR1-SNAPSHOT 2017-12-21 15:06:00 +01:00
3710 changed files with 1246491 additions and 25662 deletions

No files matched your search

+10
View File
@@ -22,6 +22,16 @@ jdk:
install: true
before_install:
- "export PHANTOMJS_VERSION=2.1.1"
- "phantomjs --version"
- "export PATH=$PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64/bin:$PATH"
- "phantomjs --version"
- "if [ $(phantomjs --version) != '$PHANTOMJS_VERSION' ]; then rm -rf $PWD/travis_phantomjs; mkdir -p $PWD/travis_phantomjs; fi"
- "if [ $(phantomjs --version) != '$PHANTOMJS_VERSION' ]; then wget https://github.com/Medium/phantomjs/releases/download/v$PHANTOMJS_VERSION/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 -O $PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2; fi"
- "if [ $(phantomjs --version) != '$PHANTOMJS_VERSION' ]; then tar -xvf $PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 -C $PWD/travis_phantomjs; fi"
- "phantomjs --version"
script:
- ./travis-run-tests.sh $TESTS
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -174,6 +174,10 @@ public class BearerTokenRequestAuthenticator {
@Override
public boolean challenge(HttpFacade facade) {
if (deployment.getPolicyEnforcer() != null) {
deployment.getPolicyEnforcer().enforce(OIDCHttpFacade.class.cast(facade));
return true;
}
OIDCAuthenticationError error = new OIDCAuthenticationError(reason, description);
facade.getRequest().setError(error);
facade.getResponse().addHeader("WWW-Authenticate", challenge);
@@ -0,0 +1,97 @@
package org.keycloak.adapters.authentication;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.common.util.Time;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.representations.JsonWebToken;
/**
* Client authentication based on JWT signed by client secret instead of private key .
* See <a href="http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication">specs</a> for more details.
*
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/
public class JWTClientSecretCredentialsProvider implements ClientCredentialsProvider {
private static final Logger logger = Logger.getLogger(JWTClientSecretCredentialsProvider.class);
public static final String PROVIDER_ID = "secret-jwt";
private SecretKey clientSecret;
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public void init(KeycloakDeployment deployment, Object config) {
if (config == null || !(config instanceof Map)) {
throw new RuntimeException("Configuration of jwt credentials by client secret is missing or incorrect for client '" + deployment.getResourceName() + "'. Check your adapter configuration");
}
Map<String, Object> cfg = (Map<String, Object>) config;
String clientSecretString = (String) cfg.get("secret");
if (clientSecretString == null) {
throw new RuntimeException("Missing parameter secret-jwt in configuration of jwt for client " + deployment.getResourceName());
}
setClientSecret(clientSecretString);
}
@Override
public void setClientCredentials(KeycloakDeployment deployment, Map<String, String> requestHeaders, Map<String, String> formParams) {
String signedToken = createSignedRequestToken(deployment.getResourceName(), deployment.getRealmInfoUrl());
formParams.put(OAuth2Constants.CLIENT_ASSERTION_TYPE, OAuth2Constants.CLIENT_ASSERTION_TYPE_JWT);
formParams.put(OAuth2Constants.CLIENT_ASSERTION, signedToken);
}
public void setClientSecret(String clientSecretString) {
// Get client secret and validate signature
// According to <a href="http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication">OIDC's client authentication spec</a>,
// The HMAC (Hash-based Message Authentication Code) is calculated using the octets of the UTF-8 representation of the client_secret as the shared key.
// Use "HmacSHA256" consulting <a href="https://docs.oracle.com/javase/jp/8/docs/api/javax/crypto/Mac.html">java8 api</a>
// because it must be implemented in every java platform.
try {
clientSecret = new SecretKeySpec(clientSecretString.getBytes("UTF-8"), "HmacSHA256");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Failed to create secret key spec due to unsupported encoding.");
}
}
public String createSignedRequestToken(String clientId, String realmInfoUrl) {
JsonWebToken jwt = createRequestToken(clientId, realmInfoUrl);
// JOSE header {"alg":"HS256","typ" : "JWT"} no need "kid" due to using only one registered client secret.
// Use "HmacSHA256" consulting <a href="https://docs.oracle.com/javase/jp/8/docs/api/javax/crypto/Mac.html">java8 api</a>.
// because it must be implemented in every java platform.
return new JWSBuilder().jsonContent(jwt).hmac256(clientSecret);
}
private JsonWebToken createRequestToken(String clientId, String realmInfoUrl) {
// According to <a href="http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication">OIDC's client authentication spec</a>,
// JWT claims is the same as one by private_key_jwt
JsonWebToken reqToken = new JsonWebToken();
reqToken.id(AdapterUtils.generateId());
reqToken.issuer(clientId);
reqToken.subject(clientId);
reqToken.audience(realmInfoUrl);
int now = Time.currentTime();
reqToken.issuedAt(now);
// the same as in KEYCLOAK-2986, JWTClientCredentialsProvider's timeout field
reqToken.expiration(now + 10);
reqToken.notBefore(now);
return reqToken;
}
}
@@ -18,7 +18,6 @@
package org.keycloak.adapters.authorization;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -65,54 +64,58 @@ public abstract class AbstractPolicyEnforcer {
return createEmptyAuthorizationContext(true);
}
Request request = httpFacade.getRequest();
String path = getPath(request);
PathConfig pathConfig = this.pathMatcher.matches(path, this.paths);
KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();
if (securityContext != null) {
AccessToken accessToken = securityContext.getToken();
if (securityContext == null) {
if (pathConfig != null) {
challenge(pathConfig, getRequiredScopes(pathConfig, request), httpFacade);
}
return createEmptyAuthorizationContext(false);
}
if (accessToken != null) {
Request request = httpFacade.getRequest();
String path = getPath(request);
PathConfig pathConfig = this.pathMatcher.matches(path, this.paths);
AccessToken accessToken = securityContext.getToken();
LOGGER.debugf("Checking permissions for path [%s] with config [%s].", request.getURI(), pathConfig);
if (accessToken != null) {
LOGGER.debugf("Checking permissions for path [%s] with config [%s].", request.getURI(), pathConfig);
if (pathConfig == null) {
if (EnforcementMode.PERMISSIVE.equals(enforcementMode)) {
return createAuthorizationContext(accessToken, null);
}
LOGGER.debugf("Could not find a configuration for path [%s]", path);
if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
return createAuthorizationContext(accessToken, null);
}
handleAccessDenied(httpFacade);
return createEmptyAuthorizationContext(false);
if (pathConfig == null) {
if (EnforcementMode.PERMISSIVE.equals(enforcementMode)) {
return createAuthorizationContext(accessToken, null);
}
if (EnforcementMode.DISABLED.equals(pathConfig.getEnforcementMode())) {
return createEmptyAuthorizationContext(true);
LOGGER.debugf("Could not find a configuration for path [%s]", path);
if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
return createAuthorizationContext(accessToken, null);
}
MethodConfig methodConfig = getRequiredScopes(pathConfig, request);
handleAccessDenied(httpFacade);
if (isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
try {
return createAuthorizationContext(accessToken, pathConfig);
} catch (Exception e) {
throw new RuntimeException("Error processing path [" + pathConfig.getPath() + "].", e);
}
return createEmptyAuthorizationContext(false);
}
if (EnforcementMode.DISABLED.equals(pathConfig.getEnforcementMode())) {
return createEmptyAuthorizationContext(true);
}
MethodConfig methodConfig = getRequiredScopes(pathConfig, request);
if (isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
try {
return createAuthorizationContext(accessToken, pathConfig);
} catch (Exception e) {
throw new RuntimeException("Error processing path [" + pathConfig.getPath() + "].", e);
}
}
LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
if (!challenge(pathConfig, methodConfig, httpFacade)) {
LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
handleAccessDenied(httpFacade);
}
if (!challenge(pathConfig, methodConfig, httpFacade)) {
LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
handleAccessDenied(httpFacade);
}
}
@@ -139,7 +142,7 @@ public abstract class AbstractPolicyEnforcer {
boolean hasPermission = false;
for (Permission permission : permissions) {
if (permission.getResourceSetId() != null) {
if (permission.getResourceId() != null) {
if (isResourcePermission(actualPathConfig, permission)) {
hasPermission = true;
@@ -292,6 +295,6 @@ public abstract class AbstractPolicyEnforcer {
}
private boolean matchResourcePermission(PathConfig actualPathConfig, Permission permission) {
return permission.getResourceSetId().equals(actualPathConfig.getId());
return permission.getResourceId().equals(actualPathConfig.getId());
}
}
@@ -23,11 +23,11 @@ import org.jboss.logging.Logger;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.representation.PermissionRequest;
import org.keycloak.authorization.client.resource.PermissionResource;
import org.keycloak.authorization.client.resource.ProtectionResource;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import org.keycloak.representations.idm.authorization.PermissionRequest;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -42,45 +42,40 @@ public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
@Override
protected boolean challenge(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade facade) {
if (getEnforcerConfig().getUserManagedAccess() != null) {
challengeUmaAuthentication(pathConfig, methodConfig, facade);
} else {
challengeEntitlementAuthentication(facade);
HttpFacade.Response response = facade.getResponse();
AuthzClient authzClient = getAuthzClient();
String ticket = getPermissionTicket(pathConfig, methodConfig, authzClient);
if (ticket == null) {
response.setStatus(403);
return true;
}
String realm = authzClient.getConfiguration().getRealm();
String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString();
response.setStatus(401);
StringBuilder wwwAuthenticate = new StringBuilder("UMA realm=\"").append(realm).append("\"").append(",as_uri=\"").append(authorizationServerUri).append("\"");
if (ticket != null) {
wwwAuthenticate.append(",ticket=\"").append(ticket).append("\"");
}
response.setHeader("WWW-Authenticate", wwwAuthenticate.toString());
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending UMA challenge");
}
return true;
}
private void challengeEntitlementAuthentication(OIDCHttpFacade facade) {
HttpFacade.Response response = facade.getResponse();
AuthzClient authzClient = getAuthzClient();
String clientId = authzClient.getConfiguration().getResource();
String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/entitlement";
response.setStatus(401);
response.setHeader("WWW-Authenticate", "KC_ETT realm=\"" + clientId + "\",as_uri=\"" + authorizationServerUri + "\"");
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending Entitlement challenge");
}
}
private void challengeUmaAuthentication(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade facade) {
HttpFacade.Response response = facade.getResponse();
AuthzClient authzClient = getAuthzClient();
String ticket = getPermissionTicket(pathConfig, methodConfig, authzClient);
String clientId = authzClient.getConfiguration().getResource();
String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/authorize";
response.setStatus(401);
response.setHeader("WWW-Authenticate", "UMA realm=\"" + clientId + "\",as_uri=\"" + authorizationServerUri + "\",ticket=\"" + ticket + "\"");
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending UMA challenge");
}
}
private String getPermissionTicket(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AuthzClient authzClient) {
ProtectionResource protection = authzClient.protection();
PermissionResource permission = protection.permission();
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceSetId(pathConfig.getId());
permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
return permission.forResource(permissionRequest).getTicket();
if (getEnforcerConfig().getUserManagedAccess() != null) {
ProtectionResource protection = authzClient.protection();
PermissionResource permission = protection.permission();
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceId(pathConfig.getId());
permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
return permission.create(permissionRequest).getTicket();
}
return null;
}
}
@@ -19,25 +19,23 @@ package org.keycloak.adapters.authorization;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.authorization.client.AuthorizationDeniedException;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.representation.AuthorizationRequest;
import org.keycloak.authorization.client.representation.AuthorizationResponse;
import org.keycloak.authorization.client.representation.EntitlementRequest;
import org.keycloak.authorization.client.representation.EntitlementResponse;
import org.keycloak.authorization.client.representation.PermissionRequest;
import org.keycloak.authorization.client.representation.PermissionResponse;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import org.keycloak.representations.idm.authorization.AuthorizationRequest;
import org.keycloak.representations.idm.authorization.AuthorizationResponse;
import org.keycloak.representations.idm.authorization.Permission;
import org.keycloak.representations.idm.authorization.PermissionRequest;
import org.keycloak.representations.idm.authorization.PermissionResponse;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -90,6 +88,12 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
@Override
protected void handleAccessDenied(OIDCHttpFacade facade) {
KeycloakSecurityContext securityContext = facade.getSecurityContext();
if (securityContext == null) {
return;
}
String accessDeniedPath = getEnforcerConfig().getOnDenyRedirectTo();
HttpFacade.Response response = facade.getResponse();
@@ -103,45 +107,41 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
private AccessToken requestAuthorizationToken(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade) {
try {
String accessToken = httpFacade.getSecurityContext().getTokenString();
KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();
String accessTokenString = securityContext.getTokenString();
AuthzClient authzClient = getAuthzClient();
KeycloakDeployment deployment = getPolicyEnforcer().getDeployment();
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceId(pathConfig.getId());
permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
AccessToken accessToken = securityContext.getToken();
AuthorizationRequest authzRequest;
if (getEnforcerConfig().getUserManagedAccess() != null) {
LOGGER.debug("Obtaining authorization for authenticated user.");
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceSetId(pathConfig.getId());
permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
PermissionResponse permissionResponse = authzClient.protection().permission().forResource(permissionRequest);
AuthorizationRequest authzRequest = new AuthorizationRequest(permissionResponse.getTicket());
AuthorizationResponse authzResponse = authzClient.authorization(accessToken).authorize(authzRequest);
if (authzResponse != null) {
return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment);
}
return null;
PermissionResponse permissionResponse = authzClient.protection().permission().create(permissionRequest);
authzRequest = new AuthorizationRequest();
authzRequest.setTicket(permissionResponse.getTicket());
} else {
LOGGER.debug("Obtaining entitlements for authenticated user.");
AccessToken token = httpFacade.getSecurityContext().getToken();
if (token.getAuthorization() == null) {
EntitlementResponse authzResponse = authzClient.entitlement(accessToken).getAll(authzClient.getConfiguration().getResource());
return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment);
} else {
EntitlementRequest request = new EntitlementRequest();
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceSetId(pathConfig.getId());
permissionRequest.setResourceSetName(pathConfig.getName());
permissionRequest.setScopes(new HashSet<>(pathConfig.getScopes()));
LOGGER.debugf("Sending entitlements request: resource_set_id [%s], resource_set_name [%s], scopes [%s].", permissionRequest.getResourceSetId(), permissionRequest.getResourceSetName(), permissionRequest.getScopes());
request.addPermission(permissionRequest);
EntitlementResponse authzResponse = authzClient.entitlement(accessToken).get(authzClient.getConfiguration().getResource(), request);
return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment);
authzRequest = new AuthorizationRequest();
if (accessToken.getAuthorization() != null) {
authzRequest.addPermission(pathConfig.getId(), methodConfig.getScopes());
}
}
if (accessToken.getAuthorization() != null) {
authzRequest.setRpt(accessTokenString);
}
LOGGER.debug("Obtaining authorization for authenticated user.");
AuthorizationResponse authzResponse = authzClient.authorization(accessTokenString).authorize(authzRequest);
if (authzResponse != null) {
return AdapterRSATokenVerifier.verifyToken(authzResponse.getToken(), deployment);
}
return null;
} catch (AuthorizationDeniedException e) {
LOGGER.debug("Authorization denied", e);
return null;
@@ -18,8 +18,8 @@
package org.keycloak.adapters.authorization;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.representation.ResourceRepresentation;
@@ -95,7 +95,9 @@ class PathMatcher {
}
if (WILDCARD == expectedUri.charAt(expectedUri.length() - 1)) {
matchingAnyPath = entry;
if (matchingAnyPath == null || matchingAnyPath.getPath().length() < matchingUri.length()) {
matchingAnyPath = entry;
}
} else {
int suffixIndex = expectedUri.indexOf(WILDCARD + ".");
@@ -221,11 +223,11 @@ class PathMatcher {
private PathConfig resolvePathConfig(PathConfig originalConfig, String path) {
if (originalConfig.hasPattern()) {
ProtectedResource resource = this.authzClient.protection().resource();
Set<String> search = resource.findByFilter("uri=" + path);
List<ResourceRepresentation> search = resource.findByUri(path);
if (!search.isEmpty()) {
// resource does exist on the server, cache it
ResourceRepresentation targetResource = resource.findById(search.iterator().next()).getResourceDescription();
ResourceRepresentation targetResource = search.get(0);
PathConfig config = PolicyEnforcer.createPathConfig(targetResource);
config.setScopes(originalConfig.getScopes());
@@ -17,6 +17,15 @@
*/
package org.keycloak.adapters.authorization;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.jboss.logging.Logger;
import org.keycloak.AuthorizationContext;
import org.keycloak.adapters.KeycloakDeployment;
@@ -25,7 +34,6 @@ import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.ClientAuthenticator;
import org.keycloak.authorization.client.Configuration;
import org.keycloak.authorization.client.representation.RegistrationResponse;
import org.keycloak.authorization.client.representation.ResourceRepresentation;
import org.keycloak.authorization.client.representation.ScopeRepresentation;
import org.keycloak.authorization.client.resource.ProtectedResource;
@@ -34,14 +42,6 @@ import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import org.keycloak.representations.idm.authorization.Permission;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
@@ -58,10 +58,15 @@ public class PolicyEnforcer {
public PolicyEnforcer(KeycloakDeployment deployment, AdapterConfig adapterConfig) {
this.deployment = deployment;
this.enforcerConfig = adapterConfig.getPolicyEnforcerConfig();
this.authzClient = AuthzClient.create(new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), deployment.getClient()), new ClientAuthenticator() {
Configuration configuration = new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), deployment.getClient());
this.authzClient = AuthzClient.create(configuration, new ClientAuthenticator() {
@Override
public void configureClientCredentials(HashMap<String, String> requestParams, HashMap<String, String> requestHeaders) {
ClientCredentialsProviderUtils.setClientCredentials(PolicyEnforcer.this.deployment, requestHeaders, requestParams);
public void configureClientCredentials(Map<String, List<String>> requestParams, Map<String, String> requestHeaders) {
Map<String, String> formparams = new HashMap<>();
ClientCredentialsProviderUtils.setClientCredentials(PolicyEnforcer.this.deployment, requestHeaders, formparams);
for (Entry<String, String> param : formparams.entrySet()) {
requestParams.put(param.getKey(), Arrays.asList(param.getValue()));
}
}
});
this.pathMatcher = new PathMatcher(this.authzClient);
@@ -142,26 +147,34 @@ public class PolicyEnforcer {
Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>());
for (PathConfig pathConfig : enforcerConfig.getPaths()) {
Set<String> search;
ResourceRepresentation resource;
String resourceName = pathConfig.getName();
String path = pathConfig.getPath();
if (resourceName != null) {
LOGGER.debugf("Trying to find resource with name [%s] for path [%s].", resourceName, path);
search = protectedResource.findByFilter("name=" + resourceName);
resource = protectedResource.findByName(resourceName);
} else {
LOGGER.debugf("Trying to find resource with uri [%s] for path [%s].", path, path);
search = protectedResource.findByFilter("uri=" + path);
List<ResourceRepresentation> resources = protectedResource.findByUri(path);
if (resources.size() == 1) {
resource = resources.get(0);
} else if (resources.size() > 1) {
throw new RuntimeException("Multiple resources found with the same uri");
} else {
resource = null;
}
}
if (search.isEmpty()) {
if (resource == null) {
if (enforcerConfig.isCreateResources()) {
LOGGER.debugf("Creating resource on server for path [%s].", pathConfig);
ResourceRepresentation resource = new ResourceRepresentation();
ResourceRepresentation representation = new ResourceRepresentation();
resource.setName(resourceName);
resource.setType(pathConfig.getType());
resource.setUri(path);
representation.setName(resourceName);
representation.setType(pathConfig.getType());
representation.setUri(path);
HashSet<ScopeRepresentation> scopes = new HashSet<>();
@@ -173,16 +186,16 @@ public class PolicyEnforcer {
scopes.add(scope);
}
resource.setScopes(scopes);
representation.setScopes(scopes);
RegistrationResponse registrationResponse = protectedResource.create(resource);
ResourceRepresentation registrationResponse = protectedResource.create(representation);
pathConfig.setId(registrationResponse.getId());
} else {
throw new RuntimeException("Could not find matching resource on server with uri [" + path + "] or name [" + resourceName + "]. Make sure you have created a resource on the server that matches with the path configuration.");
}
} else {
pathConfig.setId(search.iterator().next());
pathConfig.setId(resource.getId());
}
PathConfig existingPath = null;
@@ -210,8 +223,7 @@ public class PolicyEnforcer {
Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>());
for (String id : protectedResource.findAll()) {
RegistrationResponse response = protectedResource.findById(id);
ResourceRepresentation resourceDescription = response.getResourceDescription();
ResourceRepresentation resourceDescription = protectedResource.findById(id);
if (resourceDescription.getUri() != null) {
PathConfig pathConfig = createPathConfig(resourceDescription);
@@ -16,4 +16,5 @@
#
org.keycloak.adapters.authentication.ClientIdAndSecretCredentialsProvider
org.keycloak.adapters.authentication.JWTClientCredentialsProvider
org.keycloak.adapters.authentication.JWTClientCredentialsProvider
org.keycloak.adapters.authentication.JWTClientSecretCredentialsProvider
@@ -21,6 +21,7 @@ import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.junit.Test;
import org.keycloak.adapters.authentication.ClientIdAndSecretCredentialsProvider;
import org.keycloak.adapters.authentication.JWTClientCredentialsProvider;
import org.keycloak.adapters.authentication.JWTClientSecretCredentialsProvider;
import org.keycloak.adapters.rotation.HardcodedPublicKeyLocator;
import org.keycloak.adapters.rotation.JWKPublicKeyLocator;
import org.keycloak.common.enums.RelativeUrlsUsed;
@@ -94,4 +95,9 @@ public class KeycloakDeploymentBuilderTest {
assertEquals(JWTClientCredentialsProvider.PROVIDER_ID, deployment.getClientAuthenticator().getId());
}
@Test
public void loadSecretJwtCredentials() throws Exception {
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getClass().getResourceAsStream("/keycloak-secret-jwt.json"));
assertEquals(JWTClientSecretCredentialsProvider.PROVIDER_ID, deployment.getClientAuthenticator().getId());
}
}
@@ -0,0 +1,12 @@
{
"realm": "demo",
"resource": "customer-portal",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "https://localhost:8443/auth",
"ssl-required": "external",
"credentials": {
"secret-jwt": {
"secret": "234234-234234-234234"
}
}
}
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-as7-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-as7-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-integration-pom</artifactId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak AS7 / JBoss EAP 6 Integration</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+10 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -63,6 +63,15 @@
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
<artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
</dependency>
</dependencies>
<build>
@@ -53,7 +53,13 @@ public class KeycloakCliSso {
login();
} else if (args[0].equalsIgnoreCase("login-manual")) {
loginManual();
} else if (args[0].equalsIgnoreCase("token")) {
}
/*
else if (args[0].equalsIgnoreCase("login-cli")) {
loginCli();
}
*/
else if (args[0].equalsIgnoreCase("token")) {
token();
} else if (args[0].equalsIgnoreCase("logout")) {
logout();
@@ -69,6 +75,7 @@ public class KeycloakCliSso {
System.err.println("Commands:");
System.err.println(" login - login with desktop browser if available, otherwise do manual login. Output is access token.");
System.err.println(" login-manual - manual login");
//System.err.println(" login-cli - attempt Keycloak proprietary cli protocol. Otherwise do normal login");
System.err.println(" token - print access token if logged in");
System.err.println(" logout - logout.");
System.exit(1);
@@ -110,7 +117,7 @@ public class KeycloakCliSso {
return config;
}
public boolean checkToken() throws Exception {
public boolean checkToken(boolean outputToken) throws Exception {
String token = getTokenResponse();
if (token == null) return false;
@@ -127,7 +134,7 @@ public class KeycloakCliSso {
AdapterConfig config = getConfig();
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
installed.refreshToken(tokenResponse.getRefreshToken());
processResponse(installed);
processResponse(installed, outputToken);
return true;
} catch (Exception e) {
System.err.println("Error processing existing token");
@@ -184,11 +191,19 @@ public class KeycloakCliSso {
}
public void login() throws Exception {
if (checkToken()) return;
if (checkToken(true)) return;
AdapterConfig config = getConfig();
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
installed.login();
processResponse(installed);
processResponse(installed, true);
}
public void loginCli() throws Exception {
if (checkToken(false)) return;
AdapterConfig config = getConfig();
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
if (!installed.loginCommandLine()) installed.login();
processResponse(installed, false);
}
public String getHome() {
@@ -210,7 +225,7 @@ public class KeycloakCliSso {
return Paths.get(getHome(), System.getProperty("basepath", ".keycloak-sso"), System.getProperty("KEYCLOAK_REALM"), System.getProperty("KEYCLOAK_CLIENT") + ".json").toFile();
}
private void processResponse(KeycloakInstalled installed) throws IOException {
private void processResponse(KeycloakInstalled installed, boolean outputToken) throws IOException {
AccessTokenResponse tokenResponse = installed.getTokenResponse();
tokenResponse.setExpiresIn(Time.currentTime() + tokenResponse.getExpiresIn());
tokenResponse.setIdToken(null);
@@ -220,16 +235,16 @@ public class KeycloakCliSso {
fos.write(output.getBytes("UTF-8"));
fos.flush();
fos.close();
System.out.println(tokenResponse.getToken());
if (outputToken) System.out.println(tokenResponse.getToken());
}
public void loginManual() throws Exception {
if (checkToken()) return;
if (checkToken(true)) return;
AdapterConfig config = getConfig();
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config);
KeycloakInstalled installed = new KeycloakInstalled(deployment);
installed.loginManual();
processResponse(installed);
processResponse(installed, true);
}
public void logout() throws Exception {
@@ -17,7 +17,8 @@
package org.keycloak.adapters.installed;
import org.apache.commons.codec.Charsets;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
import org.keycloak.adapters.KeycloakDeployment;
@@ -32,6 +33,11 @@ import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
@@ -40,6 +46,7 @@ import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.PushbackInputStream;
import java.io.Reader;
import java.net.ServerSocket;
import java.net.Socket;
@@ -48,6 +55,8 @@ import java.net.URISyntaxException;
import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -77,6 +86,9 @@ public class KeycloakInstalled {
private Locale locale;
private HttpResponseWriter loginResponseWriter;
private HttpResponseWriter logoutResponseWriter;
Pattern callbackPattern = Pattern.compile("callback\\s*=\\s*\"([^\"]+)\"");
Pattern paramPattern = Pattern.compile("param=\"([^\"]+)\"\\s+label=\"([^\"]+)\"\\s+mask=(\\S+)");
Pattern codePattern = Pattern.compile("code=([^&]+)");
@@ -290,6 +302,86 @@ public class KeycloakInstalled {
status = Status.LOGGED_MANUAL;
}
public boolean loginCommandLine() throws IOException, ServerRequest.HttpFailure, VerificationException {
String redirectUri = "urn:ietf:wg:oauth:2.0:oob";
return loginCommandLine(redirectUri);
}
/**
* Experimental proprietary WWW-Authentication challenge protocol.
* WWW-Authentication: X-Text-Form-Challenge callback="{url}" param="{param-name}" label="{param-display-label}"
*
* @param redirectUri
* @return
* @throws IOException
* @throws ServerRequest.HttpFailure
* @throws VerificationException
*/
public boolean loginCommandLine(String redirectUri) throws IOException, ServerRequest.HttpFailure, VerificationException {
String authUrl = deployment.getAuthUrl().clone()
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
.build().toString();
ResteasyClient client = new ResteasyClientBuilder().disableTrustManager().build();
try {
Response response = client.target(authUrl).request().get();
if (response.getStatus() != 401) {
return false;
}
while (true) {
String authenticationHeader = response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE);
if (authenticationHeader == null) {
return false;
}
if (!authenticationHeader.contains("X-Text-Form-Challenge")) {
return false;
}
if (response.getMediaType() != null) {
String splash = response.readEntity(String.class);
System.console().writer().println(splash);
}
Matcher m = callbackPattern.matcher(authenticationHeader);
if (!m.find()) return false;
String callback = m.group(1);
//System.err.println("callback: " + callback);
m = paramPattern.matcher(authenticationHeader);
Form form = new Form();
while (m.find()) {
String param = m.group(1);
String label = m.group(2);
String mask = m.group(3).trim();
boolean maskInput = mask.equals("true");
String value = null;
if (maskInput) {
char[] txt = System.console().readPassword(label);
value = new String(txt);
} else {
value = System.console().readLine(label);
}
form.param(param, value);
}
response = client.target(callback).request().post(Entity.form(form));
if (response.getStatus() == 401) continue;
if (response.getStatus() != 302) return false;
String location = response.getLocation().toString();
m = codePattern.matcher(location);
if (!m.find()) return false;
String code = m.group(1);
processCode(code, redirectUri);
return true;
}
} finally {
client.close();
}
}
public String getTokenString() throws VerificationException, IOException, ServerRequest.HttpFailure {
return tokenString;
}
@@ -382,6 +474,86 @@ public class KeycloakInstalled {
return sb.toString();
}
public static class MaskingThread extends Thread {
private volatile boolean stop;
private char echochar = '*';
public MaskingThread() {
}
/**
* Begin masking until asked to stop.
*/
public void run() {
int priority = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
try {
stop = true;
while(stop) {
System.out.print("\010" + echochar);
try {
// attempt masking at this rate
Thread.currentThread().sleep(1);
}catch (InterruptedException iex) {
Thread.currentThread().interrupt();
return;
}
}
} finally { // restore the original priority
Thread.currentThread().setPriority(priority);
}
}
/**
* Instruct the thread to stop masking.
*/
public void stopMasking() {
this.stop = false;
}
}
public static String readMasked(Reader reader) {
MaskingThread et = new MaskingThread();
Thread mask = new Thread(et);
mask.start();
BufferedReader in = new BufferedReader(reader);
String password = "";
try {
password = in.readLine();
} catch (IOException ioe) {
ioe.printStackTrace();
}
// stop masking
et.stopMasking();
// return the password entered by the user
return password;
}
private String readLine(Reader reader, boolean mask) throws IOException {
if (mask) {
System.out.print(" ");
return readMasked(reader);
}
StringBuilder sb = new StringBuilder();
char cb[] = new char[1];
while (reader.read(cb) != -1) {
char c = cb[0];
if ((c == '\n') || (c == '\r')) {
break;
} else {
sb.append(c);
}
}
return sb.toString();
}
public class CallbackListener extends Thread {
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+2 -2
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak Jetty Integration</name>
@@ -33,6 +33,7 @@
<modules>
<module>jetty-core</module>
<module>jetty8.1</module>
<module>jetty9.1</module>
<module>jetty9.2</module>
<module>jetty9.3</module>
</modules>
@@ -46,7 +47,6 @@
</property>
</activation>
<modules>
<module>jetty9.1</module>
<module>jetty9.4</module>
</modules>
</profile>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -18,14 +18,14 @@
(function( window, undefined ) {
var KeycloakAuthorization = function (keycloak) {
var KeycloakAuthorization = function (keycloak, options) {
var _instance = this;
this.rpt = null;
this.init = function () {
var request = new XMLHttpRequest();
request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/.well-known/uma-configuration');
request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/.well-known/uma2-configuration');
request.onreadystatechange = function () {
if (request.readyState == 4) {
if (request.status == 200) {
@@ -47,68 +47,61 @@
* necessary information to ask a Keycloak server for authorization data using both UMA and Entitlement protocol,
* depending on how the policy enforcer at the resource server was configured.
*/
this.authorize = function (wwwAuthenticateHeader) {
this.authorize = function (authorizationRequest) {
this.then = function (onGrant, onDeny, onError) {
if (wwwAuthenticateHeader.indexOf('UMA') != -1) {
var params = wwwAuthenticateHeader.split(',');
if (authorizationRequest && authorizationRequest.ticket) {
var request = new XMLHttpRequest();
for (i = 0; i < params.length; i++) {
var param = params[i].split('=');
request.open('POST', _instance.config.token_endpoint, true);
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);
if (param[0] == 'ticket') {
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (request.readyState == 4) {
var status = request.status;
request.open('POST', _instance.config.rpt_endpoint, true);
request.setRequestHeader('Content-Type', 'application/json')
request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token)
request.onreadystatechange = function () {
if (request.readyState == 4) {
var status = request.status;
if (status >= 200 && status < 300) {
var rpt = JSON.parse(request.responseText).rpt;
_instance.rpt = rpt;
onGrant(rpt);
} else if (status == 403) {
if (onDeny) {
onDeny();
} else {
console.error('Authorization request was denied by the server.');
}
} else {
if (onError) {
onError();
} else {
console.error('Could not obtain authorization data from server.');
}
}
if (status >= 200 && status < 300) {
var rpt = JSON.parse(request.responseText).access_token;
_instance.rpt = rpt;
onGrant(rpt);
} else if (status == 403) {
if (onDeny) {
onDeny();
} else {
console.error('Authorization request was denied by the server.');
}
};
var ticket = param[1].substring(1, param[1].length - 1).trim();
request.send(JSON.stringify(
{
ticket: ticket,
rpt: _instance.rpt
} else {
if (onError) {
onError();
} else {
console.error('Could not obtain authorization data from server.');
}
));
}
}
};
var params = "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket&client_id=" + keycloak.clientId + "&ticket=" + authorizationRequest.ticket;
if (authorizationRequest.submitRequest != undefined) {
params += "&submit_request=" + authorizationRequest.submitRequest;
}
} else if (wwwAuthenticateHeader.indexOf('KC_ETT') != -1) {
var params = wwwAuthenticateHeader.substring('KC_ETT'.length).trim().split(',');
var clientId = null;
for (i = 0; i < params.length; i++) {
var param = params[i].split('=');
var metadata = authorizationRequest.metadata;
if (param[0] == 'realm') {
clientId = param[1].substring(1, param[1].length - 1).trim();
if (metadata) {
if (metadata.responseIncludeResourceName) {
params += "&response_include_resource_name=" + metadata.responseIncludeResourceName;
}
if (metadata.responsePermissionsLimit) {
params += "&response_permissions_limit=" + metadata.responsePermissionsLimit;
}
}
_instance.entitlement(clientId).then(onGrant, onDeny, onError);
if (_instance.rpt && (authorizationRequest.incrementalAuthorization == undefined || authorizationRequest.incrementalAuthorization)) {
params += "&rpt=" + _instance.rpt;
}
request.send(params);
}
};
@@ -116,20 +109,22 @@
};
/**
* Obtains all entitlements from a Keycloak Server based on a give resourceServerId.
* Obtains all entitlements from a Keycloak Server based on a given resourceServerId.
*/
this.entitlement = function (resourceSeververId, entitlementRequest ) {
this.entitlement = function (resourceServerId, authorizationRequest) {
this.then = function (onGrant, onDeny, onError) {
var request = new XMLHttpRequest();
request.open('POST', _instance.config.token_endpoint, true);
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);
request.onreadystatechange = function () {
if (request.readyState == 4) {
var status = request.status;
if (status >= 200 && status < 300) {
var rpt = JSON.parse(request.responseText).rpt;
var rpt = JSON.parse(request.responseText).access_token;
_instance.rpt = rpt;
onGrant(rpt);
} else if (status == 403) {
@@ -148,19 +143,62 @@
}
};
var erJson = null
if(entitlementRequest) {
request.open('POST', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/authz/entitlement/' + resourceSeververId, true);
request.setRequestHeader("Content-type", "application/json");
erJson = JSON.stringify(entitlementRequest)
} else {
request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/authz/entitlement/' + resourceSeververId, true);
if (!authorizationRequest) {
authorizationRequest = {};
}
request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token)
request.send(erJson);
var params = "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket&client_id=" + keycloak.clientId;
if (authorizationRequest.claimToken) {
params += "&claim_token=" + authorizationRequest.claimToken;
if (authorizationRequest.claimTokenFormat) {
params += "&claim_token_format=" + authorizationRequest.claimTokenFormat;
}
}
params += "&audience=" + resourceServerId;
var permissions = authorizationRequest.permissions;
if (!permissions) {
permissions = [];
}
for (i = 0; i < permissions.length; i++) {
var resource = permissions[i];
var permission = resource.id;
if (resource.scopes && resource.scopes.length > 0) {
permission += "#";
for (j = 0; j < resource.scopes.length; j++) {
var scope = resource.scopes[j];
if (permission.indexOf('#') != permission.length - 1) {
permission += ",";
}
permission += scope;
}
}
params += "&permission=" + permission;
}
var metadata = authorizationRequest.metadata;
if (metadata) {
if (metadata.responseIncludeResourceName) {
params += "&response_include_resource_name=" + metadata.responseIncludeResourceName;
}
if (metadata.responsePermissionsLimit) {
params += "&response_permissions_limit=" + metadata.responsePermissionsLimit;
}
}
if (_instance.rpt) {
params += "&rpt=" + _instance.rpt;
}
request.send(params);
};
return this;
+7 -2
View File
@@ -39,8 +39,13 @@ declare namespace Keycloak {
/**
* @private Undocumented.
*/
adapter?: KeycloakAdapterName;
useNonce?: boolean;
/**
* @private Undocumented.
*/
adapter?: KeycloakAdapterName;
/**
* Specifies an action to do on load.
*/
@@ -78,7 +83,7 @@ declare namespace Keycloak {
* Set the interval to check login state (in seconds).
* @default 5
*/
checkLoginIframeInterval?: boolean;
checkLoginIframeInterval?: number;
/**
* Set the OpenID Connect response mode to send to Keycloak upon login.
+285 -155
View File
@@ -40,6 +40,8 @@
}
}
var useNonce = true;
kc.init = function (initOptions) {
kc.authenticated = false;
@@ -58,6 +60,10 @@
}
if (initOptions) {
if (typeof initOptions.useNonce !== 'undefined') {
useNonce = initOptions.useNonce;
}
if (typeof initOptions.checkLoginIframe !== 'undefined') {
loginIframe.enable = initOptions.checkLoginIframe;
}
@@ -159,8 +165,11 @@
var callback = parseCallback(window.location.href);
if (callback) {
window.history.replaceState({}, null, callback.newUrl);
}
if (callback && callback.valid) {
return setupCheckLoginIframe().success(function() {
window.history.replaceState({}, null, callback.newUrl);
processCallback(callback, initPromise);
}).error(function (e) {
initPromise.setError();
@@ -232,22 +241,25 @@
callbackStorage.add(callbackState);
var action = 'auth';
var baseUrl;
if (options && options.action == 'register') {
action = 'registrations';
baseUrl = kc.endpoints.register();
} else {
baseUrl = kc.endpoints.authorize();
}
var scope = (options && options.scope) ? "openid " + options.scope : "openid";
var url = getRealmUrl()
+ '/protocol/openid-connect/' + action
var url = baseUrl
+ '?client_id=' + encodeURIComponent(kc.clientId)
+ '&redirect_uri=' + encodeURIComponent(redirectUri)
+ '&state=' + encodeURIComponent(state)
+ '&nonce=' + encodeURIComponent(nonce)
+ '&response_mode=' + encodeURIComponent(kc.responseMode)
+ '&response_type=' + encodeURIComponent(kc.responseType)
+ '&scope=' + encodeURIComponent(scope);
if (useNonce) {
url = url + '&nonce=' + encodeURIComponent(nonce);
}
if (options && options.prompt) {
url += '&prompt=' + encodeURIComponent(options.prompt);
@@ -277,8 +289,7 @@
}
kc.createLogoutUrl = function(options) {
var url = getRealmUrl()
+ '/protocol/openid-connect/logout'
var url = kc.endpoints.logout()
+ '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options, false));
return url;
@@ -297,11 +308,14 @@
}
kc.createAccountUrl = function(options) {
var url = getRealmUrl()
var realm = getRealmUrl();
var url = undefined;
if (typeof realm !== 'undefined') {
url = realm
+ '/account'
+ '?referrer=' + encodeURIComponent(kc.clientId)
+ '&referrer_uri=' + encodeURIComponent(adapter.redirectUri(options));
}
return url;
}
@@ -349,7 +363,7 @@
}
kc.loadUserInfo = function() {
var url = getRealmUrl() + '/protocol/openid-connect/userinfo';
var url = kc.endpoints.userinfo();
var req = new XMLHttpRequest();
req.open('GET', url, true);
req.setRequestHeader('Accept', 'application/json');
@@ -414,7 +428,7 @@
promise.setSuccess(false);
} else {
var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
var url = getRealmUrl() + '/protocol/openid-connect/token';
var url = kc.endpoints.token();
refreshQueue.push(promise);
@@ -488,10 +502,14 @@
}
function getRealmUrl() {
if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
if (typeof kc.authServerUrl !== 'undefined') {
if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
} else {
return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
}
} else {
return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
return undefined;
}
}
@@ -525,7 +543,7 @@
if ((kc.flow != 'implicit') && code) {
var params = 'code=' + code + '&grant_type=authorization_code';
var url = getRealmUrl() + '/protocol/openid-connect/token';
var url = kc.endpoints.token();
var req = new XMLHttpRequest();
req.open('POST', url, true);
@@ -562,9 +580,9 @@
setToken(accessToken, refreshToken, idToken, timeLocal);
if ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
if (useNonce && ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
(kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) ||
(kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce)) {
(kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce))) {
console.info('[KEYCLOAK] Invalid nonce, clearing token');
kc.clearToken();
@@ -589,6 +607,65 @@
configUrl = config;
}
function setupOidcEndoints(oidcConfiguration) {
if (! oidcConfiguration) {
kc.endpoints = {
authorize: function() {
return getRealmUrl() + '/protocol/openid-connect/auth';
},
token: function() {
return getRealmUrl() + '/protocol/openid-connect/token';
},
logout: function() {
return getRealmUrl() + '/protocol/openid-connect/logout';
},
checkSessionIframe: function() {
var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
if (kc.iframeVersion) {
src = src + '?version=' + kc.iframeVersion;
}
return src;
},
register: function() {
return getRealmUrl() + '/protocol/openid-connect/registrations';
},
userinfo: function() {
return getRealmUrl() + '/protocol/openid-connect/userinfo';
}
};
} else {
kc.endpoints = {
authorize: function() {
return oidcConfiguration.authorization_endpoint;
},
token: function() {
return oidcConfiguration.token_endpoint;
},
logout: function() {
if (!oidcConfiguration.end_session_endpoint) {
throw "Not supported by the OIDC server";
}
return oidcConfiguration.end_session_endpoint;
},
checkSessionIframe: function() {
if (!oidcConfiguration.check_session_iframe) {
throw "Not supported by the OIDC server";
}
return oidcConfiguration.check_session_iframe;
},
register: function() {
throw 'Redirection to "Register user" page not supported in standard OIDC mode';
},
userinfo: function() {
if (!oidcConfiguration.userinfo_endpoint) {
throw "Not supported by the OIDC server";
}
return oidcConfiguration.userinfo_endpoint;
}
}
}
}
if (configUrl) {
var req = new XMLHttpRequest();
req.open('GET', configUrl, true);
@@ -603,7 +680,7 @@
kc.realm = config['realm'];
kc.clientId = config['resource'];
kc.clientSecret = (config['credentials'] || {})['secret'];
setupOidcEndoints(null);
promise.setSuccess();
} else {
promise.setError();
@@ -613,30 +690,62 @@
req.send();
} else {
if (!config['url']) {
var scripts = document.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src.match(/.*keycloak\.js/)) {
config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
break;
}
}
}
if (!config.realm) {
throw 'realm missing';
}
if (!config.clientId) {
throw 'clientId missing';
}
kc.authServerUrl = config.url;
kc.realm = config.realm;
kc.clientId = config.clientId;
kc.clientSecret = (config.credentials || {}).secret;
promise.setSuccess();
var oidcProvider = config['oidcProvider'];
if (!oidcProvider) {
if (!config['url']) {
var scripts = document.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src.match(/.*keycloak\.js/)) {
config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
break;
}
}
}
if (!config.realm) {
throw 'realm missing';
}
kc.authServerUrl = config.url;
kc.realm = config.realm;
setupOidcEndoints(null);
promise.setSuccess();
} else {
if (typeof oidcProvider === 'string') {
var oidcProviderConfigUrl;
if (oidcProvider.charAt(oidcProvider.length - 1) == '/') {
oidcProviderConfigUrl = oidcProvider + '.well-known/openid-configuration';
} else {
oidcProviderConfigUrl = oidcProvider + '/.well-known/openid-configuration';
}
var req = new XMLHttpRequest();
req.open('GET', oidcProviderConfigUrl, true);
req.setRequestHeader('Accept', 'application/json');
req.onreadystatechange = function () {
if (req.readyState == 4) {
if (req.status == 200 || fileLoaded(req)) {
var oidcProviderConfig = JSON.parse(req.responseText);
setupOidcEndoints(oidcProviderConfig);
promise.setSuccess();
} else {
promise.setError();
}
}
};
req.send();
} else {
setupOidcEndoints(oidcProvider);
promise.setSuccess();
}
}
}
return promise.promise;
@@ -755,23 +864,137 @@
}
function parseCallback(url) {
var oauth = new CallbackParser(url, kc.responseMode).parseUri();
var oauth = parseCallbackUrl(url);
if (!oauth) {
return;
}
var oauthState = callbackStorage.get(oauth.state);
if (oauthState && (oauth.code || oauth.error || oauth.access_token || oauth.id_token)) {
if (oauthState) {
oauth.valid = true;
oauth.redirectUri = oauthState.redirectUri;
oauth.storedNonce = oauthState.nonce;
oauth.prompt = oauthState.prompt;
}
if (oauth.fragment) {
oauth.newUrl += '#' + oauth.fragment;
return oauth;
}
function parseCallbackUrl(url) {
var supportedParams;
switch (kc.flow) {
case 'standard':
supportedParams = ['code', 'state', 'session_state'];
break;
case 'implicit':
supportedParams = ['access_token', 'id_token', 'state', 'session_state'];
break;
case 'hybrid':
supportedParams = ['access_token', 'id_token', 'code', 'state', 'session_state'];
break;
}
supportedParams.push('error');
supportedParams.push('error_description');
supportedParams.push('error_uri');
var queryIndex = url.indexOf('?');
var fragmentIndex = url.indexOf('#');
var newUrl;
var parsed;
if (kc.responseMode === 'query' && queryIndex !== -1) {
newUrl = url.substring(0, queryIndex);
parsed = parseCallbackParams(url.substring(queryIndex + 1, fragmentIndex !== -1 ? fragmentIndex : url.length), supportedParams);
if (parsed.paramsString !== '') {
newUrl += '?' + parsed.paramsString;
}
if (fragmentIndex !== -1) {
newUrl += url.substring(fragmentIndex);
}
} else if (kc.responseMode === 'fragment' && fragmentIndex !== -1) {
newUrl = url.substring(0, fragmentIndex);
parsed = parseCallbackParams(url.substring(fragmentIndex + 1), supportedParams);
if (parsed.paramsString !== '') {
newUrl += '#' + parsed.paramsString;
}
}
return oauth;
if (parsed && parsed.oauthParams) {
if (kc.flow === 'standard' || kc.flow === 'hybrid') {
if ((parsed.oauthParams.code || parsed.oauthParams.error) && parsed.oauthParams.state) {
parsed.oauthParams.newUrl = newUrl;
return parsed.oauthParams;
}
} else if (kc.flow === 'implicit') {
if ((parsed.oauthParams.access_token || parsed.oauthParams.error) && parsed.oauthParams.state) {
parsed.oauthParams.newUrl = newUrl;
return parsed.oauthParams;
}
}
}
}
function parseCallbackParams(paramsString, supportedParams) {
var p = paramsString.split('&');
var result = {
paramsString: '',
oauthParams: {}
}
for (var i = 0; i < p.length; i++) {
var t = p[i].split('=');
if (supportedParams.indexOf(t[0]) !== -1) {
result.oauthParams[t[0]] = t[1];
} else {
if (result.paramsString !== '') {
result.paramsString += '&';
}
result.paramsString += p[i];
}
}
return result;
}
function createPromise() {
if (typeof Promise === "function") {
return createNativePromise();
} else {
return createLegacyPromise();
}
}
function createNativePromise() {
// Need to create a native Promise which also preserves the
// interface of the custom promise type previously used by the API
var p = {
setSuccess: function(result) {
p.success = true;
p.resolve(result);
},
setError: function(result) {
p.success = false;
p.reject(result);
}
};
p.promise = new Promise(function(resolve, reject) {
p.resolve = resolve;
p.reject = reject;
});
p.promise.success = function(callback) {
p.promise.then(callback);
return p.promise;
}
p.promise.error = function(callback) {
p.promise.catch(callback);
return p.promise;
}
return p;
}
function createLegacyPromise() {
var p = {
setSuccess: function(result) {
p.success = true;
@@ -828,22 +1051,18 @@
loginIframe.iframe = iframe;
iframe.onload = function() {
var realmUrl = getRealmUrl();
if (realmUrl.charAt(0) === '/') {
var authUrl = kc.endpoints.authorize();
if (authUrl.charAt(0) === '/') {
loginIframe.iframeOrigin = getOrigin();
} else {
loginIframe.iframeOrigin = realmUrl.substring(0, realmUrl.indexOf('/', 8));
loginIframe.iframeOrigin = authUrl.substring(0, authUrl.indexOf('/', 8));
}
promise.setSuccess();
setTimeout(check, loginIframe.interval * 1000);
}
var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
if (kc.iframeVersion) {
src = src + '?version=' + kc.iframeVersion;
}
var src = kc.endpoints.checkSessionIframe();
iframe.setAttribute('src', src );
iframe.setAttribute('title', 'keycloak-session-iframe' );
iframe.style.display = 'none';
@@ -923,7 +1142,12 @@
},
accountManagement : function() {
window.location.href = kc.createAccountUrl();
var accountUrl = kc.createAccountUrl();
if (typeof accountUrl !== 'undefined') {
window.location.href = accountUrl;
} else {
throw "Not supported by the OIDC server";
}
return createPromise().promise;
},
@@ -937,12 +1161,7 @@
} else if (kc.redirectUri) {
return kc.redirectUri;
} else {
var redirectUri = location.href;
if (location.hash && encodeHash) {
redirectUri = redirectUri.substring(0, location.href.indexOf('#'));
redirectUri += (redirectUri.indexOf('?') == -1 ? '?' : '&') + 'redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
}
return redirectUri;
return location.href;
}
}
};
@@ -1044,12 +1263,16 @@
accountManagement : function() {
var accountUrl = kc.createAccountUrl();
var ref = cordovaOpenWindowWrapper(accountUrl, '_blank', 'location=no');
ref.addEventListener('loadstart', function(event) {
if (event.url.indexOf('http://localhost') == 0) {
ref.close();
}
});
if (typeof accountUrl !== 'undefined') {
var ref = cordovaOpenWindowWrapper(accountUrl, '_blank', 'location=no');
ref.addEventListener('loadstart', function(event) {
if (event.url.indexOf('http://localhost') == 0) {
ref.close();
}
});
} else {
throw "Not supported by the OIDC server";
}
},
redirectUri: function(options) {
@@ -1179,99 +1402,6 @@
return new CookieStorage();
}
var CallbackParser = function(uriToParse, responseMode) {
if (!(this instanceof CallbackParser)) {
return new CallbackParser(uriToParse, responseMode);
}
var parser = this;
var initialParse = function() {
var baseUri = null;
var queryString = null;
var fragmentString = null;
var questionMarkIndex = uriToParse.indexOf("?");
var fragmentIndex = uriToParse.indexOf("#", questionMarkIndex + 1);
if (questionMarkIndex == -1 && fragmentIndex == -1) {
baseUri = uriToParse;
} else if (questionMarkIndex != -1) {
baseUri = uriToParse.substring(0, questionMarkIndex);
queryString = uriToParse.substring(questionMarkIndex + 1);
if (fragmentIndex != -1) {
fragmentIndex = queryString.indexOf("#");
fragmentString = queryString.substring(fragmentIndex + 1);
queryString = queryString.substring(0, fragmentIndex);
}
} else {
baseUri = uriToParse.substring(0, fragmentIndex);
fragmentString = uriToParse.substring(fragmentIndex + 1);
}
return { baseUri: baseUri, queryString: queryString, fragmentString: fragmentString };
}
var parseParams = function(paramString) {
var result = {};
var params = paramString.split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
var paramName = decodeURIComponent(p[0]);
var paramValue = decodeURIComponent(p[1]);
result[paramName] = paramValue;
}
return result;
}
var handleQueryParam = function(paramName, paramValue, oauth) {
var supportedOAuthParams = [ 'code', 'state', 'error', 'error_description' ];
for (var i = 0 ; i< supportedOAuthParams.length ; i++) {
if (paramName === supportedOAuthParams[i]) {
oauth[paramName] = paramValue;
return true;
}
}
return false;
}
parser.parseUri = function() {
var parsedUri = initialParse();
var queryParams = {};
if (parsedUri.queryString) {
queryParams = parseParams(parsedUri.queryString);
}
var oauth = { newUrl: parsedUri.baseUri };
for (var param in queryParams) {
switch (param) {
case 'redirect_fragment':
oauth.fragment = queryParams[param];
break;
default:
if (responseMode != 'query' || !handleQueryParam(param, queryParams[param], oauth)) {
oauth.newUrl += (oauth.newUrl.indexOf('?') == -1 ? '?' : '&') + param + '=' + encodeURIComponent(queryParams[param]);
}
break;
}
}
if (responseMode === 'fragment') {
var fragmentParams = {};
if (parsedUri.fragmentString) {
fragmentParams = parseParams(parsedUri.fragmentString);
}
for (var param in fragmentParams) {
oauth[param] = fragmentParams[param];
}
}
return oauth;
}
}
}
if ( typeof module === "object" && module && typeof module.exports === "object" ) {
@@ -79,6 +79,10 @@
function receiveMessage(event)
{
if (typeof event.data !== 'string') {
return
}
var origin = event.origin;
var data = event.data.split(' ');
if (data.length != 2) {
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<name>Keycloak OIDC Client Adapter Modules</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -4,7 +4,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<artifactId>spring-boot-container-bundle</artifactId>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak Tomcat Integration</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-tomcat-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -22,7 +22,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak WildFly Integration</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-parent</artifactId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -21,7 +21,7 @@
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-parent</artifactId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<name>Keycloak Adapters</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-saml-eap-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak SAML EAP Integration</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-eap-integration-pom</artifactId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+6 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -71,6 +71,11 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
@@ -27,35 +27,36 @@ import org.keycloak.adapters.cloned.AdapterHttpClientConfig;
*/
public class IDP implements Serializable {
public static class SingleSignOnService implements Serializable {
private boolean signRequest;
private boolean validateResponseSignature;
private Boolean signRequest;
private Boolean validateResponseSignature;
private String requestBinding;
private String responseBinding;
private String bindingUrl;
private String assertionConsumerServiceUrl;
private boolean validateAssertionSignature;
private Boolean validateAssertionSignature;
private boolean signaturesRequired = false;
public boolean isSignRequest() {
return signRequest;
return signRequest == null ? signaturesRequired : signRequest;
}
public void setSignRequest(boolean signRequest) {
public void setSignRequest(Boolean signRequest) {
this.signRequest = signRequest;
}
public boolean isValidateResponseSignature() {
return validateResponseSignature;
return validateResponseSignature == null ? signaturesRequired : validateResponseSignature;
}
public void setValidateResponseSignature(boolean validateResponseSignature) {
public void setValidateResponseSignature(Boolean validateResponseSignature) {
this.validateResponseSignature = validateResponseSignature;
}
public boolean isValidateAssertionSignature() {
return validateAssertionSignature;
return validateAssertionSignature == null ? false : validateAssertionSignature;
}
public void setValidateAssertionSignature(boolean validateAssertionSignature) {
public void setValidateAssertionSignature(Boolean validateAssertionSignature) {
this.validateAssertionSignature = validateAssertionSignature;
}
@@ -90,47 +91,52 @@ public class IDP implements Serializable {
public void setAssertionConsumerServiceUrl(String assertionConsumerServiceUrl) {
this.assertionConsumerServiceUrl = assertionConsumerServiceUrl;
}
private void setSignaturesRequired(boolean signaturesRequired) {
this.signaturesRequired = signaturesRequired;
}
}
public static class SingleLogoutService implements Serializable {
private boolean signRequest;
private boolean signResponse;
private boolean validateRequestSignature;
private boolean validateResponseSignature;
private Boolean signRequest;
private Boolean signResponse;
private Boolean validateRequestSignature;
private Boolean validateResponseSignature;
private String requestBinding;
private String responseBinding;
private String postBindingUrl;
private String redirectBindingUrl;
private boolean signaturesRequired = false;
public boolean isSignRequest() {
return signRequest;
return signRequest == null ? signaturesRequired : signRequest;
}
public void setSignRequest(boolean signRequest) {
public void setSignRequest(Boolean signRequest) {
this.signRequest = signRequest;
}
public boolean isSignResponse() {
return signResponse;
return signResponse == null ? signaturesRequired : signResponse;
}
public void setSignResponse(boolean signResponse) {
public void setSignResponse(Boolean signResponse) {
this.signResponse = signResponse;
}
public boolean isValidateRequestSignature() {
return validateRequestSignature;
return validateRequestSignature == null ? signaturesRequired : validateRequestSignature;
}
public void setValidateRequestSignature(boolean validateRequestSignature) {
public void setValidateRequestSignature(Boolean validateRequestSignature) {
this.validateRequestSignature = validateRequestSignature;
}
public boolean isValidateResponseSignature() {
return validateResponseSignature;
return validateResponseSignature == null ? signaturesRequired : validateResponseSignature;
}
public void setValidateResponseSignature(boolean validateResponseSignature) {
public void setValidateResponseSignature(Boolean validateResponseSignature) {
this.validateResponseSignature = validateResponseSignature;
}
@@ -165,6 +171,10 @@ public class IDP implements Serializable {
public void setRedirectBindingUrl(String redirectBindingUrl) {
this.redirectBindingUrl = redirectBindingUrl;
}
private void setSignaturesRequired(boolean signaturesRequired) {
this.signaturesRequired = signaturesRequired;
}
}
public static class HttpClientConfig implements AdapterHttpClientConfig {
@@ -258,6 +268,7 @@ public class IDP implements Serializable {
private SingleLogoutService singleLogoutService;
private List<Key> keys;
private AdapterHttpClientConfig httpClientConfig = new HttpClientConfig();
private boolean signaturesRequired = false;
public String getEntityID() {
return entityID;
@@ -273,6 +284,9 @@ public class IDP implements Serializable {
public void setSingleSignOnService(SingleSignOnService singleSignOnService) {
this.singleSignOnService = singleSignOnService;
if (singleSignOnService != null) {
singleSignOnService.setSignaturesRequired(signaturesRequired);
}
}
public SingleLogoutService getSingleLogoutService() {
@@ -281,6 +295,9 @@ public class IDP implements Serializable {
public void setSingleLogoutService(SingleLogoutService singleLogoutService) {
this.singleLogoutService = singleLogoutService;
if (singleLogoutService != null) {
singleLogoutService.setSignaturesRequired(signaturesRequired);
}
}
public List<Key> getKeys() {
@@ -315,4 +332,12 @@ public class IDP implements Serializable {
this.httpClientConfig = httpClientConfig;
}
public boolean isSignaturesRequired() {
return signaturesRequired;
}
public void setSignaturesRequired(boolean signaturesRequired) {
this.signaturesRequired = signaturesRequired;
}
}
@@ -114,16 +114,16 @@ public class Key implements Serializable {
return signing;
}
public void setSigning(boolean signing) {
this.signing = signing;
public void setSigning(Boolean signing) {
this.signing = signing != null && signing;
}
public boolean isEncryption() {
return encryption;
}
public void setEncryption(boolean encryption) {
this.encryption = encryption;
public void setEncryption(Boolean encryption) {
this.encryption = encryption != null && encryption;
}
public KeyStoreConfig getKeystore() {
@@ -26,13 +26,14 @@ import java.util.List;
* @version $Revision: 1 $
*/
public class KeycloakSamlAdapter implements Serializable {
private List<SP> sps = new LinkedList<>();
private final List<SP> sps = new LinkedList<>();
public List<SP> getSps() {
return sps;
}
public void setSps(List<SP> sps) {
this.sps = sps;
public void addSp(SP sp) {
sps.add(sp);
}
}
@@ -80,24 +80,24 @@ public class SP implements Serializable {
return forceAuthentication;
}
public void setForceAuthentication(boolean forceAuthentication) {
this.forceAuthentication = forceAuthentication;
public void setForceAuthentication(Boolean forceAuthentication) {
this.forceAuthentication = forceAuthentication != null && forceAuthentication;
}
public boolean isIsPassive() {
return isPassive;
}
public void setIsPassive(boolean isPassive) {
this.isPassive = isPassive;
public void setIsPassive(Boolean isPassive) {
this.isPassive = isPassive != null && isPassive;
}
public boolean isTurnOffChangeSessionIdOnLogin() {
return turnOffChangeSessionIdOnLogin;
}
public void setTurnOffChangeSessionIdOnLogin(boolean turnOffChangeSessionIdOnLogin) {
this.turnOffChangeSessionIdOnLogin = turnOffChangeSessionIdOnLogin;
public void setTurnOffChangeSessionIdOnLogin(Boolean turnOffChangeSessionIdOnLogin) {
this.turnOffChangeSessionIdOnLogin = turnOffChangeSessionIdOnLogin != null && turnOffChangeSessionIdOnLogin;
}
public List<Key> getKeys() {
@@ -152,7 +152,7 @@ public class SP implements Serializable {
return autodetectBearerOnly;
}
public void setAutodetectBearerOnly(boolean autodetectBearerOnly) {
this.autodetectBearerOnly = autodetectBearerOnly;
public void setAutodetectBearerOnly(Boolean autodetectBearerOnly) {
this.autodetectBearerOnly = autodetectBearerOnly != null && autodetectBearerOnly;
}
}
@@ -0,0 +1,59 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.saml.common.parsers.AbstractStaxParser;
import org.keycloak.saml.common.util.StaxParserUtil;
import org.keycloak.saml.processing.core.parsers.util.QNameEnumLookup;
import java.util.Collections;
import java.util.Set;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.stream.events.StartElement;
/**
*
*/
public abstract class AbstractKeycloakSamlAdapterV1Parser<T> extends AbstractStaxParser<T, KeycloakSamlAdapterV1QNames> {
protected static final QNameEnumLookup<KeycloakSamlAdapterV1QNames> LOOKUP = new QNameEnumLookup(KeycloakSamlAdapterV1QNames.values());
private static final Set<String> ALTERNATE_NAMESPACES = Collections.singleton(XMLConstants.NULL_NS_URI);
public AbstractKeycloakSamlAdapterV1Parser(KeycloakSamlAdapterV1QNames expectedStartElement) {
super(expectedStartElement.getQName(), KeycloakSamlAdapterV1QNames.UNKNOWN_ELEMENT);
}
@Override
protected KeycloakSamlAdapterV1QNames getElementFromName(QName name) {
return (ALTERNATE_NAMESPACES.contains(name.getNamespaceURI()))
? LOOKUP.from(new QName(KeycloakSamlAdapterV1QNames.NS_URI, name.getLocalPart()))
: LOOKUP.from(name);
}
@Override
protected void validateStartElement(StartElement startElement) {
QName name = startElement.getName();
QName validatedQName = ALTERNATE_NAMESPACES.contains(name.getNamespaceURI())
? new QName(name.getNamespaceURI(), expectedStartElement.getLocalPart())
: expectedStartElement;
StaxParserUtil.validate(startElement, validatedQName);
}
}
@@ -1,88 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ConfigXmlConstants {
public static final String KEYCLOAK_SAML_ADAPTER = "keycloak-saml-adapter";
public static final String SP_ELEMENT = "SP";
public static final String ENTITY_ID_ATTR = "entityID";
public static final String SSL_POLICY_ATTR = "sslPolicy";
public static final String NAME_ID_POLICY_FORMAT_ATTR = "nameIDPolicyFormat";
public static final String FORCE_AUTHENTICATION_ATTR = "forceAuthentication";
public static final String IS_PASSIVE_ATTR = "isPassive";
public static final String TURN_OFF_CHANGE_SESSSION_ID_ON_LOGIN_ATTR = "turnOffChangeSessionIdOnLogin";
public static final String SIGNATURE_ALGORITHM_ATTR = "signatureAlgorithm";
public static final String SIGNATURE_CANONICALIZATION_METHOD_ATTR = "signatureCanonicalizationMethod";
public static final String LOGOUT_PAGE_ATTR = "logoutPage";
public static final String AUTODETECT_BEARER_ONLY_ATTR = "autodetectBearerOnly";
public static final String KEYS_ELEMENT = "Keys";
public static final String KEY_ELEMENT = "Key";
public static final String SIGNING_ATTR = "signing";
public static final String ENCRYPTION_ATTR = "encryption";
public static final String CERTIFICATE_PEM_ELEMENT = "CertificatePem";
public static final String PRIVATE_KEY_PEM_ELEMENT = "PrivateKeyPem";
public static final String PUBLIC_KEY_PEM_ELEMENT = "PublicKeyPem";
public static final String FILE_ATTR = "file";
public static final String TYPE_ATTR = "type";
public static final String RESOURCE_ATTR = "resource";
public static final String PASSWORD_ATTR = "password";
public static final String ALIAS_ATTR = "alias";
public static final String KEYS_STORE_ELEMENT = "KeyStore";
public static final String CERTIFICATE_ELEMENT = "Certificate";
public static final String PRIVATE_KEY_ELEMENT = "PrivateKey";
public static final String PRINCIPAL_NAME_MAPPING_ELEMENT = "PrincipalNameMapping";
public static final String POLICY_ATTR = "policy";
public static final String ATTRIBUTE_ATTR = "attribute";
public static final String ROLE_IDENTIFIERS_ELEMENT = "RoleIdentifiers";
public static final String ATTRIBUTE_ELEMENT = "Attribute";
public static final String NAME_ATTR = "name";
public static final String IDP_ELEMENT = "IDP";
public static final String SIGNATURES_REQUIRED_ATTR = "signaturesRequired";
public static final String SINGLE_SIGN_ON_SERVICE_ELEMENT = "SingleSignOnService";
public static final String SINGLE_LOGOUT_SERVICE_ELEMENT = "SingleLogoutService";
public static final String SIGN_REQUEST_ATTR = "signRequest";
public static final String SIGN_RESPONSE_ATTR = "signResponse";
public static final String REQUEST_BINDING_ATTR = "requestBinding";
public static final String RESPONSE_BINDING_ATTR = "responseBinding";
public static final String BINDING_URL_ATTR = "bindingUrl";
public static final String VALIDATE_RESPONSE_SIGNATURE_ATTR = "validateResponseSignature";
public static final String VALIDATE_ASSERTION_SIGNATURE_ATTR = "validateAssertionSignature";
public static final String VALIDATE_REQUEST_SIGNATURE_ATTR = "validateRequestSignature";
public static final String POST_BINDING_URL_ATTR = "postBindingUrl";
public static final String REDIRECT_BINDING_URL_ATTR = "redirectBindingUrl";
public static final String ASSERTION_CONSUMER_SERVICE_URL_ATTR = "assertionConsumerServiceUrl";
public static final String HTTP_CLIENT_ELEMENT = "HttpClient";
public static final String ALLOW_ANY_HOSTNAME_ATTR = "allowAnyHostname";
public static final String CLIENT_KEYSTORE_ATTR = "clientKeystore";
public static final String CLIENT_KEYSTORE_PASSWORD_ATTR = "clientKeystorePassword";
public static final String CONNECTION_POOL_SIZE_ATTR = "connectionPoolSize";
public static final String DISABLE_TRUST_MANAGER_ATTR = "disableTrustManager";
public static final String PROXY_URL_ATTR = "proxyUrl";
public static final String TRUSTSTORE_ATTR = "truststore";
public static final String TRUSTSTORE_PASSWORD_ATTR = "truststorePassword";
}
@@ -59,7 +59,7 @@ public class DeploymentBuilder {
idp.setSingleSignOnService(sso);
idp.setSingleLogoutService(slo);
KeycloakSamlAdapter adapter = (KeycloakSamlAdapter)(new KeycloakSamlAdapterXMLParser().parse(xml));
KeycloakSamlAdapter adapter = (KeycloakSamlAdapter) KeycloakSamlAdapterParser.getInstance().parse(xml);
SP sp = adapter.getSps().get(0);
deployment.setConfigured(true);
deployment.setEntityID(sp.getEntityID());
@@ -0,0 +1,65 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.IDP.HttpClientConfig;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.StartElement;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class HttpClientParser extends AbstractKeycloakSamlAdapterV1Parser<HttpClientConfig> {
private static final HttpClientParser INSTANCE = new HttpClientParser();
private HttpClientParser() {
super(KeycloakSamlAdapterV1QNames.HTTP_CLIENT);
}
public static HttpClientParser getInstance() {
return INSTANCE;
}
@Override
protected HttpClientConfig instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException {
final HttpClientConfig config = new HttpClientConfig();
final Boolean allowAnyHostname = StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_ALLOW_ANY_HOSTNAME);
config.setAllowAnyHostname(allowAnyHostname == null ? false : allowAnyHostname);
config.setClientKeystore(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_CLIENT_KEYSTORE));
config.setClientKeystorePassword(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_CLIENT_KEYSTORE_PASSWORD));
final Integer connPoolSize = StaxParserUtil.getIntegerAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_CONNECTION_POOL_SIZE);
config.setConnectionPoolSize(connPoolSize == null ? 0 : connPoolSize);
final Boolean disableTrustManager = StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_DISABLE_TRUST_MANAGER);
config.setDisableTrustManager(disableTrustManager == null ? false : disableTrustManager);
config.setProxyUrl(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_PROXY_URL));
config.setTruststore(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_TRUSTSTORE));
config.setTruststorePassword(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_TRUSTSTORE_PASSWORD));
return config;
}
@Override
protected void processSubElement(XMLEventReader xmlEventReader, HttpClientConfig target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException {
}
}
@@ -1,163 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.IDP;
import org.keycloak.adapters.saml.config.Key;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.AbstractParser;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.util.List;
import org.keycloak.adapters.saml.config.IDP.HttpClientConfig;
import static org.keycloak.adapters.saml.config.parsers.SPXmlParser.getAttributeValue;
import static org.keycloak.adapters.saml.config.parsers.SPXmlParser.getBooleanAttributeValue;
import static org.keycloak.adapters.saml.config.parsers.SPXmlParser.getIntegerAttributeValue;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class IDPXmlParser extends AbstractParser {
@Override
public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.IDP_ELEMENT);
IDP idp = new IDP();
String entityID = getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
if (entityID == null) {
throw new ParsingException("entityID must be set on IDP");
}
idp.setEntityID(entityID);
boolean signaturesRequired = getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNATURES_REQUIRED_ATTR);
idp.setSignatureCanonicalizationMethod(getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
idp.setSignatureAlgorithm(getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR));
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
break;
if (xmlEvent instanceof EndElement) {
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
String endElementName = StaxParserUtil.getEndElementName(endElement);
if (endElementName.equals(ConfigXmlConstants.IDP_ELEMENT))
break;
else
continue;
}
startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
if (startElement == null)
break;
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.SINGLE_SIGN_ON_SERVICE_ELEMENT)) {
IDP.SingleSignOnService sso = parseSingleSignOnService(xmlEventReader, signaturesRequired);
idp.setSingleSignOnService(sso);
} else if (tag.equals(ConfigXmlConstants.SINGLE_LOGOUT_SERVICE_ELEMENT)) {
IDP.SingleLogoutService slo = parseSingleLogoutService(xmlEventReader, signaturesRequired);
idp.setSingleLogoutService(slo);
} else if (tag.equals(ConfigXmlConstants.HTTP_CLIENT_ELEMENT)) {
HttpClientConfig config = parseHttpClientElement(xmlEventReader);
idp.setHttpClientConfig(config);
} else if (tag.equals(ConfigXmlConstants.KEYS_ELEMENT)) {
KeysXmlParser parser = new KeysXmlParser();
List<Key> keys = (List<Key>)parser.parse(xmlEventReader);
idp.setKeys(keys);
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
}
return idp;
}
protected IDP.SingleLogoutService parseSingleLogoutService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException {
IDP.SingleLogoutService slo = new IDP.SingleLogoutService();
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
slo.setSignRequest(getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
slo.setValidateResponseSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
slo.setValidateRequestSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR, signaturesRequired));
slo.setRequestBinding(getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
slo.setResponseBinding(getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
slo.setSignResponse(getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR, signaturesRequired));
slo.setPostBindingUrl(getAttributeValue(element, ConfigXmlConstants.POST_BINDING_URL_ATTR));
slo.setRedirectBindingUrl(getAttributeValue(element, ConfigXmlConstants.REDIRECT_BINDING_URL_ATTR));
return slo;
}
protected IDP.SingleSignOnService parseSingleSignOnService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException {
IDP.SingleSignOnService sso = new IDP.SingleSignOnService();
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
sso.setSignRequest(getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
sso.setValidateResponseSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
sso.setValidateAssertionSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_ASSERTION_SIGNATURE_ATTR));
sso.setRequestBinding(getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
sso.setResponseBinding(getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
sso.setBindingUrl(getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
sso.setAssertionConsumerServiceUrl(getAttributeValue(element, ConfigXmlConstants.ASSERTION_CONSUMER_SERVICE_URL_ATTR));
return sso;
}
private HttpClientConfig parseHttpClientElement(XMLEventReader xmlEventReader) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.HTTP_CLIENT_ELEMENT);
HttpClientConfig config = new HttpClientConfig();
config.setAllowAnyHostname(getBooleanAttributeValue(startElement, ConfigXmlConstants.ALLOW_ANY_HOSTNAME_ATTR, false));
config.setClientKeystore(getAttributeValue(startElement, ConfigXmlConstants.CLIENT_KEYSTORE_ATTR));
config.setClientKeystorePassword(getAttributeValue(startElement, ConfigXmlConstants.CLIENT_KEYSTORE_PASSWORD_ATTR));
config.setConnectionPoolSize(getIntegerAttributeValue(startElement, ConfigXmlConstants.CONNECTION_POOL_SIZE_ATTR, 0));
config.setDisableTrustManager(getBooleanAttributeValue(startElement, ConfigXmlConstants.ALLOW_ANY_HOSTNAME_ATTR, false));
config.setProxyUrl(getAttributeValue(startElement, ConfigXmlConstants.PROXY_URL_ATTR));
config.setTruststore(getAttributeValue(startElement, ConfigXmlConstants.TRUSTSTORE_ATTR));
config.setTruststorePassword(getAttributeValue(startElement, ConfigXmlConstants.TRUSTSTORE_PASSWORD_ATTR));
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
break;
if (xmlEvent instanceof EndElement) {
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
String endElementName = StaxParserUtil.getEndElementName(endElement);
if (endElementName.equals(ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT))
break;
else
continue;
}
String tag = StaxParserUtil.getStartElementName(startElement);
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
return config;
}
@Override
public boolean supports(QName qname) {
return false;
}
}
@@ -0,0 +1,77 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.IDP;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.StartElement;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class IdpParser extends AbstractKeycloakSamlAdapterV1Parser<IDP> {
private static final IdpParser INSTANCE = new IdpParser();
private IdpParser() {
super(KeycloakSamlAdapterV1QNames.IDP);
}
public static IdpParser getInstance() {
return INSTANCE;
}
@Override
protected IDP instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException {
final IDP idp = new IDP();
idp.setEntityID(StaxParserUtil.getRequiredAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_ENTITY_ID));
Boolean signaturesRequired = StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SIGNATURES_REQUIRED);
idp.setSignaturesRequired(signaturesRequired == null ? false : signaturesRequired);
idp.setSignatureCanonicalizationMethod(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SIGNATURE_CANONICALIZATION_METHOD));
idp.setSignatureAlgorithm(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SIGNATURE_ALGORITHM));
return idp;
}
@Override
protected void processSubElement(XMLEventReader xmlEventReader, IDP target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException {
switch (element) {
case HTTP_CLIENT:
target.setHttpClientConfig(HttpClientParser.getInstance().parse(xmlEventReader));
break;
case KEYS:
target.setKeys(KeysParser.getInstance().parse(xmlEventReader));
break;
case SINGLE_SIGN_ON_SERVICE:
target.setSingleSignOnService(SingleSignOnServiceParser.getInstance().parse(xmlEventReader));
break;
case SINGLE_LOGOUT_SERVICE:
target.setSingleLogoutService(SingleLogoutServiceParser.getInstance().parse(xmlEventReader));
break;
}
}
}
@@ -0,0 +1,79 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.Key;
import org.keycloak.common.util.StringPropertyReplacer;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.StartElement;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeyParser extends AbstractKeycloakSamlAdapterV1Parser<Key> {
private static final KeyParser INSTANCE = new KeyParser();
private KeyParser() {
super(KeycloakSamlAdapterV1QNames.KEY);
}
public static KeyParser getInstance() {
return INSTANCE;
}
@Override
protected Key instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException {
Key key = new Key();
key.setSigning(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SIGNING));
key.setEncryption(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_ENCRYPTION));
return key;
}
@Override
protected void processSubElement(XMLEventReader xmlEventReader, Key target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException {
String value;
switch (element) {
case KEY_STORE:
target.setKeystore(KeyStoreParser.getInstance().parse(xmlEventReader));
break;
case CERTIFICATE_PEM:
StaxParserUtil.advance(xmlEventReader);
value = StaxParserUtil.getElementText(xmlEventReader);
target.setCertificatePem(StringPropertyReplacer.replaceProperties(value));
break;
case PUBLIC_KEY_PEM:
StaxParserUtil.advance(xmlEventReader);
value = StaxParserUtil.getElementText(xmlEventReader);
target.setPublicKeyPem(StringPropertyReplacer.replaceProperties(value));
break;
case PRIVATE_KEY_PEM:
StaxParserUtil.advance(xmlEventReader);
value = StaxParserUtil.getElementText(xmlEventReader);
target.setPrivateKeyPem(StringPropertyReplacer.replaceProperties(value));
break;
}
}
}
@@ -0,0 +1,73 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.Key;
import org.keycloak.adapters.saml.config.Key.KeyStoreConfig;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.StartElement;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeyStoreParser extends AbstractKeycloakSamlAdapterV1Parser<KeyStoreConfig> {
private static final KeyStoreParser INSTANCE = new KeyStoreParser();
private KeyStoreParser() {
super(KeycloakSamlAdapterV1QNames.KEY_STORE);
}
public static KeyStoreParser getInstance() {
return INSTANCE;
}
@Override
protected KeyStoreConfig instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException {
final KeyStoreConfig keyStore = new Key.KeyStoreConfig();
keyStore.setType(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_TYPE));
keyStore.setAlias(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_ALIAS));
keyStore.setFile(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_FILE));
keyStore.setResource(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_RESOURCE));
keyStore.setPassword(StaxParserUtil.getRequiredAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_PASSWORD));
if (keyStore.getFile() == null && keyStore.getResource() == null) {
throw new ParsingException("KeyStore element must have the url or classpath attribute set");
}
return keyStore;
}
@Override
protected void processSubElement(XMLEventReader xmlEventReader, KeyStoreConfig target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException {
switch (element) {
case CERTIFICATE:
target.setCertificateAlias(StaxParserUtil.getRequiredAttributeValueRP(elementDetail, KeycloakSamlAdapterV1QNames.ATTR_ALIAS));
break;
case PRIVATE_KEY:
target.setPrivateKeyAlias(StaxParserUtil.getRequiredAttributeValueRP(elementDetail, KeycloakSamlAdapterV1QNames.ATTR_ALIAS));
target.setPrivateKeyPassword(StaxParserUtil.getRequiredAttributeValueRP(elementDetail, KeycloakSamlAdapterV1QNames.ATTR_PASSWORD));
break;
}
}
}
@@ -1,145 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.Key;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.AbstractParser;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeyXmlParser extends AbstractParser {
@Override
public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.KEY_ELEMENT);
Key key = new Key();
key.setSigning(SPXmlParser.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNING_ATTR));
key.setEncryption(SPXmlParser.getBooleanAttributeValue(startElement, ConfigXmlConstants.ENCRYPTION_ATTR));
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
break;
if (xmlEvent instanceof EndElement) {
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
String endElementName = StaxParserUtil.getEndElementName(endElement);
if (endElementName.equals(ConfigXmlConstants.KEY_ELEMENT))
break;
else
throw logger.parserUnknownEndElement(endElementName);
}
startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
if (startElement == null)
break;
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.KEYS_STORE_ELEMENT)) {
key.setKeystore(parseKeyStore(xmlEventReader));
} else if (tag.equals(ConfigXmlConstants.CERTIFICATE_PEM_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
key.setCertificatePem(SPXmlParser.getElementText(xmlEventReader));
} else if (tag.equals(ConfigXmlConstants.PUBLIC_KEY_PEM_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
key.setPublicKeyPem(SPXmlParser.getElementText(xmlEventReader));
} else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_PEM_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
key.setPrivateKeyPem(SPXmlParser.getElementText(xmlEventReader));
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
}
return key;
}
protected Key.KeyStoreConfig parseKeyStore(XMLEventReader xmlEventReader) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYS_STORE_ELEMENT);
Key.KeyStoreConfig keyStore = new Key.KeyStoreConfig();
keyStore.setType(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.TYPE_ATTR));
keyStore.setAlias(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.ALIAS_ATTR));
keyStore.setFile(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.FILE_ATTR));
keyStore.setResource(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.RESOURCE_ATTR));
if (keyStore.getFile() == null && keyStore.getResource() == null) {
throw new ParsingException("KeyStore element must have the url or classpath attribute set");
}
keyStore.setPassword(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.PASSWORD_ATTR));
if (keyStore.getPassword() == null) {
throw new ParsingException("KeyStore element must have the password attribute set");
}
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
break;
if (xmlEvent instanceof EndElement) {
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
String endElementName = StaxParserUtil.getEndElementName(endElement);
if (endElementName.equals(ConfigXmlConstants.KEYS_STORE_ELEMENT))
break;
else
continue;
}
startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
if (startElement == null)
break;
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.CERTIFICATE_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
keyStore.setCertificateAlias(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
if (keyStore.getCertificateAlias() == null) {
throw new ParsingException("KeyStore Certificate element must have the alias attribute set");
}
} else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
keyStore.setPrivateKeyAlias(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
if (keyStore.getPrivateKeyAlias() == null) {
throw new ParsingException("KeyStore PrivateKey element must have the alias attribute set");
}
keyStore.setPrivateKeyPassword(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.PASSWORD_ATTR));
if (keyStore.getPrivateKeyPassword() == null) {
throw new ParsingException("KeyStore PrivateKey element must have the password attribute set");
}
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
}
return keyStore;
}
@Override
public boolean supports(QName qname) {
return false;
}
}
@@ -0,0 +1,85 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.saml.common.ErrorCodes;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.AbstractParser;
import org.keycloak.saml.common.parsers.StaxParser;
import org.keycloak.saml.common.util.StaxParserUtil;
import java.util.HashMap;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
/**
*
* @author hmlnarik
*/
public class KeycloakSamlAdapterParser extends AbstractParser {
private interface ParserFactory {
public StaxParser create();
}
private static final Map<QName, ParserFactory> PARSERS = new HashMap<QName, ParserFactory>();
// No-namespace variant
private static final QName ALTERNATE_KEYCLOAK_SAML_ADAPTER_V1 = new QName(KeycloakSamlAdapterV1QNames.KEYCLOAK_SAML_ADAPTER.getQName().getLocalPart());
static {
PARSERS.put(KeycloakSamlAdapterV1QNames.KEYCLOAK_SAML_ADAPTER.getQName(), new ParserFactory() { @Override public StaxParser create() { return KeycloakSamlAdapterV1Parser.getInstance(); }});
PARSERS.put(ALTERNATE_KEYCLOAK_SAML_ADAPTER_V1, new ParserFactory() { @Override public StaxParser create() { return KeycloakSamlAdapterV1Parser.getInstance(); }});
}
private static final KeycloakSamlAdapterParser INSTANCE = new KeycloakSamlAdapterParser();
public static KeycloakSamlAdapterParser getInstance() {
return INSTANCE;
}
protected KeycloakSamlAdapterParser() {
}
/**
* @see {@link org.keycloak.saml.common.parsers.ParserNamespaceSupport#parse(XMLEventReader)}
*/
@Override
public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent instanceof StartElement) {
StartElement startElement = (StartElement) xmlEvent;
final QName name = startElement.getName();
ParserFactory pf = PARSERS.get(name);
if (pf == null) {
throw logger.parserException(new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + name + "::location="
+ startElement.getLocation()));
}
return pf.create().parse(xmlEventReader);
}
StaxParserUtil.getNextEvent(xmlEventReader);
}
throw new RuntimeException(ErrorCodes.FAILED_PARSING + "SAML Parsing has failed");
}
}
@@ -18,12 +18,9 @@
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.KeycloakSamlAdapter;
import org.keycloak.adapters.saml.config.SP;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.AbstractParser;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.StartElement;
@@ -31,32 +28,33 @@ import javax.xml.stream.events.StartElement;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakSamlAdapterXMLParser extends AbstractParser {
public class KeycloakSamlAdapterV1Parser extends AbstractKeycloakSamlAdapterV1Parser<KeycloakSamlAdapter> {
@Override
public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
KeycloakSamlAdapter adapter = new KeycloakSamlAdapter();
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYCLOAK_SAML_ADAPTER);
while (xmlEventReader.hasNext()) {
startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
if (startElement == null)
break;
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.SP_ELEMENT)) {
SPXmlParser parser = new SPXmlParser();
SP sp = (SP)parser.parse(xmlEventReader);
if (sp != null) adapter.getSps().add(sp);
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
private static final KeycloakSamlAdapterV1Parser INSTANCE = new KeycloakSamlAdapterV1Parser();
}
return adapter;
private KeycloakSamlAdapterV1Parser() {
super(KeycloakSamlAdapterV1QNames.KEYCLOAK_SAML_ADAPTER);
}
public static KeycloakSamlAdapterV1Parser getInstance() {
return INSTANCE;
}
@Override
public boolean supports(QName qname) {
return false;
protected KeycloakSamlAdapter instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException {
return new KeycloakSamlAdapter();
}
@Override
protected void processSubElement(XMLEventReader xmlEventReader, KeycloakSamlAdapter target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException {
switch (element) {
case SP:
target.addSp(SpParser.getInstance().parse(xmlEventReader));
break;
default:
// Ignore unknown tags
StaxParserUtil.bypassElementBlock(xmlEventReader);
}
}
}
@@ -0,0 +1,114 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.saml.processing.core.parsers.util.HasQName;
import javax.xml.namespace.QName;
/**
*
* @author hmlnarik
*/
public enum KeycloakSamlAdapterV1QNames implements HasQName {
ATTRIBUTE("Attribute"),
CERTIFICATE("Certificate"),
CERTIFICATE_PEM("CertificatePem"),
HTTP_CLIENT("HttpClient"),
IDP("IDP"),
KEY("Key"),
KEYCLOAK_SAML_ADAPTER("keycloak-saml-adapter"),
KEYS("Keys"),
KEY_STORE("KeyStore"),
PRINCIPAL_NAME_MAPPING("PrincipalNameMapping"),
PRIVATE_KEY("PrivateKey"),
PRIVATE_KEY_PEM("PrivateKeyPem"),
PUBLIC_KEY_PEM("PublicKeyPem"),
ROLE_IDENTIFIERS("RoleIdentifiers"),
SINGLE_LOGOUT_SERVICE("SingleLogoutService"),
SINGLE_SIGN_ON_SERVICE("SingleSignOnService"),
SP("SP"),
ATTR_ALIAS(null, "alias"),
ATTR_ALLOW_ANY_HOSTNAME(null, "allowAnyHostname"),
ATTR_ASSERTION_CONSUMER_SERVICE_URL(null, "assertionConsumerServiceUrl"),
ATTR_ATTRIBUTE(null, "attribute"),
ATTR_AUTODETECT_BEARER_ONLY(null, "autodetectBearerOnly"),
ATTR_BINDING_URL(null, "bindingUrl"),
ATTR_CLIENT_KEYSTORE(null, "clientKeystore"),
ATTR_CLIENT_KEYSTORE_PASSWORD(null, "clientKeystorePassword"),
ATTR_CONNECTION_POOL_SIZE(null, "connectionPoolSize"),
ATTR_DISABLE_TRUST_MANAGER(null, "disableTrustManager"),
ATTR_ENCRYPTION(null, "encryption"),
ATTR_ENTITY_ID(null, "entityID"),
ATTR_FILE(null, "file"),
ATTR_FORCE_AUTHENTICATION(null, "forceAuthentication"),
ATTR_IS_PASSIVE(null, "isPassive"),
ATTR_LOGOUT_PAGE(null, "logoutPage"),
ATTR_NAME(null, "name"),
ATTR_NAME_ID_POLICY_FORMAT(null, "nameIDPolicyFormat"),
ATTR_PASSWORD(null, "password"),
ATTR_POLICY(null, "policy"),
ATTR_POST_BINDING_URL(null, "postBindingUrl"),
ATTR_PROXY_URL(null, "proxyUrl"),
ATTR_REDIRECT_BINDING_URL(null, "redirectBindingUrl"),
ATTR_REQUEST_BINDING(null, "requestBinding"),
ATTR_RESOURCE(null, "resource"),
ATTR_RESPONSE_BINDING(null, "responseBinding"),
ATTR_SIGNATURES_REQUIRED(null, "signaturesRequired"),
ATTR_SIGNATURE_ALGORITHM(null, "signatureAlgorithm"),
ATTR_SIGNATURE_CANONICALIZATION_METHOD(null, "signatureCanonicalizationMethod"),
ATTR_SIGNING(null, "signing"),
ATTR_SIGN_REQUEST(null, "signRequest"),
ATTR_SIGN_RESPONSE(null, "signResponse"),
ATTR_SSL_POLICY(null, "sslPolicy"),
ATTR_TRUSTSTORE(null, "truststore"),
ATTR_TRUSTSTORE_PASSWORD(null, "truststorePassword"),
ATTR_TURN_OFF_CHANGE_SESSSION_ID_ON_LOGIN(null, "turnOffChangeSessionIdOnLogin"),
ATTR_TYPE(null, "type"),
ATTR_VALIDATE_ASSERTION_SIGNATURE(null, "validateAssertionSignature"),
ATTR_VALIDATE_REQUEST_SIGNATURE(null, "validateRequestSignature"),
ATTR_VALIDATE_RESPONSE_SIGNATURE(null, "validateResponseSignature"),
UNKNOWN_ELEMENT("")
;
public static final String NS_URI = "urn:keycloak:saml:adapter";
private final QName qName;
private KeycloakSamlAdapterV1QNames(String localName) {
this(NS_URI, localName);
}
private KeycloakSamlAdapterV1QNames(HasQName source) {
this.qName = source.getQName();
}
private KeycloakSamlAdapterV1QNames(String nsUri, String localName) {
this.qName = new QName(nsUri == null ? null : nsUri, localName);
}
@Override
public QName getQName() {
return qName;
}
public QName getQName(String prefix) {
return new QName(this.qName.getNamespaceURI(), this.qName.getLocalPart(), prefix);
}
}
@@ -0,0 +1,59 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.Key;
import org.keycloak.adapters.saml.config.SP;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.StaxParserUtil;
import java.util.LinkedList;
import java.util.List;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.StartElement;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeysParser extends AbstractKeycloakSamlAdapterV1Parser<List<Key>> {
private static final KeysParser INSTANCE = new KeysParser();
private KeysParser() {
super(KeycloakSamlAdapterV1QNames.KEYS);
}
public static KeysParser getInstance() {
return INSTANCE;
}
@Override
protected List<Key> instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException {
return new LinkedList<>();
}
@Override
protected void processSubElement(XMLEventReader xmlEventReader, List<Key> target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException {
switch (element) {
case KEY:
target.add(KeyParser.getInstance().parse(xmlEventReader));
break;
}
}
}
@@ -1,76 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.Key;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.AbstractParser;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeysXmlParser extends AbstractParser {
@Override
public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYS_ELEMENT);
List<Key> keys = new LinkedList<>();
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
break;
if (xmlEvent instanceof EndElement) {
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
String endElementName = StaxParserUtil.getEndElementName(endElement);
if (endElementName.equals(ConfigXmlConstants.KEYS_ELEMENT))
break;
else
throw logger.parserUnknownEndElement(endElementName);
}
startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
if (startElement == null)
break;
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.KEY_ELEMENT)) {
KeyXmlParser parser = new KeyXmlParser();
Key key = (Key)parser.parse(xmlEventReader);
keys.add(key);
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
}
return keys;
}
@Override
public boolean supports(QName qname) {
return false;
}
}
@@ -0,0 +1,56 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.SP;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.StartElement;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class PrincipalNameMappingParser extends AbstractKeycloakSamlAdapterV1Parser<SP.PrincipalNameMapping> {
private static final PrincipalNameMappingParser INSTANCE = new PrincipalNameMappingParser();
private PrincipalNameMappingParser() {
super(KeycloakSamlAdapterV1QNames.PRINCIPAL_NAME_MAPPING);
}
public static PrincipalNameMappingParser getInstance() {
return INSTANCE;
}
@Override
protected SP.PrincipalNameMapping instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException {
final SP.PrincipalNameMapping mapping = new SP.PrincipalNameMapping();
mapping.setPolicy(StaxParserUtil.getRequiredAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_POLICY));
mapping.setAttributeName(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_ATTRIBUTE));
return mapping;
}
@Override
protected void processSubElement(XMLEventReader xmlEventReader, SP.PrincipalNameMapping target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException {
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.StaxParserUtil;
import java.util.HashSet;
import java.util.Set;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.StartElement;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RoleMappingParser extends AbstractKeycloakSamlAdapterV1Parser<Set<String>> {
private static final RoleMappingParser INSTANCE = new RoleMappingParser();
private RoleMappingParser() {
super(KeycloakSamlAdapterV1QNames.ROLE_IDENTIFIERS);
}
public static RoleMappingParser getInstance() {
return INSTANCE;
}
@Override
protected Set<String> instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException {
return new HashSet<>();
}
@Override
protected void processSubElement(XMLEventReader xmlEventReader, Set<String> target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException {
switch (element) {
case ATTRIBUTE:
target.add(StaxParserUtil.getRequiredAttributeValueRP(elementDetail, KeycloakSamlAdapterV1QNames.ATTR_NAME));
break;
}
}
}
@@ -1,181 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.IDP;
import org.keycloak.adapters.saml.config.Key;
import org.keycloak.adapters.saml.config.SP;
import org.keycloak.common.util.StringPropertyReplacer;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.AbstractParser;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SPXmlParser extends AbstractParser {
public static String getAttributeValue(StartElement startElement, String tag) {
String str = StaxParserUtil.getAttributeValue(startElement, tag);
if (str != null)
return StringPropertyReplacer.replaceProperties(str);
else
return str;
}
public static int getIntegerAttributeValue(StartElement startElement, String tag, int defaultValue) {
String result = getAttributeValue(startElement, tag);
if (result == null)
return defaultValue;
return Integer.valueOf(result);
}
public static boolean getBooleanAttributeValue(StartElement startElement, String tag, boolean defaultValue) {
String result = getAttributeValue(startElement, tag);
if (result == null)
return defaultValue;
return Boolean.valueOf(result);
}
public static boolean getBooleanAttributeValue(StartElement startElement, String tag) {
return getBooleanAttributeValue(startElement, tag, false);
}
public static String getElementText(XMLEventReader xmlEventReader) throws ParsingException {
String result = StaxParserUtil.getElementText(xmlEventReader);
if (result != null)
result = StringPropertyReplacer.replaceProperties(result);
return result;
}
@Override
public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.SP_ELEMENT);
SP sp = new SP();
String entityID = getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
if (entityID == null) {
throw new ParsingException("entityID must be set on SP");
}
sp.setEntityID(entityID);
sp.setSslPolicy(getAttributeValue(startElement, ConfigXmlConstants.SSL_POLICY_ATTR));
sp.setLogoutPage(getAttributeValue(startElement, ConfigXmlConstants.LOGOUT_PAGE_ATTR));
sp.setNameIDPolicyFormat(getAttributeValue(startElement, ConfigXmlConstants.NAME_ID_POLICY_FORMAT_ATTR));
sp.setForceAuthentication(getBooleanAttributeValue(startElement, ConfigXmlConstants.FORCE_AUTHENTICATION_ATTR));
sp.setIsPassive(getBooleanAttributeValue(startElement, ConfigXmlConstants.IS_PASSIVE_ATTR));
sp.setAutodetectBearerOnly(getBooleanAttributeValue(startElement, ConfigXmlConstants.AUTODETECT_BEARER_ONLY_ATTR));
sp.setTurnOffChangeSessionIdOnLogin(getBooleanAttributeValue(startElement, ConfigXmlConstants.TURN_OFF_CHANGE_SESSSION_ID_ON_LOGIN_ATTR));
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
break;
if (xmlEvent instanceof EndElement) {
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
String endElementName = StaxParserUtil.getEndElementName(endElement);
if (endElementName.equals(ConfigXmlConstants.SP_ELEMENT))
break;
else
continue;
}
startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
if (startElement == null)
break;
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.KEYS_ELEMENT)) {
KeysXmlParser parser = new KeysXmlParser();
List<Key> keys = (List<Key>) parser.parse(xmlEventReader);
sp.setKeys(keys);
} else if (tag.equals(ConfigXmlConstants.PRINCIPAL_NAME_MAPPING_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
String policy = getAttributeValue(element, ConfigXmlConstants.POLICY_ATTR);
if (policy == null) {
throw new ParsingException("PrincipalNameMapping element must have the policy attribute set");
}
String attribute = getAttributeValue(element, ConfigXmlConstants.ATTRIBUTE_ATTR);
SP.PrincipalNameMapping mapping = new SP.PrincipalNameMapping();
mapping.setPolicy(policy);
mapping.setAttributeName(attribute);
sp.setPrincipalNameMapping(mapping);
} else if (tag.equals(ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT)) {
parseRoleMapping(xmlEventReader, sp);
} else if (tag.equals(ConfigXmlConstants.IDP_ELEMENT)) {
IDPXmlParser parser = new IDPXmlParser();
IDP idp = (IDP) parser.parse(xmlEventReader);
sp.setIdp(idp);
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
}
return sp;
}
protected void parseRoleMapping(XMLEventReader xmlEventReader, SP sp) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT);
Set<String> roleAttributes = new HashSet<>();
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
break;
if (xmlEvent instanceof EndElement) {
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
String endElementName = StaxParserUtil.getEndElementName(endElement);
if (endElementName.equals(ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT))
break;
else
continue;
}
startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
if (startElement == null)
break;
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.ATTRIBUTE_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
String attributeValue = getAttributeValue(element, ConfigXmlConstants.NAME_ATTR);
if (attributeValue == null) {
throw new ParsingException("RoleMapping Attribute element must have the name attribute set");
}
roleAttributes.add(attributeValue);
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
}
sp.setRoleAttributes(roleAttributes);
}
@Override
public boolean supports(QName qname) {
return false;
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.IDP;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.StartElement;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SingleLogoutServiceParser extends AbstractKeycloakSamlAdapterV1Parser<IDP.SingleLogoutService> {
private static final SingleLogoutServiceParser INSTANCE = new SingleLogoutServiceParser();
private SingleLogoutServiceParser() {
super(KeycloakSamlAdapterV1QNames.SINGLE_LOGOUT_SERVICE);
}
public static SingleLogoutServiceParser getInstance() {
return INSTANCE;
}
@Override
protected IDP.SingleLogoutService instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException {
final IDP.SingleLogoutService slo = new IDP.SingleLogoutService();
slo.setSignRequest(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SIGN_REQUEST));
slo.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_VALIDATE_RESPONSE_SIGNATURE));
slo.setValidateRequestSignature(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_VALIDATE_REQUEST_SIGNATURE));
slo.setRequestBinding(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_REQUEST_BINDING));
slo.setResponseBinding(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_RESPONSE_BINDING));
slo.setSignResponse(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SIGN_RESPONSE));
slo.setPostBindingUrl(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_POST_BINDING_URL));
slo.setRedirectBindingUrl(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_REDIRECT_BINDING_URL));
return slo;
}
@Override
protected void processSubElement(XMLEventReader xmlEventReader, IDP.SingleLogoutService target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException {
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.IDP;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.StartElement;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SingleSignOnServiceParser extends AbstractKeycloakSamlAdapterV1Parser<IDP.SingleSignOnService> {
private static final SingleSignOnServiceParser INSTANCE = new SingleSignOnServiceParser();
private SingleSignOnServiceParser() {
super(KeycloakSamlAdapterV1QNames.SINGLE_SIGN_ON_SERVICE);
}
public static SingleSignOnServiceParser getInstance() {
return INSTANCE;
}
@Override
protected IDP.SingleSignOnService instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException {
final IDP.SingleSignOnService sso = new IDP.SingleSignOnService();
sso.setSignRequest(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SIGN_REQUEST));
sso.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_VALIDATE_RESPONSE_SIGNATURE));
sso.setValidateAssertionSignature(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_VALIDATE_ASSERTION_SIGNATURE));
sso.setRequestBinding(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_REQUEST_BINDING));
sso.setResponseBinding(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_RESPONSE_BINDING));
sso.setBindingUrl(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_BINDING_URL));
sso.setAssertionConsumerServiceUrl(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_ASSERTION_CONSUMER_SERVICE_URL));
return sso;
}
@Override
protected void processSubElement(XMLEventReader xmlEventReader, IDP.SingleSignOnService target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException {
}
}
@@ -0,0 +1,80 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.SP;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.StartElement;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SpParser extends AbstractKeycloakSamlAdapterV1Parser<SP> {
private static final SpParser INSTANCE = new SpParser();
private SpParser() {
super(KeycloakSamlAdapterV1QNames.SP);
}
public static SpParser getInstance() {
return INSTANCE;
}
@Override
protected SP instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException {
final SP sp = new SP();
sp.setEntityID(StaxParserUtil.getRequiredAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_ENTITY_ID));
sp.setSslPolicy(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SSL_POLICY));
sp.setLogoutPage(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_LOGOUT_PAGE));
sp.setNameIDPolicyFormat(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_NAME_ID_POLICY_FORMAT));
sp.setForceAuthentication(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_FORCE_AUTHENTICATION));
sp.setIsPassive(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_IS_PASSIVE));
sp.setAutodetectBearerOnly(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_AUTODETECT_BEARER_ONLY));
sp.setTurnOffChangeSessionIdOnLogin(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_TURN_OFF_CHANGE_SESSSION_ID_ON_LOGIN));
return sp;
}
@Override
protected void processSubElement(XMLEventReader xmlEventReader, SP target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException {
switch (element) {
case KEYS:
target.setKeys(KeysParser.getInstance().parse(xmlEventReader));
break;
case PRINCIPAL_NAME_MAPPING:
target.setPrincipalNameMapping(PrincipalNameMappingParser.getInstance().parse(xmlEventReader));
break;
case ROLE_IDENTIFIERS:
target.setRoleAttributes(RoleMappingParser.getInstance().parse(xmlEventReader));
break;
case IDP:
target.setIdp(IdpParser.getInstance().parse(xmlEventReader));
break;
}
}
}
@@ -35,6 +35,7 @@ import org.keycloak.saml.common.constants.JBossSAMLConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.DocumentUtil;
import org.keycloak.saml.processing.core.parsers.saml.xmldsig.XmlDSigQNames;
import org.keycloak.saml.processing.core.util.NamespaceContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -88,7 +89,7 @@ public class SamlDescriptorIDPKeysExtractor {
}
private KeyInfo processKeyDescriptor(Element keyDescriptor) throws MarshalException {
NodeList childNodes = keyDescriptor.getElementsByTagNameNS(JBossSAMLURIConstants.XMLDSIG_NSURI.get(), JBossSAMLConstants.KEY_INFO.get());
NodeList childNodes = keyDescriptor.getElementsByTagNameNS(JBossSAMLURIConstants.XMLDSIG_NSURI.get(), XmlDSigQNames.KEY_INFO.getQName().getLocalPart());
if (childNodes.getLength() == 0) {
return null;
@@ -14,7 +14,7 @@ import javax.xml.crypto.dsig.keyinfo.X509Data;
import static org.hamcrest.CoreMatchers.*;
import org.junit.Test;
import static org.junit.Assert.*;
import org.keycloak.adapters.saml.config.parsers.ConfigXmlConstants;
import org.keycloak.adapters.saml.config.parsers.KeycloakSamlAdapterV1QNames;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
import org.keycloak.saml.common.exceptions.ParsingException;
@@ -41,15 +41,15 @@ public class HttpAdapterUtilsTest {
assertThat(res, notNullValue());
assertThat(res.keySet(), hasItems(KeyTypes.SIGNING.value()));
assertThat(res.get(ConfigXmlConstants.SIGNING_ATTR), notNullValue());
assertThat(res.get(ConfigXmlConstants.SIGNING_ATTR).size(), equalTo(2));
assertThat(res.get(KeycloakSamlAdapterV1QNames.ATTR_SIGNING.getQName().getLocalPart()), notNullValue());
assertThat(res.get(KeycloakSamlAdapterV1QNames.ATTR_SIGNING.getQName().getLocalPart()).size(), equalTo(2));
KeyInfo ki;
KeyName keyName;
X509Data x509data;
X509Certificate x509certificate;
ki = res.get(ConfigXmlConstants.SIGNING_ATTR).get(0);
ki = res.get(KeycloakSamlAdapterV1QNames.ATTR_SIGNING.getQName().getLocalPart()).get(0);
assertThat(ki.getContent().size(), equalTo(2));
assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(X509Data.class)));
assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(KeyName.class)));
@@ -63,7 +63,7 @@ public class HttpAdapterUtilsTest {
assertThat(x509certificate, notNullValue());
assertThat(x509certificate.getSigAlgName(), equalTo("SHA256withRSA"));
ki = res.get(ConfigXmlConstants.SIGNING_ATTR).get(1);
ki = res.get(KeycloakSamlAdapterV1QNames.ATTR_SIGNING.getQName().getLocalPart()).get(1);
assertThat(ki.getContent().size(), equalTo(2));
assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(X509Data.class)));
assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(KeyName.class)));
@@ -30,6 +30,8 @@ import java.io.InputStream;
import org.junit.Rule;
import org.junit.rules.ExpectedException;
import org.keycloak.saml.common.exceptions.ParsingException;
import java.io.IOException;
import org.hamcrest.Matchers;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -67,7 +69,7 @@ public class KeycloakSamlAdapterXMLParserTest {
@Test
public void testValidationKeyInvalid() throws Exception {
InputStream schemaIs = KeycloakSamlAdapterXMLParser.class.getResourceAsStream(CURRENT_XSD_LOCATION);
InputStream schemaIs = KeycloakSamlAdapterV1Parser.class.getResourceAsStream(CURRENT_XSD_LOCATION);
InputStream is = getClass().getResourceAsStream("keycloak-saml-invalid.xml");
assertNotNull(is);
assertNotNull(schemaIs);
@@ -77,12 +79,14 @@ public class KeycloakSamlAdapterXMLParserTest {
}
@Test
public void testXmlParser() throws Exception {
InputStream is = getClass().getResourceAsStream("keycloak-saml.xml");
assertNotNull(is);
KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser();
public void testParseSimpleFileNoNamespace() throws Exception {
KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-no-namespace.xml", KeycloakSamlAdapter.class);
}
@Test
public void testXmlParserBaseFile() throws Exception {
KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml.xml", KeycloakSamlAdapter.class);
KeycloakSamlAdapter config = (KeycloakSamlAdapter)parser.parse(is);
assertNotNull(config);
assertEquals(1, config.getSps().size());
SP sp = config.getSps().get(0);
@@ -121,7 +125,7 @@ public class KeycloakSamlAdapterXMLParserTest {
assertEquals("POST", idp.getSingleSignOnService().getRequestBinding());
assertEquals("url", idp.getSingleSignOnService().getBindingUrl());
assertTrue(idp.getSingleLogoutService().isSignRequest());
assertFalse(idp.getSingleLogoutService().isSignRequest());
assertTrue(idp.getSingleLogoutService().isSignResponse());
assertTrue(idp.getSingleLogoutService().isValidateRequestSignature());
assertTrue(idp.getSingleLogoutService().isValidateResponseSignature());
@@ -135,14 +139,17 @@ public class KeycloakSamlAdapterXMLParserTest {
assertEquals("cert pem", idp.getKeys().get(0).getCertificatePem());
}
private <T> T parseKeycloakSamlAdapterConfig(String fileName, Class<T> targetClass) throws ParsingException, IOException {
try (InputStream is = getClass().getResourceAsStream(fileName)) {
KeycloakSamlAdapterParser parser = KeycloakSamlAdapterParser.getInstance();
return targetClass.cast(parser.parse(is));
}
}
@Test
public void testXmlParserMultipleSigningKeys() throws Exception {
InputStream is = getClass().getResourceAsStream("keycloak-saml-multiple-signing-keys.xml");
assertNotNull(is);
KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser();
KeycloakSamlAdapter config = (KeycloakSamlAdapter) parser.parse(is);
KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-multiple-signing-keys.xml", KeycloakSamlAdapter.class);
assertNotNull(config);
assertEquals(1, config.getSps().size());
SP sp = config.getSps().get(0);
@@ -158,11 +165,7 @@ public class KeycloakSamlAdapterXMLParserTest {
@Test
public void testXmlParserHttpClientSettings() throws Exception {
InputStream is = getClass().getResourceAsStream("keycloak-saml-wth-http-client-settings.xml");
assertNotNull(is);
KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser();
KeycloakSamlAdapter config = (KeycloakSamlAdapter) parser.parse(is);
KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-wth-http-client-settings.xml", KeycloakSamlAdapter.class);
assertNotNull(config);
assertEquals(1, config.getSps().size());
SP sp = config.getSps().get(0);
@@ -178,4 +181,66 @@ public class KeycloakSamlAdapterXMLParserTest {
assertThat(idp.getHttpClientConfig().isAllowAnyHostname(), is(true));
assertThat(idp.getHttpClientConfig().isDisableTrustManager(), is(true));
}
@Test
public void testXmlParserSystemPropertiesNoPropertiesSet() throws Exception {
KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-properties.xml", KeycloakSamlAdapter.class);
assertNotNull(config);
assertThat(config.getSps(), Matchers.contains(instanceOf(SP.class)));
SP sp = config.getSps().get(0);
IDP idp = sp.getIdp();
assertThat(sp.getEntityID(), is("sp"));
assertThat(sp.getSslPolicy(), is("${keycloak-saml-properties.sslPolicy}"));
assertThat(idp.isSignaturesRequired(), is(false));
assertThat(idp.getSingleLogoutService().isSignRequest(), is(true));
assertThat(idp.getSingleLogoutService().isSignResponse(), is(false));
assertThat(idp.getSingleSignOnService().isSignRequest(), is(true));
assertThat(idp.getSingleSignOnService().isValidateResponseSignature(), is(true));
// These should take default from IDP.signaturesRequired
assertThat(idp.getSingleLogoutService().isValidateRequestSignature(), is(false));
assertThat(idp.getSingleLogoutService().isValidateResponseSignature(), is(false));
assertThat(idp.getSingleSignOnService().isValidateAssertionSignature(), is(false));
}
@Test
public void testXmlParserSystemPropertiesWithPropertiesSet() throws Exception {
try {
System.setProperty("keycloak-saml-properties.entityID", "meid");
System.setProperty("keycloak-saml-properties.sslPolicy", "INTERNAL");
System.setProperty("keycloak-saml-properties.signaturesRequired", "true");
KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-properties.xml", KeycloakSamlAdapter.class);
assertNotNull(config);
assertThat(config.getSps(), Matchers.contains(instanceOf(SP.class)));
SP sp = config.getSps().get(0);
IDP idp = sp.getIdp();
assertThat(sp.getEntityID(), is("meid"));
assertThat(sp.getSslPolicy(), is("INTERNAL"));
assertThat(idp.isSignaturesRequired(), is(true));
assertThat(idp.getSingleLogoutService().isSignRequest(), is(true));
assertThat(idp.getSingleLogoutService().isSignResponse(), is(false));
assertThat(idp.getSingleSignOnService().isSignRequest(), is(true));
assertThat(idp.getSingleSignOnService().isValidateResponseSignature(), is(true));
// These should take default from IDP.signaturesRequired
assertThat(idp.getSingleLogoutService().isValidateRequestSignature(), is(true));
assertThat(idp.getSingleLogoutService().isValidateResponseSignature(), is(true));
// This is false by default
assertThat(idp.getSingleSignOnService().isValidateAssertionSignature(), is(false));
} finally {
System.clearProperty("keycloak-saml-properties.entityID");
System.clearProperty("keycloak-saml-properties.sslPolicy");
System.clearProperty("keycloak-saml-properties.signaturesRequired");
}
}
}
@@ -15,30 +15,41 @@
~ limitations under the License.
-->
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_7.xsd">
<SP entityID="http://localhost:8081/sales-post-sig-email/"
<keycloak-saml-adapter>
<SP entityID="${keycloak-saml-properties.entityID:sp}"
sslPolicy="EXTERNAL"
logoutPage="/logout.jsp"
forceAuthentication="false">
nameIDPolicyFormat="format"
forceAuthentication="true"
isPassive="true">
<Keys>
<Key signing="true" >
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
<PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
<Certificate alias="http://localhost:8080/sales-post-sig/"/>
<Key signing="true">
<KeyStore file="file" resource="cp" password="pw">
<PrivateKey alias="private alias" password="private pw"/>
<Certificate alias="cert alias"/>
</KeyStore>
</Key>
<Key encryption="true">
<PrivateKeyPem>
private pem
</PrivateKeyPem>
<PublicKeyPem>
public pem
</PublicKeyPem>
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
<PrincipalNameMapping policy="FROM_ATTRIBUTE" attribute="attribute"/>
<RoleIdentifiers>
<Attribute name="Role"/>
<Attribute name="member"/>
</RoleIdentifiers>
<IDP entityID="idp">
<IDP entityID="idp"
signatureAlgorithm="RSA_SHA256"
signatureCanonicalizationMethod="canon"
signaturesRequired="true"
>
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
bindingUrl="url"
/>
<SingleLogoutService
@@ -46,18 +57,18 @@
validateResponseSignature="true"
signRequest="true"
signResponse="true"
requestBinding="POST"
requestBinding="REDIRECT"
responseBinding="POST"
postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
postBindingUrl="posturl"
redirectBindingUrl="redirecturl"
/>
<Keys>
<Key signing="true">
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
<Certificate alias="demo"/>
</KeyStore>
<CertificatePem>
cert pem
</CertificatePem>
</Key>
</Keys>
</IDP>
</SP>
</keycloak-saml-adapter>
</SP>
</keycloak-saml-adapter>
@@ -0,0 +1,74 @@
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_9.xsd">
<SP entityID="${keycloak-saml-properties.entityID:sp}"
sslPolicy="${keycloak-saml-properties.sslPolicy}"
nameIDPolicyFormat="format"
forceAuthentication="true"
isPassive="true">
<Keys>
<Key signing="true">
<KeyStore file="file" resource="cp" password="pw">
<PrivateKey alias="private alias" password="private pw"/>
<Certificate alias="cert alias"/>
</KeyStore>
</Key>
<Key encryption="true">
<PrivateKeyPem>
private pem
</PrivateKeyPem>
<PublicKeyPem>
public pem
</PublicKeyPem>
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_ATTRIBUTE" attribute="attribute"/>
<RoleIdentifiers>
<Attribute name="member"/>
</RoleIdentifiers>
<IDP entityID="idp"
signatureAlgorithm="RSA_SHA256"
signatureCanonicalizationMethod="canon"
signaturesRequired="${keycloak-saml-properties.signaturesRequired:false}"
>
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
requestBinding="POST"
bindingUrl="url"
/>
<SingleLogoutService
signRequest="true"
signResponse="false"
requestBinding="REDIRECT"
responseBinding="POST"
postBindingUrl="posturl"
redirectBindingUrl="redirecturl"
/>
<Keys>
<Key signing="true">
<CertificatePem>
cert pem
</CertificatePem>
</Key>
</Keys>
</IDP>
</SP>
</keycloak-saml-adapter>
@@ -57,7 +57,7 @@
<SingleLogoutService
validateRequestSignature="true"
validateResponseSignature="true"
signRequest="true"
signRequest="false"
signResponse="true"
requestBinding="REDIRECT"
responseBinding="POST"
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak SAML Jetty Integration</name>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<name>Keycloak SAML Client Adapter Modules</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak SAML Tomcat Integration</name>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-saml-tomcat-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-saml-tomcat-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-saml-tomcat-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.2.Final</version>
<version>4.0.0.Beta1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
Loaded 100 of 3710 files, more files were not shown because too many files have changed in this diff. Show more