Compare commits

..
1075 Commits
Author SHA1 Message Date
keycloak-bot a035b2c4ed Set version to 4.4.0.Final 2018-09-05 15:47:04 +00:00
Hynek Mlnarik 812e76c39b KEYCLOAK-8163 Improve SAML validations 2018-09-05 15:47:03 +02:00
vramik 8761819b24 KEYCLOAK-8176 fix export issue for required action 2018-09-05 08:40:31 +02:00
Pedro Igor 47066e1b89 [KEYCLOAK-8012] - Fix offline session support in authorization services 2018-09-04 15:07:49 -03:00
Pedro Igor 6a0a1031a1 [KEYCLOAK-7754] - Fixing compat issues with UMA spec in RPT Introspection Provider 2018-09-04 11:41:09 -03:00
Pedro Igor 33efcc6b93 [KEYCLOAK-8142] - Fixing regression when setting path enforcement mode to disabled 2018-09-04 10:32:06 -03:00
Pedro Igor 2eddfd3ea8 [KEYCLOAK-7160] - Completely broken AuthZ Configs in IE 2018-09-04 14:40:14 +02:00
Dmitry Telegin bc8763ccf3 KEYCLOAK-7858 - OIDC servlet filter adapter OSGi support 2018-09-04 11:29:45 +02:00
Hynek Mlnarik 9f839f001f KEYCLOAK-8218 Do not clear SAML REDIRECT query parameters 2018-09-04 11:16:06 +02:00
June Zhang 237318dfd3 KEYCLOAK-7751 Auth welcome page 2018-09-04 07:55:08 +02:00
Hynek Mlnarik 5fe1905e4b KEYCLOAK-6803 Prevent duplicating required actions in JPA user storage 2018-09-03 19:42:18 +02:00
mposolda f0ba8f6591 KEYCLOAK-8139 Added wildfly-deprecated module for adapters testing. Remove wildfly9 and wildfly10 2018-09-03 08:56:09 +02:00
Hynek Mlnarik 54b5ec206e KEYCLOAK-8183 Improve authz caching for negative cases 2018-08-31 18:31:55 +02:00
mposolda befee75917 KEYCLOAK-8174 Admin console test for the "Client Scopes" tab of the client 2018-08-31 12:32:17 +02:00
mposolda 5ae126b10e KEYCLOAK-7584 Fix some admin console tests broken by adding clientScopes support 2018-08-31 12:32:17 +02:00
Johannes Knutsen c0b5c12dee KEYCLOAK-8147: Add support for Content-Security-Policy-Report-Only response headers 2018-08-31 10:38:56 +02:00
vramik 214a8e1fed KEYCLOAK-8176 fix requiredActionsPriority test 2018-08-31 10:25:28 +02:00
vramik f89637bd8f KEYCLOAK-8178 fix AdminEventTest 2018-08-30 15:16:33 +02:00
Hynek Mlnarik bee3894cdf KEYCLOAK-8150 Improve loading user list 2018-08-30 13:03:49 +02:00
vramik df76afb513 KEYCLOAK-8167 fix ExportImportTest on undertow 2018-08-29 15:18:24 +02:00
vramik c266e90a77 KEYCLOAK-6746 ability to skip adapter test 2018-08-29 14:40:44 +02:00
mposolda b70468341e KEYCLOAK-7470 Ability to order client scopes 2018-08-29 14:37:27 +02:00
mhajas 21b71e83dd KEYCLOAK-7161 Stabilize authz tests as they are running on undertow -> in Travis 2018-08-29 13:13:06 +02:00
mhajas ccba07a5c0 KEYCLOAK-7213 Make example tests running on app-server-undertow 2018-08-29 13:13:06 +02:00
Jani 42553cdc44 [KEYCLOAK-7695] Restore token_type and expires_in for implicit flow
As KEYCLOAK-6585 concerns only hybrid flow, this commit restores the behavior for implicit flow.

This commit partially reverts #5041 (061049e41a6b0e6fb45c75f05748023ad7ab7d92).
2018-08-29 13:00:57 +02:00
Hynek Mlnarik 8a7a545628 KEYCLOAK-7944 Remove duplicate offline client sessions 2018-08-29 10:55:38 +02:00
Pavel Drozd d37eb5d10b KEYCLOAK-8138 Fixed tests for product profile 2018-08-29 10:31:10 +02:00
AlistairDoswald 36837ae4b6 Added a ScriptMapper for SAML for KEYCLOAK-5520
Added mapper, tests and entry in the ProtocolMapper file.
This code is adapted from the following module: https://github.com/cloudtrust/keycloak-client-mappers
2018-08-29 09:39:30 +02:00
mposolda 31270e2f52 KEYCLOAK-7437 Support for prompt=consent 2018-08-29 08:35:29 +02:00
Pedro Igor 3ccffdb77e [KEYCLOAK-8145] - Configure keystore when running KeycloakServer using TLS 2018-08-29 08:33:16 +02:00
Johannes Knutsen 56c97407d4 KEYCLOAK-8152: Allow passing the current locale to OAuth2 identity providers 2018-08-28 15:52:23 +02:00
Grzegorz Grzybek bf7a173e1e [KEYCLOAK-7394] Change Content-Type to x-www-form-urlencoded for RealmAdminResource#testLDAPConnection 2018-08-28 04:56:08 -03:00
Hynek Mlnarik 2077975b1c KEYCLOAK-6411 Fix list of keywords on MySQL/MariaDB 2018-08-28 09:51:58 +02:00
mposolda e4d05a7852 KEYCLOAK-8127 Added support for app-server-eap71. Make sure ConsoleProtectionTest is executed just for app-server-eap71 2018-08-27 12:52:53 +02:00
6fc99cd749 KEYCLOAK-7594 Upgrade to Wildfly 13. Cross-DC: Upgrade to infinispan server 9.2.4 and JDG 7.2
Co-authored-by: Douglas Palmer <dpalmer@redhat.com>
Co-authored-by: stianst <stianst@gmail.com>
Co-authored-by: Hynek Mlnarik <hmlnarik@redhat.com>
2018-08-27 12:52:53 +02:00
vramik 01b0b6b345 KEYCLOAK-7975 fix updating execution with Oracle DB 2018-08-24 15:04:48 +02:00
Martin Kanis 59082e0b5f KEYCLOAK-7943 NPE when SAML User Property mapper is empty 2018-08-24 14:39:24 +02:00
vramik 9e072cb174 KEYCLOAK-8119 Migration tests doesn't reflect if authorization features is enabled or not 2018-08-24 14:38:36 +02:00
Pedro Igor c0508aded7 [KEYCLOAK-7757] - Deleting scopes in account console doesn't work 2018-08-24 09:35:52 -03:00
Pedro Igor 9882341ecf [KEYCLOAK-7725] - CORS should be set based on client making the request 2018-08-24 09:35:38 -03:00
Pedro Igor 3c2339ba33 [KEYCLOAK-4902] - Only set effect if result exists and removing ignore from tests 2018-08-24 09:34:39 -03:00
Hynek Mlnařík 457c6a9d80 KEYCLOAK-5226 Test with trusty image 2018-08-24 13:21:51 +02:00
Hynek Mlnarik 1f840b1fbc KEYCLOAK-8109 Fix getDOMElement 2018-08-24 10:03:33 +02:00
mhajas 694966b613 KEYCLOAK-8120 Fix NullPointerException in ClaimInformationPointProviderTest 2018-08-24 09:00:35 +02:00
Dan Hooper 0a8fca7ec4 Created common interface for parsed tokens in typescript declaration file 2018-08-23 16:14:17 -04:00
Frank Schmager 3e2e0ac91c Renamed factory and java doc 2018-08-22 16:39:55 +02:00
Frank Schmager b21e784826 ignoring bin/ directories instead of bin files 2018-08-22 16:39:55 +02:00
Frank Schmager dda365e002 initial exposing of BasicAuthRequestAuthenticator to make extensible 2018-08-22 16:39:55 +02:00
vmuzikar 653d3f4f5d KEYCLOAK-8093 Add IDs to HTML elements in new Account Console 2018-08-22 09:30:20 -04:00
Alex Szczuczko 3dcc106c59 KEYCLOAK-8054 Switch to BOMREST as the source for productized 3rd party versions
This will allow the versions to be kept up to date automatically by DA within PNC.

A side effect is fixing issues like KEYCLOAK-7896.

Also remove additional dependencyExclusions that can be filled by PME BOMREST
2018-08-22 10:24:07 +02:00
Martin Kanis 248654a75e KEYCLOAK-6706 E-mail verification won't let user back into the app 2018-08-21 16:30:15 +02:00
Grzegorz Grzybek fdc9882709 [KEYCLOAK-8101] Return just cached deployment to prevent NPE 2018-08-21 09:56:58 +02:00
Gregor Tudan b606a25684 KEYCLOAK-7991: add pagination params to the RoleResource 2018-08-21 08:19:33 +02:00
rmartinc 1b88eaf817 KEYCLOAK-8080 Audit the realm event configuration change 2018-08-20 21:01:38 +02:00
stianst dc8268b30c Update triggers for Docker Hub 2018-08-20 20:38:57 +02:00
Corentin Dupont b80701589c [KEYCLOAK-7804] - Option to return resource body 2018-08-20 13:07:29 -03:00
Martin Kanis d04791243c KEYCLOAK-7970-KEYCLOAK-7222 Add clientId to action tokens 2018-08-20 15:25:24 +02:00
Wolfgang Zenker c5f861a522 Make cli usable on FreeBSD 2018-08-20 09:08:02 +02:00
Alex Szczuczko b6d545b332 KEYCLOAK-7815 Set product.filename.version based on the value of product.rhsso.version
When `product.filename.version` is hardcoded to 7.2, it obviously doesn't
update when `product.rhsso.version` changes. By using
build-helper-maven-plugin, it's now derived automatically, and we shouldn't see
this issue pop up again for e.g. 7.4.
2018-08-20 09:06:45 +02:00
Alex Szczuczko a35ed671e6 KEYCLOAK-7480 Make fuse7 tomcat8 adapter community-only 2018-08-20 09:06:45 +02:00
Alex Szczuczko f0a2f7a675 KEYCLOAK-7480 Make fuse7 adapter's jetty94 conditional on the community profile
In commit d70859ef keycloak-pax-web-jetty94 was added.

org.keycloak:keycloak-jetty94-adapter:jar is a dependency of this module, and
isn't produced outside of the community profile. So, the jetty94 module here
must be consistent with that.
2018-08-20 09:06:45 +02:00
stianst 4cb9944067 Trigger Docker hub image on merge to master 2018-08-20 09:05:01 +02:00
Pedro Igor 625f613128 [KEYCLOAK-4902] - Using streams to process requested permissions and limit support for scope responses 2018-08-17 11:00:53 -03:00
stianst e406e8f1f0 KEYCLOAK-8069 Simplify config for fixed hostname provider 2018-08-17 14:47:14 +02:00
stianst 90bafee5eb Fixes to examples-dist for removed Fuse example 2018-08-15 16:57:23 +02:00
stianst ab77140454 KEYCLOAK-8036 Fix misplaced IdP buttons 2018-08-15 11:32:02 +02:00
stianst d1ae13c208 Fix product versions 2018-08-15 11:04:18 +02:00
Sebastian Laskawiec e9604a49e1 KEYCLOAK-8046 Prevent from sending Client twice 2018-08-14 17:35:37 +02:00
Hynek Mlnarik 645a72482b KEYCLOAK-8048 Fix testsuite compilation issue 2018-08-14 13:56:45 +02:00
Hiroyuki Wada 730377a843 KEYCLOAK-7528 Set Cache-Control and Pragma header in token endpoint 2018-08-14 11:41:12 +02:00
Stefan Guilhen f36e45cb10 [KEYCLOAK-4902] - Using streams to process scopes and cache improvements 2018-08-14 06:29:10 -03:00
Steffen Kreutz ed72097862 KEYCLOAK-5289 Add support for Google's hd parameter 2018-08-14 11:08:57 +02:00
Stefan Guilhen 0b95cdacb8 [KEYCLOAK-7885] Add user policy support to the policy API 2018-08-13 22:09:17 -03:00
Stefan Guilhen 1912a8acf4 [KEYCLOAK-7885] Fix javadoc/log message typos 2018-08-13 22:09:17 -03:00
vmuzikar 79774d2f07 KEYCLOAK-8035 Fix failing GitLab Social Login test 2018-08-13 08:46:06 -04:00
Erin Recachinas fa8cb004a1 KEYCLOAK-6086 Casting Jetty WebAppContext in Spring Adapter checks validity and unwraps 2018-08-13 11:16:19 +02:00
Sebastian Laskawiec 3449401ae2 KEYCLOAK-7635: Subject DN validation for x509ClientAuthenticator 2018-08-13 09:36:02 +02:00
sebastienblanc 02b2a8aab0 KEYCLOAK-7635 : Authenticate clients with x509 certificate 2018-08-13 09:36:02 +02:00
mposolda 575851d45c KEYCLOAK-6038 Kerberos cross-realm trust test 2018-08-10 13:31:36 +02:00
Stefan Guilhen 060b3b8d0f [KEYCLOAK-4902] - Using streams when fetching resources 2018-08-09 16:28:31 -03:00
Hynek Mlnarik a8a9631d4f KEYCLOAK-6832 Unify Destination attribute handling 2018-08-09 10:30:30 +02:00
vmuzikar 2efc7ebb48 KEYCLOAK-8007 Fix compilation errors in Console UI and WelcomePage tests 2018-08-09 07:26:07 +02:00
Pedro Igor 905fd3ae00 [KEYCLOAK-8003] - Migration to 4.2.1 extracting RESOURCE_URIs fails with fine-grained admin permissions 2018-08-08 11:00:25 +02:00
Martin Kanis ee8c35a48e KEYCLOAK-6119 Create CI job for Postgres with schema 2018-08-08 08:57:11 +02:00
Hynek Mlnarik fb58214fcc KEYCLOAK-7994 Move examples to test-apps 2018-08-08 08:55:38 +02:00
ssilvert@win.redhat.com 4554798cec KEYCLOAK-8002: Add missing TS files to notification component 2018-08-07 15:27:42 -04:00
Pedro Igor 80e5227bcd [KEYCLOAK-4902] - Refactoring and improvements to processing of authz requests 2018-08-07 10:53:40 -03:00
vmuzikar 65f51b7b83 KEYCLOAK-6736 Base UI tests for mobile and desktop browsers 2018-08-07 13:53:31 +02:00
Richard Kolkovich 72750b9882 KEYCLOAK-7954 treat empty string as null for skipping token verification 2018-08-07 11:13:15 +02:00
mposolda 27719565ae KEYCLOAK-4298 Migrate LDAP tests to the new testsuite 2018-08-06 12:08:19 +02:00
Hynek Mlnarik 65030e2c73 KEYCLOAK-7993 Fix failing Fuse 7 tests 2018-08-06 08:22:30 +02:00
wyvie b5d56e2f3b [KEYCLOAK-7838] made tests ordered so they don't fail because of order 2018-08-03 20:52:54 +02:00
mposolda a63676ce93 KEYCLOAK-7989 Running server config migration fails due the Hostname SPI 2018-08-03 13:47:17 +02:00
wyvie 01af40413e [KEYCLOAK-7868] code fixed, unignoring the test 2018-08-02 15:33:31 +02:00
Hynek Mlnarik f6a4ba98de KEYCLOAK-7986 Fix realm definition 2018-08-02 15:32:42 +02:00
Hiroyuki Wada 263792a4ab KEYCLOAK-7984 Fix migration issue 2018-08-02 14:58:20 +02:00
alva.huang 3380fdc119 [KEYCLOAK-7985] fix the table name error, from RESOURCE_URI to RESOURCE_URIS
Signed-off-by: alva.huang <alva@izhiju.cn>
2018-08-02 08:31:44 -03:00
mposolda 959cd035ba Set version to 4.3.0.Final-SNAPSHOT 2018-08-01 22:40:05 +02:00
ssilvert@win.redhat.com e7e15652cf KEYCLOAK-7479: Sanitize 2018-08-01 14:22:39 -04:00
Hynek Mlnarik f57cc3a9c0 KEYCLOAK-5257 Clarify usage of TokenVerifier 2018-08-01 13:38:31 +02:00
mposolda 29da7d3d90 KEYCLOAK-7562 Fix ClientInitiatedAccountLinkTest#testErrorConditions 2018-08-01 13:33:23 +02:00
stianst f99299ee39 KEYCLOAK-7967 Introduce Hostname SPI 2018-08-01 11:57:45 +02:00
stianst ae47b7fa80 KEYCLOAK-7967 Remove injection of UriInfo 2018-08-01 11:57:45 +02:00
mposolda a2afe7c205 KEYCLOAK-7977 Release failing due the NPE during swagger2markup-maven-plugin execution 2018-07-31 22:05:34 +02:00
Cedric Veilleux f32e258a18 i18n : Unicode encoding issue in french email
One accentuated character was missing unicode escaping in french reset email translation.
2018-07-31 21:55:34 +02:00
Takashi Norimatsu 665bcaebbb KEYCLOAK-7959 OAuth 2.0 Certificate Bound Access Tokens in Rev Proxy 2018-07-31 21:53:46 +02:00
Hiroyuki Wada 398f7d950f KEYCLOAK-7910 Store credentials when updating user via Admin REST API 2018-07-31 15:36:21 +02:00
Martin Kanis e6ad0d63a1 KEYCLOAK-7974 Fix typo in remove credentials alert 2018-07-31 15:29:50 +02:00
Martin Kanis f0808d6882 KEYCLOAK-7973 Possibility to add classpath elements to KeycloakServer 2018-07-31 14:32:58 +02:00
Martin Kanis a4fd7e9f7f KEYCLOAK-6308 Disable secret question credentials fails 2018-07-31 12:15:48 +02:00
Hynek Mlnarik 7a978ff3f6 KEYCLOAK-4407 Execute cross-dc tests selectively in travis 2018-07-31 10:30:38 +02:00
mhajas 9b0930a289 KEYCLOAK-7792 Add tests for fragment in redirect URL 2018-07-31 10:24:58 +02:00
Takashi Mogi 959e7b1b01 KEYCLOAK-7201 OIDC Identity Brokering with Client parameter forward
Forward "custom" (non-standard) query parameters to external IDP
2018-07-31 10:18:29 +02:00
ssilvert@win.redhat.com 40cc826586 Fix test side effect. 2018-07-30 13:15:02 -04:00
ssilvert@win.redhat.com 6c593bab5a Check credential confirmation on server side. 2018-07-30 13:15:02 -04:00
Timothy Lee 9c18d0e628 remove a redundant div end tag 2018-07-30 18:41:03 +02:00
vramikandHynek Mlnarik ecd3fcc0af KEYCLOAK-7924 Speed-up crossdc tests
Co-Authored-By: Hynek Mlnarik <hmlnarik@redhat.com>
2018-07-27 20:53:58 +02:00
vramik 38017d3cec KEYCLOAK-4407 Ability to restart arquillian containers from test
Co-Authored-By: Hynek Mlnarik <hmlnarik@redhat.com>
KEYCLOAK-4407 Fix connection error if underlying container restarts (63b9da857a8174a0b5e65e70c47ef2e2842f4d4e)
2018-07-27 20:53:58 +02:00
Tair Sabirgaliev d88568266f KEYCLOAK-7821 Enable tomcat-specific features: * (all roles), ** (authenticated user) in authRoles constraint 2018-07-27 14:24:49 +02:00
Hynek Mlnarik f43519a16e KEYCLOAK-6708 Fix NPE when email not set for email NameIDFormat 2018-07-27 11:10:35 +02:00
fisache 771d7f1724 [KEYCLOAK-7872] Fix. Remove Identity Provider Mapper when remove identity provider 2018-07-26 08:45:26 +02:00
ssilvert@win.redhat.com 0844aa8d68 KEYCLOAK-7857: Fix notifications 2018-07-25 08:59:25 -04:00
ssilvert@win.redhat.com d73c4288ae KEYCLOAK-7294: Password page - Angular 2018-07-25 08:59:25 -04:00
vramik 9c1a411c6e KEYCLOAK-7310 Add migration test from 3.4.x to 4.x 2018-07-25 13:48:02 +02:00
Tomas Kyjovsky 8eabd4ae89 KEYCLOAK-7934 Dataset generator cannot create "empty" mappings 2018-07-24 23:53:48 +02:00
vramik 524ab44160 KEYCLOAK-6866 Error 404 after changing locale while authenticating using X.509 2018-07-24 17:24:32 +02:00
Tomas Kyjovsky ea8eaaff07 KEYCLOAK-7599 Improve handling of test datasets 2018-07-24 15:07:32 +02:00
mhajas a6e4f4f9aa KEYCLOAK-7922 Use Time.currentTimeMillis() instead of System.currentTimeMillis() in PathCache 2018-07-24 08:52:48 -03:00
Daniil Filippov af72c1374a KEYCLOAK-7823 Fix HTTP status returned during SPNEGO auth 2018-07-24 10:38:42 +02:00
Hiroyuki Wada 7c0ca9aad2 KEYCLOAK-6313 Add required action's priority for customizing the execution order 2018-07-23 22:21:04 +02:00
Hynek Mlnarik b43392bac8 KEYCLOAK-6577 KEYCLOAK-5609 Support dot in claim names by escaping with backslash 2018-07-23 14:46:25 +02:00
Hynek Mlnarik c8bc0d6d7b KEYCLOAK-7400 Remove dead code
This commit can only be merged once the Camel 2.21.2 would be
released, otherwise the code won't compile due to missing dependencies.

See https://issues.apache.org/jira/browse/CAMEL-12514 for details.
2018-07-23 14:46:00 +02:00
Peter Zaoral c4b375c1fc KEYCLOAK-7802 Fix broken HoKTest
Signed-off-by: Peter Zaoral <pzaoral@redhat.com>
2018-07-23 12:30:54 +02:00
mhajas 1308a3231d KEYCLOAK-7931 Correct wrong JPA changelog filenames 2018-07-23 11:49:54 +02:00
pskopek 2eefd60344 [KEYCLOAK-7898] Align versions to match EAP 7.1.4.CR1 2018-07-20 21:32:48 +02:00
sebastienblanc 5472364265 fixed the bom 2018-07-20 10:02:20 +02:00
Hynek Mlnarik 26bf7f251d KEYCLOAK-7888 Update Fuse adapter examples to new CXF registration 2018-07-19 15:22:42 +02:00
Pedro Igor 7c14a6a503 [KEYCLOAK-6547] - AuthenticatedActionsHandler should process responses after identity is established 2018-07-19 10:05:04 -03:00
Pedro Igor acc5f5c6d1 [KEYCLOAK-7864] - Authorization claim not set in refresh token when issuing a new refresh token 2018-07-19 09:56:59 -03:00
Grzegorz Grzybek 2cb7ec9432 [KEYCLOAK-7703] HierarchicalPathBasedKeycloakConfigResolver for more fine/coarse grained Keycloak configuration in Karaf 2018-07-19 14:25:52 +02:00
Pedro Igor 8b6979ac18 [KEYCLOAK-7849] - Improvements to RPT upgrade 2018-07-18 16:40:55 -03:00
Martin Kanis 34407957b9 KEYCLOAK-6314 Internal server error after T&C rejection 2018-07-18 15:05:22 +02:00
Jannik Hüls 44f5cf3020 KEYCLOAK-6707 change verfiyEmail tooltip 2018-07-18 09:14:41 -03:00
vramik 54fcbf12b0 KEYCLOAK-7666 - adapter tests - eap6-fuse6 provider 2018-07-18 13:46:56 +02:00
wyvie 31f6cf9ab5 [KEYCLOAK-7868] fix correct condition for spring boot tests
Also ignore the AccountLinkSpringBootTest#testErrorConditions for now
until we have a clue why it does not work as intended
2018-07-18 13:46:07 +02:00
Gregor Tudan 3417b569c0 KEYCLOAK-2606: add support for native browsers on cordova
KEYCLOAK-2606 Added cordova native

KEYCLOAK-2606 Some more fixes and tweaks

Fix redirect in example realm

feature(cordova-native): fix universalLinks and kc options

Added 'cordova-native' to typings

Added an option to define a "default" redirectUri in keycloak.js

Added 'login' and 'logout' event to universalLinks configuration in config.xml

Improved 'cordova-native' example to always use a redirectUri and
update state after successfull logout

Setting the 'authenticated' flag for the keycloak instance to 'false'
after a logout redirect

KEYCLOAK-2606: Simplify example for cordova-native

I wanted to make it explicit which options are actually needed, so I didn't want to reuse the keycloak conf

KEYCLOAK-2606: simplify example

The update state after logout shouldn't be necessary as it is set in `keycloak.onAuthLogout = updateState;`
Not sure why it is called after the login promise...

Fixes
2018-07-18 10:51:59 +02:00
vramik 8e20986335 KEYCLOAK-7876 Improve stability of fuse7 hawtio test 2018-07-18 10:51:32 +02:00
ssilvert@win.redhat.com 385e1c6122 KEYCLOAK-7846: Add missing features.ts file 2018-07-17 12:44:06 -04:00
ssilvert@win.redhat.com 3e158c0321 KEYCLOAK-7846: Turn off disallowed features 2018-07-17 12:44:06 -04:00
ssilvert@win.redhat.com c969c4c903 Cleanup console.log 2018-07-17 12:43:20 -04:00
ssilvert@win.redhat.com 8533bc7f00 KEYCLOAK-7795: "Back to <app>" missing from Welcome Page 2018-07-17 12:43:20 -04:00
ssilvert@win.redhat.com 2adff375db KEYCLOAK-7497: Remove babel transpiler. Use UMD files from
patternfly-ng.
2018-07-17 12:42:14 -04:00
ssilvert@win.redhat.com d959e501ae KEYCLOAK-7524: Vertical Nav doesn't close on secondary click 2018-07-17 12:42:14 -04:00
Phani Kiran Thaticharla 886362ebf2 Fixed typo in misc/Testsuite.md 2018-07-17 10:05:05 +02:00
wyvie 8e221ea597 [KEYCLOAK-7835] BrokerLinkAndTokenExchangeTest turned off
Until TOKEN_ECHANGE is enabled (means currently turned off
for prod profile)
2018-07-16 10:27:56 +02:00
mhajas 432ea277a7 KEYCLOAK-7816 Assume preview profile in authz tests 2018-07-13 12:53:46 -03:00
Martin Kanis b520dda3ef KEYCLOAK-4662 Keycloak adapter missing configuration attribute proxy-url 2018-07-13 14:30:40 +02:00
Pedro Igor 90bfa2bff5 [KEYCLOAK-7781] - More validations to authorization requests 2018-07-13 09:18:05 -03:00
Pedro Igor 3fc6eed1ce [KEYCLOAK-7827] - Missing org.jboss.logging dependency to keycloak-authz-client module 2018-07-13 09:17:49 -03:00
Juravenator fbbc670d1f KEYCLOAK-7854: Tiny typo in ExampleServiceImpl.java 2018-07-13 09:11:17 -03:00
stianst f022bc1269 [KEYCLOAK-5629] Add credential endpoints to account service 2018-07-12 13:00:25 -04:00
mhajas 5aebc74f8c KEYCLOAK-7269 Setting more uris for Authorization Resource 2018-07-11 17:48:34 -03:00
Tommy Cheng fba2bf0b2f [KEYCLOAK-7822] - Fix access token and refresh token timeout in resource server 2018-07-11 15:13:50 -03:00
Pedro Igor 55550f2023 [KEYCLOAK-6547] - AuthenticatedActionsHandler should process responses after identity is established 2018-07-11 11:33:31 -03:00
vramik 401a347f6f KEYCLOAK-7817 Update eap6.version in testsuite 2018-07-11 15:28:33 +02:00
vmuzikar 0432a566dd KEYCLOAK-7805 Fix PayPal and Bitbucket Social Login tests 2018-07-11 10:19:24 +02:00
rmartinc 4a82979792 KEYCLOAK-1925: SAML adapter multitenant support 2018-07-10 13:21:11 +02:00
Sebastian Laskawiec 3918dbed59 KEYCLOAK-2886 Turn off clustered tests from IDE 2018-07-10 12:37:21 +02:00
zhutaoredhat b80b480769 Fixes KEYCLOAK-7535 - reupdate resource html 2018-07-06 07:40:41 -03:00
mposolda d0a824dde4 Updating version to 4.2.0.Final-SNAPSHOT 2018-07-05 07:42:48 -04:00
mposolda 01da592f77 KEYCLOAK-7769 Outdated tabs on realm-keys-generic.html 2018-07-04 10:42:30 +02:00
mposolda 40d129cf54 KEYCLOAK-7489 Replace failed for entity repeats infinitely 2018-07-04 10:22:49 +02:00
mposolda 8c66f520af KEYCLOAK-7745 JTA error if offline sessions can't be preloaded at startup within 5 minutes 2018-07-04 10:22:13 +02:00
vmuzikar 64b391cc1b KEYCLOAK-7761 Fix Instagram Social Login test 2018-07-04 09:00:54 +02:00
vramik 8a37395760 KEYCLOAK-7722 Move eap6 specific config files to its module 2018-07-04 08:56:24 +02:00
sebastienblanc f5d00ddffb making Spring Boot 2 the default starter 2018-07-03 22:04:16 +02:00
Pedro Igor dafd567e68 [KEYCLOAK-7763] - NPE when enabling authorization to security-admin-console 2018-07-03 13:18:53 -03:00
ssilvert@win.redhat.com d55ccf5312 KEYCLOAK-7015: Not allowing two users to have empty string emails addrs. 2018-07-03 11:04:36 -04:00
Pedro Igor 871be4ad87 [KEYCLOAK-7764] - Error when processing resource-less permissions 2018-07-03 10:35:11 -03:00
Pedro Igor 6f3c59e086 [KEYCLOAK-7062] - Groups claim should be optional 2018-07-03 10:03:20 -03:00
June Zhang fc0d510e85 Fixes KEYCLOAK-7689 Authenticator - Mobile Setup HTML 2018-07-03 08:58:17 -04:00
vramik 742a280f5d KEYCLOAK-5556 support for POST for AuthorizationEndpoint 2018-07-03 10:38:10 +02:00
vramik 87126704cd KEYCLOAK-7746 remove dead code 2018-07-03 10:35:11 +02:00
wyvie 1450a7fad4 [KEYCLOAK-7569] support for authentication flow update
Added support for the PUT method of the authentication flow endpoint in
the admin API.

Now it's possible to run the 'update' method for authentication/flows in
kcadm.sh.
2018-07-03 10:31:23 +02:00
ssilvert@win.redhat.com 65551159e0 KEYCLOAK-7634: Export error on client if no mappers 2018-07-03 08:54:40 +02:00
vmuzikar d99dca2db3 KEYCLOAK-7743 Fix broken X.509 tests 2018-07-02 12:42:50 +02:00
vmuzikar 3355399b4e KEYCLOAK-7741 Fix broken test modules 2018-06-29 10:17:06 -03:00
Pedro Igor dcadc61220 [KEYCLOAK-7670] - PEP not returning correct status code when authorization header is not set 2018-06-29 09:39:55 -03:00
stianst 3c5027de3c KEYCLOAK-7701 Refactor key providers to support additional algorithms 2018-06-29 14:14:25 +02:00
Hiroyuki Wada a5d155a35a KEYCLOAK-7674 Fix colspan 2018-06-28 18:39:10 -03:00
zhutaoredhat 05bbd9ace3 Fixes KEYCLOAK-7535 - My Resources - HTML update
Updated the responsive status to make the page looks much more clear.
This PR is to provide HTML and CSS code
2018-06-28 13:08:17 -04:00
vramik 559a8b2a2c KEYCLOAK-7588, KEYCLOAK-7589 - update HOW-TO-RUN 2018-06-28 16:27:38 +02:00
vramik c97e7e720e KEYCLOAK-7550 - adapter tests - Fuse7.1 provider 2018-06-28 16:24:02 +02:00
Pedro Igor f10c47955f [KEYCLOAK-7427] - Fix to support writing to response when doing programmatic logouts 2018-06-28 11:08:28 -03:00
Pedro Igor 23db2b852b [KEYCLOAK-7679] - Wildfly adapter must be disabled when using Elytron 2018-06-28 11:08:28 -03:00
vramik 591093f867 KEYCLOAK-7730 - revert OSGiApplicationArchiveProcessor moved into fuse app servers 2018-06-28 10:22:25 -03:00
vramik 9039b44f4d KEYCLOAK-7718 DemoFilterServletAdapterTest test not configured correctly 2018-06-28 09:33:52 -03:00
stianst 5f0c86a49f KEYCLOAK-6663 Add test to check custom uri scheme in redirect URI 2018-06-28 11:14:05 +02:00
vramik 8ac7bda52c KEYCLOAK-7589 - adapter tests - Fuse7.0 provider 2018-06-28 08:45:02 +02:00
Martin Kanis 0e2e867e4a KEYCLOAK-5023 database migration fails if mysql schema name has hyphen 2018-06-27 10:11:09 +02:00
stianst 0d9ccba566 Some work on deprecated testsuite migration 2018-06-27 08:16:14 +02:00
zhutaoredhat 30e8071d1d Fixes KEYCLOAK-7535 - My Resources - HTML
This PR is to provide HTML and CSS code
JIRA task: https://issues.jboss.org/browse/KEYCLOAK-7535
2018-06-26 13:30:08 -04:00
vramik 39cbf4e9ab KEYCLOAK-7588 - adapter tests - Fuse6.3 provider 2018-06-26 16:47:01 +02:00
Jean-Loup Maillet af47bd5da8 corrected groups set to be able to add group & explicit imports 2018-06-26 13:30:44 +02:00
J-Loup 0ee5c97b1c Tooltip correction on group selection
Tooltip correction on group selection for hardcoded-ldap-group-mapper
2018-06-26 13:30:44 +02:00
Jean-Loup Maillet d07f13eace hardcoded-ldap-group-mapper 2018-06-26 13:30:44 +02:00
Johannes Knutsen fc3ca33033 Set hardcoded user session attribute after IDP first login flow 2018-06-26 10:31:55 +02:00
Takashi Norimatsu 2fb022e501 KEYCLOAK-7688 Offline Session Max for Offline Token 2018-06-26 08:25:06 +02:00
vramik b478472b35 KEYCLOAK-7478 Add key query param to change locale url 2018-06-26 08:19:25 +02:00
vramik 8fdadcc596 KEYCLOAK-7475 adapter tests - add Wildfly10 and Wildfly9 providers 2018-06-25 14:31:11 +02:00
June Zhang ca06043b00 Fixes KEYCLOAK-7533 Authenticator List page - HTML 2018-06-22 15:43:24 -04:00
Grzegorz GrzybekandHynek Mlnarik 3c9d3c2c04 [KEYCLOAK-7681] Review pax-web OSGi dependencies
Co-Authored-By: Hynek Mlnarik <hmlnarik@redhat.com>
2018-06-22 13:47:20 +02:00
Martin Kanis 998227ac53 KEYCLOAK-5461 Upgrade to Liquibase 3.5.5 2018-06-22 13:20:10 +02:00
Oskars Pakers 30a066db41 [master]: Fix translation for allowed-clock-skew 2018-06-22 11:45:35 +02:00
vramik d9f79fae79 KEYCLOAK-7510 Add Support for server specific ArchiveProcessor 2018-06-22 11:38:57 +02:00
Hynek Mlnarik 530a710dce KEYCLOAK-7412 Tests for Fuse 7.0 2018-06-22 08:59:44 +02:00
Thomas Darimont f5ca4840d6 Fix typo (TThis) 2018-06-22 06:43:51 +02:00
Hynek Mlnarik d3a6b6f587 KEYCLOAK-7678 Remove osgi-thirdparty module 2018-06-21 15:35:03 +02:00
Grzegorz Grzybek f90e0fc14c [KEYCLOAK-7425] Correctly import packages of httpclient and http core, fix Karaf feature 2018-06-21 14:28:47 +02:00
Hynek Mlnarik 6b968796ce KEYCLOAK-7667 Fix namespace handling when decrypting assertion 2018-06-21 13:09:18 +02:00
zhutaoredhat 1277f8bb3b Fixes KEYCLOAK-7384 - Linked Accounts HTML Update
This PR is to provide HTML and CSS code
JIRA task: https://issues.jboss.org/browse/KEYCLOAK-7384
2018-06-20 21:30:27 -04:00
ssilvert@win.redhat.com 63a6ba53df KEYCLOAK-7657: My Resources placeholder components. 2018-06-19 16:28:17 -04:00
ssilvert@win.redhat.com 9af51fb664 KEYCLOAK-7662: Placeholder components for authenticator. 2018-06-19 15:56:37 -04:00
Hiroyuki Wada c2012a595b KEYCLOAK-7650 Don't display disabled identity providers 2018-06-19 08:55:24 -04:00
vramik 2fcfa5cf71 KEYCLOAK-7094 Support redirect to external logout page for saml filter adapter 2018-06-19 13:23:18 +02:00
ssilvert@win.redhat.com e638391182 KEYCLOAK-7330 KEYCLOAK-7439 KEYCLOAK-7503 404-error on Token tab 2018-06-18 15:12:21 -04:00
stianst e98fcfe56e KEYCLOAK-7641 Add details on how to build server-only, including some fixes to poms 2018-06-18 10:38:15 +02:00
June Zhang 2f366b0d5d Fixes KEYCLOAK-7637 UI background color issue 2018-06-15 14:19:38 -04:00
stianst e1a0e581b9 Update to 4.1.0.Final-SNAPSHOT 2018-06-14 14:22:28 +02:00
Dennis Bayer c43d8b3d85 Get role list to update directly from the security context rather than from previously created subject info.
Roles within groups must be unpacked added separately in order to contain all roles.
2018-06-13 21:03:30 -03:00
Dennis Bayer 6a2a121d4e Map group members of authenticated subject into subject info.
This commit contains a POC for the issue "Roles get lost after security context was propagated back to wildfly-swarm, if using jwt for authentication" (KEYCLOAK-7309).
2018-06-13 21:03:30 -03:00
Hynek Mlnarik 9dc5709ce7 KEYCLOAK-7593 Setters for httpContext 2018-06-13 16:32:39 +02:00
vramik ccb09fbf45 KEYCLOAK-7616 fix NPE for UserStorageConsentTest 2018-06-13 15:53:41 +02:00
Hynek Mlnarik fb883254f6 KEYCLOAK-7434 2018-06-13 14:31:44 +02:00
vramik 5f1f3dff5e KEYCLOAK-7094 Support redirect to external logout page for elytron adapter 2018-06-13 12:50:38 +02:00
Pedro Igor dd93de75d9 [KEYCLOAK-7579] - Fixing test to use client scopes instead of old scope param required (#5259) 2018-06-12 15:44:03 -03:00
Stefan Guilhen d897159560 [KEYCLOAK-7598] - Set CIP config when defining paths in policy enforcer config (#5264) 2018-06-12 11:24:17 -03:00
Vlasta Ramik 182c975e01 KEYCLOAK-7597 fix logger classes (#5263) 2018-06-12 11:02:04 -03:00
vramik f19a324030 KEYCLOAK-7587 Some system properties are not included 2018-06-12 11:42:10 +02:00
vramik 9cf965a157 Ignore non-related failing ClientInitiatedAccountLinkTest#testErrorConditions 2018-06-11 13:46:59 +02:00
vramik 9e42be09d7 KEYCLOAK-7517 - adapter tests - EAP6 provider 2018-06-11 13:46:59 +02:00
vramik 7d466be94e KEYCLOAK-6541 rename AppServerContainerSPI to AppServerContainerProvider 2018-06-11 13:46:59 +02:00
vramik a5c0cbc3b4 KEYCLOAK-7473 app-server-eap provider 2018-06-11 13:46:59 +02:00
vramik bb5dc4c473 KEYCLOAK-6745 Adapter tests - remove abstract adapter test classes 2018-06-11 13:46:59 +02:00
vramik 132386f64d KEYCLOAK-6541 app server wildfly provider 2018-06-11 13:46:59 +02:00
vramik b0c89d739b KEYCLOAK-6541 app server undertow support 2018-06-11 13:46:59 +02:00
vramik 6a07a7ed2c KEYCLOAK-6541 base changes 2018-06-11 13:46:59 +02:00
vramik f293ab86c3 KEYCLOAK-7267 change a way how to install adapters in testsuite from online to offline scripts 2018-06-11 13:46:59 +02:00
Pedro Igor db60abc604 [KEYCLOAK-7543] - Policy enforcer should not delegate decisions when using UMA (#5252) 2018-06-11 08:17:40 -03:00
Lorent Lempereur f55c93a1e4 Javascript Adapter - Add 'cordovaOptions' to the Typescript definition of KeycloakLoginOptions (#5250) 2018-06-11 08:21:04 +02:00
Grzegorz Grzybek 08296bd3ce [KEYCLOAK-7508] Fix a typo: resouces -> resources (#5243) 2018-06-11 08:18:32 +02:00
Grzegorz Grzybek fca6da3a5a KEYCLOAK-7523 better context path detection in PathBasedKeycloakConfigResolver 2018-06-08 21:32:14 +02:00
Marek Posoldaandvramik 49407c2e4f KEYCLOAK-6630 Client scopes initial support (#5076)
* KEYCLOAK-6630 KEYCLOAK-349 Client Scopes

Co-authored-by: vramik <vramik@redhat.com>

* KEYCLOAK-6630 Change some clientTemplate occurences to clientScope
2018-06-08 15:38:38 +02:00
Vlasta Ramik 4d1474afe0 KEYCLOAK-7549 Update artifactId according to latest Fuse7 build (#5253) 2018-06-08 15:15:34 +02:00
Pedro Igor aa128d6c07 Merge pull request #5240 from pedroigor/KEYCLOAK-7353
[KEYCLOAK-7353] Support Policy Management in Protection API
2018-06-07 11:05:49 -03:00
Ola Bergefall c8c76cc03f KEYCLOAK-7316: Default back to false if isPassive is missing in request. 2018-06-07 08:50:32 +02:00
Federico M. Facca 5a9bfea419 [KEYCLOAK-7353] Support Policy Management in Protection API
See https://issues.jboss.org/browse/KEYCLOAK-7353
2018-06-06 19:36:42 -03:00
vramik dffe70e40a KEYCLOAK-7518 DeploymentArchiveProcessor assumes that every archive contains jboss-deployment-structure.xml file 2018-06-06 21:15:39 +02:00
Tomas Kyjovsky 1b4d9a6147 KEYCLOAK-7440 Modcluster configuration for functional cluster test is broken 2018-06-06 20:40:50 +02:00
Stan Silvert b7fb3ec9a9 KEYCLOAK-7513 Minor changes to vertical nav (#5244) 2018-06-06 14:39:00 -04:00
Hynek Mlnarik 7ff18ca14b KEYCLOAK-7331 Fix NPE when SAML Issuer not set in AuthnRequest 2018-06-06 16:21:18 +02:00
Your Name 6052b1546d removed LANG-dependent check #7444 2018-06-06 12:53:58 +02:00
Hynek Mlnarik 5a241392cf KEYCLOAK-7094 Support redirect to external logout page 2018-06-05 14:51:18 +02:00
Takashi Norimatsu c586c63533 KEYCLOAK-6771 Holder of Key mechanism
OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound Access
Tokens
2018-06-05 08:18:29 +02:00
Pedro Igor f8919f8baa Merge pull request #5211 from pedroigor/KEYCLOAK-7367
[KEYCLOAK-7367] - User-Managed Policy Provider
2018-06-04 09:35:13 -03:00
Stan Silvert f45ed1613e KEYCLOAK-7334: Update vertical na/Integrate patternfly-ng (#5241) 2018-06-04 07:53:37 -04:00
Pedro Igor bc665fdbc3 Merge pull request #5195 from suem/bugfix_FilterSessionStore
NullPointerException in FilterSessionStore when restoring request
2018-06-01 10:51:28 -03:00
Pavel Drozd 6c3e6bc90c KEYCLOAK-7476 - sshLoginTest - changed command result 2018-06-01 10:35:38 +02:00
Stan Silvert cb3b7f3cb2 KEYCLOAK-5934: Disable paste for event filters (#5026) 2018-06-01 10:28:56 +02:00
June Zhang 5e8db32329 Fixes KEYCLOAK-7382 - Application Response HTML Update (#5235) 2018-05-31 13:18:25 -04:00
Jared Blashka 65c39763eb KEYCLOAK-7356 Code to Token flow fails if initial redirect_uri contains a session_state parameter 2018-05-31 08:53:11 +02:00
Martin Kanis f429469fc8 KEYCLOAK-5270 Realm cookie path for IE<=11 users (#5106) 2018-05-31 08:44:34 +02:00
Pedro Igor 6339b62f8d Merge pull request #5219 from tkyjovsk/KEYCLOAK-6759
KEYCLOAK-6759 Create the databases for import during perf tests
2018-05-30 09:51:21 -03:00
Pedro Igor 473e9ab55c Merge pull request #5227 from pedroigor/KEYCLOAK-7449
[KEYCLOAK-7449] - When clicking link or actions the row is expanded
2018-05-29 14:38:23 -03:00
June Zhang 77e503f5ca Fixes KEYCLOAK-7381 Device Activity Response - HTML update (#5230) 2018-05-29 09:30:48 -04:00
Johannes Knutsen c64ecb1ab8 Remove expired objects from cache cluster instead of local only 2018-05-29 10:27:43 +02:00
Rick van den Hof 2e22dcfc47 Add unit tests 2018-05-29 10:03:54 +02:00
Rick van den Hof 16fd6558a6 Enable adding of default groups 2018-05-29 10:03:54 +02:00
Takashi Norimatsu eb97151476 KEYCLOAK-7451 OAuth Authorization Server Metadata for Proof Key for Code Exchange 2018-05-28 22:15:43 +02:00
Tomas Kyjovsky b8adee4004 KEYCLOAK-6759 Create the databases for import during perf tests 2018-05-28 17:52:27 +02:00
Pedro Igor 65f5eacdd4 [KEYCLOAK-7449] - When clicking link or actions the row is expanded 2018-05-25 17:57:03 -03:00
Pedro Igor 2b6597e9f1 [KEYCLOAK-7367] - User-Managed Policy Provider 2018-05-25 16:18:15 -03:00
Hynek Mlnarik 6a2f73df9c KEYCLOAK-7433 Update licenses 2018-05-25 11:27:51 +02:00
Hynek Mlnarik 60a03d0293 KEYCLOAK-7434 Align versions to match EAP 7.1.3 2018-05-25 11:27:51 +02:00
Hynek Mlnarik dd6735285d KEYCLOAK-7438 Don't create symlinks in download-license-files.sh 2018-05-25 09:50:41 +02:00
Stian Thorgersen dbf5c395b0 Bump version to 4.0.0.Final (#5224) 2018-05-24 19:02:30 +02:00
Hynek Mlnarik 8261ffeeeb KEYCLOAK-7378 Fix missing OSGi import for Fuse 6.3 JAX-RS example app 2018-05-23 13:14:07 +02:00
Hynek Mlnarik 1e438cdc45 KEYCLOAK-7277 KEYCLOAK-7282 Tomcat 8/Pax Web integration 2018-05-23 13:14:07 +02:00
Hynek Mlnarik d70859ef1b KEYCLOAK-7277 KEYCLOAK-7282 Jetty/Pax Web integration 2018-05-23 13:14:07 +02:00
Hynek Mlnarik 3ab8ff2ea1 KEYCLOAK-5522 Features and distibution 2018-05-23 13:14:07 +02:00
Hynek Mlnarik adea414695 KEYCLOAK-7279 Camel example 2018-05-23 13:14:07 +02:00
Hynek Mlnarik cace03c3cc KEYCLOAK-7279 Camel/Undertow integration 2018-05-23 13:14:07 +02:00
Hynek Mlnarik d55b1d7259 KEYCLOAK-7280 CXF JAX-WS example 2018-05-23 13:14:07 +02:00
Hynek Mlnarik 78e963688c KEYCLOAK-7278 CXF JAX-RS example 2018-05-23 13:14:07 +02:00
Hynek Mlnarik b2df872ad4 KEYCLOAK-7278 KEYCLOAK-7280 CXF/Undertow integration 2018-05-23 13:14:07 +02:00
Hynek Mlnarik fea4201f46 KEYCLOAK-7282 Pax Web Whiteboard Extender (Product portal app) 2018-05-23 13:14:07 +02:00
Hynek Mlnarik 073a2f4d65 KEYCLOAK-7277 External config example 2018-05-23 13:14:07 +02:00
Hynek Mlnarik 06df678d33 KEYCLOAK-7277 Pax Web Extender WAR example (Customer portal app) 2018-05-23 13:14:07 +02:00
Hynek Mlnarik dd65c231f9 KEYCLOAK-7277 KEYCLOAK-7282 Undertow/Pax Web integration 2018-05-23 13:14:07 +02:00
Hynek Mlnarik ae690e0679 KEYCLOAK-5522 Base for Fuse 7 adapter 2018-05-23 13:14:07 +02:00
Pedro Igor 06f108df3b Merge pull request #5213 from pedroigor/KEYCLOAK-7082
[KEYCLOAK-7082] - Making lists more aligned with patternfly listview component
2018-05-22 10:52:16 -03:00
Pedro Igor a8319d1131 Merge pull request #5204 from pedroigor/KEYCLOAK-7360
[KEYCLOAK-7360] - Both time and rules policy not showing number fields
2018-05-22 08:12:43 -03:00
Pedro Igor f800290d89 [KEYCLOAK-7082] - Making lists more aligned with patternfly listview component 2018-05-21 10:54:13 -03:00
June Zhang e79de47f5e Fixes KEYCLOAK-7348 Password/Personal Info Response Design - HTML Update (#5209) 2018-05-21 07:59:34 -04:00
Pedro Igor 08c22416a2 Merge pull request #5208 from ASzc/KEYCLOAK-7362
KEYCLOAK-7362 Disable mvn-golang-wrapper in the product build
2018-05-18 13:51:17 -03:00
Pedro Igor dac5d313b3 Merge pull request #5166 from pedroigor/KEYCLOAK-7021
[KEYCLOAK-7021] - keycloak-authz.js and keycloak-authz.d.ts do not work with TypeScript
2018-05-17 17:42:51 -03:00
Pedro Igor 21d139c6c2 Merge pull request #5173 from pedroigor/KEYCLOAK-7148
[KEYCLOAK-7148] - Associate sub resources to a parent resource
2018-05-17 16:51:55 -03:00
Pedro Igor e5d997a6c0 Merge pull request #5203 from martel-innovate/separate-ticket-permission-and-uma-permission-API
[KEYCLOAK-7354] - Split ticket management and permission endpoint
2018-05-17 15:22:55 -03:00
Martin Kanis a2e54dc94f Merge pull request #5207 from ASzc/KEYCLOAK-7361
KEYCLOAK-7361 Add `!community` profile to prod-arguments.json
2018-05-17 15:00:53 +02:00
Martin Kanis 28040109ac Merge pull request #5210 from vramik/KEYCLOAK-6991-fix-test-not-to-affect-other-tests
KEYCLOAK-6991 fixed ExportImportTest not to affect other tests
2018-05-17 12:08:45 +02:00
vramik 39b6bf62ba KEYCLOAK-6991 fixed ExportImportTest not to affect other tests (PermissionsTest, RealmTest) on auth-server-undertow 2018-05-17 11:07:29 +02:00
Alex Szczuczko 6a19a8fb7e KEYCLOAK-7362 Disable mvn-golang-wrapper in the product build
com.igormaznitsa:mvn-golang-wrapper is a new plugin used by the testsuite. It
depends on arbitrary internet resources, including github and
storage.googleapis.com. This isn't permissible in a product build, and so PNC
blocks it.

As a short-term solution, I've added a product profile the sets the skip
parameter to true for this plugin. Other approaches, like changing the phase of
the get goals, didn't work.
2018-05-16 16:53:39 -06:00
Alex Szczuczko e914b6fe8d KEYCLOAK-7361 Add !community profile to prod-arguments.json
This causes the community profile to be disabled in the product build.

The product profile is active already, but unfortunately maven doesn't allow
chained profile activation, so there's no way to automatically disable the
community profile when the product profile is active.

An alternative solution would have been to add `-Dproduct` to the properties
list, but then it wouldn't be clear what was trying to be done with the extra
argument.

I've checked with `git grep -B3 '!product'`, and the only profiles that use the
`!product` property activation condition are the community profiles.
2018-05-16 13:56:48 -06:00
Bill Burke e5a6dbd77a Merge pull request #5192 from ttaylor249/master
Force the mvn-golang plugin to use the maven proxy
2018-05-16 09:47:01 -04:00
Pedro Igor 343dc05f82 [KEYCLOAK-7360] - Both time and rules policy not showing number fields 2018-05-16 10:38:56 -03:00
Federico M. Facca 76076cdb3c [KEYCLOAK-7354] split ticket management and permission endpoint
see (https://issues.jboss.org/browse/KEYCLOAK-7354)

* created new endpoint for ticket management /permission/ticket
* removed unused class
* support for direct creation of ticket by resource owner
* fix DELETE ticket
2018-05-16 15:10:39 +02:00
Timo Knapp 487539542a KEYCLOAK-7325: Fix Issue regarding HTTP 500 Server Error for resource_set Endpoint in ProtectionService (#5196)
* KEYCLOAK-7325: Fix Issue regarding HTTP 500 Server Error for resource_set Endpoint in ProctectionService
2018-05-15 14:57:33 -03:00
Pedro Igor 1634bef28a Merge pull request #5194 from pedroigor/KEYCLOAK-7322
[KEYCLOAK-7322] - NPE when removing group from representation
2018-05-15 06:05:54 -03:00
Pedro Igor 6882f7a9c0 Merge pull request #5200 from martel-innovate/KEYCLOAK-7337
[KEYCLOAK-7337] Implement return of requester in UMA permissions
2018-05-14 09:07:48 -03:00
June Zhang 943c8507f7 Fixes KEYCLOAK-7247 HTML for responsive welcome page (#5197) 2018-05-14 07:20:25 -04:00
vramik 99119562bb KEYCLOAK-7312 removed as7, wf8 test modules 2018-05-14 09:10:05 +02:00
Federico M. Facca 5cbe595fe3 This commit implement feature KEYCLOAK-7337
* return requester

when returnNames=true

* return requesterName
* return owernName
2018-05-11 21:08:16 +02:00
June Zhang eb8257a52e Fixes KEYCLOAK-7090 Applications page - HTML (#5189) 2018-05-10 18:14:56 -04:00
Lorent Lempereur 27d8afe4a7 Javascript Adapter - Reject 'login' promise when users close their cordova in-app-browser on purpose (#5000) 2018-05-09 15:49:38 -07:00
Samuel Ueltschi 3391ec5377 check if content-type is null when restoring request 2018-05-09 12:47:14 +02:00
pedroigor 88f21eae87 [KEYCLOAK-7322] - NPW when removing group from representation 2018-05-08 14:03:33 -03:00
Bill Burke 1258923a0d Merge pull request #5188 from patriot1burke/keycloak-7304
KEYCLOAK-7304
2018-05-08 07:31:05 -04:00
Stan Silvert 344286e037 KEYCLOAK-7244: Lang dropdowns should sometimes be hidden (#5175) 2018-05-07 15:56:22 -04:00
mhajas 3ced81a2c2 KEYCLOAK-7315 Fix issues in JavascriptAdapter tests (#5193)
more in issue KEYCLOAK-7315
2018-05-07 14:47:56 -04:00
Clément Poissonnier 032c48fb25 i18n(login): add missing keys for french language (#5145) 2018-05-07 00:49:46 +02:00
Lorent Lempereur f6125a2542 [KEYCLOAK-6655] Javascript Adapter - Allow users to provide cordova-specific options to login and register (#4998)
* Javascript Adapter - Allow users to pass cordova-specific options (in-app-browser) to the login and register functions

* Javascript Adapter - Allow users to pass cordova-specific options (in-app-browser) to the login and register functions

* [KEYCLOAK-6655] On Android 8, explicit hidden=no fails on in-app-browser load.
2018-05-07 00:26:46 +02:00
Tim Taylor 49a03a86fb Force the mvn-golang plugin to use the maven proxy 2018-05-04 15:31:49 -04:00
Pedro Igor e84acd9898 Merge pull request #5177 from pedroigor/KEYCLOAK-7206
[KEYCLOAK-7206] - Search by user id on admin console
2018-05-04 09:11:49 -03:00
Vlasta Ramik 8022a74565 KEYCLOAK-7274 Hardcoded config in offline adapter installation scripts (#5184) 2018-05-03 19:50:36 +02:00
Bill Burke fdc6fc59b8 KEYCLOAK-7304 2018-05-03 12:14:30 -04:00
Stan Silvert f3340b113f KEYCLOAK-6977: Stop using yarn for new admin console (#5181) 2018-05-03 09:29:13 -04:00
Stan Silvert 20f24bffc4 KEYCLOAK-7248: Fixes for IE 11 (#5182) 2018-05-02 16:15:31 -04:00
pedroigor 7ebcc69cb9 [KEYCLOAK-7148] - Associate sub resources to a parent resource 2018-05-02 13:04:11 -03:00
Stian Thorgersen 90e5c7f3eb Bump version to 4.0.0.Beta3-SNAPSHOT (#5185) 2018-05-02 14:32:20 +02:00
vramik 81918713dd KEYCLOAK-6744 Adapter tests - set container based on property 2018-05-02 13:52:59 +02:00
Martin Kanis 9505925363 Revert "KEYCLOAK-5270 Realm cookie path for IE<=11 users (#5106)" (#5183)
This reverts commit a67da7bc59.
2018-05-02 09:31:42 +02:00
mhajas 6e123bcea2 KEYCLOAK-6847 Fix OIDC adapter tests 2018-05-02 09:28:26 +02:00
pedroigor ddceaaf3d5 [KEYCLOAK-7206] - Search by user id on admin console 2018-04-30 11:44:33 -03:00
Tomas Kyjovsky a3fb0a5ac5 KEYCLOAK-7255 Compilation error in perf testsuite 2018-04-30 10:26:07 +02:00
zschwarz 1ef5379648 KEYCLOAK-6647 Don't use remote debugging for domain mode 2018-04-27 15:04:10 +02:00
vmuzikar 5ec50461ee KEYCLOAK-7101 Fix DockerClientTest 2018-04-27 15:02:10 +02:00
Pedro Igor e960642399 Merge pull request #5144 from pedroigor/KEYCLOAK-4903
[KEYCLOAK-4903] - Pushed Claims
2018-04-26 15:59:13 -03:00
Lubos.Palisek 2bab2acf5b [KEYCLOAK-7239] Fixed ConcurrentModificationException while importing from LDAP with "ignoreMissingGroups" checked.
Fixed test so that now it checks this use case.
2018-04-26 18:54:00 +02:00
Stan Silvert 5a56a822b0 KEYCLAOK-7170 device activity component (#5169)
* KEYCLOAK-7170: Create Device Activity Page

* KEYCLOAK-7170: Create Device Activity Page

* Fixes KEYCLOAK-7205 - Device activity - update HTML
2018-04-25 15:04:56 -04:00
Stan Silvert 35154db50f KEYCLOAK-7123: l10n dropdowns (#5170)
* KEYCLOAK-7196: Add kc_locale to keycloak.js

* KEYCLOAK-7123: Localization dropdowns

* Update keycloak-service to latest keycloak.js
2018-04-25 15:04:12 -04:00
Tomas Kyjovsky fe2ae6ec68 KEYCLOAK-7199 Optional logout step in registration perfromance test 2018-04-25 15:31:18 +02:00
pedroigor b249a48dcf [KEYCLOAK-7147] - Support obtaining a buffered input stream in HttpFacade.Request 2018-04-25 10:16:41 -03:00
pedroigor 035ebc881a [KEYCLOAK-4903] - Claim Information point Provider SPI and configuration 2018-04-25 10:16:41 -03:00
vramik 8b5fd2b4ac KEYCLOAK-7091 fuse7 testsuite 2018-04-25 14:35:56 +02:00
pedroigor e813fcd9c8 [KEYCLOAK-4903] - Pushing claims when obtaining a permission ticket 2018-04-24 19:47:28 -03:00
mposolda 634e7170e3 KEYCLOAK-7158 RestartLoginCookie throws error when KC_RESTART cookie created by Keycloak 1.9 2018-04-23 21:56:13 +02:00
Stan Silvert b6a0303a4c KEYCLOAK-7196: Add kc_locale to keycloak.js (#5165)
* KEYCLOAK-7196: Add kc_locale to keycloak.js

* Update keycloak.d.ts
2018-04-23 11:45:32 -04:00
pedroigor 824b900a43 [KEYCLOAK-7021] - keycloak-authz.js and keycloak-authz.d.ts do not work with TypeScript 2018-04-23 08:48:42 -03:00
June Zhang 49a83db2e9 Fixes KEYCLOAK-6499 Add password update - HTML (#5154) 2018-04-23 06:46:02 -04:00
Tomas Kyjovsky af7e42d640 KEYCLOAK-5830 Automated stress test 2018-04-23 09:25:08 +02:00
Tomas Kyjovsky 9183cba02e KEYCLOAK-7189 Customizable provisioning parameters for admin user 2018-04-23 09:20:02 +02:00
pedroigor c3d297dd05 [KEYCLOAK-7162] - Expose WWW-Authenticate Header when using CORS 2018-04-23 08:46:54 +02:00
pedroigor 527d6ca4d8 [KEYCLOAK-6414] - Empty response body with 200 response when using Elytron 2018-04-23 08:46:54 +02:00
Ingo Bauersachs 5e4d173f1d KEYCLOAK-7194: avoid NullPointerException (#5157) 2018-04-20 09:24:12 +02:00
Wojciech Trocki c3c1a0fb4e Allow to use custom adapter (#5067)
* Allow to use custom adapter

* fix: improve TypeScript documentation
2018-04-20 09:18:46 +02:00
Eduardo Mínguez 7b8c691402 Fix link (#5159) 2018-04-20 08:36:27 +02:00
mhajas a1e7351072 KEYCLOAK-6815 Use htmlunit as default browser for adapter tests 2018-04-19 15:01:14 +02:00
Martin Kanis 7efa45126c KEYCLOAK-6991 NPE when importing realm from file 2018-04-19 14:26:50 +02:00
June Zhang c12cd7fd57 Fixes KEYCLOAK-7114 Fix localization for Welcome page - HTML/FTL (#5152) 2018-04-18 12:13:29 -04:00
wyvie 4ddff9ee16 [KEYCLOAK-7017] fixed ActionTokenCrossDCTest failures
Test was failing because of change of login page design. Element which
contained title headers was moved from header title to another element.
Was not fixed upon initial commit (ca15db) because test's only turned on
by profile.
2018-04-17 16:14:10 +02:00
Pedro Igor 615cb24cce Merge pull request #5149 from vmuzikar/fix-authz-test
KEYCLOAK-7100 Fix AggregatePolicyManagement Console UI test
2018-04-16 10:13:32 -03:00
Oskars 3bef6d5066 KEYCLOAK-4538 Configurable clock skew when validating tokens (#5014)
* [master]: fix type for checkLoginIframeInterval

* [master]: KEYCLOAK-4538 Feature to tolerate a configurable amount of seconds of clock skew when validating tokens

* [master]: KEYCLOAK-4538 Fix unit test scenarios for token clock skew

* [master]: KEYCLOAK-4538 Reverted wildcard imports

* [master]: fix unit test to use longer intervals to make test less fragile.
2018-04-16 11:09:25 +02:00
June Zhang e8a07c9a6c Fixes KEYCLOAK-6970 Update HTML for profile page (#5146) 2018-04-13 13:34:24 -04:00
vmuzikar 3041499cd4 KEYCLOAK-7100 Fix AggregatePolicyManagement Console UI test 2018-04-13 15:58:33 +02:00
June Zhang 03590f117e Fixes KEYCLOAK-6838 Update RH-SSO logo style (#5147) 2018-04-13 08:10:55 +02:00
Vlastimil Eliáš c1311e4619 KEYCLOAK-6849 - LinkedIn social login provider updated to new LinkedIn OAuth2 endpoint (#5125)
* KEYCLOAK-6849 - LinkedIn social login provider updated to new LinkedIn
OAuth2 endpoint

* KEYCLOAK-6849 - LinkedIn social login provider test updated

* KEYCLOAK-6849 - LinkedIn social login provider test updated to
conditionally handle consent page when shown only

* Simplify the LinkedIn app authorization

This reverts commit c12359e7a13d9ff231fe2e25cddba66ad679a9cd.
2018-04-13 08:09:27 +02:00
vramik 9a94004fc9 KEYCLOAK-7137 Polish testsuite a bit 2018-04-12 16:46:59 +02:00
Stan Silvert 095fec95e5 KEYCLOAK-7022 Fix l10n on Welcome page (#5143) 2018-04-11 12:05:07 -04:00
mhajas b78f0aa9db KEYCLOAK-6806 Wait for query error to appear in query response mode test (#5130) 2018-04-10 09:58:23 +02:00
Hugo Guerrero fac3118b0a KEYCLOAK-6448 - implement instagram social broker (#4963)
* KEYCLOAK-6448 - implement instagram social broker

* Instagram SocialLogin Tests
2018-04-09 17:30:27 +02:00
Douglas Palmer cf056b3464 [KEYCLOAK-6069] Allow configuration of LDAP connection pooling 2018-04-06 20:27:11 +02:00
mhajas 9ed221b168 KEYCLOAK-6873 Fix jboss cli script for removing elytron subsystem 2018-04-06 17:07:17 +02:00
Alex Szczuczko e12752f9ac KEYCLOAK-6906 Store product build arguments (#5140)
This adds a json file with the arguments for maven and PME. Variables are
included for the product build pipeline to fill in with appropriate internal
values.

The community build is unaffected by this change.
2018-04-06 11:48:01 +02:00
mhajas 70b3f6bd92 KEYCLOAK-6840 Remove untracked file and unpack js adapter directly to testsuite provider target directory (#5133) 2018-04-06 11:47:10 +02:00
mhajas 5022015e4a KEYCLOAK-6836 Fix adding log level for adapters on EAP6 (#5132) 2018-04-06 11:46:03 +02:00
Joe32 f41a7000de KEYCLOAK-6305, Slovak translation (#4926) 2018-04-06 11:37:39 +02:00
Martin Kanis a67da7bc59 KEYCLOAK-5270 Realm cookie path for IE<=11 users (#5106) 2018-04-06 09:26:29 +02:00
wyvie 943bd9e48e [KEYCLOAK-6808] fix for the WildflyConsoleProtectionTest
Access Control is not an 'a' tag, but rather 'span'
Also two tests do not behave correctly with a same
Before annotated initialization, so reduce number
of tests to one, which calls the other one
2018-04-06 07:28:29 +02:00
Tomas Kyjovsky 2b14469a58 KEYCLOAK-7056 Support SSL in performance testsuite 2018-04-05 15:43:02 +02:00
Tomas Kyjovsky 2be6dec238 KEYCLOAK-7064 Ability to run performance testsuite with different server versions 2018-04-05 15:23:03 +02:00
Pedro Igor e1f5245145 Merge pull request #5120 from pedroigor/KEYCLOAK-7029
[KEYCLOAK-7029] - Configuration of cache policies for cached resources/path
2018-04-05 09:33:23 -03:00
wyvie b3513e3203 [KEYCLOAK-6872] fixed account link test 2018-04-05 10:53:00 +02:00
Bill Burke ffd9d957f4 Merge pull request #5123 from patriot1burke/kcadm-token
KEYCLOAK-7044 KEYCLOAK-7046
2018-04-04 17:22:17 -04:00
Pedro Igor 3cea54e3a6 Merge pull request #5128 from sguilhen/KC6853
[KEYCLOAK-6853] Make TimePolicyProvider use the kc.date.time_date con…
2018-04-04 15:58:04 -03:00
Pedro Igor ef4223858b Merge pull request #5127 from sguilhen/KC5864
[KEYCLOAK-5864] Display user friendly error message when a rule polic…
2018-04-04 15:55:56 -03:00
Stian Thorgersen 5514812a4d KEYCLOAK-7048 Clear token if refresh fails (#5124) 2018-04-04 20:12:25 +02:00
Stefan Guilhen 87abe5e648 [KEYCLOAK-6853] Make TimePolicyProvider use the kc.date.time_date contextual attribute when evaluating policies 2018-04-04 14:37:03 -03:00
Stefan Guilhen 7e3703b2bf [KEYCLOAK-5864] Display user friendly error message when a rule policy artifact can't be retrieved 2018-04-04 14:33:31 -03:00
Stan Silvert f652b13520 KEYCLOAK-7024: Fix logout dropdown (#5109) 2018-04-04 09:40:15 -04:00
Stan Silvert baf6b96db6 KEYCLOAK-7037: Welcome Page Links (#5113) 2018-04-04 09:39:35 -04:00
Stan Silvert 701c318b60 KEYCLOAK-7047: Fix RegistrationEmailAsUsername and EditUserNameAllowed (#5122)
on personal info page.
2018-04-04 09:31:38 -04:00
mhajas b3b81d6a76 KEYCLOAK-6806 timeSkew tolerance in tests (#5110) 2018-04-04 11:19:18 +02:00
Bill Burke 8a5428808e KEYCLOAK-7044 KEYCLOAK-7046 2018-04-03 21:29:31 -04:00
pedroigor a939c45d58 [KEYCLOAK-7029] - Configuration of cache policies for cached resources/path 2018-04-03 16:44:27 -03:00
Bill Burke 0b2fe75828 Merge pull request #5115 from patriot1burke/kcinit-browser
KEYCLOAK-7004 KEYCLOAK-7003 KEYCLOAK-6999 KEYCLOAK-7033
2018-04-03 10:31:30 -04:00
Pedro Igor 28d0ab9da8 Merge pull request #5114 from pedroigor/KEYCLOAK-7028
[KEYCLOAK-7028] - Propagating AuthorizationContext when enforcement-mode is disabled for a path
2018-04-03 10:01:57 -03:00
Martin Kanis 103a3c4ca7 KEYCLOAK-7042 Align versions to match EAP 7.1.2.CR1 2018-04-03 13:53:31 +02:00
pedroigor 5c52da80c6 [KEYCLOAK-7028] - Propagating AuthorizationContext when enforcement-mode is disable for a path 2018-04-02 11:10:43 -03:00
Bill Burke 8fe10b3331 Merge pull request #5090 from TorstenF76/configure-request-timeout
configurable request timeout
2018-04-01 13:34:13 -04:00
Bill Burke 04a72b9608 bump kcinit version tag 2018-03-31 22:34:37 -04:00
June Zhang 7280234422 Fixes KEYCLOAK-6969 Update the welcome page (#5105) 2018-03-31 13:33:12 -04:00
Bill Burke 4078e84fb6 server driven success page 2018-03-31 10:16:44 -04:00
Bill Burke 06f32a47ec fake browser tests 2018-03-30 08:24:30 -04:00
Bill Burke f4a5e49b63 initial 2018-03-29 17:14:36 -04:00
Bill Burke 5d74b776d6 Merge remote-tracking branch 'upstream/master' 2018-03-29 09:46:16 -04:00
Pedro Igor 5cae1bb134 Merge pull request #5093 from pedroigor/KEYCLOAK-4102
[KEYCLOAK-4102] - Support lazy load paths
2018-03-29 09:16:34 -03:00
Bill Burke 8d3dc790df Merge pull request #5087 from patriot1burke/kcinit
KEYCLOAK-6813
2018-03-28 17:35:33 -04:00
Bill Burke f5bacb79c1 review changes 2018-03-28 16:45:52 -04:00
pedroigor 4a425c2674 [KEYCLOAK-4102] - Support lazy loading of paths via policy enforcer config 2018-03-28 09:23:59 -03:00
Bill Burke c38b6d585e KEYCLOAK-528 (#5103) 2018-03-28 11:15:37 +02:00
Bill Burke ad5f3fefc5 Merge remote-tracking branch 'upstream/master' into kcinit 2018-03-27 16:38:35 -04:00
dongay 707b80f436 KEYCLOAK-3210 Apply @Nationalized attribute to entity fields that should support unicode character sets 2018-03-27 20:55:59 +02:00
Stan Silvert 80feb67fc2 KEYCLOAK-6494: Address load time of new acct mgt console (#5100)
* Optimize loading. min bundles, stop double-loading, rxjs-system instead of
plain rxjs, clean up 404's

* Create module loading hierarchy.  Allows for lazy loading.

* Upgrade NG, remove jquery, load keycloak.js only from auth/js

* Delay systemjs loading.  Load home page instead of account.

* KEYCLOAK-6496: Cleanup and polish code after optimizations.

* Fix message bundle to be back the way it was.

* Remove unused png's. Remove comments in index.ftl. Remove javaMessages.
2018-03-27 12:42:13 -04:00
Bill Burke aae63de755 Merge remote-tracking branch 'upstream/master' 2018-03-27 11:04:19 -04:00
pedroigor e9e376419d [KEYCLOAK-4102] - Removing create-resources configuration option 2018-03-27 09:51:13 -03:00
Pedro Igor ffeb0420bf Merge pull request #5079 from pedroigor/KEYCLOAK-6529
[KEYCLOAK-6529] - Resource Attributes
2018-03-27 09:30:38 -03:00
mhajas a63bb44ba2 KEYCLOAK-3164 Migrate SAML ECP tests to integration-arquillian 2018-03-27 12:07:07 +02:00
sebastienblanc 91135c95ae KEYCLOAK-6732 : Make Spring Sec Dep optional 2018-03-27 10:59:29 +02:00
June Zhang bff7831912 KEYCLOAK-6942 Cut off background on the new login page 2018-03-26 18:23:36 +02:00
stianst 66186273ca Removed authz from examples-dist 2018-03-26 18:17:38 +02:00
stianst 07fea02146 Bump versions to 4.0.0.Beta2-SNAPSHOT 2018-03-26 18:17:38 +02:00
Pedro Igor 593f57fd2c Merge pull request #5088 from pedroigor/KEYCLOAK-6878
[KEYCLOAK-6878] - Always refresh token option not working for invalid tokens
2018-03-26 09:38:05 -03:00
wyvie d40e9bd3c1 [KEYCLOAK-6814] check if HMAC exists during session restart 2018-03-26 10:05:39 +02:00
Pavel Drozd 92aba77cc1 Merge pull request #5094 from vmuzikar/fix-stackoverflow
KEYCLOAK-6510 Fix StackOverflow social login test
2018-03-26 08:22:32 +02:00
Marko Strukelj e4e14cd9f4 Merge pull request #5086 from tkyjovsk/KEYCLOAK-6827
KEYCLOAK-6827 Performance test for user registration
2018-03-22 19:11:05 +01:00
Tomas Kyjovsky 327a159a10 KEYCLOAK-6827 Performance test for user registration 2018-03-22 16:59:05 +01:00
Ryan Dawson d57fb445eb adapter for spring boot 2
remove built directory

update snapshot version references

refactor out core library to remove duplication

adapter for spring boot 2

remove built directory

update snapshot version references

Revert "merge from upstream"

This reverts commit 88c39a2f23b8f2d4b25360e2b46e683d11b4972b, reversing
changes made to f0811145ceeb8ec609ed66b06067f797e288aa89.

setting correct versions

updating to latest keycloak

arquillian test app for spring boot2

update to 2.0.0.RELEASE

added Rest Customizer
2018-03-22 14:23:55 +01:00
Patric Vormstein fe98c30077 [KEYCLOAK-6412] - Handle Proxy Restriction Tag incl. Test 2018-03-22 11:15:22 +01:00
pedroigor 758ae41999 [KEYCLOAK-6529] - Constraint name too long 2018-03-22 07:05:03 -03:00
Bill Burke 67229912e6 use kcinit branch 2018-03-21 13:38:40 -04:00
TorstenF76 51c593b8cd add configuration entry for request timeout 2018-03-21 16:01:14 +01:00
Bill Burke 39f93dfa33 fix providers test 2018-03-21 10:01:40 -04:00
pedroigor 3559c5dc3c [KEYCLOAK-6878] - Always refresh token option not working for invalid tokens 2018-03-21 10:01:02 -03: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
Bill Burke f000cedcbb Merge remote-tracking branch 'upstream/master' into kcinit 2018-03-20 16:49:43 -04:00
Bill Burke 681e3d751e golang integration 2018-03-20 16:42:35 -04: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
Bill Burke 8926837a3e tests 2018-03-19 16:47:13 -04: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
pedroigor 08896ee9c9 [KEYCLOAK-6529] - Resource Attributes 2018-03-19 13:21:39 -03: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
Bill Burke 4bba11cd94 kcinit 2018-03-16 12:11:57 -04: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
Bill Burke 9599003bc3 Merge remote-tracking branch 'upstream/master' 2018-03-06 17:38:26 -05: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 986f39838c Merge remote-tracking branch 'upstream/master' 2018-03-02 18:54:22 -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 c8c86d2bad KEYCLOAK-6510 Fix StackOverflow social login test 2018-02-05 17:16:33 +01: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
Marko Strukelj 23d0afbfd8 KEYCLOAK-6058 Partial import should ignore built-in clients 2017-12-21 13:52:58 +01:00
stianst e3995c0154 KEYCLOAK-6079 2017-12-21 12:18:12 +01:00
stianst e4185496fd KEYCLOAK-6078
Account2 is listed as disabled feature in product even though it's not included at all
2017-12-21 11:45:00 +01:00
stianst c7ce279aef KEYCLOAK-6076 Exclude transitive dependencies in Fuse ZIP 2017-12-21 09:03:21 +01:00
John Ament 156ce0c0ea KEYCLOAK-4853 Introduce dedicated utility for parsing ID from URI 2017-12-20 15:00:07 +01:00
Hynek Mlnarik 0f047ac9f5 KEYCLOAK-6067 Allow http for performance testsuite realms 2017-12-20 14:56:42 +01:00
stianst f0c5752ef9 KEYCLOAK-5443 Fix update user account when both email as username and edit username are enabled 2017-12-20 14:40:03 +01:00
Martin Kanis 351dbffaf2 KEYCLOAK-5172 Set oidc as default protocol to clients 2017-12-20 13:38:12 +01:00
Bruno Oliveira 811cd3a04a KEYCLOAK-6011 2017-12-20 13:37:11 +01:00
Marko Strukelj ae573f4814 KEYCLOAK-5617 KEYCLOAK-5993 Race condition putting new user in user cache 2017-12-20 13:35:35 +01:00
Pedro Igor 7e072abf74 Merge pull request #4873 from pedroigor/master
[KEYCLOAK-5806] - Returning from child policy not restoring state
2017-12-20 08:53:50 -02:00
stianst e96c6a4bcb KEYCLOAK-6068 Fix preflight request on admin endpoints 2017-12-20 10:19:34 +01:00
stianst 7d2d7e41d9 KEYCLOAK-6065 Prevent password managers from saving credentials in admin console 2017-12-20 06:46:54 +01:00
Hynek Mlnarik 626004e782 KEYCLOAK-6066 Be less strict when handling cookies 2017-12-19 21:39:41 +01:00
pedroigor 3846cfdb4f [KEYCLOAK-5806] - Changing apply policy component and more tests 2017-12-19 17:28:46 -02:00
stianst 902b799bdf KEYCLOAK-6064 Fix identity providers page when realm has no configured providers 2017-12-19 17:56:48 +01:00
stianst 465675ac28 KEYCLOAK-5019 Fixes for password managers 2017-12-19 16:13:16 +01:00
pedroigor 2ace312501 [KEYCLOAK-5806] - More UI tests for parent/child policy flow 2017-12-19 12:13:04 -02:00
pedroigor c00d89aec3 [KEYCLOAK-5806] - Returning from child policy not restoring state 2017-12-19 10:00:14 -02:00
stianst 61fe554617 KEYCLOAK-6051 Fix licenses 2017-12-19 08:20:29 +01:00
mposolda 5a66f577eb KEYCLOAK-5982 Fix NPEs when client 'account' was renamed/removed 2017-12-18 21:47:17 +01:00
pskopek e29ccebc21 KEYCLOAK-5666 removed not needed decodeToObject methods 2017-12-18 19:19:00 +01:00
stianst 27b5e1aae2 KEYCLOAK-6050 Fix export doesn't export internal realm rep 2017-12-18 13:15:42 +01:00
stianst 71e6f52f7d KEYCLOAK-6022 2017-12-18 11:48:39 +01:00
stianst b303acaaba KEYCLOAK-2120 Added manual setup page for OTP 2017-12-18 11:20:20 +01:00
Bill Burke 118e998570 Merge pull request #4834 from pedroigor/KEYCLOAK-5806
[KEYCLOAK-5806] - Create policy button to associated policies
2017-12-16 23:44:35 -05:00
Bill Burke f3fcac2d8d Merge pull request #4865 from patriot1burke/master
KEYCLOAK-5420
2017-12-16 14:31:37 -05:00
Bill Burke b5ae7e836d smaller times 2017-12-16 13:18:41 -05:00
Bill Burke a27097e9ef reset defaults 2017-12-16 11:28:15 -05:00
Bill Burke 003f27e9bd fix more 2017-12-16 08:31:33 -05:00
Bill Burke 76cccc3f2b fix more 2017-12-16 08:29:58 -05:00
Bill Burke 80be4c9dbc fix more 2017-12-16 07:12:32 -05:00
pedroigor 5d7ba39e0c [KEYCLOAK-5806] - Create policy component to permission pages 2017-12-15 23:41:52 -02:00
pedroigor e2118856ca [KEYCLOAK-5806] - Adding table of selected policies 2017-12-15 22:04:37 -02:00
pedroigor 3ee760ed23 [KEYCLOAK-5806] - More tests asserting creation of child policies 2017-12-15 22:04:37 -02:00
pedroigor eba47b3c89 [KEYCLOAK-5806] - Create policy button to Aggregated Policies 2017-12-15 22:04:37 -02:00
Pavel Drozd b121306749 Merge pull request #4866 from pdrozd/KEYCLOAK-6054
KEYCLOAK-6054 Fuse adapter tests - add httpclient dependency
2017-12-15 23:44:33 +01:00
Pavel Drozd 4097a81142 Merge pull request #4860 from pdrozd/KEYCLOAK-6047
KEYCLOAK-6047 Added ide time window to mellon test
2017-12-15 22:23:19 +01:00
Pavel Drozd ce6f73ce74 KEYCLOAK-6054 Fuse adapter tests - add httpclient dependency 2017-12-15 22:22:16 +01:00
Bill Burke 1eec2747ef fix stupidity 2017-12-15 15:31:14 -05:00
Pedro Igor 6bfdc9451d Merge pull request #4861 from pedroigor/KEYCLOAK-6023
[KEYCLOAK-6023] - Upgrading Drools to 6.5.0.Final
2017-12-15 15:46:28 -02:00
Pedro Igor a66fe003d3 Merge pull request #4863 from pedroigor/KEYCLOAK-5877
[KEYCLOAK-5877] - Allow save permissions without policies
2017-12-15 15:45:38 -02:00
Bill Burke 7cb39c2dfc KEYCLOAK-5420 2017-12-15 12:16:24 -05:00
Bill Burke 90b5dd60fd Merge pull request #4864 from patriot1burke/master
KEYCLOAK-4427
2017-12-15 11:44:16 -05:00
Stan Silvert a719b73808 KEYCLOAK-5935: No success msg on client role add/remove 2017-12-15 10:54:43 -05:00
Bill Burke 1c38cec10f KEYCLOAK-4427 2017-12-15 10:53:13 -05:00
Bill Burke e7c25f76a7 KEYCLOAK-4427 2017-12-15 10:49:51 -05:00
pedroigor 94928323ee [KEYCLOAK-5877] - Allow saving permissions without policies 2017-12-15 12:55:07 -02:00
Hynek Mlnarik e4a91c0706 KEYCLOAK-6042 Encode user ID before storing in auth session 2017-12-15 15:16:26 +01:00
Pavel Drozd a7c6eb4999 KEYCLOAK-6047 Added ide time window to mellon test 2017-12-15 14:57:26 +01:00
Pavel Drozd 1b14f9e73e Merge pull request #4847 from vramik/KEYCLOAK-4793
KEYCLOAK-4793 moved profile a lever lower due to migration test
2017-12-15 14:52:04 +01:00
pedroigor 569378b446 [KEYCLOAK-6023] - Upgrading Drools to 6.5.0.Final 2017-12-15 10:22:05 -02:00
stianst a8943fb323 KEYCLOAK-6043 Use same urls for get and posts in account 2017-12-15 08:31:04 +01:00
Bruno Oliveira 1a541889f4 [KEYCLOAK-6015] replyTo can be empty string in DB 2017-12-15 07:01:15 +01:00
stianst b672229efc KEYCLOAK-6032 Fix error page when internationalization is enabled 2017-12-15 06:32:00 +01:00
Stan Silvert 2b11938084 KEYCLOAK-5932: Remove social idp from dropdown if already defined 2017-12-14 20:18:56 -05:00
Stan Silvert 97e3b26a5c KEYCLOAK-5934: Event filters allowing free values in select2 inputs 2017-12-14 16:38:02 -05:00
Vlastimil Elias 7e20a65989 KEYCLOAK-6040 AuthenticationSessionModel pushing into
EmailTemplateProvider
2017-12-14 15:51:04 +01:00
Martin Kanis 47721957b7 KEYCLOAK-6018 Create BOM for Drools dependencies 2017-12-14 14:51:25 +01:00
Stan Silvert 068785f227 KEYCLOAK-6042: Remove keycloak-preview from product build. 2017-12-14 14:09:04 +01:00
sebastienblanc a96c9d34c9 set auth contraint to true when wildcard is used 2017-12-14 14:00:03 +01:00
Hisanobu Okuda 3d0512efd8 KEYCLOAK-3842 SPNEGO: Support for multiple kerberos realms 2017-12-14 12:54:20 +01:00
vramik abea430802 KEYCLOAK-4793 2017-12-14 11:30:28 +01:00
Hynek Mlnarik 2a2e6c839b KEYCLOAK-5635 2017-12-13 21:07:46 +01:00
mposolda 6696c0f0b2 KEYCLOAK-5245 Restart failures when deleting a client with existing sessions/offline_tokens 2017-12-13 15:53:10 +01:00
Hynek Mlnarik 958185ec51 KEYCLOAK-4809 Support for SAML AttributeAuthorityDescriptor.Attribute elements 2017-12-13 11:55:40 +01:00
Hynek Mlnarik e6a64e234b KEYCLOAK-5644 Skip Advice tag in SAML messages 2017-12-13 11:55:40 +01:00
mposolda b793e42c53 KEYCLOAK-5017 Adding user to newly created group caused sync all groups to LDAP 2017-12-13 09:15:47 +01:00
Bill Burke f80811a065 Merge pull request #4841 from patriot1burke/master
KEYCLOAK-5923
2017-12-12 16:07:52 -05:00
Bill Burke ea3417253f KEYCLOAK-5923 2017-12-12 16:07:09 -05:00
Hynek Mlnarik 7174c0b4ec KEYCLOAK-6025 Simplify easy access to current session in action token handlers 2017-12-12 17:53:44 +01:00
Bruno Oliveira 0a9055e112 [KEYCLOAK-5175] Importing certificate to saml client takes too long 2017-12-12 14:45:24 +01:00
stianst f939818252 KEYCLOAK-5907 Use client manager to delete clients in client registration services 2017-12-12 14:25:05 +01:00
mposolda b8416dfa3e KEYCLOAK-5981 Test Impersonation works when authenticationSession exists 2017-12-12 09:43:34 +01:00
mposolda 63efee6e15 KEYCLOAK-5938 Authentication sessions: Support for logins of multiple tabs of same client 2017-12-12 08:01:02 +01:00
Pavel Drozd c3855510ef Merge pull request #4822 from vmuzikar/fix-nodejs-test
KEYCLOAK-6007 Fix failing integration Node.js adapter test
2017-12-12 00:10:37 +01:00
Pavel Drozd 20f8e5e572 Merge pull request #4821 from mhajas/KEYCLOAK-6005
KEYCLOAK-6005 Fail build when adapters installation fails
2017-12-12 00:09:45 +01:00
Pavel Drozd 047fdb55a2 Merge pull request #4803 from mhajas/KEYCLOAK-5896
KEYCLOAK-5896 Run localization test only in community
2017-12-12 00:09:02 +01:00
Pavel Drozd be36f5358c Merge pull request #4802 from mhajas/KEYCLOAK-5986
KEYCLOAK-5986 Fix token javacsript tests
2017-12-12 00:07:16 +01:00
Pavel Drozd 21dcea87c5 Merge pull request #4795 from mhajas/KEYCLOAK-4995
KEYCLOAK-4995 Fix Cluster tests on product and wildfly 10
2017-12-12 00:06:35 +01:00
Bruno Oliveira 79c4d3e7cf [KEYCLOAK-6002] - keycloak-test-helper should use the same resteasy dependency version as the parent 2017-12-11 14:34:28 +01:00
Stan Silvert a60fdcedcd KEYCLOAK-5929: Submit buttons in wrong position 2017-12-11 08:27:39 -05:00
stianst dac6c6bd7e KEYCLOAK-6000 Fix output in execute actions email 2017-12-11 14:24:37 +01:00
stianst 867de9de50 KEYCLOAK-6010 Add CORS headers to keycloak.js 2017-12-11 14:24:12 +01:00
stianst 74442ae820 Added script to check Java version in JARs within ZIPs 2017-12-11 13:15:24 +01:00
k-tamura d7a90817f2 KEYCLOAK-6009 Fix incorrect String.format usage 2017-12-10 20:56:36 +01:00
Bill Burke c9b218db71 Merge pull request #4823 from patriot1burke/master
KEYCLOAK-5724
2017-12-08 20:03:05 -05:00
Bill Burke 7c031505e2 add test 2017-12-08 17:38:00 -05:00
Tomas Kyjovsky 03e43f8964 KEYCLOAK-5966 Adjust defaults for performance testsuite provisioning 2017-12-08 21:06:12 +01:00
Tomas Kyjovsky b5c7be574b KEYCLOAK-5967 Move configuration of load-balancer from Dockerfile to maven 2017-12-08 21:05:26 +01:00
Bill Burke eea315a83b KEYCLOAK-5724 2017-12-08 10:26:28 -05:00
Bill Burke ce9f4bf97a KEYCLOAK-5724 2017-12-08 10:25:30 -05:00
Bill Burke 5d5a200413 Merge pull request #4818 from patriot1burke/master
KEYCLOAK-5926
2017-12-08 09:59:32 -05:00
vmuzikar 874dbcb3e1 KEYCLOAK-6007 Fix failing integration Node.js adapter test 2017-12-08 15:46:17 +01:00
mhajas 10219c1157 KEYCLOAK-6005 Fail build when adapters installation fails 2017-12-08 13:32:44 +01:00
Hynek Mlnarik 00fb36437d KEYCLOAK-5861 Remove AUTH_SESSION_ID when END_AFTER_REQUIRED_ACTIONS set 2017-12-08 09:52:14 +01:00
Hynek Mlnarik 4a012b73ea KEYCLOAK-4998 Fix NPE in AttributeToRoleMapper 2017-12-08 09:21:21 +01:00
Bill Burke fce16fe030 last fix 2017-12-08 02:30:48 -05:00
Bill Burke 67149ce3c3 fix test 2017-12-08 02:23:41 -05:00
Bill Burke 49ba71fd8f add logic for sync 2017-12-07 20:03:10 -05:00
Bill Burke 0dee393071 KEYCLOAK-5926 2017-12-07 19:49:10 -05:00
Bill Burke 464535ac81 Merge pull request #4815 from abstractj/KEYCLOAK-5467-i18-buttons
KEYCLOAK-5467 X.509 Auth - missing internationalization support
2017-12-07 10:08:11 -05:00
Bill Burke efa5949f69 Merge pull request #4814 from patriot1burke/master
KEYCLOAK-5350
2017-12-07 10:07:35 -05:00
Bruno Oliveira 3f3d50b9ad KEYCLOAK-5467 X.509 Auth - missing internationalization support
This adds the missing i18n support for login buttons
2017-12-07 08:52:16 -02:00
Pavel Drozd e567dcb888 Merge pull request #4809 from vramik/KEYCLOAK-4641
KEYCLOAK-4641 migrate remaining Adapter tests from old testsuite
2017-12-07 11:06:47 +01:00
stianst f2ba699520 Update RH-SSO target version 2017-12-07 10:46:13 +01:00
stianst c055ffb083 KEYCLOAK-4215 Consider session expiration when setting token timeouts 2017-12-07 10:45:02 +01:00
stianst 5fd3c9161d KEYCLOAK-5868 2017-12-07 10:42:21 +01:00
Tomas Kyjovsky 5fd00a0def KEYCLOAK-5971 Replace "numOfIterations" parameter with "steadyLoadPeriod" 2017-12-06 22:46:53 +01:00
Bill Burke 64f8d7ce25 KEYCLOAK-5350 2017-12-06 16:00:23 -05:00
Stan Silvert b8da95e901 KEYCLOAK-5930: Submit button on cred screen should start as disabled 2017-12-06 15:35:00 -05:00
stianst cccddebfd0 KEYCLOAK-5984 Fix error message in client initiated 2017-12-06 19:46:11 +01:00
vramik 5a8ff72cb6 KEYCLOAK-4641 migrate remaining Adapter tests from old testsuite 2017-12-06 15:12:37 +01:00
stianst ecfabe4ebe KEYCLOAK-5913 Move Chinese translations into resources-community 2017-12-06 13:58:52 +01:00
mposolda 8a0fa521c4 KEYCLOAK-5915 Support for sticky sessions managed by loadbalancer. Support for KeyAffinityService 2017-12-06 13:06:54 +01:00
mhajas 09348b2aff KEYCLOAK-5896 Run localization test only in community 2017-12-06 10:09:46 +01:00
mhajas 896e216f99 KEYCLOAK-5986 Fix token javacsript tests 2017-12-06 10:08:32 +01:00
stianst 35e60e0aa4 KEYCLOAK-5957 2017-12-06 08:59:40 +01:00
stianst 6d1c33ccdc KEYCLOAK-5667 2017-12-06 06:45:23 +01:00
Bill Burke f669fdf0df Merge pull request #4797 from stianst/KEYCLOAK-5734
KEYCLOAK-5734
2017-12-05 17:31:36 -05:00
Stan Silvert 30ad2899b8 KEYCLOAK-5933: Configure TOTP to Configure OTP 2017-12-05 17:24:54 -05:00
stianst 94ce97b972 KEYCLOAK-5734 2017-12-05 21:22:47 +01:00
Tomas Kyjovsky 3a7fd9e732 KEYCLOAK-5980 Dataset properties not loaded properly before test run - fix 2017-12-05 20:56:31 +01:00
Tomas Kyjovsky 503ad712dc KEYCLOAK-5955 Add ability to remotely monitor Infinispan and load balancer 2017-12-05 13:36:23 +01:00
mposolda f9e40326c1 KEYCLOAK-5961 Remove CrossDataCenter README file from codebase 2017-12-05 12:15:36 +01:00
mhajas 76029c7006 KEYCLOAK-4995 Fix Cluster tests on product and wildfly 10 2017-12-05 10:29:55 +01:00
James Stapleton 92cce7a6d4 [KEYCLOAK-5912] Add better improper SAML assertion error handling 2017-12-05 09:48:48 +01:00
Tomas Kyjovsky 916ba573bc KEYCLOAK-5965 fix 2017-12-05 09:21:56 +01:00
mposolda 6c34b4c418 KEYCLOAK-5914 Periodic clean of detached client sessions 2017-12-05 08:25:30 +01:00
stianst 5467d67c91 KEYCLOAK-5945 Strip default ports from urls 2017-12-04 19:56:01 +01:00
stianst c3d9f4704e KEYCLOAK-5946 Make sure wildcard origin is never returned 2017-12-04 19:55:34 +01:00
stianst 4541acc628 KEYCLOAK-5176 Strip headers from PEM when uploading to client 2017-12-04 19:54:15 +01:00
Stan Silvert 19cfbbf7ff KEYCLOAK-5972: i18n broken on keycloak-preview acct mgt 2017-12-04 13:44:16 -05:00
mposolda ff6fcd30d9 KEYCLOAK-4478 OIDC auth response lacks session_state in some cases 2017-12-04 16:13:22 +01:00
Samuel Mendenhall d69fe27cf9 set error instead of throw 2017-12-04 16:03:47 +01:00
Samuel Mendenhall ca324c29e8 processInit should return a promise for setupCheckLoginIframe and should only call processCallback if that setupCheckLoginIframe is successful 2017-12-04 16:03:47 +01:00
Hynek Mlnarik b4532ec629 KEYCLOAK-5964 Fix keycloak-install-base.cli client session caches 2017-12-04 12:15:07 +01:00
stianst 3ee269da87 Align dependencies 2017-12-04 10:14:30 +01:00
pedroigor b1127ffafe [KEYCLOAK-5953] - Making elytron dependencies optional 2017-12-04 10:13:55 +01:00
stianst 37de8e9f69 Bump version to 3.4.2.Final-SNAPSHOT 2017-12-01 09:34:48 +01:00
Stan Silvert 4d4ee33d1d KEYCLOAK-5937: Not possible to select role w/ hardocded LDAP role mapper 2017-11-30 15:29:36 -05:00
stianst 4daf6aaec5 KEYCLOAK-5940 2017-11-30 13:43:15 +01:00
vramik 6bb04617a6 KEYCLOAK-5802 fix install-adapters.sh script for wildfly10 and below 2017-11-30 12:58:11 +01:00
mposolda 7b03eed9c8 KEYCLOAK-5797 Refactoring authenticationSessions to support login in multiple browser tabs with different clients 2017-11-30 12:56:45 +01:00
Hynek Mlnarik b466f4d0b6 KEYCLOAK-5910 Retry sending notification when remote cache is unavailable 2017-11-30 11:48:26 +01:00
Peter Nalyvayko b8e5fd2b99 KC-4335: working on adding a reverse proxy support to allow X.509 client certificate authentication when running keycloak behind a reverse proxy
KC-4335: reverse proxy => a swtich to change a type of reverse proxy when running the X509 integration tests; changes to the names of the reverse proxy providers

KC-4335: updated the migration scripts to add x509 spi to standalone and domain configurations; removed the HAproxy and apache x509 spi configuration
2017-11-30 11:00:32 +01:00
Domenico Briganti b72b01bb9d fix logger class 2017-11-30 10:52:26 +01:00
Martin Kanis 603052c594 KEYCLOAK-5883 Deploy testsuite during product build 2017-11-30 10:50:06 +01:00
Marko Strukelj 106599b52c KEYCLOAK-5742 [Admin CLI] Output filtering for nested attributes doesn't work 2017-11-30 10:47:40 +01:00
pedroigor 17748d5ba8 [KEYCLOAK-5660] - Adding UserQueryProvider.getUsersCount(realm, includeServiceAccount) method 2017-11-30 10:45:54 +01:00
pedroigor 4caa40fd0a [KEYCLOAK-5660] - Fixing test, it should not consider users count for service accounts 2017-11-30 10:45:54 +01:00
pedroigor 674fb31a2c [KEYCLOAK-5660] - Rest API User count returns wrong value 2017-11-30 10:45:54 +01:00
Marko Strukelj c5d9301951 KEYCLOAK-4920 NPE when exporting configuration without alias 2017-11-30 10:40:25 +01:00
pedroigor 5f43a6a342 [KEYCLOAK-3629] - Adding confidential-port attribute to wildfly adapter subsystem 2017-11-30 10:39:21 +01:00
pedroigor 6587cfa084 [KEYCLOAK-3629] - Some Adapters do not work with SSL Redirect 2017-11-30 10:39:21 +01:00
Bruno Oliveira 6a528a3ee6 [KEYCLOAK-2645] Reset password page says 'You need to change your password to activate your account.' 2017-11-30 10:37:21 +01:00
stianst 2be78a0239 KEYCLOAK-5924 Add error handler for uncaught errors 2017-11-30 10:33:13 +01:00
Pavel Drozd 4408cdb5c7 Merge pull request #4756 from tkyjovsk/KEYCLOAK-5922
KEYCLOAK-5922 Cluster tests don't work with non-undertow server
2017-11-30 09:24:39 +01:00
Marek Posolda a3288d5f8e Merge pull request #4767 from hmlnarik/KEYCLOAK-5908-ClassCastException-to-org-infinispan-xsite-XSiteReplicateCommand
KEYCLOAK-5908 Fix relay configuration
2017-11-30 07:04:48 +01:00
Hynek Mlnarik 9cae8b79e4 KEYCLOAK-5908 Fix relay configuration 2017-11-29 22:22:35 +01:00
stianst 0bd2e63162 KEYCLOAK-5939 Align dependencies with WildFly 11 2017-11-29 20:39:10 +01:00
pedroigor 9ffc11d04f [KEYCLOAK-4231] - Unable to import PEM certificate > 2048 2017-11-29 20:26:22 +01:00
Bruno Oliveira af66c5dbd2 [KEYCLOAK-5483] X.509 Auth - log in attempt is not sometimes logged in the Login Events 2017-11-29 20:08:22 +01:00
Tomas Kyjovsky 83136c1bd8 KEYCLOAK-5897 Support for multiple provisioning options in performance testsuite 2017-11-29 18:05:31 +01:00
Pedro Igor d22c58ee30 Merge pull request #4760 from pedroigor/KEYCLOAK-5900
[KEYCLOAK-5900] - Returning error response when resource does not exist
2017-11-29 10:38:44 -02:00
Pedro Igor de6301e65a Merge pull request #4759 from pedroigor/KEYCLOAK-5901
[KEYCLOAK-5901] - Changing response to return a 400 in case scope is invalid
2017-11-29 10:38:07 -02:00
Pavel Drozd c02c07d78e Merge pull request #4749 from wyvie/springboot-testing-fix
[KEYCLOAK-3837] added additional profile to springboot prod testing
2017-11-29 00:05:21 +01:00
Pedro Igor f48509c32c Merge pull request #4741 from pedroigor/KEYCLOAK-2517
[KEYCLOAK-2517] - Doesn't work always refresh token
2017-11-28 20:58:56 -02:00
pedroigor c5b06f23e9 [KEYCLOAK-5900] - Returning error response when resource does not exist 2017-11-28 19:46:18 -02:00
Stan Silvert fc8a8b1dd8 KEYCLOAK-5905: 'Disable Credentials' not displayed after reset password. 2017-11-28 16:40:41 -05:00
pedroigor bf73375a5c [KEYCLOAK-5901] - Changing response to return a 400 in case scope is invalid 2017-11-28 19:32:41 -02:00
Tomas Kyjovsky 4240295af9 KEYCLOAK-5922 Cluster tests don't work with non-undertow server 2017-11-28 17:35:13 +01:00
Bill Burke 3afb635110 Merge pull request #4754 from patriot1burke/master
KEYCLOAK-5904
2017-11-28 09:14:51 -05:00
Bill Burke c36b3cd296 KEYCLOAK-5904 2017-11-28 09:10:43 -05:00
Bill Burke 683fe6fa03 Merge pull request #4744 from mposolda/crossdc-rebase
KEYCLOAK-4188 CrossDataCenter docs in the README file
2017-11-28 08:58:03 -05:00
Bill Burke 0a8995efc7 Merge pull request #4747 from mstruk/KEYCLOAK-5741
KEYCLOAK-5741 [Admin CLI] Fix instructions in build-in help
2017-11-28 08:57:29 -05:00
Bill Burke c398f6619f Merge pull request #4748 from mstruk/KEYCLOAK-5762
KEYCLOAK-5762 [Client Registration CLI] Fix instructions in built-in help
2017-11-28 08:57:15 -05:00
Bill Burke 33c38ceb8b Merge pull request #4753 from pedroigor/KEYCLOAK-5925
[KEYCLOAK-5925] - Trace-level should log tokens without signatures
2017-11-28 08:56:14 -05:00
wyvie b0b28e6282 [KEYCLOAK-3837] added additional profile to springboot prod testing 2017-11-28 14:44:08 +01:00
pedroigor 792ffdf39b [KEYCLOAK-5925] - Trace-level should log tokens without their signatures 2017-11-28 09:54:57 -02:00
stianst 36314c51d6 KEYCLOAK-5856 Fix infinite loop 2017-11-28 07:54:49 +01:00
stianst 24b7e318ca KEYCLOAK-5913 Fix all locales showing in RH-SSO 2017-11-28 07:54:26 +01:00
Stian Thorgersen cf485c3fc9 KEYCLOAK-5308 Fix updating protocol mappers on Oracle 2017-11-27 19:46:12 +01:00
Stian Thorgersen 5666bfe88b KEYCLOAK-4962 Fix updating mappers for identity providers on Oracle 2017-11-27 19:46:12 +01:00
Stan Silvert 27268044ae KEYCLOAK-5906: Active page not highlighted in nav sidebar 2017-11-27 12:43:43 -05:00
Alex Szczuczko c860ca61c3 KEYCLOAK-5843 Add custom license processing plugin to avoid directory symlinks (#4723)
Symlinks are frequently unavailable on Windows (must be on NTFS and user must
have SeCreateSymbolicLinkPrivilege). Removing the symlinks for licenses/common/
should enable the build to function mostly normally on Windows. The individual
license files will be incorrect, but that shouldn't matter for local builds.
Release builds are done on *nix.

The plugin rolls several different plugin executions into one. The common files
are distributed using a resource jar, used by and unpacked by the plugin.
2017-11-27 12:31:31 -05:00
mposolda 1782d220c7 KEYCLOAK-4188 CrossDC documentation. Addressed some questions and comments based on feedback. 2017-11-27 18:04:07 +01:00
Marko Strukelj c35c6e6ab7 KEYCLOAK-5762 [Client Registration CLI] Fix instructions in built-in help 2017-11-27 17:00:48 +01:00
Marko Strukelj 0e2332196d KEYCLOAK-5741 [Admin CLI] Fix instructions in build-in help 2017-11-27 16:12:00 +01:00
Pavel Drozd a8bcdfb401 Merge pull request #4735 from vmuzikar/fix-ui
KEYCLOAK-5816, KEYCLOAK-5815 UI tests fixes
2017-11-27 15:12:35 +01:00
Pavel Drozd 96e481fe45 Merge pull request #4732 from vramik/KEYCLOAK-5705
KEYCLOAK-5705 add missing dependency to module.xml for mssql migration server
2017-11-27 15:10:26 +01:00
Pavel Drozd 87b17a6e6c Merge pull request #4743 from wyvie/springboot-testing-fix
[KEYCLOAK-3837] fix to spring boot test application for jenkins
2017-11-27 15:07:22 +01:00
wyvie 6035e5c831 [KEYCLOAK-3837] fix to spring boot test application for jenkins 2017-11-27 13:58:36 +01:00
Bruno Oliveira 9d35891e7d [KEYCLOAK-5467] X.509 Auth - missing internationalization support 2017-11-27 13:44:38 +01:00
Bruno Oliveira 00677a6b92 [KEYCLOAK-5898] X.509 Auth - add tests for CRL with direct grant 2017-11-27 13:43:37 +01:00
Bruno Oliveira 697caaa805 [KEYCLOAK-4683] Add key usage tests for X.509 Authentication
These tests cover the scenarios already available at our certificates:
* Key Usage with the flag critical
* Extended Key Usage without the flag critical
2017-11-27 13:42:57 +01:00
mposolda fda491a966 KEYCLOAK-4188 CrossDataCenter docs in the README file 2017-11-27 13:37:38 +01:00
Pedro Igor 92504e8ba3 Merge pull request #4653 from cadimmek/patch-1
KEYCLOAK-5134
2017-11-27 09:12:21 -02:00
Thomas Kuestermann bb900f9db8 KEYCLOAK-5753 fixed NPE thrown when using custom RequestMatcher 2017-11-27 09:55:32 +01:00
pedroigor 819a60932e [KEYCLOAK-2517] - Doesn't work always refresh token 2017-11-24 23:40:29 -02:00
Pedro Igor e4a6ee19d1 Merge pull request #4740 from pedroigor/KEYCLOAK-5909
[KEYCLOAK-5909] - Allow providing username as owner when creating resources
2017-11-24 17:57:50 -02:00
pedroigor aef682cd5b [KEYCLOAK-5909] - Allow providing username as owner when creating resources 2017-11-24 17:08:21 -02:00
Marek Posolda dd6502013e Merge pull request #4734 from rmartinc/ui_locales
KEYCLOAK-5896: Parameter "ui_locales" not redirected to login page in java adapters
2017-11-24 10:59:26 +01:00
rmartinc ecbf6e5386 KEYCLOAK-5896: adding a test for the ui_locales change. 2017-11-24 08:21:37 +01:00
pedroigor 2721e6a5e4 [KEYCLOAK-5770] - Logout event test 2017-11-23 21:08:07 +01:00
pedroigor e3c9fa25a3 [KEYCLOAK-5770] - Global Saml Logout doesn't create logout event 2017-11-23 21:08:07 +01:00
mposolda 6d91ab674b KEYCLOAK-5895 CrossDC: NotSerializableException when opening sessions tab in admin console 2017-11-23 20:03:12 +01:00
vmuzikar 6f4ab8870e KEYCLOAK-5816, KEYCLOAK-5815 UI tests fixes 2017-11-23 13:51:38 +01:00
rmartinc 0b3ae30473 Parameter "ui_locales" not redirected to login page in java adapters 2017-11-23 11:18:29 +01:00
Pavel Drozd 94ba85c210 Merge pull request #4720 from vramik/KEYCLOAK-5872
KEYCLOAK-5872 add preview assumption to InvalidationCrossDCTest.authz…
2017-11-23 07:42:31 +01:00
Bill Burke 2117db5e6d Merge pull request #4730 from patriot1burke/master
KEYCLOAK-4715
2017-11-22 12:45:23 -05:00
Bill Burke 116bfb05c2 fix 2017-11-22 11:55:10 -05:00
Bill Burke aee6d16f58 fix more stupidity 2017-11-22 10:22:47 -05:00
Bill Burke ae29e36e1f fix my stupidity 2017-11-22 08:19:30 -05:00
vramik 02220558e6 KEYCLOAK-5705 add missing dependency to module.xml for mssql migration server 2017-11-22 14:09:01 +01:00
Pavel Drozd 12bdf48ecc Merge pull request #4707 from abstractj/KEYCLOAK-5858
[KEYCLOAK-5858] Failing TokensTest in Console UI Tests
2017-11-22 13:22:28 +01:00
mposolda bd1072d2eb KEYCLOAK-5747 Ensure refreshToken doesn't need to send request to the other DC. Other fixes and polishing 2017-11-22 11:55:12 +01:00
mposolda 61c5a332b4 KEYCLOAK-5832 Fix the issue with RemoveExpiredCommand exception on JDG side 2017-11-22 11:55:12 +01:00
Bill Burke 75d517a1e8 cleanup test 2017-11-21 21:49:51 -05:00
Bill Burke 8993ca08ad KEYCLOAK-4715 2017-11-21 17:46:48 -05:00
vramik afd906b9a9 KEYCLOAK-5705 add missing dependency to module.xml for mssql in eap module 2017-11-21 21:51:19 +01:00
vramik c083c1c4cf KEYCLOAK-5873 set DB_CLOSE_ON_EXIT to false when crossdc tests are run on in memory H2 2017-11-21 21:46:59 +01:00
Bill Burke d78bc37253 Merge pull request #4724 from mstruk/KEYCLOAK-5842
KEYCLOAK-5842 Fix NPE in admin-cli ParseUtil.mergeAttributes
2017-11-21 12:36:38 -05:00
Bill Burke d0ee1e6ee5 Merge pull request #4725 from hmlnarik/KEYCLOAK-5183-Support-for-AssertionConsumerServiceUrl-in-Saml-Adapter-subsystem
KEYCLOAK-5183 Support for AssertionConsumerServiceUrl in Saml Adapter subsystem
2017-11-21 12:35:10 -05:00
Bill Burke c16c02b0cf Merge pull request #4727 from vmuzikar/fix-social
KEYCLOAK-5879 Fix SocialLoginTest with -Pauth-server-wildfly
2017-11-21 12:34:28 -05:00
vmuzikar 7fd237b40b KEYCLOAK-5879 Fix SocialLoginTest with -Pauth-server-wildfly 2017-11-21 11:12:21 +01:00
pskopek a993f6fb75 [KEYCLOAK-4979] make schema location map unmodifiable after initial setup + log message change 2017-11-21 09:28:15 +01:00
Thomas Skjølberg 5f20df00d0 [KEYCLOAK-4979] Move picketlink schema, fix resolver and some related tests 2017-11-21 09:28:15 +01:00
Bill Burke 186b10ab53 Merge pull request #4726 from patriot1burke/master
KEYCLOAK-5878
2017-11-20 17:39:58 -05:00
Bill Burke 06762ba13d KEYCLOAK-5878 2017-11-20 17:03:28 -05:00
Bill Burke 08ca03051c Merge pull request #4549 from pkadej/KEYCLOAK-5662
KEYCLOAK-5662
2017-11-20 14:37:07 -05:00
Marko Strukelj 8f82440103 KEYCLOAK-5842 Fix NPE in admin-cli ParseUtil.mergeAttributes 2017-11-20 16:06:27 +01:00
David De VreeseandHynek Mlnarik 9485a63157 KEYCLOAK-5183 Support for AssertionConsumerServiceUrl in Saml Adapter subsystem
Co-Authored-By: Hynek Mlnarik <hmlnarik@redhat.com>
2017-11-20 15:59:47 +01:00
vramik 37b625fd99 KEYCLOAK-5872 add preview assumption to InvalidationCrossDCTest.authzResourceInvalidationTest 2017-11-20 11:30:44 +01:00
Marek Posolda 8e53ccf5ab Merge pull request #4706 from stianst/KEYCLOAK-5383
KEYCLOAK-5383 Fix creating password in LDAP through admin create user…
2017-11-20 09:17:45 +01:00
Bill Burke 7c0c48da01 Merge pull request #4717 from patriot1burke/master
KEYCLOAK-5715
2017-11-17 12:59:36 -05:00
Bill Burke ff5010cdd0 Merge pull request #4663 from mstruk/KEYCLOAK-5702
KEYCLOAK-5702 kcadm delete realm fails with nullpointer
2017-11-17 11:57:58 -05:00
Bill Burke 0940c922ef Merge pull request #4711 from tkyjovsk/KEYCLOAK-5588
KEYCLOAK-5588 Cross-dc: docker setup
2017-11-17 11:55:13 -05:00
Bill Burke 83ff0eab10 remove irrelevant comments 2017-11-17 11:36:49 -05:00
Bill Burke c66ff60c58 KEYCLOAK-5715 2017-11-17 11:34:32 -05:00
Stian Thorgersen feaf834184 KEYCLOAK-5863 Fix error when updating mapper twice in a row 2017-11-17 12:20:43 +01:00
Tomas Kyjovsky 805e9288fa KEYCLOAK-5588 Cross-dc: docker setup 2017-11-16 21:25:58 +01:00
Bruno Oliveira 641069d4fd [KEYCLOAK-5866] MigrationTest fails for extracting realm keys 2017-11-16 19:44:09 +01:00
Pavel Drozd a06fd31412 Merge pull request #4688 from vramik/KEYCLOAK-5678
KEYCLOAK-5678 configs for rh-sso and keycloak are different, skip tes…
2017-11-16 08:38:12 +01:00
Bruno Oliveira 07aa718cb9 [KEYCLOAK-5379] MigrationTest fails for migration to 3.3.0 2017-11-16 07:22:57 +01:00
Shon T. Urbas f186ea12c6 Removal of duplicate cuff. 2017-11-16 07:18:49 +01:00
Stan Silvert dc05134e41 KEYCLOAK-5850: Del realm role broken on permissions & Users tabs. 2017-11-16 07:05:08 +01:00
Pedro Igor f96c3312e2 [KEYCLOAK-5841] - Test 2017-11-16 07:03:08 +01:00
Bruno Oliveira d1579f62a3 [KEYCLOAK-5858] Failing TokensTest in Console UI Tests 2017-11-16 00:24:12 -02:00
Stian Thorgersen 86fb18395e KEYCLOAK-5383 Fix creating password in LDAP through admin create user endpoint 2017-11-15 21:20:00 +01:00
Bruno Oliveira bc28223a77 KEYCLOAK-5859 2017-11-15 20:00:58 +01:00
Stan Silvert f923211e9f KEYCLOAK-5849: Multiple errors in acct page displays '<br/>' 2017-11-15 19:59:54 +01:00
Marek Posolda 6f2ba821a6 Merge pull request #4701 from mposolda/master
LDAP performance improvements
2017-11-15 16:34:45 +01:00
Bill Burke 8839278da7 Merge pull request #4702 from patriot1burke/master
KEYCLOAK-5676
2017-11-15 10:30:12 -05:00
Bill Burke 485ba4a3e4 KEYCLOAK-5676 2017-11-15 10:29:02 -05:00
mposolda bd25040e22 KEYCLOAK-5827 Retrieve member attribute from LDAP on group/role queries just when necessary 2017-11-15 15:29:19 +01:00
mposolda c4a1764801 KEYCLOAK-5836 More logging around LDAP performance. Added LdapManyObjectsInitializerCommand to easily add many users and groups to the LDAP 2017-11-15 15:29:19 +01:00
Hynek Mlnařík 3c48ee39d9 Merge pull request #4699 from hmlnarik/KEYCLOAK-5425-Manual-migration---cannot-apply-generated-sql-script
KEYCLOAK-5425 Have preconditions evaluated in manual mode
2017-11-15 14:51:59 +01:00
Hynek Mlnarik a787cfa33a KEYCLOAK-5425 Have preconditions evaluated in manual mode 2017-11-15 13:37:32 +01:00
Pedro Igor 1bd2f0e98f Merge pull request #4674 from thomasdarimont/issue/fix-npe-in-userpermissions
KEYCLOAK-5841 Fix NPE in deletePermissionSetup in UserPermissions
2017-11-15 10:22:44 -02:00
Hynek Mlnařík 393fae74b3 Merge pull request #4693 from hmlnarik/KEYCLOAK-5349-JS-client-breaks-login-session
KEYCLOAK-5349 User session count in IdP-initiated flow tests
2017-11-15 12:35:19 +01:00
Hynek Mlnarik a2f6c16764 KEYCLOAK-5349 User session count in IdP-initiated flow tests 2017-11-15 11:41:45 +01:00
Bill Burke a70461b726 Merge pull request #4695 from patriot1burke/master
KEYCLOAK-5459 KEYCLOAK-5855
2017-11-14 22:02:13 -05:00
Bill Burke 6b8ead6c4b KEYCLOAK-5459 2017-11-14 19:37:07 -05:00
Stan Silvert 8b023f57f9 KEYCLOAK-5474: Unexp behavior with "login w/ email" and "Dup emails" (#4683)
settings.
2017-11-14 16:12:56 -05:00
Pedro Igor eebf0b0499 Merge pull request #4690 from pedroigor/KEYCLOAK-5824
[KEYCLOAK-5824] - Keycloak throws "Error while evaluating permissions" exception often
2017-11-14 18:35:56 -02:00
Pedro Igor 63a01b1e1f Merge pull request #4689 from pedroigor/KEYCLOAK-5844
[KEYCLOAK-5844] - Refreshing PAT instead of obtaining a new one every time
2017-11-14 18:25:24 -02:00
Marek Posolda 913c94dbd1 Merge pull request #4691 from mposolda/master
KEYCLOAK-5848 Possibility to configure different attribute for GET_GR…
2017-11-14 16:09:26 +01:00
mposolda 0c414eee80 KEYCLOAK-5848 Possibility to configure different attribute for GET_GROUPS_FROM_USER_MEMBEROF_STRATEGY 2017-11-14 15:05:26 +01:00
Pedro Igor b0ccce397a [KEYCLOAK-5824] - Fixing logging of error mesages 2017-11-14 11:28:21 -02:00
Pedro Igor 6731c55429 [KEYCLOAK-5824] - Wrong usage of SimpleDateFormat 2017-11-14 11:27:57 -02:00
Pedro Igor fdb618219f [KEYCLOAK-5844] - Refreshing PAT instead of obtaining a new one every time 2017-11-14 11:24:45 -02:00
Stian Thorgersen 89f4b87038 KEYCLOAK-5567 Set correct status code on login error pages 2017-11-14 12:33:29 +01:00
vramik ea869a3008 KEYCLOAK-5678 configs for rh-sso and keycloak are different, skip test interfaces for now 2017-11-14 11:27:03 +01:00
Hynek Mlnařík 1412fed265 Merge pull request #4676 from abstractj/KEYCLOAK-2052
[KEYCLOAK-2052] Allows independently set timeouts for e-mail verification link and rest e.g. forgot password link
2017-11-14 09:19:57 +01:00
Stian Thorgersen de72542151 KEYCLOAK-5795 Strip ids of client export from admin console 2017-11-14 08:49:58 +01:00
Bruno OliveiraandHynek Mlnarik 03d0488335 [KEYCLOAK-2052] Allows independently set timeouts for e-mail verification link and rest e.g. forgot password link
Co-authored-by: Hynek Mlnarik <hmlnarik@redhat.com>
2017-11-13 19:57:04 -02:00
Stian Thorgersen 925d5e1dea KEYCLOAK-3173 enable logout offline refresh token using OIDC logout endpoint 2017-11-13 18:23:39 +01:00
Martin Kanis d8b3654011 KEYCLOAK-5187 Split jackson version property 2017-11-13 18:22:32 +01:00
Stian Thorgersen 51c7917853 KEYCLOAK-5772 Missing produces type on welcome resource post 2017-11-13 16:38:42 +01:00
Stian Thorgersen d30bf938ee KEYCLOAK-5821 Fix basic auth tests with embedded Undertow 2017-11-13 16:34:25 +01:00
Thomas Darimont 5d8f449ea0 KEYCLOAK-5840 Remove unused config map in AddUser command
The only value put into this config map is the
password hash iterations count.
But `iterations` is used directly with the
hashProvider.

See:
https://lgtm.com/projects/g/keycloak/keycloak/snapshot/73c82d296e70e09343f6f55eeea56a085075b289/files/wildfly/adduser/src/main/java/org/keycloak/wildfly/adduser/AddUser.java?sort=name&dir=ASC&mode=heatmap&excluded=false#x490b537b199a8415:1
2017-11-13 15:41:11 +01:00
Hynek Mlnařík 290f6ed2ad Merge pull request #4671 from thomasdarimont/issues/remove-duplicate-check-in-saml11requestwriter
KEYCLOAK-5837 Remove duplicate check in SAML11AuthenticationQueryType
2017-11-13 14:49:11 +01:00
Hynek Mlnařík 98a46c4497 Merge pull request #4672 from thomasdarimont/issues/fix-comparison-in-saml-11-request-response-parsers
KEYCLOAK-5838 Fix comparison in SAMLSloRequestParser and SAMLSloResponseParser
2017-11-13 14:46:56 +01:00
Stian Thorgersen d02ffd33b3 KEYCLOAK-5721 Moved state checker from separate cookie to claim on identity cookie 2017-11-13 14:11:28 +01:00
Thomas Darimont a5b73a365d KEYCLOAK-5841 Fix NPE in deletePermissionSetup in UserPermissions
Previously a call to `UserPermissions#deletePermissionSetup`
always resulted in a NPE if the usersResource was null.

We now only try to delete the resourceStore information if
the given usersResource is not null.
2017-11-13 13:35:40 +01:00
Thomas Darimont 71df504834 KEYCLOAK-5838 Fix comparison in SAMLSloRequestParser and SAMLSloResponseParser
The previous comparison was broken (always returned false)
since it compared the enum value with a string.
Calling `.get()` on the enum value to compare the string this the
given local-part fixes the comparison.

See:
https://lgtm.com/projects/g/keycloak/keycloak/snapshot/73c82d296e70e09343f6f55eeea56a085075b289/files/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloRequestParser.java?sort=name&dir=ASC&mode=heatmap&excluded=false#xe3bb353ac67565ed:1
https://lgtm.com/projects/g/keycloak/keycloak/snapshot/73c82d296e70e09343f6f55eeea56a085075b289/files/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloResponseParser.java?sort=name&dir=ASC&mode=heatmap&excluded=false#xdd5c8cb1952defd:1
2017-11-13 13:30:01 +01:00
Thomas Darimont 8cfbb8d0b3 KEYCLOAK-5837 Remove duplicate check in SAML11AuthenticationQueryType
The same check is applied in line 83.
2017-11-13 13:26:15 +01:00
Pavel Drozd 0f4b8be2b0 Merge pull request #4643 from vramik/KEYCLOAK-5678
KEYCLOAK-5678 Extend server-config-migration tests to test also exten…
2017-11-13 12:02:46 +01:00
Pavel Drozd af97a84108 Merge pull request #4635 from vmuzikar/fix-x509
KEYCLOAK-5720 Fix X.509 tests
2017-11-13 11:56:16 +01:00
Przemysław Kadej e28f402b6b KEYCLOAK-5662 - CachePolicy.MAX_LIFESPAN - Cached LDAP users aren't being refreshed at all 2017-11-13 11:36:31 +01:00
Marek Posolda 0dfbe01c73 Merge pull request #4666 from mposolda/crossdc
KEYCLOAK-5828 Ensure sessions preloading works for remote distributed…
2017-11-10 14:13:31 +01:00
Stian Thorgersen 90900b1a1f KEYCLOAK-5825 Clear state checker for welcome on form submit 2017-11-10 13:40:29 +01:00
Stian Thorgersen 4295f4ec31 KEYCLOAK-1886 Added cors headers to errors in token endpoint 2017-11-10 12:01:21 +01:00
mposolda c530a061cc KEYCLOAK-5828 Ensure sessions preloading works for remote distributed caches as well 2017-11-10 11:27:24 +01:00
Thomas Recloux 04ad634986 Add configuration for KeycloakAuthenticatedActionsFilter
Fixes KEYCLOAK-5227
2017-11-10 10:44:12 +01:00
Bartłomiej Piech d4b9c3c014 KEYCLOAK-5679 2017-11-10 10:42:26 +01:00
Marek Posolda bc3515fd1b Merge pull request #4664 from mposolda/crossdc
KEYCLOAK-5371 SessionExpirationCrossDCTest - improve stability. Remov…
2017-11-09 23:15:48 +01:00
mposolda b033ce0669 KEYCLOAK-5371 SessionExpirationCrossDCTest - improve stability. Remove checks for counts of sent messages 2017-11-09 22:18:47 +01:00
Marko Strukelj 7035a4647d KEYCLOAK-5702 kcadm delete realm fails with nullpointer 2017-11-09 20:57:49 +01:00
Marek Posolda 39345b0b61 Merge pull request #4660 from mposolda/crossdc
KEYCLOAK-5618 Fix SessionsPreloadCrossDCTest. Update HOW-TO-RUN docs.…
2017-11-09 20:50:38 +01:00
emilienbondu 8b8e694c60 Fix https://issues.jboss.org/browse/KEYCLOAK-5636 NPE 2017-11-09 19:32:27 +01:00
mposolda a98f085be6 KEYCLOAK-5618 Fix SessionsPreloadCrossDCTest. Update HOW-TO-RUN docs. Ensure it's executed in travis. 2017-11-09 17:39:04 +01:00
Stian Thorgersen 128ff12f8f Bump versions 2017-11-09 15:37:21 +01:00
Stian Thorgersen e28652489e KEYCLOAK-5565 Change theme files to writable to permit patching 2017-11-09 15:37:01 +01:00
Marko Strukelj dae0fafc8a KEYCLOAK-5040 ProfileAssume needs to use server info endpoint 2017-11-09 14:19:06 +01:00
Xiaojian Liu 19eed51582 KEYCLOAK-5352 Basic Auth fails if password contains a ':' 2017-11-09 13:56:02 +01:00
Xiaojian Liu 9ff22f596d KEYCLOAK-5352 Basic Auth fails if password contains a ':' 2017-11-09 13:56:02 +01:00
Xiaojian Liu e1af9f133f KEYCLOAK-5352 Basic Auth fails if password contains a ':' 2017-11-09 13:56:02 +01:00
Bruno Oliveira 26e253f4a5 [KEYCLOAK-5284] 2017-11-09 13:45:06 +01:00
Marko Strukelj 2854a2006e KEYCLOAK-5810 ClientTokenExchangeTest failures with -Pproduct profile 2017-11-09 13:44:10 +01:00
cadimmek 518824e41b KEYCLOAK-5134
UnsupportedOperationException when using policy-enforcer-config with keycloak-spring-boot-adapter
2017-11-09 10:43:50 +01:00
vmuzikar 3bf1904269 KEYCLOAK-5786 Fix testsuite not working with auth-server-eap profile 2017-11-09 08:10:58 +01:00
vmuzikar 2c2a332f80 KEYCLOAK-5332 Fix GitLab social test 2017-11-09 07:19:01 +01:00
Stan Silvert 8759b42e6b KEYCLOAK-4461: Missing error msg when add idp mapper 2017-11-09 07:13:38 +01:00
Marko Strukelj bf937452c6 KEYCLOAK-5617 Intermittent ConcurrencyTest failures 2017-11-09 07:12:33 +01:00
liuzheng712 9243430b75 fix(Chinese language): add # encoding: utf-8 2017-11-09 07:10:59 +01:00
vmuzikar b21d5bbf04 KEYCLOAK-5805 Fix tests in the 'other' module 2017-11-09 07:01:27 +01:00
Pedro Igor 5119dc3ad5 Merge pull request #4650 from pedroigor/KEYCLOAK-4439
[KEYCLOAK-4439] - Fixing elytron adapter for standalone apps
2017-11-08 22:10:25 -02:00
Pedro Igor 476dd1cef5 [KEYCLOAK-4439] - Fixing saml adapter 2017-11-08 19:01:54 -02:00
Pedro Igor a8ba3eb7f9 [KEYCLOAK-4439] - Fixing elytron adapter for standalone apps 2017-11-08 14:09:34 -02:00
Stan Silvert d3b270d025 KEYCLOAK-5475: UI glitches when alias blank in auth config (#4645) 2017-11-08 08:21:28 -05:00
Albert-Jan Verhees b8f3c8a445 Fixed typo in Dutch translations 2017-11-08 12:59:55 +01:00
Alex Szczuczko eb46f403d6 Extract the license files with maven-dependency-plugin instead of wildfly-server-provisioning-maven-plugin
This works around WFBUILD-27 aka wildfly/wildfly-build-tools#39. Once that's merged and new version is released this commit can be reverted.
2017-11-08 12:58:38 +01:00
Alex Szczuczko 9c0ec8cb10 Enforce a standard sort order on deduplication in download-license-files.sh
This will avoid noise in the diffs (files switching between symlink and regular
status) when users on systems with a different default sort order run the
script. `LC_ALL=C sort` will sort by byte order.
2017-11-08 12:58:38 +01:00
Alex Szczuczko d268412738 Initial run of download-license-files.sh on license XMLs
Command line used:

find -path '*/src/main/resources/licenses' | while read -r r; do pushd "$r"; common/download-license-files.sh rh-sso/licenses.xml; common/download-license-files.sh keycloak/licenses.xml; popd; done
2017-11-08 12:58:38 +01:00
Alex Szczuczko e360aadcd4 Explicitly define the dependency for unpacking in js-adapter-zip.
This makes it visible to append-product-licenses.groovy and other plugins.
2017-11-08 12:58:38 +01:00
Alex Szczuczko f88b3cddb6 Port forward license data from maintenance, with improvements.
org.keycloak dependencies will be automatically added to the xml during the
build, removing the need for runs of download-license-files.sh every time the
keycloak version changes.

Documentation on "why and how" for the license data has also been added.

To reduce code duplication issues, plugin definitions are stored in
keycloak-parent, but only active in the projects that need them (not bound to
any phase by default). Also, the common files have been moved into
licenses/common/, so that a single symlink will suffice to replicate the
current and future files needed by the plugin executions. While the
assembly.xml definitions remain duplicated, they are fairly minimal and
shouldn't need to change often.

License data is available for all adapters shipped in the product, plus
server-feature-pack.

The keycloak slot is populated with data, in addition to the rh-sso slot. A
number of the adapters don't depend on any third-party artifacts, so they have
(mostly) blank license.xml files.
2017-11-08 12:58:38 +01:00
Stian Thorgersen 56548fcaf0 Upgrade to EAP 7.1 CR4 (#4649) 2017-11-08 11:40:06 +01:00
Marek Posolda fce9cc205c Merge pull request #4647 from mposolda/crossdc
KEYCLOAK-5371 More stable cross-dc tests
2017-11-08 11:14:37 +01:00
mposolda 701b7acd80 KEYCLOAK-5371 More stable cross-dc tests 2017-11-08 10:03:04 +01:00
vramik 819181007c KEYCLOAK-5678 Extend server-config-migration tests to test also extensions and management 2017-11-07 12:46:13 +01:00
Marek Posolda 7cf52044b2 Merge pull request #4640 from mposolda/crossdc
KEYCLOAK-5716 KEYCLOAK-5738 Avoid infinispan deadlock. Ensure code-to…
2017-11-07 11:04:10 +01:00
Paramvir-JIndal d1468eaa45 [KEYCLOAK-5767]IE9 sometimes using HTTP status code 1223 instead of 204 (#4628) 2017-11-07 10:46:13 +01:00
Hynek Mlnařík afb4a59b19 KEYCLOAK-5789 Update PR policy (#4634) 2017-11-07 10:32:26 +01:00
Stian Thorgersen b1a05dfce2 KEYCLOAK-5664 (#4604) 2017-11-07 10:09:34 +01:00
mposolda 62a1c187a2 KEYCLOAK-5716 KEYCLOAK-5738 Avoid infinispan deadlock. Ensure code-to-token works correctly in cross-dc 2017-11-07 09:01:59 +01:00
Stan Silvert 1db3134df8 KEYCLOAK-5506: Set empty fed config prop fails in admin console (#4625) 2017-11-06 12:44:13 -05:00
Hynek Mlnařík ffc0d7fc7c Merge pull request #4636 from hmlnarik/KEYCLOAK-5581-SAML2-Undeclared-name-space-prefix-ds-
KEYCLOAK-5581 Fix SAML identity broker context serialization
2017-11-03 22:05:13 +01:00
Hynek Mlnarik fe2f65daac KEYCLOAK-5581 Fix SAML identity broker context serialization 2017-11-03 21:09:18 +01:00
vmuzikar ef8adc15f4 KEYCLOAK-5720 Fix X.509 tests 2017-11-03 17:09:46 +01:00
Pedro Igor 1ac9bd510b Merge pull request #4623 from pedroigor/KEYCLOAK-5728
[KEYCLOAK-5728] - Permission Claims support
2017-11-03 05:42:06 -03:00
Hynek Mlnařík 4d0d28c222 Merge pull request #4621 from hmlnarik/KEYCLOAK-5230-Indexes-on-USER-ID-missing-from-the-federated-user-tables
KEYCLOAK-5230 Add indices to FED_* tables
2017-10-31 08:48:34 +01:00
Pedro Igor 6b0c58bab3 Merge pull request #4622 from pedroigor/KEYCLOAK-5763
[KEYCLOAK-5763] - Forward credentials when using Elytron Adapter
2017-10-27 14:45:13 -03:00
Pedro Igor 3716fa44ac [KEYCLOAK-5728] - Permission Claims support 2017-10-27 12:40:30 -02:00
Pedro Igor d3dee07956 [KEYCLOAK-5763] - Forward credentials when using Elytron Adapter 2017-10-27 12:34:31 -02:00
Stan Silvert 986540ab34 KEYCLOAK-4383: Dbl-click login button leads to "already logged in" page (#4614) 2017-10-27 10:29:37 -04:00
Hynek Mlnarik c9aa5e638e KEYCLOAK-5230 Add indices to FED_* tables 2017-10-27 12:43:22 +02:00
Hynek Mlnařík 8d879867b5 Merge pull request #4620 from hmlnarik/KEYCLOAK-5745-Extract-client-sessions-from-user-sessions-2
KEYCLOAK-5745 Fix ActionTokenCrossDCTest and improve logging
2017-10-27 09:55:03 +02:00
Hynek Mlnarik 92027e4c57 KEYCLOAK-5745 Fix ActionTokenCrossDCTest and improve logging 2017-10-27 08:45:05 +02:00
Pedro Igor 081ad09ed8 Merge pull request #4619 from pedroigor/KEYCLOAK-4901
[KEYCLOAK-4901] - Reviewing methods on provider spi
2017-10-26 15:33:09 -03:00
Hynek Mlnařík b0d88be420 Merge pull request #4572 from tkyjovsk/db-failover
KEYCLOAK-5691 Galera cluster, full testsuite
2017-10-26 20:22:47 +02:00
Pedro Igor 57d3c44bb7 [KEYCLOAK-4901] - New policy mgmt rest api should return specific representations for a policy type 2017-10-26 15:26:40 -02:00
Pedro Igor a70cab502c [KEYCLOAK-4901] - Reviewing methods on provider spis 2017-10-26 13:39:57 -02:00
Pavel Drozd 12b2e72162 Merge pull request #4618 from vramik/KEYCLOAK-5244
KEYCLOAK-5244 fix blacklists.path in tests
2017-10-26 15:30:14 +02:00
Tomas Kyjovsky a45a2acc4c KEYCLOAK-5691 Galera cluster, full testsuite 2017-10-26 15:27:57 +02:00
vramik 8cbdbb2fed KEYCLOAK-5244 add PasswordPolicyTest to travis-run-tests.sh 2017-10-26 14:38:57 +02:00
Marek Posolda 9e2ab2750a Merge pull request #4616 from vramik/KEYCLOAK-5749
KEYCLOAK-5749 Update migration scripts for WildFly 11 Final
2017-10-26 13:24:29 +02:00
Marek Posolda aabd47ecdf Merge pull request #4613 from tkyjovsk/KEYCLOAK-5751
KEYCLOAK-5751 Login/logout performance test scenario failing
2017-10-26 13:11:41 +02:00
Hynek Mlnařík 248da4687a Merge pull request #4610 from hmlnarik/KEYCLOAK-5745-Extract-client-sessions-from-user-sessions
KEYCLOAK-5745 Separate user and client sessions in infinispan
2017-10-26 13:09:06 +02:00
vramik 2b44cb70af KEYCLOAK-5244 fix blacklists.path in tests 2017-10-26 12:28:50 +02:00
vramik 906538e6aa KEYCLOAK-5749 Update migration scripts for WildFly 11 Final 2017-10-26 11:21:48 +02:00
Hynek Mlnarik 75c354fd94 KEYCLOAK-5745 Separate user and client sessions in infinispan 2017-10-26 10:39:41 +02:00
Tomas Kyjovsky 9bd5d31c1f Added "&amp; to &" rewrite for login form URI in the perf tests. 2017-10-25 22:28:46 +02:00
Bill Burke 903a4dd849 Merge pull request #4612 from patriot1burke/master
KEYCLOAK-5273
2017-10-25 13:54:32 -04:00
Bill Burke de6eab6d5d fix 2017-10-25 13:00:58 -04:00
Bill Burke 8c1a3253fb KEYCLOAK-5273 2017-10-25 10:31:11 -04:00
Bruno Oliveira da Silva 375e01a074 KEYCLOAK-5278 (#4606) 2017-10-25 15:27:24 +02:00
Marek Posolda 74f5c1c160 Merge pull request #4611 from mposolda/ldap-eviction-day
KEYCLOAK-5746 Bad label for evictionDay in admin console in EVICT_WEE…
2017-10-25 11:14:18 +02:00
mposolda 5f889dd3c6 KEYCLOAK-5746 Bad label for evictionDay in admin console in EVICT_WEEKLY cache policy 2017-10-25 09:08:33 +02:00
Bill Burke 50ccb5e5f6 Merge pull request #4591 from abstractj/KEYCLOAK-5717
KEYCLOAK-5717
2017-10-24 17:38:28 -04:00
Marek Posolda 9793e6aa25 Merge pull request #4608 from mposolda/wf-upgrade
KEYCLOAK-4936 Upgrade to WildFly 11.0.0.Final
2017-10-24 22:35:40 +02:00
mposolda 2dd6a2e655 KEYCLOAK-4936 Upgrade to WildFly 11.0.0.Final 2017-10-24 21:23:19 +02:00
Pedro Igor 1840cc54e4 Merge pull request #4601 from pedroigor/KEYCLOAK-5726
[KEYCLOAK-5726] - Support define enforcement mode for scopes on the adapter config
2017-10-24 12:51:52 -02:00
Hynek Mlnařík 7190820fc4 Merge pull request #4566 from vramik/KEYCLOAK-4928
KEYCLOAK-4928 Add primary key constraints
2017-10-24 15:40:37 +02:00
Pedro Igor 80e9b08bb6 [KEYCLOAK-5726] - Tests for scopes-enforcement-mode ALL and ANY 2017-10-24 11:37:41 -02:00
Bruno Oliveira 4d762159ef KEYCLOAK-5717 2017-10-24 10:55:02 -02:00
Marek Posolda a7bc294a08 Merge pull request #4594 from hmlnarik/KEYCLOAK-5688-Externalizer-for-entities-shared-across-DC-2
KEYCLOAK-5688 Externalizers for cluster messages and predicates
2017-10-24 14:41:16 +02:00
Hynek Mlnařík 8e0cc2a5ea Merge pull request #4605 from mposolda/master
KEYCLOAK-5710 Change cache-server to use backups based caches
2017-10-24 14:40:01 +02:00
Pedro Igor bc12193c3c [KEYCLOAK-5726] - Fixes the parameter value when making queries 2017-10-24 10:39:55 -02:00
Pedro Igor 7dd7b6b984 [KEYCLOAK-5726] - Defaults to true in case no required scopes are defined 2017-10-24 10:39:55 -02:00
Pedro Igor 711aa83d31 [KEYCLOAK-5726] - Fixing web origins on photoz quickstart 2017-10-24 10:39:55 -02:00
Pedro Igor a6e1413d58 [KEYCLOAK-5726] - Support define enforcement mode for scopes on the adapter configuration 2017-10-24 10:39:54 -02:00
Pavel Drozd a4ec32ba66 Merge pull request #4602 from vramik/KEYCLOAK-5244
KEYCLOAK-5244 fix PasswordPolicyTest.testBlacklistPasswordPolicyWithT…
2017-10-24 14:17:35 +02:00
mposolda 9a19e95b60 KEYCLOAK-5710 Change cache-server to use backups based caches 2017-10-24 11:52:08 +02:00
Stian Thorgersen f0bbcbf0fd KEYCLOAK-5487 (#4603) 2017-10-24 10:49:08 +02:00
vramik 223713bc53 KEYCLOAK-4928 Add primary key constraints 2017-10-24 10:46:46 +02:00
Stan Silvert 330f2acc29 KEYCLOAK-5732: Freemarker pages using ?html (#4607) 2017-10-23 16:35:45 -04:00
Pavel Drozd 4010e5e247 Merge pull request #4588 from tkyjovsk/KEYCLOAK-5711
KEYCLOAK-5711 Adapter test module for Wildfly has compilation error
2017-10-23 22:27:31 +02:00
Stan Silvert 574fd42534 KEYCLOAK-5681: Upgrade AngularJS for Admin Console (#4571) 2017-10-23 12:03:46 -04:00
Stan Silvert 9083e5fe5c KEYCLOAK-5298: Enable autoescaping in Freemarker (#4561)
* KEYCLOAK-5298: Enable autoescaping in Freemarker

* Fix several of the failing tests.

* Fix broken tests in integration-deprecated

* Fix last failing test.
2017-10-23 12:03:00 -04:00
Pavel Drozd 1eb2d894e5 Merge pull request #4593 from vmuzikar/ssl-fix
KEYCLOAK-5719 fix auth server ssl with base testsuite
2017-10-23 16:39:22 +02:00
Stian Thorgersen 9b75b603e3 KEYCLOAK-5234 (#4585) 2017-10-23 16:13:22 +02:00
Hynek Mlnarik faf830dc77 KEYCLOAK-5688 Externalizers for cluster messages and predicates 2017-10-23 12:18:56 +02:00
vramik a3a1761eb7 KEYCLOAK-5244 fix PasswordPolicyTest.testBlacklistPasswordPolicyWithTestBlacklist on Wildfly 2017-10-23 12:11:36 +02:00
vmuzikar 1dd2a90e20 KEYCLOAK-5719 fix auth server ssl with base testsuite 2017-10-20 14:10:06 +02:00
Tomas Kyjovsky bd706418c9 KEYCLOAK-5711 Adapter test module for Wildfly has compilation error 2017-10-19 19:41:31 +02:00
6431 changed files with 1400793 additions and 93520 deletions

No files matched your search

+4 -1
View File
@@ -8,6 +8,9 @@
.project
.settings
.classpath
bin/
.factorypath
# NetBeans #
############
@@ -54,4 +57,4 @@ target
##########
# KEYCLOAK-5391: We will re-exclude node_modules when node_modules handling is worked out.
# For now, we keep our js libraries checked into GitHub, so we don't ignore.
#node_modules
#node_modules
+16 -2
View File
@@ -1,5 +1,4 @@
language: java
dist: precise
cache:
cache: false
@@ -15,14 +14,29 @@ env:
- TESTS=server-group3
- TESTS=server-group4
- TESTS=old
- TESTS=crossdc
- TESTS=crossdc-server
- TESTS=crossdc-adapter
- TESTS=ssl
jdk:
- oraclejdk8
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
after_success:
- .travis/docker-hub-master-trigger.sh
sudo: false
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
if [ "$DOCKER_HUB_TOKEN" != "" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_REPO_SLUG" == "keycloak/keycloak" ] && [ "$TRAVIS_BRANCH" == "master" ]; then
curl -H "Content-Type: application/json" --data '{"docker_tag": "master"}' -X POST https://registry.hub.docker.com/u/jboss/keycloak/trigger/$DOCKER_HUB_TOKEN/
echo "Triggered Docker hub build"
elif [ "$DOCKER_HUB_TOKEN" != "" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_REPO_SLUG" == "keycloak/keycloak" ] && [ "$TRAVIS_BRANCH" == "openshift-integration2" ]; then
curl -H "Content-Type: application/json" --data '{"docker_tag": "nightly-openshift-integration2"}' -X POST https://registry.hub.docker.com/u/jboss/keycloak/trigger/$DOCKER_HUB_TOKEN/
echo "Triggered Docker hub build"
fi
+4
View File
@@ -32,6 +32,10 @@ To build the distribution run:
Once completed you will find distribution archives in `distribution`.
To build only the server run:
mvn -Pdistribution -pl distribution/server-dist -am -Dmaven.test.skip clean install
Starting Keycloak
-----------------
+11 -2
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.0.Final</version>
<version>4.4.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -36,7 +36,16 @@
</keycloak.osgi.export>
<keycloak.osgi.import>
org.keycloak.*;version="${project.version}",
org.apache.http.*;version=${apache.httpcomponents.version},
org.apache.http.auth.*;version=${apache.httpcomponents.version},
org.apache.http.client.*;version=${apache.httpcomponents.version},
org.apache.http.conn.*;version=${apache.httpcomponents.version},
org.apache.http.cookie.*;version=${apache.httpcomponents.version},
org.apache.http.impl.auth.*;version=${apache.httpcomponents.version},
org.apache.http.impl.client.*;version=${apache.httpcomponents.version},
org.apache.http.impl.conn.*;version=${apache.httpcomponents.version},
org.apache.http.impl.cookie.*;version=${apache.httpcomponents.version},
org.apache.http.impl.execchain.*;version=${apache.httpcomponents.version},
org.apache.http.*;version=${apache.httpcomponents.httpcore.version},
org.apache.karaf.jaas.boot.principal;resolution:=optional,
org.apache.karaf.jaas.modules;resolution:=optional,
*;resolution:=optional
@@ -206,6 +206,16 @@ public class AdapterDeploymentContext {
public void setBearerOnly(boolean bearerOnly) {
delegate.setBearerOnly(bearerOnly);
}
@Override
public boolean isAutodetectBearerOnly() {
return delegate.isAutodetectBearerOnly();
}
@Override
public void setAutodetectBearerOnly(boolean autodetectBearerOnly) {
delegate.setAutodetectBearerOnly(autodetectBearerOnly);
}
@Override
public boolean isEnableBasicAuth() {
@@ -277,6 +287,16 @@ public class AdapterDeploymentContext {
delegate.setSslRequired(sslRequired);
}
@Override
public int getConfidentialPort() {
return delegate.getConfidentialPort();
}
@Override
public void setConfidentialPort(int confidentialPort) {
delegate.setConfidentialPort(confidentialPort);
}
@Override
public TokenStore getTokenStore() {
return delegate.getTokenStore();
@@ -102,6 +102,14 @@ public class AuthenticatedActionsHandler {
KeycloakSecurityContext securityContext = facade.getSecurityContext();
String origin = facade.getRequest().getHeader(CorsHeaders.ORIGIN);
String exposeHeaders = deployment.getCorsExposedHeaders();
if (deployment.getPolicyEnforcer() != null) {
if (exposeHeaders != null) {
exposeHeaders += ",";
}
exposeHeaders += "WWW-Authenticate";
}
String requestOrigin = UriUtils.getOrigin(facade.getRequest().getURI());
log.debugv("Origin: {0} uri: {1}", origin, facade.getRequest().getURI());
if (securityContext != null && origin != null && !origin.equals(requestOrigin)) {
@@ -71,9 +71,10 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
AccessTokenResponse atr=null;
try {
String userpw=new String(Base64.decode(tokenString));
String[] parts=userpw.split(":");
atr = getToken(parts[0], parts[1]);
int seperatorIndex = userpw.indexOf(":");
String user = userpw.substring(0, seperatorIndex);
String pw = userpw.substring(seperatorIndex + 1);
atr = getToken(user, pw);
tokenString = atr.getToken();
} catch (Exception e) {
log.debug("Failed to obtain token", e);
@@ -82,9 +83,9 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
}
return authenticateToken(exchange, atr.getToken());
}
private AccessTokenResponse getToken(String username, String password) throws Exception {
}
protected AccessTokenResponse getToken(String username, String password) throws Exception {
AccessTokenResponse tokenResponse=null;
HttpClient client = deployment.getClient();
@@ -23,6 +23,8 @@ import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.common.VerificationException;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.representations.AccessToken;
import javax.security.cert.X509Certificate;
@@ -83,6 +85,16 @@ public class BearerTokenRequestAuthenticator {
}
protected AuthOutcome authenticateToken(HttpFacade exchange, String tokenString) {
log.debug("Verifying access_token");
if (log.isTraceEnabled()) {
try {
JWSInput jwsInput = new JWSInput(tokenString);
String wireString = jwsInput.getWireString();
log.tracef("\taccess_token: %s", wireString.substring(0, wireString.lastIndexOf(".")) + ".signature");
} catch (JWSInputException e) {
log.errorf(e, "Failed to parse access_token: %s", tokenString);
}
}
try {
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
} catch (VerificationException e) {
@@ -124,6 +136,7 @@ public class BearerTokenRequestAuthenticator {
}
surrogate = chain[0].getSubjectDN().getName();
}
log.debug("successful authorized");
return AuthOutcome.AUTHENTICATED;
}
@@ -161,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);
@@ -57,6 +57,8 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
/**
* Abstraction for creating HttpClients. Allows SSL configuration.
@@ -289,6 +291,7 @@ public class HttpClientBuilder {
cm = new SingleClientConnManager(registry);
}
BasicHttpParams params = new BasicHttpParams();
params.setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY);
if (proxyHost != null) {
params.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxyHost);
@@ -68,6 +68,7 @@ public class KeycloakDeployment {
protected String scope;
protected SslRequired sslRequired = SslRequired.ALL;
protected int confidentialPort = -1;
protected TokenStore tokenStore = TokenStore.SESSION;
protected String stateCookieName = "OAuth_Token_Request_State";
protected boolean useResourceRoleMappings;
@@ -153,6 +154,8 @@ public class KeycloakDeployment {
log.debug("resolveUrls");
}
authServerBaseUrl = authUrlBuilder.build().toString();
String login = authUrlBuilder.clone().path(ServiceUrlConstants.AUTH_PATH).build(getRealm()).toString();
authUrl = KeycloakUriBuilder.fromUri(login);
realmInfoUrl = authUrlBuilder.clone().path(ServiceUrlConstants.REALM_INFO_PATH).build(getRealm()).toString();
@@ -277,6 +280,14 @@ public class KeycloakDeployment {
this.sslRequired = sslRequired;
}
public int getConfidentialPort() {
return confidentialPort;
}
public void setConfidentialPort(int confidentialPort) {
this.confidentialPort = confidentialPort;
}
public TokenStore getTokenStore() {
return tokenStore;
}
@@ -78,6 +78,11 @@ public class KeycloakDeploymentBuilder {
} else {
deployment.setSslRequired(SslRequired.EXTERNAL);
}
if (adapterConfig.getConfidentialPort() != -1) {
deployment.setConfidentialPort(adapterConfig.getConfidentialPort());
}
if (adapterConfig.getTokenStore() != null) {
deployment.setTokenStore(TokenStore.valueOf(adapterConfig.getTokenStore().toUpperCase()));
} else {
@@ -66,7 +66,7 @@ public class OAuthRequestAuthenticator {
this.reqAuthenticator = requestAuthenticator;
this.facade = facade;
this.deployment = deployment;
this.sslRedirectPort = sslRedirectPort;
this.sslRedirectPort = deployment.getConfidentialPort() != -1 ? deployment.getConfidentialPort() : sslRedirectPort;
this.tokenStore = tokenStore;
}
@@ -170,6 +170,9 @@ public class OAuthRequestAuthenticator {
String maxAge = getQueryParamValue(OAuth2Constants.MAX_AGE);
url = UriUtils.stripQueryParam(url, OAuth2Constants.MAX_AGE);
String uiLocales = getQueryParamValue(OAuth2Constants.UI_LOCALES_PARAM);
url = UriUtils.stripQueryParam(url, OAuth2Constants.UI_LOCALES_PARAM);
KeycloakUriBuilder redirectUriBuilder = deployment.getAuthUrl().clone()
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
@@ -188,6 +191,9 @@ public class OAuthRequestAuthenticator {
if (maxAge != null && maxAge.length() > 0) {
redirectUriBuilder.queryParam(OAuth2Constants.MAX_AGE, maxAge);
}
if (uiLocales != null && uiLocales.length() > 0) {
redirectUriBuilder.queryParam(OAuth2Constants.UI_LOCALES_PARAM, uiLocales);
}
scope = TokenUtil.attachOIDCScope(scope);
redirectUriBuilder.queryParam(OAuth2Constants.SCOPE, scope);
@@ -344,6 +350,14 @@ public class OAuthRequestAuthenticator {
tokenString = tokenResponse.getToken();
refreshToken = tokenResponse.getRefreshToken();
idTokenString = tokenResponse.getIdToken();
log.debug("Verifying tokens");
if (log.isTraceEnabled()) {
logToken("\taccess_token", tokenString);
logToken("\tid_token", idTokenString);
logToken("\trefresh_token", refreshToken);
}
try {
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
if (idTokenString != null) {
@@ -376,7 +390,8 @@ public class OAuthRequestAuthenticator {
protected String stripOauthParametersFromRedirect() {
KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(facade.getRequest().getURI())
.replaceQueryParam(OAuth2Constants.CODE, null)
.replaceQueryParam(OAuth2Constants.STATE, null);
.replaceQueryParam(OAuth2Constants.STATE, null)
.replaceQueryParam(OAuth2Constants.SESSION_STATE, null);
return builder.build().toString();
}
@@ -398,4 +413,13 @@ public class OAuthRequestAuthenticator {
return originalUri;
}
private void logToken(String name, String token) {
try {
JWSInput jwsInput = new JWSInput(token);
String wireString = jwsInput.getWireString();
log.tracef("\t%s: %s", name, wireString.substring(0, wireString.lastIndexOf(".")) + ".signature");
} catch (JWSInputException e) {
log.errorf(e, "Failed to parse %s: %s", name, token);
}
}
}
@@ -32,7 +32,7 @@ import java.util.Map;
*/
public class ClientIdAndSecretCredentialsProvider implements ClientCredentialsProvider {
private static Logger logger = Logger.getLogger(ClientCredentialsProviderUtils.class);
private static Logger logger = Logger.getLogger(ClientIdAndSecretCredentialsProvider.class);
public static final String PROVIDER_ID = CredentialRepresentation.SECRET;
@@ -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;
}
}
@@ -17,23 +17,29 @@
*/
package org.keycloak.adapters.authorization;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.jboss.logging.Logger;
import org.keycloak.AuthorizationContext;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.HttpFacade.Request;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.ClientAuthorizationContext;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessToken.Authorization;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.EnforcementMode;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.MethodConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.ScopeEnforcementMode;
import org.keycloak.representations.idm.authorization.Permission;
/**
@@ -42,103 +48,118 @@ import org.keycloak.representations.idm.authorization.Permission;
public abstract class AbstractPolicyEnforcer {
private static Logger LOGGER = Logger.getLogger(AbstractPolicyEnforcer.class);
private final PolicyEnforcerConfig enforcerConfig;
private static final String HTTP_METHOD_DELETE = "DELETE";
private final PolicyEnforcer policyEnforcer;
private Map<String, PathConfig> paths;
private AuthzClient authzClient;
private PathMatcher pathMatcher;
public AbstractPolicyEnforcer(PolicyEnforcer policyEnforcer) {
protected AbstractPolicyEnforcer(PolicyEnforcer policyEnforcer) {
this.policyEnforcer = policyEnforcer;
this.enforcerConfig = policyEnforcer.getEnforcerConfig();
this.authzClient = policyEnforcer.getClient();
this.pathMatcher = policyEnforcer.getPathMatcher();
this.paths = policyEnforcer.getPaths();
}
public AuthorizationContext authorize(OIDCHttpFacade httpFacade) {
EnforcementMode enforcementMode = this.enforcerConfig.getEnforcementMode();
EnforcementMode enforcementMode = getEnforcerConfig().getEnforcementMode();
if (EnforcementMode.DISABLED.equals(enforcementMode)) {
return createEmptyAuthorizationContext(true);
}
Request request = httpFacade.getRequest();
PathConfig pathConfig = getPathConfig(request);
KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();
if (securityContext != null) {
AccessToken accessToken = securityContext.getToken();
if (securityContext == null) {
if (!isDefaultAccessDeniedUri(request)) {
if (pathConfig != null) {
if (EnforcementMode.DISABLED.equals(pathConfig.getEnforcementMode())) {
return createEmptyAuthorizationContext(true);
} else {
challenge(pathConfig, getRequiredScopes(pathConfig, request), httpFacade);
}
} else {
handleAccessDenied(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();
if (accessToken != null) {
if (LOGGER.isDebugEnabled()) {
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);
if (LOGGER.isDebugEnabled()) {
LOGGER.debugf("Could not find a configuration for path [%s]", getPath(request));
}
Set<String> requiredScopes = getRequiredScopes(pathConfig, request);
if (isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade)) {
try {
return createAuthorizationContext(accessToken, pathConfig);
} catch (Exception e) {
throw new RuntimeException("Error processing path [" + pathConfig.getPath() + "].", e);
}
if (isDefaultAccessDeniedUri(request)) {
return createAuthorizationContext(accessToken, null);
}
handleAccessDenied(httpFacade);
return createEmptyAuthorizationContext(false);
}
if (EnforcementMode.DISABLED.equals(pathConfig.getEnforcementMode())) {
return createAuthorizationContext(accessToken, pathConfig);
}
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);
}
}
if (methodConfig != null && ScopeEnforcementMode.DISABLED.equals(methodConfig.getScopesEnforcementMode())) {
return createEmptyAuthorizationContext(true);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
}
if (!challenge(pathConfig, requiredScopes, httpFacade)) {
if (!challenge(pathConfig, methodConfig, httpFacade)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
handleAccessDenied(httpFacade);
}
handleAccessDenied(httpFacade);
}
}
return createEmptyAuthorizationContext(false);
}
protected abstract boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade);
protected abstract boolean challenge(PathConfig pathConfig, MethodConfig methodConfig, OIDCHttpFacade facade);
protected boolean isAuthorized(PathConfig actualPathConfig, Set<String> requiredScopes, AccessToken accessToken, OIDCHttpFacade httpFacade) {
protected boolean isAuthorized(PathConfig actualPathConfig, MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
Request request = httpFacade.getRequest();
PolicyEnforcerConfig enforcerConfig = getEnforcerConfig();
if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
if (isDefaultAccessDeniedUri(request)) {
return true;
}
AccessToken.Authorization authorization = accessToken.getAuthorization();
Authorization authorization = accessToken.getAuthorization();
if (authorization == null) {
return false;
}
List<Permission> permissions = authorization.getPermissions();
boolean hasPermission = false;
Collection<Permission> grantedPermissions = authorization.getPermissions();
for (Permission permission : permissions) {
if (permission.getResourceSetId() != null) {
for (Permission permission : grantedPermissions) {
if (permission.getResourceId() != null) {
if (isResourcePermission(actualPathConfig, permission)) {
hasPermission = true;
@@ -146,16 +167,19 @@ public abstract class AbstractPolicyEnforcer {
continue;
}
if (hasResourceScopePermission(requiredScopes, permission, actualPathConfig)) {
LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions);
if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) {
this.paths.remove(actualPathConfig);
if (hasResourceScopePermission(methodConfig, permission)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, grantedPermissions);
}
return true;
if (HTTP_METHOD_DELETE.equalsIgnoreCase(request.getMethod()) && actualPathConfig.isInstance()) {
policyEnforcer.getPathMatcher().removeFromCache(getPath(request));
}
return hasValidClaims(actualPathConfig, permission, httpFacade, authorization);
}
}
} else {
if (hasResourceScopePermission(requiredScopes, permission, actualPathConfig)) {
if (hasResourceScopePermission(methodConfig, permission)) {
hasPermission = true;
return true;
}
@@ -166,45 +190,83 @@ public abstract class AbstractPolicyEnforcer {
return true;
}
LOGGER.debugf("Authorization FAILED for path [%s]. No enough permissions [%s].", actualPathConfig, permissions);
if (LOGGER.isDebugEnabled()) {
LOGGER.debugf("Authorization FAILED for path [%s]. Not enough permissions [%s].", actualPathConfig, grantedPermissions);
}
return false;
}
private boolean hasValidClaims(PathConfig actualPathConfig, Permission permission, OIDCHttpFacade httpFacade, Authorization authorization) {
Map<String, Set<String>> grantedClaims = permission.getClaims();
if (grantedClaims != null) {
Map<String, List<String>> claims = resolveClaims(actualPathConfig, httpFacade);
if (claims.isEmpty()) {
return false;
}
for (Entry<String, Set<String>> entry : grantedClaims.entrySet()) {
List<String> requestClaims = claims.get(entry.getKey());
if (requestClaims == null || requestClaims.isEmpty() || !entry.getValue().containsAll(requestClaims)) {
return false;
}
}
}
return true;
}
protected void handleAccessDenied(OIDCHttpFacade httpFacade) {
httpFacade.getResponse().sendError(403);
}
private boolean isDefaultAccessDeniedUri(Request request, PolicyEnforcerConfig enforcerConfig) {
String accessDeniedPath = enforcerConfig.getOnDenyRedirectTo();
if (accessDeniedPath != null) {
if (request.getURI().contains(accessDeniedPath)) {
return true;
}
}
return false;
}
private boolean hasResourceScopePermission(Set<String> requiredScopes, Permission permission, PathConfig actualPathConfig) {
Set<String> allowedScopes = permission.getScopes();
return (allowedScopes.containsAll(requiredScopes) || allowedScopes.isEmpty());
}
protected AuthzClient getAuthzClient() {
return this.authzClient;
return policyEnforcer.getClient();
}
protected PolicyEnforcerConfig getEnforcerConfig() {
return enforcerConfig;
return policyEnforcer.getEnforcerConfig();
}
protected PolicyEnforcer getPolicyEnforcer() {
return policyEnforcer;
}
private boolean isDefaultAccessDeniedUri(Request request) {
String accessDeniedPath = getEnforcerConfig().getOnDenyRedirectTo();
return accessDeniedPath != null && request.getURI().contains(accessDeniedPath);
}
private boolean hasResourceScopePermission(MethodConfig methodConfig, Permission permission) {
List<String> requiredScopes = methodConfig.getScopes();
Set<String> allowedScopes = permission.getScopes();
if (allowedScopes.isEmpty()) {
return true;
}
PolicyEnforcerConfig.ScopeEnforcementMode enforcementMode = methodConfig.getScopesEnforcementMode();
if (PolicyEnforcerConfig.ScopeEnforcementMode.ALL.equals(enforcementMode)) {
return allowedScopes.containsAll(requiredScopes);
}
if (PolicyEnforcerConfig.ScopeEnforcementMode.ANY.equals(enforcementMode)) {
for (String requiredScope : requiredScopes) {
if (allowedScopes.contains(requiredScope)) {
return true;
}
}
}
return requiredScopes.isEmpty();
}
private AuthorizationContext createEmptyAuthorizationContext(final boolean granted) {
return new ClientAuthorizationContext(authzClient) {
return new ClientAuthorizationContext(getAuthzClient()) {
@Override
public boolean hasPermission(String resourceName, String scopeName) {
return granted;
@@ -236,24 +298,26 @@ public abstract class AbstractPolicyEnforcer {
return request.getRelativePath();
}
private Set<String> getRequiredScopes(PathConfig pathConfig, Request request) {
Set<String> requiredScopes = new HashSet<>();
requiredScopes.addAll(pathConfig.getScopes());
private MethodConfig getRequiredScopes(PathConfig pathConfig, Request request) {
String method = request.getMethod();
for (PolicyEnforcerConfig.MethodConfig methodConfig : pathConfig.getMethods()) {
for (MethodConfig methodConfig : pathConfig.getMethods()) {
if (methodConfig.getMethod().equals(method)) {
requiredScopes.addAll(methodConfig.getScopes());
return methodConfig;
}
}
return requiredScopes;
MethodConfig methodConfig = new MethodConfig();
methodConfig.setMethod(request.getMethod());
methodConfig.setScopes(pathConfig.getScopes());
methodConfig.setScopesEnforcementMode(PolicyEnforcerConfig.ScopeEnforcementMode.ANY);
return methodConfig;
}
private AuthorizationContext createAuthorizationContext(AccessToken accessToken, PathConfig pathConfig) {
return new ClientAuthorizationContext(accessToken, pathConfig, this.paths, authzClient);
return new ClientAuthorizationContext(accessToken, pathConfig, getAuthzClient());
}
private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) {
@@ -269,6 +333,34 @@ public abstract class AbstractPolicyEnforcer {
}
private boolean matchResourcePermission(PathConfig actualPathConfig, Permission permission) {
return permission.getResourceSetId().equals(actualPathConfig.getId());
return permission.getResourceId().equals(actualPathConfig.getId());
}
private PathConfig getPathConfig(Request request) {
return isDefaultAccessDeniedUri(request) ? null : policyEnforcer.getPathMatcher().matches(getPath(request));
}
protected Map<String, List<String>> resolveClaims(PathConfig pathConfig, OIDCHttpFacade httpFacade) {
Map<String, List<String>> claims = getClaims(getEnforcerConfig().getClaimInformationPointConfig(), httpFacade);
claims.putAll(getClaims(pathConfig.getClaimInformationPointConfig(), httpFacade));
return claims;
}
private Map<String, List<String>> getClaims(Map<String, Map<String, Object>>claimInformationPointConfig, HttpFacade httpFacade) {
Map<String, List<String>> claims = new HashMap<>();
if (claimInformationPointConfig != null) {
for (Entry<String, Map<String, Object>> claimDef : claimInformationPointConfig.entrySet()) {
ClaimInformationPointProviderFactory factory = getPolicyEnforcer().getClaimInformationPointProviderFactories().get(claimDef.getKey());
if (factory != null) {
claims.putAll(factory.create(claimDef.getValue()).resolve(httpFacade));
}
}
}
return claims;
}
}
@@ -1,85 +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.authorization;
import java.util.Set;
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.PathConfig;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
private static Logger LOGGER = Logger.getLogger(BearerTokenPolicyEnforcer.class);
public BearerTokenPolicyEnforcer(PolicyEnforcer enforcer) {
super(enforcer);
}
@Override
protected boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
if (getEnforcerConfig().getUserManagedAccess() != null) {
challengeUmaAuthentication(pathConfig, requiredScopes, facade);
} else {
challengeEntitlementAuthentication(facade);
}
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, Set<String> requiredScopes, OIDCHttpFacade facade) {
HttpFacade.Response response = facade.getResponse();
AuthzClient authzClient = getAuthzClient();
String ticket = getPermissionTicket(pathConfig, requiredScopes, 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, Set<String> requiredScopes, AuthzClient authzClient) {
ProtectionResource protection = authzClient.protection();
PermissionResource permission = protection.permission();
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceSetId(pathConfig.getId());
permissionRequest.setScopes(requiredScopes);
return permission.forResource(permissionRequest).getTicket();
}
}
@@ -0,0 +1,30 @@
/*
* 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.authorization;
import java.util.List;
import java.util.Map;
import org.keycloak.adapters.spi.HttpFacade;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface ClaimInformationPointProvider {
Map<String, List<String>> resolve(HttpFacade httpFacade);
}
@@ -0,0 +1,33 @@
/*
* 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.authorization;
import java.util.Map;
import org.keycloak.adapters.spi.HttpFacade;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface ClaimInformationPointProviderFactory<C extends ClaimInformationPointProvider> {
String getName();
void init(PolicyEnforcer policyEnforcer);
C create(Map<String, Object> config);
}
@@ -18,25 +18,30 @@
package org.keycloak.adapters.authorization;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.List;
import java.util.Map;
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.authorization.client.resource.PermissionResource;
import org.keycloak.authorization.client.resource.ProtectionResource;
import org.keycloak.common.util.Base64;
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.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -50,14 +55,14 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
}
@Override
protected boolean isAuthorized(PathConfig pathConfig, Set<String> requiredScopes, AccessToken accessToken, OIDCHttpFacade httpFacade) {
protected boolean isAuthorized(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
AccessToken original = accessToken;
if (super.isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade)) {
if (super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
return true;
}
accessToken = requestAuthorizationToken(pathConfig, requiredScopes, httpFacade);
accessToken = requestAuthorizationToken(pathConfig, methodConfig, httpFacade);
if (accessToken == null) {
return false;
@@ -73,17 +78,45 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
AccessToken.Authorization newAuthorization = accessToken.getAuthorization();
if (newAuthorization != null) {
authorization.getPermissions().addAll(newAuthorization.getPermissions());
Collection<Permission> grantedPermissions = authorization.getPermissions();
Collection<Permission> newPermissions = newAuthorization.getPermissions();
for (Permission newPermission : newPermissions) {
if (!grantedPermissions.contains(newPermission)) {
grantedPermissions.add(newPermission);
}
}
}
original.setAuthorization(authorization);
return super.isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade);
return super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade);
}
@Override
protected boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
handleAccessDenied(facade);
protected boolean challenge(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade) {
if (isBearerAuthorization(httpFacade)) {
HttpFacade.Response response = httpFacade.getResponse();
AuthzClient authzClient = getAuthzClient();
String ticket = getPermissionTicket(pathConfig, methodConfig, authzClient, httpFacade);
if (ticket != null) {
response.setStatus(401);
response.setHeader("WWW-Authenticate", new StringBuilder("UMA realm=\"").append(authzClient.getConfiguration().getRealm()).append("\"").append(",as_uri=\"")
.append(authzClient.getServerConfiguration().getIssuer()).append("\"").append(",ticket=\"").append(ticket).append("\"").toString());
} else {
response.setStatus(403);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending challenge");
}
return true;
}
handleAccessDenied(httpFacade);
return true;
}
@@ -100,52 +133,88 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
}
}
private AccessToken requestAuthorizationToken(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade httpFacade) {
try {
String accessToken = httpFacade.getSecurityContext().getTokenString();
AuthzClient authzClient = getAuthzClient();
KeycloakDeployment deployment = getPolicyEnforcer().getDeployment();
if (getEnforcerConfig().getUserManagedAccess() != null) {
LOGGER.debug("Obtaining authorization for authenticated user.");
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceSetId(pathConfig.getId());
permissionRequest.setScopes(requiredScopes);
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;
} 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);
}
}
} catch (AuthorizationDeniedException e) {
LOGGER.debug("Authorization denied", e);
private AccessToken requestAuthorizationToken(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade) {
if (getEnforcerConfig().getUserManagedAccess() != null) {
return null;
}
try {
KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();
String accessTokenString = securityContext.getTokenString();
KeycloakDeployment deployment = getPolicyEnforcer().getDeployment();
AccessToken accessToken = securityContext.getToken();
AuthorizationRequest authzRequest = new AuthorizationRequest();
if (isBearerAuthorization(httpFacade) || accessToken.getAuthorization() != null) {
authzRequest.addPermission(pathConfig.getId(), methodConfig.getScopes());
}
Map<String, List<String>> claims = resolveClaims(pathConfig, httpFacade);
if (!claims.isEmpty()) {
authzRequest.setClaimTokenFormat("urn:ietf:params:oauth:token-type:jwt");
authzRequest.setClaimToken(Base64.encodeBytes(JsonSerialization.writeValueAsBytes(claims)));
}
if (accessToken.getAuthorization() != null) {
authzRequest.setRpt(accessTokenString);
}
LOGGER.debug("Obtaining authorization for authenticated user.");
AuthorizationResponse authzResponse;
if (isBearerAuthorization(httpFacade)) {
authzRequest.setSubjectToken(accessTokenString);
authzResponse = getAuthzClient().authorization().authorize(authzRequest);
} else {
authzResponse = getAuthzClient().authorization(accessTokenString).authorize(authzRequest);
}
if (authzResponse != null) {
return AdapterRSATokenVerifier.verifyToken(authzResponse.getToken(), deployment);
}
} catch (AuthorizationDeniedException ignore) {
LOGGER.debug("Authorization denied", ignore);
} catch (Exception e) {
throw new RuntimeException("Unexpected error during authorization request.", e);
}
return null;
}
private String getPermissionTicket(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AuthzClient authzClient, OIDCHttpFacade httpFacade) {
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()));
Map<String, List<String>> claims = resolveClaims(pathConfig, httpFacade);
if (!claims.isEmpty()) {
permissionRequest.setClaims(claims);
}
return permission.create(permissionRequest).getTicket();
}
return null;
}
private boolean isBearerAuthorization(OIDCHttpFacade httpFacade) {
List<String> authHeaders = httpFacade.getRequest().getHeaders("Authorization");
if (authHeaders != null) {
for (String authHeader : authHeaders) {
String[] split = authHeader.trim().split("\\s+");
if (split == null || split.length != 2) continue;
if (!split[0].equalsIgnoreCase("Bearer")) continue;
return true;
}
}
return getPolicyEnforcer().getDeployment().isBearerOnly();
}
}
@@ -21,9 +21,12 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
import org.keycloak.common.util.Time;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
/**
* A simple LRU cache implementation supporting expiration and maximum number of entries.
*
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PathCache {
@@ -38,15 +41,7 @@ public class PathCache {
private final AtomicBoolean writing = new AtomicBoolean(false);
private final long maxAge;
/**
* Creates a new instance.
*
* @param maxEntries the maximum number of entries to keep in the cache
*/
public PathCache(int maxEntries) {
this(maxEntries, -1);
}
private final boolean enabled;
/**
* Creates a new instance.
@@ -62,9 +57,14 @@ public class PathCache {
}
};
this.maxAge = maxAge;
this.enabled = maxAge > 0;
}
public void put(String uri, PathConfig newValue) {
if (!enabled) {
return;
}
try {
if (parkForWriteAndCheckInterrupt()) {
return;
@@ -80,6 +80,10 @@ public class PathCache {
}
}
public boolean containsKey(String uri) {
return cache.containsKey(uri);
}
public PathConfig get(String uri) {
if (parkForReadAndCheckInterrupt()) {
return null;
@@ -151,7 +155,7 @@ public class PathCache {
if(maxAge == -1) {
expiration = -1;
} else {
expiration = System.currentTimeMillis() + maxAge;
expiration = Time.currentTimeMillis() + maxAge;
}
}
@@ -164,7 +168,7 @@ public class PathCache {
}
boolean isExpired() {
return expiration != -1 ? System.currentTimeMillis() > expiration : false;
return expiration != -1 ? Time.currentTimeMillis() > expiration : false;
}
}
}
@@ -1,242 +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.authorization;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.representation.ResourceRepresentation;
import org.keycloak.authorization.client.resource.ProtectedResource;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
class PathMatcher {
private static final char WILDCARD = '*';
private final AuthzClient authzClient;
// TODO: make this configurable
private PathCache cache = new PathCache(100, 30000);
public PathMatcher(AuthzClient authzClient) {
this.authzClient = authzClient;
}
public PathConfig matches(final String targetUri, Map<String, PathConfig> paths) {
PathConfig pathConfig = paths.get(targetUri) == null ? cache.get(targetUri) : paths.get(targetUri);
if (pathConfig != null) {
return pathConfig;
}
PathConfig matchingAnyPath = null;
PathConfig matchingAnySuffixPath = null;
for (PathConfig entry : paths.values()) {
String expectedUri = entry.getPath();
String matchingUri = null;
if (exactMatch(expectedUri, targetUri, expectedUri)) {
matchingUri = expectedUri;
}
if (isTemplate(expectedUri)) {
String templateUri = buildUriFromTemplate(expectedUri, targetUri);
if (templateUri != null) {
if (exactMatch(expectedUri, targetUri, templateUri)) {
matchingUri = templateUri;
entry = resolvePathConfig(entry, targetUri);
}
}
}
if (matchingUri != null) {
StringBuilder path = new StringBuilder(expectedUri);
int patternIndex = path.indexOf("/" + WILDCARD);
if (patternIndex != -1) {
path.delete(patternIndex, path.length());
}
patternIndex = path.indexOf("{");
if (patternIndex != -1) {
path.delete(patternIndex, path.length());
}
String pathString = path.toString();
if ("".equals(pathString)) {
pathString = "/";
}
if (matchingUri.equals(targetUri) || pathString.equals(targetUri)) {
cache.put(targetUri, entry);
return entry;
}
if (WILDCARD == expectedUri.charAt(expectedUri.length() - 1)) {
matchingAnyPath = entry;
} else {
int suffixIndex = expectedUri.indexOf(WILDCARD + ".");
if (suffixIndex != -1) {
String protectedSuffix = expectedUri.substring(suffixIndex + 1);
if (targetUri.endsWith(protectedSuffix)) {
matchingAnySuffixPath = entry;
}
}
}
}
}
if (matchingAnySuffixPath != null) {
cache.put(targetUri, matchingAnySuffixPath);
return matchingAnySuffixPath;
}
if (matchingAnyPath != null) {
cache.put(targetUri, matchingAnyPath);
}
return matchingAnyPath;
}
private boolean exactMatch(String expectedUri, String targetUri, String value) {
if (targetUri.equals(value)) {
return value.equals(targetUri);
}
if (endsWithWildcard(expectedUri)) {
return targetUri.startsWith(expectedUri.substring(0, expectedUri.length() - 2));
}
String suffix = "/*.";
int suffixIndex = expectedUri.indexOf(suffix);
if (suffixIndex != -1) {
return targetUri.endsWith(expectedUri.substring(suffixIndex + suffix.length() - 1));
}
return false;
}
public String buildUriFromTemplate(String expectedUri, String targetUri) {
int patternStartIndex = expectedUri.indexOf("{");
if (patternStartIndex == -1 || patternStartIndex >= targetUri.length()) {
return null;
}
if (expectedUri.split("/").length > targetUri.split("/").length) {
return null;
}
char[] expectedUriChars = expectedUri.toCharArray();
char[] matchingUri = Arrays.copyOfRange(expectedUriChars, 0, patternStartIndex);
int matchingUriLastIndex = matchingUri.length;
String targetUriParams = targetUri.substring(patternStartIndex);
if (Arrays.equals(matchingUri, Arrays.copyOf(targetUri.toCharArray(), matchingUri.length))) {
matchingUri = Arrays.copyOf(matchingUri, targetUri.length());
int paramIndex = 0;
for (int i = patternStartIndex; i < expectedUriChars.length; i++) {
if (matchingUriLastIndex >= matchingUri.length) {
break;
}
char c = expectedUriChars[i];
if (c == '{' || c == '*') {
String[] params = targetUriParams.split("/");
for (int k = paramIndex; k <= (c == '*' ? params.length : paramIndex); k++) {
if (k == params.length) {
break;
}
int paramLength = params[k].length();
if (matchingUriLastIndex + paramLength > matchingUri.length) {
return null;
}
for (int j = 0; j < paramLength; j++) {
matchingUri[matchingUriLastIndex++] = params[k].charAt(j);
}
if (c == '*' && matchingUriLastIndex < matchingUri.length) {
matchingUri[matchingUriLastIndex++] = '/';
}
}
i = expectedUri.indexOf('}', i);
} else {
if (c == '/') {
paramIndex++;
}
matchingUri[matchingUriLastIndex++] = c;
}
}
if (matchingUri[matchingUri.length - 1] == '\u0000') {
return null;
}
return String.valueOf(matchingUri);
}
return null;
}
public boolean endsWithWildcard(String expectedUri) {
return WILDCARD == expectedUri.charAt(expectedUri.length() - 1);
}
private boolean isTemplate(String uri) {
return uri.indexOf("{") != -1;
}
private PathConfig resolvePathConfig(PathConfig originalConfig, String path) {
if (originalConfig.hasPattern()) {
ProtectedResource resource = this.authzClient.protection().resource();
Set<String> search = resource.findByFilter("uri=" + path);
if (!search.isEmpty()) {
// resource does exist on the server, cache it
ResourceRepresentation targetResource = resource.findById(search.iterator().next()).getResourceDescription();
PathConfig config = PolicyEnforcer.createPathConfig(targetResource);
config.setScopes(originalConfig.getScopes());
config.setMethods(originalConfig.getMethods());
config.setParentConfig(originalConfig);
config.setEnforcementMode(originalConfig.getEnforcementMode());
return config;
}
}
return originalConfig;
}
}
@@ -17,6 +17,17 @@
*/
package org.keycloak.adapters.authorization;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ServiceLoader;
import org.jboss.logging.Logger;
import org.keycloak.AuthorizationContext;
import org.keycloak.adapters.KeycloakDeployment;
@@ -25,22 +36,14 @@ 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;
import org.keycloak.common.util.PathMatcher;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathCacheConfig;
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;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -52,20 +55,27 @@ public class PolicyEnforcer {
private final KeycloakDeployment deployment;
private final AuthzClient authzClient;
private final PolicyEnforcerConfig enforcerConfig;
private final PathConfigMatcher pathMatcher;
private final Map<String, PathConfig> paths;
private final PathMatcher pathMatcher;
private final Map<String, ClaimInformationPointProviderFactory> claimInformationPointProviderFactories = new HashMap<>();
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);
this.paths = configurePaths(this.authzClient.protection().resource(), this.enforcerConfig);
paths = configurePaths(this.authzClient.protection().resource(), this.enforcerConfig);
pathMatcher = new PathConfigMatcher(paths, enforcerConfig, authzClient);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Initialization complete. Path configurations:");
@@ -73,20 +83,17 @@ public class PolicyEnforcer {
LOGGER.debug(pathConfig);
}
}
loadClaimInformationPointProviders(ServiceLoader.load(ClaimInformationPointProviderFactory.class, ClaimInformationPointProviderFactory.class.getClassLoader()));
loadClaimInformationPointProviders(ServiceLoader.load(ClaimInformationPointProviderFactory.class, Thread.currentThread().getContextClassLoader()));
}
public AuthorizationContext enforce(OIDCHttpFacade facade) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debugv("Policy enforcement is enable. Enforcing policy decisions for path [{0}].", facade.getRequest().getURI());
LOGGER.debugv("Policy enforcement is enabled. Enforcing policy decisions for path [{0}].", facade.getRequest().getURI());
}
AuthorizationContext context;
if (deployment.isBearerOnly()) {
context = new BearerTokenPolicyEnforcer(this).authorize(facade);
} else {
context = new KeycloakAdapterPolicyEnforcer(this).authorize(facade);
}
AuthorizationContext context = new KeycloakAdapterPolicyEnforcer(this).authorize(facade);
if (LOGGER.isDebugEnabled()) {
LOGGER.debugv("Policy enforcement result for path [{0}] is : {1}", facade.getRequest().getURI(), context.isGranted() ? "GRANTED" : "DENIED");
@@ -99,11 +106,11 @@ public class PolicyEnforcer {
return context;
}
PolicyEnforcerConfig getEnforcerConfig() {
public PolicyEnforcerConfig getEnforcerConfig() {
return enforcerConfig;
}
AuthzClient getClient() {
public AuthzClient getClient() {
return authzClient;
}
@@ -111,14 +118,30 @@ public class PolicyEnforcer {
return paths;
}
void addPath(PathConfig pathConfig) {
paths.put(pathConfig.getPath(), pathConfig);
public PathConfigMatcher getPathMatcher() {
return pathMatcher;
}
KeycloakDeployment getDeployment() {
public KeycloakDeployment getDeployment() {
return deployment;
}
public Map<String, ClaimInformationPointProviderFactory> getClaimInformationPointProviderFactories() {
return claimInformationPointProviderFactories;
}
private void loadClaimInformationPointProviders(ServiceLoader<ClaimInformationPointProviderFactory> loader) {
Iterator<ClaimInformationPointProviderFactory> iterator = loader.iterator();
while (iterator.hasNext()) {
ClaimInformationPointProviderFactory factory = iterator.next();
factory.init(this);
claimInformationPointProviderFactories.put(factory.getName(), factory);
}
}
private Map<String, PathConfig> configurePaths(ProtectedResource protectedResource, PolicyEnforcerConfig enforcerConfig) {
boolean loadPathsFromServer = true;
@@ -131,7 +154,11 @@ public class PolicyEnforcer {
if (loadPathsFromServer) {
LOGGER.info("No path provided in configuration.");
return configureAllPathsForResourceServer(protectedResource);
Map<String, PathConfig> paths = configureAllPathsForResourceServer(protectedResource);
paths.putAll(configureDefinedPaths(protectedResource, enforcerConfig));
return paths;
} else {
LOGGER.info("Paths provided in configuration.");
return configureDefinedPaths(protectedResource, enforcerConfig);
@@ -139,56 +166,41 @@ public class PolicyEnforcer {
}
private Map<String, PathConfig> configureDefinedPaths(ProtectedResource protectedResource, PolicyEnforcerConfig enforcerConfig) {
Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>());
Map<String, PathConfig> paths = Collections.synchronizedMap(new LinkedHashMap<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.isEmpty()) {
resources = protectedResource.findByMatchingUri(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 (enforcerConfig.isCreateResources()) {
LOGGER.debugf("Creating resource on server for path [%s].", pathConfig);
ResourceRepresentation resource = new ResourceRepresentation();
resource.setName(resourceName);
resource.setType(pathConfig.getType());
resource.setUri(path);
HashSet<ScopeRepresentation> scopes = new HashSet<>();
for (String scopeName : pathConfig.getScopes()) {
ScopeRepresentation scope = new ScopeRepresentation();
scope.setName(scopeName);
scopes.add(scope);
}
resource.setScopes(scopes);
RegistrationResponse registrationResponse = protectedResource.create(resource);
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());
if (resource != null) {
pathConfig.setId(resource.getId());
}
PathConfig existingPath = null;
for (PathConfig current : paths.values()) {
if (current.getId().equals(pathConfig.getId()) && current.getPath().equals(pathConfig.getPath())) {
if (current.getPath().equals(pathConfig.getPath())) {
existingPath = current;
break;
}
@@ -209,46 +221,126 @@ public class PolicyEnforcer {
LOGGER.info("Querying the server for all resources associated with this application.");
Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>());
for (String id : protectedResource.findAll()) {
RegistrationResponse response = protectedResource.findById(id);
ResourceRepresentation resourceDescription = response.getResourceDescription();
if (!enforcerConfig.getLazyLoadPaths()) {
for (String id : protectedResource.findAll()) {
ResourceRepresentation resourceDescription = protectedResource.findById(id);
if (resourceDescription.getUri() != null) {
PathConfig pathConfig = createPathConfig(resourceDescription);
paths.put(pathConfig.getPath(), pathConfig);
if (resourceDescription.getUris() != null && !resourceDescription.getUris().isEmpty()) {
for(PathConfig pathConfig : PathConfig.createPathConfigs(resourceDescription)) {
paths.put(pathConfig.getPath(), pathConfig);
}
}
}
}
return paths;
}
static PathConfig createPathConfig(ResourceRepresentation resourceDescription) {
PathConfig pathConfig = new PathConfig();
public class PathConfigMatcher extends PathMatcher<PathConfig> {
pathConfig.setId(resourceDescription.getId());
pathConfig.setName(resourceDescription.getName());
private final Map<String, PathConfig> paths;
private final PathCache pathCache;
private final AuthzClient authzClient;
private final PolicyEnforcerConfig enforcerConfig;
String uri = resourceDescription.getUri();
public PathConfigMatcher(Map<String, PathConfig> paths, PolicyEnforcerConfig enforcerConfig, AuthzClient authzClient) {
this.paths = paths;
this.enforcerConfig = enforcerConfig;
PathCacheConfig cacheConfig = enforcerConfig.getPathCacheConfig();
if (uri == null || "".equals(uri.trim())) {
throw new RuntimeException("Failed to configure paths. Resource [" + resourceDescription.getName() + "] has an invalid or empty URI [" + uri + "].");
if (cacheConfig == null) {
cacheConfig = new PathCacheConfig();
}
pathCache = new PathCache(cacheConfig.getMaxEntries(), cacheConfig.getLifespan());
this.authzClient = authzClient;
}
pathConfig.setPath(uri);
@Override
public PathConfig matches(String targetUri) {
PathConfig pathConfig = pathCache.get(targetUri);
List<String> scopeNames = new ArrayList<>();
if (pathCache.containsKey(targetUri) || pathConfig != null) {
return pathConfig;
}
for (ScopeRepresentation scope : resourceDescription.getScopes()) {
scopeNames.add(scope.getName());
pathConfig = super.matches(targetUri);
if (enforcerConfig.getLazyLoadPaths() || enforcerConfig.getPathCacheConfig() != null) {
if ((pathConfig == null || (pathConfig.getPath().contains("*")))) {
try {
List<ResourceRepresentation> matchingResources = authzClient.protection().resource().findByMatchingUri(targetUri);
if (!matchingResources.isEmpty()) {
Map<String, Map<String, Object>> cipConfig = null;
if (pathConfig != null) {
cipConfig = pathConfig.getClaimInformationPointConfig();
}
pathConfig = PathConfig.createPathConfigs(matchingResources.get(0)).iterator().next();
if (cipConfig != null) {
pathConfig.setClaimInformationPointConfig(cipConfig);
}
}
} catch (Exception cause) {
LOGGER.errorf(cause, "Could not lazy load resource with path [" + targetUri + "] from server");
return null;
}
}
}
pathCache.put(targetUri, pathConfig);
return pathConfig;
}
pathConfig.setScopes(scopeNames);
pathConfig.setType(resourceDescription.getType());
@Override
protected String getPath(PathConfig entry) {
return entry.getPath();
}
return pathConfig;
}
@Override
protected Collection<PathConfig> getPaths() {
return paths.values();
}
public PathMatcher getPathMatcher() {
return pathMatcher;
}
@Override
protected PathConfig resolvePathConfig(PathConfig originalConfig, String path) {
if (originalConfig.hasPattern()) {
ProtectedResource resource = authzClient.protection().resource();
// search by an exact match
List<ResourceRepresentation> search = resource.findByUri(path);
// if exact match not found, try to obtain from current path the parent path.
// if path is /resource/1/test and pattern from pathConfig is /resource/{id}/*, parent path is /resource/1
// this logic allows to match sub resources of a resource instance (/resource/1) to the parent resource,
// so any permission granted to parent also applies to sub resources
if (search.isEmpty()) {
search = resource.findByUri(buildUriFromTemplate(originalConfig.getPath(), path, true));
}
if (!search.isEmpty()) {
ResourceRepresentation targetResource = search.get(0);
PathConfig config = PathConfig.createPathConfigs(targetResource).iterator().next();
config.setScopes(originalConfig.getScopes());
config.setMethods(originalConfig.getMethods());
config.setParentConfig(originalConfig);
config.setEnforcementMode(originalConfig.getEnforcementMode());
config.setClaimInformationPointConfig(originalConfig.getClaimInformationPointConfig());
return config;
}
}
return null;
}
public void removeFromCache(String pathConfig) {
pathCache.remove(pathConfig);
}
};
}
@@ -0,0 +1,76 @@
/*
* 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.authorization.cip;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.keycloak.adapters.authorization.ClaimInformationPointProvider;
import org.keycloak.adapters.authorization.util.PlaceHolders;
import org.keycloak.adapters.spi.HttpFacade;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class ClaimsInformationPointProvider implements ClaimInformationPointProvider {
private final Map<String, Object> config;
public ClaimsInformationPointProvider(Map<String, Object> config) {
this.config = config;
}
@Override
public Map<String, List<String>> resolve(HttpFacade httpFacade) {
Map<String, List<String>> claims = new HashMap<>();
for (Entry<String, Object> configEntry : config.entrySet()) {
String claimName = configEntry.getKey();
Object claimValue = configEntry.getValue();
List<String> values = new ArrayList<>();
if (claimValue instanceof String) {
values = getValues(claimValue.toString(), httpFacade);
} else if (claimValue instanceof Collection) {
Iterator iterator = Collection.class.cast(claimValue).iterator();
while (iterator.hasNext()) {
List<String> resolvedValues = getValues(iterator.next().toString(), httpFacade);
if (!resolvedValues.isEmpty()) {
values.addAll(resolvedValues);
}
}
}
if (!values.isEmpty()) {
claims.put(claimName, values);
}
}
return claims;
}
private List<String> getValues(String value, HttpFacade httpFacade) {
return PlaceHolders.resolve(value, httpFacade);
}
}
@@ -0,0 +1,43 @@
/*
* 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.authorization.cip;
import java.util.Map;
import org.keycloak.adapters.authorization.ClaimInformationPointProviderFactory;
import org.keycloak.adapters.authorization.PolicyEnforcer;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class ClaimsInformationPointProviderFactory implements ClaimInformationPointProviderFactory<ClaimsInformationPointProvider> {
@Override
public String getName() {
return "claims";
}
@Override
public void init(PolicyEnforcer policyEnforcer) {
}
@Override
public ClaimsInformationPointProvider create(Map<String, Object> config) {
return new ClaimsInformationPointProvider(config);
}
}
@@ -0,0 +1,210 @@
/*
* 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.authorization.cip;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.fasterxml.jackson.databind.JsonNode;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.util.EntityUtils;
import org.keycloak.adapters.authorization.ClaimInformationPointProvider;
import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.adapters.authorization.util.JsonUtils;
import org.keycloak.adapters.authorization.util.PlaceHolders;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.authorization.client.util.HttpResponseException;
import org.keycloak.common.util.StreamUtil;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class HttpClaimInformationPointProvider implements ClaimInformationPointProvider {
private final Map<String, Object> config;
private final HttpClient httpClient;
public HttpClaimInformationPointProvider(Map<String, Object> config, PolicyEnforcer policyEnforcer) {
this.config = config;
this.httpClient = policyEnforcer.getDeployment().getClient();
}
@Override
public Map<String, List<String>> resolve(HttpFacade httpFacade) {
try {
InputStream responseStream = executeRequest(httpFacade);
try (InputStream inputStream = new BufferedInputStream(responseStream)) {
JsonNode jsonNode = JsonSerialization.mapper.readTree(inputStream);
Map<String, List<String>> claims = new HashMap<>();
Map<String, Object> claimsDef = (Map<String, Object>) config.get("claims");
if (claimsDef == null) {
Iterator<String> nodeNames = jsonNode.fieldNames();
while (nodeNames.hasNext()) {
String nodeName = nodeNames.next();
claims.put(nodeName, JsonUtils.getValues(jsonNode.get(nodeName)));
}
} else {
for (Entry<String, Object> claimDef : claimsDef.entrySet()) {
List<String> jsonPaths = new ArrayList<>();
if (claimDef.getValue() instanceof Collection) {
jsonPaths.addAll(Collection.class.cast(claimDef.getValue()));
} else {
jsonPaths.add(claimDef.getValue().toString());
}
List<String> claimValues = new ArrayList<>();
for (String path : jsonPaths) {
claimValues.addAll(JsonUtils.getValues(jsonNode, path));
}
claims.put(claimDef.getKey(), claimValues);
}
}
return claims;
}
} catch (IOException cause) {
throw new RuntimeException("Could not obtain claims from http claim information point [" + config.get("url") + "] response", cause);
}
}
private InputStream executeRequest(HttpFacade httpFacade) {
String method = config.get("method").toString();
if (method == null) {
method = "GET";
}
RequestBuilder builder = null;
if ("GET".equalsIgnoreCase(method)) {
builder = RequestBuilder.get();
} else {
builder = RequestBuilder.post();
}
builder.setUri(config.get("url").toString());
byte[] bytes = new byte[0];
try {
setParameters(builder, httpFacade);
if (config.containsKey("headers")) {
setHeaders(builder, httpFacade);
}
HttpResponse response = httpClient.execute(builder.build());
HttpEntity entity = response.getEntity();
if (entity != null) {
bytes = EntityUtils.toByteArray(entity);
}
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode < 200 || statusCode >= 300) {
throw new HttpResponseException("Unexpected response from server: " + statusCode + " / " + statusLine.getReasonPhrase(), statusCode, statusLine.getReasonPhrase(), bytes);
}
return new ByteArrayInputStream(bytes);
} catch (Exception cause) {
try {
throw new RuntimeException("Error executing http method [" + builder + "]. Response : " + StreamUtil.readString(new ByteArrayInputStream(bytes), Charset.forName("UTF-8")), cause);
} catch (Exception e) {
throw new RuntimeException("Error executing http method [" + builder + "]", cause);
}
}
}
private void setHeaders(RequestBuilder builder, HttpFacade httpFacade) {
Object headersDef = config.get("headers");
if (headersDef != null) {
Map<String, Object> headers = Map.class.cast(headersDef);
for (Entry<String, Object> header : headers.entrySet()) {
Object value = header.getValue();
List<String> headerValues = new ArrayList<>();
if (value instanceof Collection) {
Collection values = Collection.class.cast(value);
Iterator iterator = values.iterator();
while (iterator.hasNext()) {
headerValues.addAll(PlaceHolders.resolve(iterator.next().toString(), httpFacade));
}
} else {
headerValues.addAll(PlaceHolders.resolve(value.toString(), httpFacade));
}
for (String headerValue : headerValues) {
builder.addHeader(header.getKey(), headerValue);
}
}
}
}
private void setParameters(RequestBuilder builder, HttpFacade httpFacade) {
Object config = this.config.get("parameters");
if (config != null) {
Map<String, Object> paramsDef = Map.class.cast(config);
for (Entry<String, Object> paramDef : paramsDef.entrySet()) {
Object value = paramDef.getValue();
List<String> paramValues = new ArrayList<>();
if (value instanceof Collection) {
Collection values = Collection.class.cast(value);
Iterator iterator = values.iterator();
while (iterator.hasNext()) {
paramValues.addAll(PlaceHolders.resolve(iterator.next().toString(), httpFacade));
}
} else {
paramValues.addAll(PlaceHolders.resolve(value.toString(), httpFacade));
}
for (String paramValue : paramValues) {
builder.addParameter(paramDef.getKey(), paramValue);
}
}
}
}
}
@@ -0,0 +1,45 @@
/*
* 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.authorization.cip;
import java.util.Map;
import org.keycloak.adapters.authorization.ClaimInformationPointProviderFactory;
import org.keycloak.adapters.authorization.PolicyEnforcer;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class HttpClaimInformationPointProviderFactory implements ClaimInformationPointProviderFactory<HttpClaimInformationPointProvider> {
private PolicyEnforcer policyEnforcer;
@Override
public String getName() {
return "http";
}
@Override
public void init(PolicyEnforcer policyEnforcer) {
this.policyEnforcer = policyEnforcer;
}
@Override
public HttpClaimInformationPointProvider create(Map<String, Object> config) {
return new HttpClaimInformationPointProvider(config, policyEnforcer);
}
}
@@ -0,0 +1,58 @@
/*
* 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.authorization.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.fasterxml.jackson.databind.JsonNode;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class JsonUtils {
public static List<String> getValues(JsonNode jsonNode, String path) {
return getValues(jsonNode.at(path));
}
public static List<String> getValues(JsonNode jsonNode) {
List<String> values = new ArrayList<>();
if (jsonNode.isArray()) {
Iterator<JsonNode> iterator = jsonNode.iterator();
while (iterator.hasNext()) {
String value = iterator.next().textValue();
if (value != null) {
values.add(value);
}
}
} else {
String value = jsonNode.textValue();
if (value != null) {
values.add(value);
}
}
return values;
}
}
@@ -0,0 +1,67 @@
/*
* 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.authorization.util;
import static org.keycloak.adapters.authorization.util.PlaceHolders.getParameter;
import java.util.Arrays;
import java.util.List;
import com.fasterxml.jackson.databind.JsonNode;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class KeycloakSecurityContextPlaceHolderResolver implements PlaceHolderResolver {
public static final String NAME = "keycloak";
@Override
public List<String> resolve(String placeHolder, HttpFacade httpFacade) {
String source = placeHolder.substring(placeHolder.indexOf('.') + 1);
OIDCHttpFacade oidcHttpFacade = OIDCHttpFacade.class.cast(httpFacade);
KeycloakSecurityContext securityContext = oidcHttpFacade.getSecurityContext();
if (securityContext == null) {
return null;
}
if (source.endsWith("access_token")) {
return Arrays.asList(securityContext.getTokenString());
}
if (source.endsWith("id_token")) {
return Arrays.asList(securityContext.getIdTokenString());
}
JsonNode jsonNode;
if (source.startsWith("access_token[")) {
jsonNode = JsonSerialization.mapper.valueToTree(securityContext.getToken());
} else if (source.startsWith("id_token[")) {
jsonNode = JsonSerialization.mapper.valueToTree(securityContext.getIdToken());
} else {
throw new RuntimeException("Invalid placeholder [" + placeHolder + "]");
}
return JsonUtils.getValues(jsonNode, getParameter(source, "Invalid placeholder [" + placeHolder + "]"));
}
}
@@ -0,0 +1,30 @@
/*
* 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.authorization.util;
import java.util.List;
import org.keycloak.adapters.spi.HttpFacade;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface PlaceHolderResolver {
List<String> resolve(String placeHolder, HttpFacade httpFacade);
}
@@ -0,0 +1,107 @@
/*
* 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.authorization.util;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.keycloak.adapters.spi.HttpFacade;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PlaceHolders {
private static Map<String, PlaceHolderResolver> resolvers = new HashMap<>();
static {
resolvers.put(RequestPlaceHolderResolver.NAME, new RequestPlaceHolderResolver());
resolvers.put(KeycloakSecurityContextPlaceHolderResolver.NAME, new KeycloakSecurityContextPlaceHolderResolver());
}
private static Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\{(.+?)\\}");
private static Pattern PLACEHOLDER_PARAM_PATTERN = Pattern.compile("\\[(.+?)\\]");
public static List<String> resolve(String value, HttpFacade httpFacade) {
Map<String, List<String>> placeHolders = parsePlaceHolders(value, httpFacade);
if (!placeHolders.isEmpty()) {
value = formatPlaceHolder(value);
for (Entry<String, List<String>> entry : placeHolders.entrySet()) {
List<String> values = entry.getValue();
if (values.isEmpty() || values.size() > 1) {
return values;
}
value = value.replaceAll(entry.getKey(), values.get(0)).trim();
}
}
return Arrays.asList(value);
}
static String getParameter(String source, String messageIfNotFound) {
Matcher matcher = PLACEHOLDER_PARAM_PATTERN.matcher(source);
while (matcher.find()) {
return matcher.group(1).replaceAll("'", "");
}
if (messageIfNotFound != null) {
throw new RuntimeException(messageIfNotFound);
}
return null;
}
private static Map<String, List<String>> parsePlaceHolders(String value, HttpFacade httpFacade) {
Map<String, List<String>> placeHolders = new HashMap<>();
Matcher matcher = PLACEHOLDER_PATTERN.matcher(value);
while (matcher.find()) {
String placeHolder = matcher.group(1);
int resolverNameIdx = placeHolder.indexOf('.');
if (resolverNameIdx == -1) {
throw new RuntimeException("Invalid placeholder [" + value + "]. Could not find resolver name.");
}
PlaceHolderResolver resolver = resolvers.get(placeHolder.substring(0, resolverNameIdx));
if (resolver != null) {
List<String> resolved = resolver.resolve(placeHolder, httpFacade);
if (resolved != null) {
placeHolders.put(formatPlaceHolder(placeHolder), resolved);
}
}
}
return placeHolders;
}
private static String formatPlaceHolder(String placeHolder) {
return placeHolder.replaceAll("\\{", "").replace("}", "").replace("[", "").replace("]", "").replace("[", "").replace("]", "");
}
}
@@ -0,0 +1,175 @@
/*
* 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.authorization.util;
import static org.keycloak.adapters.authorization.util.PlaceHolders.getParameter;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.fasterxml.jackson.databind.JsonNode;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.HttpFacade.Cookie;
import org.keycloak.adapters.spi.HttpFacade.Request;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class RequestPlaceHolderResolver implements PlaceHolderResolver {
static String NAME = "request";
@Override
public List<String> resolve(String placeHolder, HttpFacade httpFacade) {
String source = placeHolder.substring(placeHolder.indexOf('.') + 1);
Request request = httpFacade.getRequest();
if (source.startsWith("parameter")) {
String parameterName = getParameter(source, "Could not obtain parameter name from placeholder [" + source + "]");
String parameterValue = request.getQueryParamValue(parameterName);
if (parameterValue == null) {
parameterValue = request.getFirstParam(parameterName);
}
if (parameterValue != null) {
return Arrays.asList(parameterValue);
}
} else if (source.startsWith("header")) {
String headerName = getParameter(source, "Could not obtain header name from placeholder [" + source + "]");
List<String> headerValue = request.getHeaders(headerName);
if (headerValue != null) {
return headerValue;
}
} else if (source.startsWith("cookie")) {
String cookieName = getParameter(source, "Could not obtain cookie name from placeholder [" + source + "]");
Cookie cookieValue = request.getCookie(cookieName);
if (cookieValue != null) {
return Arrays.asList(cookieValue.getValue());
}
} else if (source.startsWith("remoteAddr")) {
String value = request.getRemoteAddr();
if (value != null) {
return Arrays.asList(value);
}
} else if (source.startsWith("method")) {
String value = request.getMethod();
if (value != null) {
return Arrays.asList(value);
}
} else if (source.startsWith("uri")) {
String value = request.getURI();
if (value != null) {
return Arrays.asList(value);
}
} else if (source.startsWith("relativePath")) {
String value = request.getRelativePath();
if (value != null) {
return Arrays.asList(value);
}
} else if (source.startsWith("secure")) {
return Arrays.asList(String.valueOf(request.isSecure()));
} else if (source.startsWith("body")) {
String contentType = request.getHeader("Content-Type");
if (contentType == null) {
contentType = "";
}
InputStream body = request.getInputStream(true);
try {
if (body == null || body.available() == 0) {
return Collections.emptyList();
}
} catch (IOException cause) {
throw new RuntimeException("Failed to check available bytes in request input stream", cause);
}
if (body.markSupported()) {
body.mark(0);
}
List<String> values = new ArrayList<>();
try {
switch (contentType) {
case "application/json":
try {
JsonNode jsonNode = JsonSerialization.mapper.readTree(new BufferedInputStream(body) {
@Override
public void close() {
// we can't close the stream because it may be used later by the application
}
});
String path = getParameter(source, null);
if (path == null) {
values.addAll(JsonUtils.getValues(jsonNode));
} else {
values.addAll(JsonUtils.getValues(jsonNode, path));
}
} catch (IOException cause) {
throw new RuntimeException("Could not extract claim from request JSON body", cause);
}
break;
default:
StringBuilder value = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(body));
try {
int ch;
while ((ch = reader.read()) != -1) {
value.append((char) ch);
}
} catch (IOException cause) {
throw new RuntimeException("Could not extract claim from request body", cause);
}
values.add(value.toString());
}
} finally {
if (body.markSupported()) {
try {
body.reset();
} catch (IOException cause) {
throw new RuntimeException("Failed to reset request input stream", cause);
}
}
}
return values;
}
return Collections.emptyList();
}
}
@@ -81,7 +81,7 @@ public class JWKPublicKeyLocator implements PublicKeyLocator {
private PublicKey lookupCachedKey(int publicKeyCacheTtl, int currentTime, String kid) {
if (lastRequestTime + publicKeyCacheTtl > currentTime) {
if (lastRequestTime + publicKeyCacheTtl > currentTime && kid != null) {
return currentKeys.get(kid);
} else {
return null;
@@ -16,4 +16,5 @@
#
org.keycloak.adapters.authentication.ClientIdAndSecretCredentialsProvider
org.keycloak.adapters.authentication.JWTClientCredentialsProvider
org.keycloak.adapters.authentication.JWTClientCredentialsProvider
org.keycloak.adapters.authentication.JWTClientSecretCredentialsProvider
@@ -0,0 +1,19 @@
#
# * 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.
#
org.keycloak.adapters.authorization.cip.ClaimsInformationPointProviderFactory
org.keycloak.adapters.authorization.cip.HttpClaimInformationPointProviderFactory
@@ -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());
}
}
@@ -17,7 +17,9 @@
package org.keycloak.adapters;
import org.junit.Test;
import org.keycloak.representations.adapters.config.AdapterConfig;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -46,4 +48,21 @@ public class KeycloakDeploymentTest {
assertTrue(keycloakDeployment.isOAuthQueryParameterEnabled());
}
@Test
public void stripDefaultPorts() {
KeycloakDeployment keycloakDeployment = new KeycloakDeployment();
keycloakDeployment.setRealm("test");
AdapterConfig config = new AdapterConfig();
config.setAuthServerUrl("http://localhost:80/auth");
keycloakDeployment.setAuthServerBaseUrl(config);
assertEquals("http://localhost/auth", keycloakDeployment.getAuthServerBaseUrl());
config.setAuthServerUrl("https://localhost:443/auth");
keycloakDeployment.setAuthServerBaseUrl(config);
assertEquals("https://localhost/auth", keycloakDeployment.getAuthServerBaseUrl());
}
}
@@ -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.0.Final</version>
<version>4.4.0.Final</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.0.Final</version>
<version>4.4.0.Final</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.0.Final</version>
<version>4.4.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -166,6 +166,13 @@ class SharedAttributeDefinitons {
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition PROXY_URL =
new SimpleAttributeDefinitionBuilder("proxy-url", ModelType.STRING, true)
.setXmlName("proxy-url")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
@@ -192,6 +199,7 @@ class SharedAttributeDefinitons {
ATTRIBUTES.add(REGISTER_NODE_PERIOD);
ATTRIBUTES.add(TOKEN_STORE);
ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE);
ATTRIBUTES.add(PROXY_URL);
}
/**
@@ -46,6 +46,7 @@ keycloak.realm.register-node-at-startup=Cluster setting
keycloak.realm.register-node-period=how often to re-register node
keycloak.realm.token-store=cookie or session storage for auth session data
keycloak.realm.principal-attribute=token attribute to use to set Principal name
keycloak.realm.proxy-url=The URL for the HTTP proxy if one is used.
keycloak.secure-deployment=A deployment secured by Keycloak
keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
@@ -82,7 +83,7 @@ keycloak.secure-deployment.principal-attribute=token attribute to use to set Pri
keycloak.secure-deployment.turn-off-change-session-id-on-login=The session id is changed by default on a successful login. Change this to true if you want to turn this off
keycloak.secure-deployment.token-minimum-time-to-live=The adapter will refresh the token if the current token is expired OR will expire in 'token-minimum-time-to-live' seconds or less
keycloak.secure-deployment.min-time-between-jwks-requests=If adapter recognize token signed by unknown public key, it will try to download new public key from keycloak server. However it won't try to download if already tried it in less than 'min-time-between-jwks-requests' seconds
keycloak.secure-deployment.proxy-url=The URL for the HTTP proxy if one is used.
keycloak.secure-deployment.credential=Credential value
keycloak.credential=Credential
@@ -65,6 +65,7 @@
<xs:element name="register-node-period" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="token-store" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="principal-attribute" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="proxy-url" type="xs:string" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
@@ -107,6 +108,7 @@
<xs:element name="turn-off-change-session-id-on-login" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="token-minimum-time-to-live" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="min-time-between-jwks-requests" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="proxy-url" type="xs:string" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
+1 -1
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.0.Final</version>
<version>4.4.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak AS7 / JBoss EAP 6 Integration</name>
-10
View File
@@ -1,10 +0,0 @@
#!/bin/sh
export KC_AUTH_SERVER=http://localhost:8080/auth
export KC_REALM=master
export KC_CLIENT=cli
export KC_ACCESS_TOKEN=`java -DKEYCLOAK_AUTH_SERVER=$KC_AUTH_SERVER -DKEYCLOAK_REALM=$KC_REALM -DKEYCLOAK_CLIENT=$KC_CLIENT -jar target/keycloak-cli-sso-3.3.0.CR1-SNAPSHOT.jar login`
-9
View File
@@ -1,9 +0,0 @@
#!/bin/sh
java -DKEYCLOAK_AUTH_SERVER=$KC_AUTH_SERVER -DKEYCLOAK_REALM=$KC_REALM -DKEYCLOAK_CLIENT=$KC_CLIENT -jar target/keycloak-cli-sso-3.3.0.CR1-SNAPSHOT.jar logout
unset KC_ACCESS_TOKEN
-84
View File
@@ -1,84 +0,0 @@
<?xml version="1.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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-cli-sso</artifactId>
<name>Keycloak CLI SSO Framework</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-installed-adapter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.keycloak.adapters.KeycloakCliSsoMain</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -1,45 +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;
import org.keycloak.adapters.installed.KeycloakCliSso;
import org.keycloak.adapters.installed.KeycloakInstalled;
import org.keycloak.common.util.Time;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.util.JsonSerialization;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakCliSsoMain extends KeycloakCliSso {
public static void main(String[] args) throws Exception {
new KeycloakCliSsoMain().mainCmd(args);
}
}
+144
View File
@@ -0,0 +1,144 @@
<?xml version="1.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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-fuse7-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>4.4.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-camel-undertow</artifactId>
<name>Keycloak Fuse 7.0 Adapter - Camel + Undertow</name>
<packaging>bundle</packaging>
<properties>
<keycloak.osgi.export>
org.keycloak.adapters.camel.undertow;version="${project.version}"
</keycloak.osgi.export>
<keycloak.osgi.import>
org.keycloak.*;version="${project.version}",
org.apache.camel.*,
org.apache.camel.component.undertow,
io.undertow.*,
*;resolution:=optional
</keycloak.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.enterprise</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-runtime</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-spi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-undertow</artifactId>
<version>2.21.2</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>2.21.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
<Export-Service>org.apache.camel.spi.ComponentResolver;component=undertow-keycloak</Export-Service>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,48 @@
/*
* 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.camel.undertow;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.camel.CamelContext;
import org.apache.camel.component.undertow.UndertowComponent;
import org.apache.camel.component.undertow.UndertowEndpoint;
/**
*
* @author hmlnarik
*/
public class UndertowKeycloakComponent extends UndertowComponent {
public UndertowKeycloakComponent() {
}
public UndertowKeycloakComponent(CamelContext context) {
super(context);
}
@Override
protected UndertowEndpoint createEndpointInstance(URI endpointUri, UndertowComponent component) throws URISyntaxException {
return new UndertowKeycloakEndpoint(endpointUri.toString(), component);
}
@Override
protected String getComponentName() {
return "undertow-keycloak";
}
}
@@ -0,0 +1,219 @@
/*
* 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.camel.undertow;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.AuthenticatedActionsHandler;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.NodesRegistrationManagement;
import org.keycloak.adapters.PreAuthActionsHandler;
import org.keycloak.adapters.RequestAuthenticator;
import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.InMemorySessionIdMapper;
import org.keycloak.adapters.spi.SessionIdMapper;
import org.keycloak.adapters.undertow.KeycloakUndertowAccount;
import org.keycloak.adapters.undertow.OIDCUndertowHttpFacade;
import org.keycloak.adapters.undertow.SessionManagementBridge;
import org.keycloak.adapters.undertow.UndertowCookieTokenStore;
import org.keycloak.adapters.undertow.UndertowRequestAuthenticator;
import org.keycloak.adapters.undertow.UndertowSessionTokenStore;
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
import org.keycloak.enums.TokenStore;
import io.undertow.security.api.SecurityContext;
import io.undertow.security.idm.Account;
import io.undertow.security.idm.Credential;
import io.undertow.security.idm.IdentityManager;
import io.undertow.security.impl.SecurityContextImpl;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.session.InMemorySessionManager;
import io.undertow.server.session.SessionManager;
import io.undertow.util.AttachmentKey;
import io.undertow.util.StatusCodes;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.apache.camel.Processor;
import org.apache.camel.component.undertow.UndertowConsumer;
/**
*
* @author hmlnarik
*/
public class UndertowKeycloakConsumer extends UndertowConsumer {
private static final Logger LOG = Logger.getLogger(UndertowKeycloakConsumer.class.getName());
public static final AttachmentKey<KeycloakPrincipal> KEYCLOAK_PRINCIPAL_KEY = AttachmentKey.create(KeycloakPrincipal.class);
private static final IdentityManager IDENTITY_MANAGER = new IdentityManager() {
@Override
public Account verify(Account account) {
return account;
}
@Override
public Account verify(String id, Credential credential) {
throw new IllegalStateException("Should never be called in Keycloak flow");
}
@Override
public Account verify(Credential credential) {
throw new IllegalStateException("Should never be called in Keycloak flow");
}
};
protected SessionIdMapper idMapper = new InMemorySessionIdMapper();
protected final NodesRegistrationManagement nodesRegistrationManagement = new NodesRegistrationManagement();
private final UndertowUserSessionManagement userSessionManagement = new UndertowUserSessionManagement();
protected final AdapterDeploymentContext deploymentContext;
protected final SessionManager sessionManager;
protected final List<String> allowedRoles;
private final int confidentialPort;
private final Pattern skipPattern;
public UndertowKeycloakConsumer(UndertowKeycloakEndpoint endpoint, Processor processor,
AdapterDeploymentContext deploymentContext, Pattern skipPattern, List<String> allowedRoles, int confidentialPort) {
super(endpoint, processor);
this.sessionManager = new InMemorySessionManager(endpoint.getEndpointUri());
this.deploymentContext = deploymentContext;
this.skipPattern = skipPattern;
this.confidentialPort = confidentialPort;
this.allowedRoles = allowedRoles == null ? Collections.<String>emptyList() : allowedRoles;
}
public int getConfidentialPort() {
return confidentialPort;
}
@Override
public void handleRequest(HttpServerExchange httpExchange) throws Exception {
if (shouldSkip(httpExchange.getRequestPath())) {
super.handleRequest(httpExchange);
return;
}
//perform only non-blocking operation on exchange
if (httpExchange.isInIoThread()) {
httpExchange.dispatch(this);
return;
}
OIDCUndertowHttpFacade facade = new OIDCUndertowHttpFacade(httpExchange);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment == null || !deployment.isConfigured()) {
httpExchange.setStatusCode(StatusCodes.FORBIDDEN);
LOG.fine("deployment not configured");
return;
}
LOG.fine("executing PreAuthActionsHandler");
SessionManagementBridge bridge = new SessionManagementBridge(userSessionManagement, sessionManager);
PreAuthActionsHandler preAuth = new PreAuthActionsHandler(bridge, deploymentContext, facade);
if (preAuth.handleRequest()) return;
SecurityContext securityContext = httpExchange.getSecurityContext();
if (securityContext == null) {
securityContext = new SecurityContextImpl(httpExchange, IDENTITY_MANAGER);
}
AdapterTokenStore tokenStore = getTokenStore(httpExchange, facade, deployment, securityContext);
tokenStore.checkCurrentToken();
LOG.fine("executing AuthenticatedActionsHandler");
RequestAuthenticator authenticator = new UndertowRequestAuthenticator(facade, deployment, confidentialPort, securityContext, httpExchange, tokenStore);
AuthOutcome outcome = authenticator.authenticate();
if (outcome == AuthOutcome.AUTHENTICATED) {
LOG.fine("AUTHENTICATED");
if (httpExchange.isResponseComplete()) {
return;
}
AuthenticatedActionsHandler actions = new AuthenticatedActionsHandler(deployment, facade);
if (actions.handledRequest()) {
return;
} else {
final Account authenticatedAccount = securityContext.getAuthenticatedAccount();
if (authenticatedAccount instanceof KeycloakUndertowAccount) {
final KeycloakUndertowAccount kua = (KeycloakUndertowAccount) authenticatedAccount;
httpExchange.putAttachment(KEYCLOAK_PRINCIPAL_KEY, (KeycloakPrincipal) kua.getPrincipal());
}
Set<String> roles = Optional
.ofNullable(authenticatedAccount.getRoles())
.orElse((Set<String>) Collections.EMPTY_SET);
LOG.log(Level.FINE, "Allowed roles: {0}, current roles: {1}", new Object[] {allowedRoles, roles});
if (isRoleAllowed(roles, httpExchange)) {
super.handleRequest(httpExchange);
} else {
httpExchange.setStatusCode(StatusCodes.FORBIDDEN);
}
return;
}
}
AuthChallenge challenge = authenticator.getChallenge();
if (challenge != null) {
LOG.fine("challenge");
challenge.challenge(facade);
return;
}
httpExchange.setStatusCode(StatusCodes.FORBIDDEN);
}
public boolean isRoleAllowed(Set<String> roles, HttpServerExchange httpExchange) throws Exception {
for (String role : allowedRoles) {
if (roles.contains(role)) {
return true;
}
}
return false;
}
protected AdapterTokenStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, KeycloakDeployment deployment, SecurityContext securityContext) {
if (deployment.getTokenStore() == TokenStore.SESSION) {
return new UndertowSessionTokenStore(exchange, deployment, userSessionManagement, securityContext);
} else {
return new UndertowCookieTokenStore(facade, deployment, securityContext);
}
}
private boolean shouldSkip(String requestPath) {
return skipPattern != null && skipPattern.matcher(requestPath).matches();
}
}
@@ -0,0 +1,152 @@
/*
* 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.camel.undertow;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.representations.adapters.config.AdapterConfig;
import io.undertow.server.HttpServerExchange;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.apache.camel.Consumer;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.component.undertow.UndertowComponent;
import org.apache.camel.component.undertow.UndertowEndpoint;
import static org.keycloak.adapters.camel.undertow.UndertowKeycloakConsumer.KEYCLOAK_PRINCIPAL_KEY;
/**
*
* @author hmlnarik
*/
public class UndertowKeycloakEndpoint extends UndertowEndpoint {
private static final Logger LOG = Logger.getLogger(UndertowKeycloakEndpoint.class.getName());
private KeycloakConfigResolver configResolver;
private AdapterConfig adapterConfig;
private String skipPattern;
private List<String> allowedRoles = Collections.emptyList();
private int confidentialPort = 8443;
public UndertowKeycloakEndpoint(String uri, UndertowComponent component) {
super(uri, component);
}
public AdapterConfig getAdapterConfig() {
return adapterConfig;
}
public void setAdapterConfig(AdapterConfig adapterConfig) {
LOG.info("adapterConfig");
this.adapterConfig = adapterConfig;
}
public String getSkipPattern() {
return skipPattern;
}
public void setSkipPattern(String skipPattern) {
this.skipPattern = skipPattern;
}
public List<String> getAllowedRoles() {
return allowedRoles;
}
public void setAllowedRoles(List<String> allowedRoles) {
this.allowedRoles = allowedRoles;
}
public void setAllowedRoles(String allowedRoles) {
this.allowedRoles = allowedRoles == null ? null : Arrays.asList(allowedRoles.split("\\s*,\\s*"));
}
public int getConfidentialPort() {
return confidentialPort;
}
public void setConfidentialPort(int confidentialPort) {
this.confidentialPort = confidentialPort;
}
public KeycloakConfigResolver getConfigResolver() {
return configResolver;
}
public void setConfigResolver(KeycloakConfigResolver configResolver) {
this.configResolver = configResolver;
}
@Override
public Consumer createConsumer(Processor processor) throws Exception {
return new UndertowKeycloakConsumer(this, processor, getDeploymentContext(), getSkipPatternAsPattern(), computeAllowedRoles(), this.confidentialPort);
}
public List<String> computeAllowedRoles() {
List<String> res = this.allowedRoles == null ? Collections.<String>emptyList() : this.allowedRoles;
if (res.isEmpty()) {
LOG.warning("No roles were configured, Keycloak will deny every request");
}
LOG.log(Level.FINE, "Allowed roles: {0}", res);
return res;
}
@Override
public Exchange createExchange(HttpServerExchange httpExchange) throws Exception {
final Exchange res = super.createExchange(httpExchange);
KeycloakPrincipal principal = httpExchange.getAttachment(KEYCLOAK_PRINCIPAL_KEY);
LOG.log(Level.FINE, "principal: {0}", principal);
if (principal != null) {
res.setProperty(KeycloakPrincipal.class.getName(), principal);
}
return res;
}
private AdapterDeploymentContext getDeploymentContext() {
if (configResolver != null) {
LOG.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolver.getClass());
return new AdapterDeploymentContext(configResolver);
} else if (adapterConfig != null) {
KeycloakDeployment kd = KeycloakDeploymentBuilder.build(adapterConfig);
return new AdapterDeploymentContext(kd);
}
LOG.warning("Adapter is unconfigured, Keycloak will deny every request");
return new AdapterDeploymentContext();
}
private Pattern getSkipPatternAsPattern() {
return skipPattern == null
? null
: Pattern.compile(skipPattern, Pattern.DOTALL);
}
}
@@ -0,0 +1 @@
class=org.keycloak.adapters.camel.undertow.UndertowKeycloakComponent
+131
View File
@@ -0,0 +1,131 @@
<?xml version="1.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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-fuse7-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>4.4.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-pax-web-jetty94</artifactId>
<name>Keycloak Fuse 7.0 Adapter - Jetty 9.4</name>
<packaging>bundle</packaging>
<properties>
<keycloak.osgi.export>
org.keycloak.adapters.osgi.jetty94.*;version="${project.version}"
</keycloak.osgi.export>
<keycloak.osgi.import>
!org.keycloak.adapters.osgi.jetty94,
org.ops4j.pax.web.*;version="[7.1,9)",
org.keycloak.*;version="${project.version}",
*;resolution:=optional
</keycloak.osgi.import>
<keycloak.osgi.fragment>org.ops4j.pax.web.pax-web-jetty</keycloak.osgi.fragment>
</properties>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-spi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.enterprise</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jetty94-adapter</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
<version>${jetty9.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
<Fragment-Host>${keycloak.osgi.fragment}</Fragment-Host>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,41 @@
/*
* 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.osgi.jetty94;
import org.ops4j.pax.web.service.AuthenticatorService;
/**
*
* @author hmlnarik
*/
public class KeycloakAuthenticatorService implements AuthenticatorService {
@Override
public <T> T getAuthenticatorService(String method, Class<T> iface) {
if (method == null || iface != org.eclipse.jetty.security.Authenticator.class) {
return null;
}
if ("KEYCLOAK".equalsIgnoreCase(method)) {
return iface.cast(new org.keycloak.adapters.jetty.KeycloakJettyAuthenticator());
}
return null;
}
}
@@ -0,0 +1,280 @@
/*
* 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.osgi.jetty94;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.util.security.Constraint;
import org.jboss.logging.Logger;
import org.ops4j.pax.web.service.WebContainer;
import org.ops4j.pax.web.service.spi.model.SecurityConstraintMappingModel;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.HttpContext;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import java.net.URL;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Integration with pax-web, which allows to inject custom jetty-web.xml configuration from current bundle classpath into {@link WebContainer}
* and allows to inject custom security constraint for securing resources by Keycloak.
*
* <p>It assumes that pax-web {@link WebContainer} is used as implementation of OSGI {@link org.osgi.service.http.HttpService}, which
* is true in karaf/fuse environment</p>
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class PaxWebIntegrationService {
protected static final Logger log = Logger.getLogger(PaxWebIntegrationService.class);
private BundleContext bundleContext;
private String jettyWebXmlLocation;
private List<Object> constraintMappings;
private ServiceTracker webContainerTracker;
private HttpContext httpContext;
public BundleContext getBundleContext() {
return bundleContext;
}
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
public String getJettyWebXmlLocation() {
return jettyWebXmlLocation;
}
public void setJettyWebXmlLocation(String jettyWebXmlLocation) {
this.jettyWebXmlLocation = jettyWebXmlLocation;
}
public List<Object> getConstraintMappings() {
return constraintMappings;
}
public void setConstraintMappings(List<Object> constraintMappings) {
this.constraintMappings = constraintMappings;
}
protected ServiceTracker getWebContainerTracker() {
return webContainerTracker;
}
protected HttpContext getHttpContext() {
return httpContext;
}
public void setHttpContext(HttpContext httpContext) {
this.httpContext = httpContext;
}
public void start() {
ServiceTrackerCustomizer trackerCustomizer = new ServiceTrackerCustomizer() {
@Override
public Object addingService(ServiceReference reference) {
return addingWebContainerCallback(reference);
}
@Override
public void modifiedService(ServiceReference reference, Object service) {
}
@Override
public void removedService(ServiceReference reference, Object service) {
removingWebContainerCallback(reference);
}
};
webContainerTracker = new ServiceTracker(bundleContext, WebContainer.class.getName(), trackerCustomizer);
webContainerTracker.open();
}
public void stop() {
webContainerTracker.remove(webContainerTracker.getServiceReference());
}
protected WebContainer addingWebContainerCallback(ServiceReference webContainerServiceReference) {
WebContainer service = (WebContainer) bundleContext.getService(webContainerServiceReference);
if (httpContext == null) {
httpContext = service.createDefaultHttpContext();
}
addJettyWebXml(service);
if (constraintMappings == null) {
throw new IllegalStateException("constraintMappings was null!");
}
List<ConstraintHandler> handlers = new ArrayList<>();
try {
handlers.add(new JettyConstraintHandler());
} catch (Throwable t) {
// Ignore
}
try {
handlers.add(new PaxWebConstraintHandler());
} catch (Throwable t) {
// Ignore
}
for (Object constraintMapping : constraintMappings) {
boolean handled = false;
for (ConstraintHandler handler : handlers) {
handled |= handler.addConstraintMapping(httpContext, service, constraintMapping);
}
if (!handled) {
log.warnv("Unable to add constraint mapping for constraint of type " + constraintMapping.getClass().toString());
}
}
service.registerLoginConfig("BASIC", "does-not-matter", null, null, httpContext);
return service;
}
protected void addJettyWebXml(WebContainer service) {
String jettyWebXmlLoc;
if (this.jettyWebXmlLocation == null) {
jettyWebXmlLoc = "/WEB-INF/jetty-web.xml";
} else {
jettyWebXmlLoc = this.jettyWebXmlLocation;
}
URL jettyWebXml = bundleContext.getBundle().getResource(jettyWebXmlLoc);
if (jettyWebXml != null) {
log.debug("Found jetty-web XML configuration on bundle classpath on " + jettyWebXmlLoc);
service.registerJettyWebXml(jettyWebXml, httpContext);
} else {
log.debug("Not found jetty-web XML configuration on bundle classpath on " + jettyWebXmlLoc);
}
}
protected void addConstraintMapping(WebContainer service, SecurityConstraintMappingModel constraintMapping) {
String name = constraintMapping.getConstraintName();
if (name == null) {
name = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
}
log.debug("Adding security constraint name=" + name + ", url=" + constraintMapping.getUrl() + ", dataConstraint=" + constraintMapping.getDataConstraint() + ", canAuthenticate="
+ constraintMapping.isAuthentication() + ", roles=" + constraintMapping.getRoles());
service.registerConstraintMapping(name, constraintMapping.getUrl(), constraintMapping.getMapping(), constraintMapping.getDataConstraint(), constraintMapping.isAuthentication(), constraintMapping.getRoles(), httpContext);
}
protected void addConstraintMapping(WebContainer service, ConstraintMapping constraintMapping) {
Constraint constraint = constraintMapping.getConstraint();
String[] roles = constraint.getRoles();
// name property is unavailable on constraint object :/
String name = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
int dataConstraint = constraint.getDataConstraint();
String dataConstraintStr;
switch (dataConstraint) {
case Constraint.DC_UNSET: dataConstraintStr = null; break;
case Constraint.DC_NONE: dataConstraintStr = "NONE"; break;
case Constraint.DC_CONFIDENTIAL: dataConstraintStr = "CONFIDENTIAL"; break;
case Constraint.DC_INTEGRAL: dataConstraintStr = "INTEGRAL"; break;
default:
log.warnv("Unknown data constraint: " + dataConstraint);
dataConstraintStr = "CONFIDENTIAL";
}
List<String> rolesList = Arrays.asList(roles);
log.debug("Adding security constraint name=" + name + ", url=" + constraintMapping.getPathSpec() + ", dataConstraint=" + dataConstraintStr + ", canAuthenticate="
+ constraint.getAuthenticate() + ", roles=" + rolesList);
service.registerConstraintMapping(name, constraintMapping.getPathSpec(), null, dataConstraintStr, constraint.getAuthenticate(), rolesList, httpContext);
}
protected void removingWebContainerCallback(ServiceReference serviceReference) {
WebContainer service = (WebContainer)bundleContext.getService(serviceReference);
if (service != null) {
service.unregisterLoginConfig(httpContext);
service.unregisterConstraintMapping(httpContext);
}
}
private interface ConstraintHandler {
boolean addConstraintMapping(HttpContext httpContext, WebContainer service, Object cm);
}
private static class PaxWebConstraintHandler implements ConstraintHandler {
public boolean addConstraintMapping(HttpContext httpContext, WebContainer service, Object cm) {
if (cm instanceof SecurityConstraintMappingModel) {
SecurityConstraintMappingModel constraintMapping = (SecurityConstraintMappingModel) cm;
String name = constraintMapping.getConstraintName();
if (name == null) {
name = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
}
log.debug("Adding security constraint name=" + name + ", url=" + constraintMapping.getUrl() + ", dataConstraint=" + constraintMapping.getDataConstraint() + ", canAuthenticate="
+ constraintMapping.isAuthentication() + ", roles=" + constraintMapping.getRoles());
service.registerConstraintMapping(name, constraintMapping.getUrl(), constraintMapping.getMapping(), constraintMapping.getDataConstraint(), constraintMapping.isAuthentication(), constraintMapping.getRoles(), httpContext);
return true;
}
return false;
}
}
private static class JettyConstraintHandler implements ConstraintHandler {
public boolean addConstraintMapping(HttpContext httpContext, WebContainer service, Object cm) {
if (cm instanceof ConstraintMapping) {
ConstraintMapping constraintMapping = (ConstraintMapping) cm;
Constraint constraint = constraintMapping.getConstraint();
String[] roles = constraint.getRoles();
// name property is unavailable on constraint object :/
String name = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
int dataConstraint = constraint.getDataConstraint();
String dataConstraintStr;
switch (dataConstraint) {
case Constraint.DC_UNSET:
dataConstraintStr = null;
break;
case Constraint.DC_NONE:
dataConstraintStr = "NONE";
break;
case Constraint.DC_CONFIDENTIAL:
dataConstraintStr = "CONFIDENTIAL";
break;
case Constraint.DC_INTEGRAL:
dataConstraintStr = "INTEGRAL";
break;
default:
log.warnv("Unknown data constraint: " + dataConstraint);
dataConstraintStr = "CONFIDENTIAL";
}
List<String> rolesList = Arrays.asList(roles);
log.debug("Adding security constraint name=" + name + ", url=" + constraintMapping.getPathSpec() + ", dataConstraint=" + dataConstraintStr + ", canAuthenticate="
+ constraint.getAuthenticate() + ", roles=" + rolesList);
service.registerConstraintMapping(name, constraintMapping.getPathSpec(), null, dataConstraintStr, constraint.getAuthenticate(), rolesList, httpContext);
return true;
}
return false;
}
}
}
@@ -0,0 +1 @@
org.keycloak.adapters.osgi.jetty94.KeycloakAuthenticatorService
+58
View File
@@ -0,0 +1,58 @@
<?xml version="1.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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.4.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-fuse7-integration-pom</artifactId>
<packaging>pom</packaging>
<name>Keycloak Fuse 7.0 Integration</name>
<properties>
<fuse7.version>7.0.1-SNAPSHOT</fuse7.version>
<tomcat.version>8.0.14</tomcat.version>
<jetty9.version>9.4.2.v20170220</jetty9.version>
</properties>
<modules>
<module>camel-undertow</module>
<module>undertow</module>
</modules>
<profiles>
<profile>
<id>community</id>
<activation>
<property>
<name>!product</name>
</property>
</activation>
<modules>
<module>jetty94</module>
<module>tomcat8</module>
</modules>
</profile>
</profiles>
</project>
+116
View File
@@ -0,0 +1,116 @@
<?xml version="1.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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-fuse7-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>4.4.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-pax-web-tomcat8</artifactId>
<name>Keycloak Fuse 7.0 Adapter - Tomcat 8</name>
<packaging>bundle</packaging>
<properties>
<keycloak.osgi.export>
org.keycloak.adapters.osgi.tomcat.*;version="${project.version}"
</keycloak.osgi.export>
<keycloak.osgi.import>
!org.keycloak.adapters.osgi.tomcat,
org.ops4j.pax.web.*;version="[7.1,9)",
org.keycloak.*;version="${project.version}",
*;resolution:=optional
</keycloak.osgi.import>
<keycloak.osgi.fragment>org.ops4j.pax.web.pax-web-tomcat</keycloak.osgi.fragment>
</properties>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-tomcat8-adapter</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>${tomcat.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
<Fragment-Host>${keycloak.osgi.fragment}</Fragment-Host>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,41 @@
/*
* 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.osgi.tomcat;
import org.ops4j.pax.web.service.AuthenticatorService;
/**
*
* @author hmlnarik
*/
public class KeycloakAuthenticatorService implements AuthenticatorService {
@Override
public <T> T getAuthenticatorService(String method, Class<T> iface) {
if (method == null || iface != org.apache.catalina.Valve.class) {
return null;
}
if ("KEYCLOAK".equalsIgnoreCase(method)) {
return iface.cast(new org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve());
}
return null;
}
}
@@ -0,0 +1 @@
org.keycloak.adapters.osgi.tomcat.KeycloakAuthenticatorService
+153
View File
@@ -0,0 +1,153 @@
<?xml version="1.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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-fuse7-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>4.4.0.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-pax-web-undertow</artifactId>
<name>Keycloak Fuse 7.0 Adapter - Undertow</name>
<packaging>bundle</packaging>
<properties>
<keycloak.osgi.export>
org.keycloak.adapters.osgi.undertow.*;version="${project.version}"
</keycloak.osgi.export>
<keycloak.osgi.import>
!org.keycloak.adapters.osgi.undertow,
org.keycloak.*;version="${project.version}",
org.keycloak.adapters.osgi.*;version="${project.version}",
org.ops4j.pax.web.*;version="[7.1,9)",
javax.servlet.*;version="[2.5,4)";resolution:=optional,
org.apache.cxf.transport.http;resolution:=optional;version="[3,4)",
org.apache.cxf.transport.http_undertow;resolution:=optional;version="[3,4)",
org.apache.cxf.transport.servlet;resolution:=optional;version="[3,4)",
io.undertow.*,
*;resolution:=optional
</keycloak.osgi.import>
<keycloak.osgi.fragment>org.ops4j.pax.web.pax-web-undertow</keycloak.osgi.fragment>
</properties>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.enterprise</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-spi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-undertow</artifactId>
<version>${cxf.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-osgi-adapter</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
<Fragment-Host>${keycloak.osgi.fragment}</Fragment-Host>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,174 @@
/*
* 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.osgi.undertow;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.NodesRegistrationManagement;
import org.keycloak.adapters.spi.InMemorySessionIdMapper;
import org.keycloak.adapters.spi.SessionIdMapper;
import org.keycloak.adapters.undertow.UndertowAuthenticationMechanism;
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
import org.keycloak.representations.adapters.config.AdapterConfig;
import io.undertow.security.api.AuthenticationMechanism;
import io.undertow.security.api.AuthenticationMode;
import io.undertow.security.handlers.AuthenticationCallHandler;
import io.undertow.security.handlers.AuthenticationConstraintHandler;
import io.undertow.security.handlers.AuthenticationMechanismsHandler;
import io.undertow.security.handlers.SecurityInitialHandler;
import io.undertow.security.idm.Account;
import io.undertow.security.idm.Credential;
import io.undertow.security.idm.IdentityManager;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.apache.cxf.transport.http_undertow.CXFUndertowHttpHandler;
/**
*
* @author hmlnarik
*/
public class CxfKeycloakAuthHandler implements CXFUndertowHttpHandler {
private static final Logger LOG = Logger.getLogger(CxfKeycloakAuthHandler.class.getName());
private static final IdentityManager IDENTITY_MANAGER = new IdentityManager() {
@Override
public Account verify(Account account) {
return account;
}
@Override
public Account verify(String id, Credential credential) {
throw new IllegalStateException("Should never be called in Keycloak flow");
}
@Override
public Account verify(Credential credential) {
throw new IllegalStateException("Should never be called in Keycloak flow");
}
};
private final UndertowUserSessionManagement userSessionManagement = new UndertowUserSessionManagement();
protected final NodesRegistrationManagement nodesRegistrationManagement = new NodesRegistrationManagement();
protected final SessionIdMapper idMapper = new InMemorySessionIdMapper();
private final AtomicReference<HttpHandler> securityHandler = new AtomicReference<>();
private Pattern skipPattern;
private int confidentialPort = 8443;
private HttpHandler next;
private KeycloakConfigResolver configResolver;
private AdapterConfig adapterConfig;
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
if (shouldSkip(exchange.getRequestPath())) {
next.handleRequest(exchange);
} else {
getSecurityHandler().handleRequest(exchange);
}
}
private HttpHandler getSecurityHandler() {
if (this.securityHandler.get() == null) {
HttpHandler handler = this.next;
handler = new AuthenticationCallHandler(handler);
handler = new AuthenticationConstraintHandler(handler);
AdapterDeploymentContext deploymentContext = buildDeploymentContext();
final List<AuthenticationMechanism> mechanisms
= Collections.<AuthenticationMechanism>singletonList(
new UndertowAuthenticationMechanism(deploymentContext, userSessionManagement, nodesRegistrationManagement, confidentialPort, null));
handler = new AuthenticationMechanismsHandler(handler, mechanisms);
this.securityHandler.compareAndSet(null, new SecurityInitialHandler(AuthenticationMode.PRO_ACTIVE, IDENTITY_MANAGER, "KEYCLOAK", handler));
}
return this.securityHandler.get();
}
private AdapterDeploymentContext buildDeploymentContext() {
if (configResolver != null) {
LOG.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolver.getClass());
return new AdapterDeploymentContext(configResolver);
} else if (adapterConfig != null) {
KeycloakDeployment kd = KeycloakDeploymentBuilder.build(adapterConfig);
return new AdapterDeploymentContext(kd);
}
LOG.warning("Adapter is unconfigured, Keycloak will deny every request");
return new AdapterDeploymentContext();
}
@Override
public void setNext(HttpHandler nextHandler) {
this.next = nextHandler;
}
private boolean shouldSkip(String requestPath) {
return skipPattern != null && skipPattern.matcher(requestPath).matches();
}
public KeycloakConfigResolver getConfigResolver() {
return configResolver;
}
public void setConfigResolver(KeycloakConfigResolver configResolver) {
this.configResolver = configResolver;
}
public int getConfidentialPort() {
return confidentialPort;
}
public void setConfidentialPort(int confidentialPort) {
this.confidentialPort = confidentialPort;
}
public AdapterConfig getAdapterConfig() {
return adapterConfig;
}
public void setAdapterConfig(AdapterConfig adapterConfig) {
this.adapterConfig = adapterConfig;
}
public String getSkipPattern() {
return skipPattern.pattern();
}
public void setSkipPattern(String skipPattern) {
this.skipPattern = Pattern.compile(skipPattern, Pattern.DOTALL);
}
}
@@ -0,0 +1,41 @@
/*
* 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.osgi.undertow;
import org.ops4j.pax.web.service.AuthenticatorService;
/**
*
* @author hmlnarik
*/
public class KeycloakAuthenticatorService implements AuthenticatorService {
@Override
public <T> T getAuthenticatorService(String method, Class<T> iface) {
if (method == null || iface != io.undertow.servlet.ServletExtension.class) {
return null;
}
if ("KEYCLOAK".equalsIgnoreCase(method)) {
return iface.cast(new org.keycloak.adapters.undertow.KeycloakServletExtension());
}
return null;
}
}
@@ -0,0 +1,142 @@
/*
* 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.osgi.undertow;
import org.keycloak.adapters.osgi.PaxWebSecurityConstraintMapping;
import org.jboss.logging.Logger;
import org.ops4j.pax.web.service.WebContainer;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.HttpContext;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import java.util.List;
/**
* Integration with pax-web, which allows to inject custom security constraint for securing resources by Keycloak.
*
* <p>It assumes that pax-web {@link WebContainer} is used as implementation of OSGI {@link org.osgi.service.http.HttpService}, which
* is true in karaf/fuse environment</p>
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class PaxWebIntegrationService {
protected static final Logger log = Logger.getLogger(PaxWebIntegrationService.class);
private BundleContext bundleContext;
private List<PaxWebSecurityConstraintMapping> constraintMappings;
private ServiceTracker webContainerTracker;
private HttpContext httpContext;
public BundleContext getBundleContext() {
return bundleContext;
}
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
public List<PaxWebSecurityConstraintMapping> getConstraintMappings() {
return constraintMappings;
}
public void setConstraintMappings(List<PaxWebSecurityConstraintMapping> constraintMappings) {
this.constraintMappings = constraintMappings;
}
protected ServiceTracker getWebContainerTracker() {
return webContainerTracker;
}
protected HttpContext getHttpContext() {
return httpContext;
}
public void setHttpContext(HttpContext httpContext) {
this.httpContext = httpContext;
}
public void start() {
ServiceTrackerCustomizer trackerCustomizer = new ServiceTrackerCustomizer() {
@Override
public Object addingService(ServiceReference reference) {
return addingWebContainerCallback(reference);
}
@Override
public void modifiedService(ServiceReference reference, Object service) {
}
@Override
public void removedService(ServiceReference reference, Object service) {
removingWebContainerCallback(reference);
}
};
webContainerTracker = new ServiceTracker(bundleContext, WebContainer.class.getName(), trackerCustomizer);
webContainerTracker.open();
}
public void stop() {
webContainerTracker.remove(webContainerTracker.getServiceReference());
}
protected WebContainer addingWebContainerCallback(ServiceReference webContainerServiceReference) {
WebContainer service = (WebContainer) bundleContext.getService(webContainerServiceReference);
if (httpContext == null) {
httpContext = service.createDefaultHttpContext();
}
if (constraintMappings == null) {
throw new IllegalStateException("constraintMappings was null!");
}
for (PaxWebSecurityConstraintMapping constraintMapping : constraintMappings) {
addConstraintMapping(service, constraintMapping);
}
service.registerLoginConfig("KEYCLOAK", "specified-in-keycloak-json", null, null, httpContext);
return service;
}
protected void addConstraintMapping(WebContainer service, PaxWebSecurityConstraintMapping cm) {
log.debug("Adding security constraint name=" + cm.getConstraintName() + ", url=" + cm.getUrl() + ", dataConstraint=" + cm.getDataConstraint() + ", canAuthenticate="
+ cm.isAuthentication() + ", roles=" + cm.getRoles());
service.registerConstraintMapping(
cm.getConstraintName(),
cm.getMapping(),
cm.getUrl(),
cm.getDataConstraint(),
cm.isAuthentication(),
cm.getRoles(),
httpContext
);
}
protected void removingWebContainerCallback(ServiceReference serviceReference) {
WebContainer service = (WebContainer)bundleContext.getService(serviceReference);
if (service != null) {
service.unregisterLoginConfig(httpContext);
service.unregisterConstraintMapping(httpContext);
}
}
}
@@ -0,0 +1 @@
org.keycloak.adapters.osgi.undertow.KeycloakAuthenticatorService
+10 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.0.Final</version>
<version>4.4.0.Final</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.1_spec</artifactId>
</dependency>
</dependencies>
<build>
@@ -0,0 +1,702 @@
/*
* 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.installed;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.ServerRequest;
import org.keycloak.common.util.Base64;
import org.keycloak.common.util.Time;
import org.keycloak.jose.jwe.*;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
import org.keycloak.util.JsonSerialization;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.*;
import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.*;
/**
* All kcinit commands that take input ask for
* <p>
* 1. . kcinit
* - setup and export KC_SESSION_KEY env var if not set.
* - checks to see if master token valid, refresh is possible, exit if token valid
* - performs command line login
* - stores master token for master client
* 2. app.sh is a wrapper for app cli.
* - token=`kcinit token app`
* - checks to see if token for app client has been fetched, refresh if valid, output token to sys.out if exists
* - if no token, login. Prompts go to stderr.
* - pass token as cmd line param to app or as environment variable.
* <p>
* 3. kcinit password {password}
* - outputs password key that is used for encryption.
* - can be used in .bashrc as export KC_SESSSION_KEY=`kcinit password {password}` or just set it in .bat file
* <p>
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KcinitDriver {
public static final String KC_SESSION_KEY = "KC_SESSION_KEY";
public static final String KC_LOGIN_CONFIG_PATH = "KC_LOGIN_CONFIG_PATH";
protected Map<String, String> config;
protected boolean debug = true;
protected static byte[] salt = new byte[]{-4, 88, 66, -101, 78, -94, 21, 105};
String[] args = null;
protected boolean forceLogin;
protected boolean browserLogin;
public void mainCmd(String[] args) throws Exception {
this.args = args;
if (args.length == 0) {
printHelp();
return;
}
if (args[0].equalsIgnoreCase("token")) {
//System.err.println("executing token");
token();
} else if (args[0].equalsIgnoreCase("login")) {
login();
} else if (args[0].equalsIgnoreCase("logout")) {
logout();
} else if (args[0].equalsIgnoreCase("env")) {
System.out.println(System.getenv().toString());
} else if (args[0].equalsIgnoreCase("install")) {
install();
} else if (args[0].equalsIgnoreCase("uninstall")) {
uninstall();
} else if (args[0].equalsIgnoreCase("password")) {
passwordKey();
} else {
KeycloakInstalled.console().writer().println("Unknown command: " + args[0]);
KeycloakInstalled.console().writer().println();
printHelp();
}
}
public String getHome() {
String home = System.getenv("HOME");
if (home == null) {
home = System.getProperty("HOME");
if (home == null) {
home = Paths.get("").toAbsolutePath().normalize().toString();
}
}
return home;
}
public void passwordKey() {
if (args.length < 2) {
printHelp();
System.exit(1);
}
String password = args[1];
try {
String encodedKey = generateEncryptionKey(password);
System.out.printf(encodedKey);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
protected String generateEncryptionKey(String password) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 100, 128);
SecretKey tmp = factory.generateSecret(spec);
byte[] aeskey = tmp.getEncoded();
return Base64.encodeBytes(aeskey);
}
public JWE createJWE() {
String key = getEncryptionKey();
if (key == null) {
throw new RuntimeException(KC_SESSION_KEY + " env var not set");
}
byte[] aesKey = null;
try {
aesKey = Base64.decode(key.getBytes("UTF-8"));
} catch (IOException e) {
throw new RuntimeException("invalid " + KC_SESSION_KEY + "env var");
}
JWE jwe = new JWE();
final SecretKey aesSecret = new SecretKeySpec(aesKey, "AES");
jwe.getKeyStorage()
.setEncryptionKey(aesSecret);
return jwe;
}
protected String encryptionKey;
protected String getEncryptionKey() {
if (encryptionKey != null) return encryptionKey;
return System.getenv(KC_SESSION_KEY);
}
public String encrypt(String payload) {
JWE jwe = createJWE();
JWEHeader jweHeader = new JWEHeader(JWEConstants.A128KW, JWEConstants.A128CBC_HS256, null);
try {
jwe.header(jweHeader).content(payload.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("cannot encode payload as UTF-8");
}
try {
return jwe.encodeJwe();
} catch (JWEException e) {
throw new RuntimeException("cannot encrypt payload", e);
}
}
public String decrypt(String encoded) {
JWE jwe = createJWE();
try {
jwe.verifyAndDecodeJwe(encoded);
byte[] content = jwe.getContent();
if (content == null) return null;
return new String(content, "UTF-8");
} catch (Exception ex) {
throw new RuntimeException("cannot decrypt payload", ex);
}
}
public static String getenv(String name, String defaultValue) {
String val = System.getenv(name);
return val == null ? defaultValue : val;
}
public File getConfigDirectory() {
return Paths.get(getHome(), getenv(KC_LOGIN_CONFIG_PATH, ".keycloak"), "kcinit").toFile();
}
public File getConfigFile() {
return Paths.get(getHome(), getenv(KC_LOGIN_CONFIG_PATH, ".keycloak"), "kcinit", "config.json").toFile();
}
public File getTokenFilePath(String client) {
return Paths.get(getHome(), getenv(KC_LOGIN_CONFIG_PATH, ".keycloak"), "kcinit", "tokens", client).toFile();
}
public File getTokenDirectory() {
return Paths.get(getHome(), getenv(KC_LOGIN_CONFIG_PATH, ".keycloak"), "kcinit", "tokens").toFile();
}
protected boolean encrypted = false;
protected void checkEnv() {
File configFile = getConfigFile();
if (!configFile.exists()) {
KeycloakInstalled.console().writer().println("You have not configured kcinit. Please run 'kcinit install' to configure.");
System.exit(1);
}
byte[] data = new byte[0];
try {
data = readFileRaw(configFile);
} catch (IOException e) {
}
if (data == null) {
KeycloakInstalled.console().writer().println("Config file unreadable. Please run 'kcinit install' to configure.");
System.exit(1);
}
String encodedJwe = null;
try {
encodedJwe = new String(data, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (encodedJwe.contains("realm")) {
encrypted = false;
return;
} else {
encrypted = true;
}
if (System.getenv(KC_SESSION_KEY) == null) {
promptLocalPassword();
}
}
protected void promptLocalPassword() {
String password = KeycloakInstalled.console().passwordPrompt("Enter password to unlock kcinit config files: ");
try {
encryptionKey = generateEncryptionKey(password);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected String readFile(File fp) {
try {
byte[] data = readFileRaw(fp);
if (data == null) return null;
String file = new String(data, "UTF-8");
if (!encrypted) {
return file;
}
String decrypted = decrypt(file);
if (decrypted == null)
throw new RuntimeException("Unable to decrypt file. Did you set your local password correctly?");
return decrypted;
} catch (IOException e) {
throw new RuntimeException("failed to decrypt file: " + fp.getAbsolutePath() + " Did you set your local password correctly?", e);
}
}
protected byte[] readFileRaw(File fp) throws IOException {
if (!fp.exists()) return null;
FileInputStream fis = new FileInputStream(fp);
byte[] data = new byte[(int) fp.length()];
fis.read(data);
fis.close();
return data;
}
protected void writeFile(File fp, String payload) {
try {
String data = payload;
if (encrypted) data = encrypt(payload);
FileOutputStream fos = new FileOutputStream(fp);
fos.write(data.getBytes("UTF-8"));
fos.flush();
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void install() {
if (getEncryptionKey() == null) {
if (KeycloakInstalled.console().confirm("Do you want to protect tokens stored locally with a password? (y/n): ")) {
String password = "p";
String confirm = "c";
do {
password = KeycloakInstalled.console().passwordPrompt("Enter local password: ");
confirm = KeycloakInstalled.console().passwordPrompt("Confirm local password: ");
if (!password.equals(confirm)) {
KeycloakInstalled.console().writer().println();
KeycloakInstalled.console().writer().println("Confirmation does not match. Try again.");
KeycloakInstalled.console().writer().println();
}
} while (!password.equals(confirm));
try {
this.encrypted = true;
this.encryptionKey = generateEncryptionKey(password);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
} else {
if (!KeycloakInstalled.console().confirm("KC_SESSION_KEY env var already set. Do you want to use this as your local encryption key? (y/n): ")) {
KeycloakInstalled.console().writer().println("Unset KC_SESSION_KEY env var and run again");
System.exit(1);
}
this.encrypted = true;
this.encryptionKey = getEncryptionKey();
}
String server = KeycloakInstalled.console().readLine("Authentication server URL [http://localhost:8080/auth]: ").trim();
String realm = KeycloakInstalled.console().readLine("Name of realm [master]: ").trim();
String client = KeycloakInstalled.console().readLine("CLI client id [kcinit]: ").trim();
String secret = KeycloakInstalled.console().readLine("CLI client secret [none]: ").trim();
if (server.equals("")) {
server = "http://localhost:8080/auth";
}
if (realm.equals("")) {
realm = "master";
}
if (client.equals("")) {
client = "kcinit";
}
File configDir = getTokenDirectory();
configDir.mkdirs();
File configFile = getConfigFile();
Map<String, String> props = new HashMap<>();
props.put("server", server);
props.put("realm", realm);
props.put("client", client);
props.put("secret", secret);
try {
String json = JsonSerialization.writeValueAsString(props);
writeFile(configFile, json);
} catch (Exception e) {
e.printStackTrace();
}
KeycloakInstalled.console().writer().println();
KeycloakInstalled.console().writer().println("Installation complete!");
KeycloakInstalled.console().writer().println();
}
public void printHelp() {
KeycloakInstalled.console().writer().println("Commands:");
KeycloakInstalled.console().writer().println(" login [-f] -f forces login");
KeycloakInstalled.console().writer().println(" logout");
KeycloakInstalled.console().writer().println(" token [client] - print access token of desired client. Defaults to default master client. Will print either 'error', 'not-allowed', or 'login-required' on error.");
KeycloakInstalled.console().writer().println(" install - Install this utility. Will store in $HOME/.keycloak/kcinit unless " + KC_LOGIN_CONFIG_PATH + " env var is set");
System.exit(1);
}
public AdapterConfig getConfig() {
File configFile = getConfigFile();
if (!configFile.exists()) {
KeycloakInstalled.console().writer().println("You have not configured kcinit. Please run 'kcinit install' to configure.");
System.exit(1);
return null;
}
AdapterConfig config = new AdapterConfig();
config.setAuthServerUrl((String) getConfigProperties().get("server"));
config.setRealm((String) getConfigProperties().get("realm"));
config.setResource((String) getConfigProperties().get("client"));
config.setSslRequired("external");
String secret = (String) getConfigProperties().get("secret");
if (secret != null && !secret.trim().equals("")) {
Map<String, Object> creds = new HashMap<>();
creds.put("secret", secret);
config.setCredentials(creds);
} else {
config.setPublicClient(true);
}
return config;
}
private Map<String, String> getConfigProperties() {
if (this.config != null) return this.config;
if (!getConfigFile().exists()) {
KeycloakInstalled.console().writer().println();
KeycloakInstalled.console().writer().println(("Config file does not exist. Run kcinit install to set it up."));
System.exit(1);
}
String json = readFile(getConfigFile());
try {
Map map = JsonSerialization.readValue(json, Map.class);
config = (Map<String, String>) map;
} catch (IOException e) {
throw new RuntimeException(e);
}
return this.config;
}
public String readToken(String client) throws Exception {
String json = getTokenResponse(client);
if (json == null) return null;
if (json != null) {
try {
AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
if (Time.currentTime() < tokenResponse.getExpiresIn()) {
return tokenResponse.getToken();
}
AdapterConfig config = getConfig();
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
installed.refreshToken(tokenResponse.getRefreshToken());
processResponse(installed, client);
return tokenResponse.getToken();
} catch (Exception e) {
File tokenFile = getTokenFilePath(client);
if (tokenFile.exists()) {
tokenFile.delete();
}
return null;
}
}
return null;
}
public String readRefreshToken(String client) throws Exception {
String json = getTokenResponse(client);
if (json == null) return null;
if (json != null) {
try {
AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
return tokenResponse.getRefreshToken();
} catch (Exception e) {
if (debug) {
e.printStackTrace();
}
File tokenFile = getTokenFilePath(client);
if (tokenFile.exists()) {
tokenFile.delete();
}
return null;
}
}
return null;
}
private String getTokenResponse(String client) throws IOException {
File tokenFile = getTokenFilePath(client);
try {
return readFile(tokenFile);
} catch (Exception e) {
if (debug) {
System.err.println("Failed to read encrypted file");
e.printStackTrace();
}
if (tokenFile.exists()) tokenFile.delete();
return null;
}
}
public void token() throws Exception {
KeycloakInstalled.console().stderrOutput();
checkEnv();
String masterClient = getMasterClient();
String client = masterClient;
if (args.length > 1) {
client = args[1];
}
//System.err.println("readToken: " + client);
String token = readToken(client);
if (token != null) {
System.out.print(token);
return;
}
if (token == null && client.equals(masterClient)) {
//System.err.println("not logged in, logging in.");
doConsoleLogin();
token = readToken(client);
if (token != null) {
System.out.print(token);
return;
}
}
String masterToken = readToken(masterClient);
if (masterToken == null) {
//System.err.println("not logged in, logging in.");
doConsoleLogin();
masterToken = readToken(masterClient);
if (masterToken == null) {
System.err.println("Login failed. Cannot retrieve token");
System.exit(1);
}
}
//System.err.println("exchange: " + client);
Client httpClient = getHttpClient();
WebTarget exchangeUrl = httpClient.target(getServer())
.path("/realms")
.path(getRealm())
.path("protocol/openid-connect/token");
Form form = new Form()
.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE)
.param(OAuth2Constants.CLIENT_ID, masterClient)
.param(OAuth2Constants.SUBJECT_TOKEN, masterToken)
.param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE)
.param(OAuth2Constants.REQUESTED_TOKEN_TYPE, OAuth2Constants.REFRESH_TOKEN_TYPE)
.param(OAuth2Constants.AUDIENCE, client);
if (getMasterClientSecret() != null) {
form.param(OAuth2Constants.CLIENT_SECRET, getMasterClientSecret());
}
Response response = exchangeUrl.request().post(Entity.form(
form
));
if (response.getStatus() == 401 || response.getStatus() == 403) {
response.close();
System.err.println("Not allowed to exchange for client token");
System.exit(1);
}
if (response.getStatus() != 200) {
if (response.getMediaType() != null && response.getMediaType().equals(MediaType.APPLICATION_JSON_TYPE)) {
try {
String json = response.readEntity(String.class);
OAuth2ErrorRepresentation error = JsonSerialization.readValue(json, OAuth2ErrorRepresentation.class);
System.err.println("Failed to exchange token: " + error.getError() + ". " + error.getErrorDescription());
System.exit(1);
} catch (Exception ignore) {
ignore.printStackTrace();
}
}
response.close();
System.err.println("Unknown error exchanging for client token: " + response.getStatus());
System.exit(1);
}
String json = response.readEntity(String.class);
response.close();
AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
if (tokenResponse.getToken() != null) {
getTokenDirectory().mkdirs();
tokenResponse.setExpiresIn(Time.currentTime() + tokenResponse.getExpiresIn());
tokenResponse.setIdToken(null);
json = JsonSerialization.writeValueAsString(tokenResponse);
writeFile(getTokenFilePath(client), json);
System.out.printf(tokenResponse.getToken());
} else {
System.err.println("Error processing token");
System.exit(1);
}
}
protected String getMasterClientSecret() {
return getProperty("secret");
}
protected String getServer() {
return getProperty("server");
}
protected String getRealm() {
return getProperty("realm");
}
public String getProperty(String name) {
return (String) getConfigProperties().get(name);
}
protected boolean forceLogin() {
return args.length > 0 && args[0].equals("-f");
}
public Client getHttpClient() {
return new ResteasyClientBuilder().disableTrustManager().build();
}
public void login() throws Exception {
checkEnv();
this.args = Arrays.copyOfRange(this.args, 1, this.args.length);
for (String arg : args) {
if (arg.equals("-f") || arg.equals("-force")) {
forceLogin = true;
this.args = Arrays.copyOfRange(this.args, 1, this.args.length);
} else if (arg.equals("-browser") || arg.equals("-b")) {
browserLogin = true;
this.args = Arrays.copyOfRange(this.args, 1, this.args.length);
} else {
System.err.println("Illegal argument: " + arg);
printHelp();
System.exit(1);
}
}
String masterClient = getMasterClient();
if (!forceLogin && readToken(masterClient) != null) {
KeycloakInstalled.console().writer().println("Already logged in. `kcinit -f` to force relogin");
return;
}
doConsoleLogin();
KeycloakInstalled.console().writer().println("Login successful!");
}
public void doConsoleLogin() throws Exception {
String masterClient = getMasterClient();
AdapterConfig config = getConfig();
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
//System.err.println("calling loginCommandLine");
if (!installed.loginCommandLine()) {
System.exit(1);
}
processResponse(installed, masterClient);
}
private String getMasterClient() {
return getProperty("client");
}
private void processResponse(KeycloakInstalled installed, String client) throws IOException {
AccessTokenResponse tokenResponse = installed.getTokenResponse();
tokenResponse.setExpiresIn(Time.currentTime() + tokenResponse.getExpiresIn());
tokenResponse.setIdToken(null);
String json = JsonSerialization.writeValueAsString(tokenResponse);
getTokenDirectory().mkdirs();
writeFile(getTokenFilePath(client), json);
}
public void logout() throws Exception {
String token = readRefreshToken(getMasterClient());
if (token != null) {
try {
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getConfig());
ServerRequest.invokeLogout(deployment, token);
} catch (Exception e) {
if (debug) {
e.printStackTrace();
}
}
}
if (getTokenDirectory().exists()) {
for (File fp : getTokenDirectory().listFiles()) fp.delete();
}
}
public void uninstall() throws Exception {
File configFile = getConfigFile();
if (configFile.exists()) configFile.delete();
if (getTokenDirectory().exists()) {
for (File fp : getTokenDirectory().listFiles()) fp.delete();
}
}
}
@@ -1,266 +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.installed;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.ServerRequest;
import org.keycloak.common.util.Time;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.util.JsonSerialization;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
*
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakCliSso {
public void mainCmd(String[] args) throws Exception {
if (args.length != 1) {
printHelp();
return;
}
if (args[0].equalsIgnoreCase("login")) {
login();
} else if (args[0].equalsIgnoreCase("login-manual")) {
loginManual();
} else if (args[0].equalsIgnoreCase("token")) {
token();
} else if (args[0].equalsIgnoreCase("logout")) {
logout();
} else if (args[0].equalsIgnoreCase("env")) {
System.out.println(System.getenv().toString());
} else {
printHelp();
}
}
public void printHelp() {
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(" token - print access token if logged in");
System.err.println(" logout - logout.");
System.exit(1);
}
public AdapterConfig getConfig() {
String url = System.getProperty("KEYCLOAK_AUTH_SERVER");
if (url == null) {
System.err.println("KEYCLOAK_AUTH_SERVER property not set");
System.exit(1);
}
String realm = System.getProperty("KEYCLOAK_REALM");
if (realm == null) {
System.err.println("KEYCLOAK_REALM property not set");
System.exit(1);
}
String client = System.getProperty("KEYCLOAK_CLIENT");
if (client == null) {
System.err.println("KEYCLOAK_CLIENT property not set");
System.exit(1);
}
String secret = System.getProperty("KEYCLOAK_CLIENT_SECRET");
AdapterConfig config = new AdapterConfig();
config.setAuthServerUrl(url);
config.setRealm(realm);
config.setResource(client);
config.setSslRequired("external");
if (secret != null) {
Map<String, Object> creds = new HashMap<>();
creds.put("secret", secret);
config.setCredentials(creds);
} else {
config.setPublicClient(true);
}
return config;
}
public boolean checkToken() throws Exception {
String token = getTokenResponse();
if (token == null) return false;
if (token != null) {
Matcher m = Pattern.compile("\\{.*\\}\\z").matcher(token);
if (m.find()) {
String json = m.group(0);
try {
AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
if (Time.currentTime() < tokenResponse.getExpiresIn()) {
return true;
}
AdapterConfig config = getConfig();
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
installed.refreshToken(tokenResponse.getRefreshToken());
processResponse(installed);
return true;
} catch (Exception e) {
System.err.println("Error processing existing token");
e.printStackTrace();
}
}
}
return false;
}
private String getTokenResponse() throws IOException {
String token = null;
File tokenFile = getTokenFilePath();
if (tokenFile.exists()) {
FileInputStream fis = new FileInputStream(tokenFile);
byte[] data = new byte[(int) tokenFile.length()];
fis.read(data);
fis.close();
token = new String(data, "UTF-8");
}
return token;
}
public void token() throws Exception {
String token = getTokenResponse();
if (token == null) {
System.err.println("There is no token for client");
System.exit(1);
} else {
Matcher m = Pattern.compile("\\{.*\\}\\z").matcher(token);
if (m.find()) {
String json = m.group(0);
try {
AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
if (Time.currentTime() < tokenResponse.getExpiresIn()) {
System.out.println(tokenResponse.getToken());
return;
} else {
System.err.println("token in response file is expired");
System.exit(1);
}
} catch (Exception e) {
System.err.println("Failure processing token response file");
e.printStackTrace();
System.exit(1);
}
} else {
System.err.println("Could not find json within token response file");
System.exit(1);
}
}
}
public void login() throws Exception {
if (checkToken()) return;
AdapterConfig config = getConfig();
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
installed.login();
processResponse(installed);
}
public String getHome() {
String home = System.getenv("HOME");
if (home == null) {
home = System.getProperty("HOME");
if (home == null) {
home = Paths.get("").toAbsolutePath().normalize().toString();
}
}
return home;
}
public File getTokenDirectory() {
return Paths.get(getHome(), System.getProperty("basepath", ".keycloak-sso"), System.getProperty("KEYCLOAK_REALM")).toFile();
}
public File getTokenFilePath() {
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 {
AccessTokenResponse tokenResponse = installed.getTokenResponse();
tokenResponse.setExpiresIn(Time.currentTime() + tokenResponse.getExpiresIn());
tokenResponse.setIdToken(null);
String output = JsonSerialization.writeValueAsString(tokenResponse);
getTokenDirectory().mkdirs();
FileOutputStream fos = new FileOutputStream(getTokenFilePath());
fos.write(output.getBytes("UTF-8"));
fos.flush();
fos.close();
System.out.println(tokenResponse.getToken());
}
public void loginManual() throws Exception {
if (checkToken()) return;
AdapterConfig config = getConfig();
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config);
KeycloakInstalled installed = new KeycloakInstalled(deployment);
installed.loginManual();
processResponse(installed);
}
public void logout() throws Exception {
String token = getTokenResponse();
if (token != null) {
Matcher m = Pattern.compile("\\{.*\\}\\z").matcher(token);
if (m.find()) {
String json = m.group(0);
try {
AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
if (Time.currentTime() > tokenResponse.getExpiresIn()) {
System.err.println("Login is expired");
System.exit(1);
}
AdapterConfig config = getConfig();
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config);
ServerRequest.invokeLogout(deployment, tokenResponse.getRefreshToken());
for (File fp : getTokenDirectory().listFiles()) fp.delete();
System.out.println("logout complete");
} catch (Exception e) {
System.err.println("Failure processing token response file");
e.printStackTrace();
System.exit(1);
}
} else {
System.err.println("Could not find json within token response file");
System.exit(1);
}
} else {
System.err.println("Not logged in");
System.exit(1);
}
}
}
@@ -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,15 +33,13 @@ 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;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URI;
@@ -48,6 +47,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>
@@ -56,6 +57,7 @@ public class KeycloakInstalled {
public interface HttpResponseWriter {
void success(PrintWriter pw, KeycloakInstalled ki);
void failure(PrintWriter pw, KeycloakInstalled ki);
}
@@ -77,7 +79,10 @@ public class KeycloakInstalled {
private Locale locale;
private HttpResponseWriter loginResponseWriter;
private HttpResponseWriter logoutResponseWriter;
private ResteasyClient resteasyClient;
Pattern callbackPattern = Pattern.compile("callback\\s*=\\s*\"([^\"]+)\"");
Pattern paramPattern = Pattern.compile("param=\"([^\"]+)\"\\s+label=\"([^\"]+)\"\\s+mask=(\\S+)");
Pattern codePattern = Pattern.compile("code=([^&]+)");
public KeycloakInstalled() {
@@ -93,70 +98,12 @@ public class KeycloakInstalled {
this.deployment = deployment;
}
private static HttpResponseWriter defaultLoginWriter = new HttpResponseWriter() {
@Override
public void success(PrintWriter pw, KeycloakInstalled ki) {
pw.println("HTTP/1.1 200 OK");
pw.println("Content-Type: text/html");
pw.println();
pw.println("<html><h1>Login completed.</h1><div>");
pw.println("This browser will remain logged in until you close it, logout, or the session expires.");
pw.println("</div></html>");
pw.flush();
}
@Override
public void failure(PrintWriter pw, KeycloakInstalled ki) {
pw.println("HTTP/1.1 200 OK");
pw.println("Content-Type: text/html");
pw.println();
pw.println("<html><h1>Login attempt failed.</h1><div>");
pw.println("</div></html>");
pw.flush();
}
};
private static HttpResponseWriter defaultLogoutWriter = new HttpResponseWriter() {
@Override
public void success(PrintWriter pw, KeycloakInstalled ki) {
pw.println("HTTP/1.1 200 OK");
pw.println("Content-Type: text/html");
pw.println();
pw.println("<html><h1>Logout completed.</h1><div>");
pw.println("You may close this browser tab.");
pw.println("</div></html>");
pw.flush();
}
@Override
public void failure(PrintWriter pw, KeycloakInstalled ki) {
pw.println("HTTP/1.1 200 OK");
pw.println("Content-Type: text/html");
pw.println();
pw.println("<html><h1>Logout failed.</h1><div>");
pw.println("You may close this browser tab.");
pw.println("</div></html>");
pw.flush();
}
};
public HttpResponseWriter getLoginResponseWriter() {
if (loginResponseWriter == null) {
return defaultLoginWriter;
} else {
return loginResponseWriter;
}
return null;
}
public HttpResponseWriter getLogoutResponseWriter() {
if (logoutResponseWriter == null) {
return defaultLogoutWriter;
} else {
return logoutResponseWriter;
}
return null;
}
public void setLoginResponseWriter(HttpResponseWriter loginResponseWriter) {
@@ -167,6 +114,10 @@ public class KeycloakInstalled {
this.logoutResponseWriter = logoutResponseWriter;
}
public void setResteasyClient(ResteasyClient resteasyClient) {
this.resteasyClient = resteasyClient;
}
public Locale getLocale() {
return locale;
}
@@ -290,6 +241,272 @@ public class KeycloakInstalled {
status = Status.LOGGED_MANUAL;
}
public static class Console {
protected java.io.Console console = System.console();
protected PrintWriter writer;
protected BufferedReader reader;
static Console SINGLETON = new Console();
private Console() {
}
public PrintWriter writer() {
if (console == null) {
if (writer == null) {
writer = new PrintWriter(System.err, true);
}
return writer;
}
return console.writer();
}
public Reader reader() {
if (console == null) {
return getReader();
}
return console.reader();
}
protected BufferedReader getReader() {
if (reader != null) return reader;
reader = new BufferedReader(new BufferedReader(new InputStreamReader(System.in)));
return reader;
}
public Console format(String fmt, Object... args) {
if (console == null) {
writer().format(fmt, args);
return this;
}
console.format(fmt, args);
return this;
}
public Console printf(String format, Object... args) {
if (console == null) {
writer().printf(format, args);
return this;
}
console.printf(format, args);
return this;
}
public String readLine(String fmt, Object... args) {
if (console == null) {
format(fmt, args);
return readLine();
}
return console.readLine(fmt, args);
}
public boolean confirm(String fmt, Object... args) {
String prompt = "";
while (!"y".equals(prompt) && !"n".equals(prompt)) {
prompt = readLine(fmt, args);
}
return "y".equals(prompt);
}
public String prompt(String fmt, Object... args) {
String prompt = "";
while (prompt.equals("")) {
prompt = readLine(fmt, args).trim();
}
return prompt;
}
public String passwordPrompt(String fmt, Object... args) {
String prompt = "";
while (prompt.equals("")) {
char[] val = readPassword(fmt, args);
prompt = new String(val);
prompt = prompt.trim();
}
return prompt;
}
public String readLine() {
if (console == null) {
try {
return getReader().readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return console.readLine();
}
public char[] readPassword(String fmt, Object... args) {
if (console == null) {
return readLine(fmt, args).toCharArray();
}
return console.readPassword(fmt, args);
}
public char[] readPassword() {
if (console == null) {
return readLine().toCharArray();
}
return console.readPassword();
}
public void flush() {
if (console == null) {
System.err.flush();
return;
}
console.flush();
}
public void stderrOutput() {
//System.err.println("not using System.console()");
console = null;
}
}
public static Console console() {
return Console.SINGLETON;
}
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("display", "console")
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
.build().toString();
ResteasyClient client = createResteasyClient();
try {
//System.err.println("initial request");
Response response = client.target(authUrl).request().get();
while (true) {
if (response.getStatus() == 403) {
if (response.getMediaType() != null) {
String splash = response.readEntity(String.class);
console().writer().println(splash);
} else {
System.err.println("Forbidden to login");
}
return false;
} else if (response.getStatus() == 401) {
String authenticationHeader = response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE);
if (authenticationHeader == null) {
System.err.println("Failure: Invalid protocol. No WWW-Authenticate header");
return false;
}
//System.err.println("got header: " + authenticationHeader);
if (!authenticationHeader.contains("X-Text-Form-Challenge")) {
System.err.println("Failure: Invalid WWW-Authenticate header.");
return false;
}
if (response.getMediaType() != null) {
String splash = response.readEntity(String.class);
console().writer().println(splash);
} else {
response.close();
}
Matcher m = callbackPattern.matcher(authenticationHeader);
if (!m.find()) {
System.err.println("Failure: Invalid WWW-Authenticate header.");
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 = console().readPassword(label);
value = new String(txt);
} else {
value = console().readLine(label);
}
form.param(param, value);
}
response.close();
client.close();
client = createResteasyClient();
response = client.target(callback).request().post(Entity.form(form));
} else if (response.getStatus() == 302) {
int redirectCount = 0;
do {
String location = response.getLocation().toString();
Matcher m = codePattern.matcher(location);
if (!m.find()) {
response.close();
client.close();
client = createResteasyClient();
response = client.target(location).request().get();
} else {
response.close();
client.close();
String code = m.group(1);
processCode(code, redirectUri);
return true;
}
if (response.getStatus() == 302 && redirectCount++ > 4) {
System.err.println("Too many redirects. Aborting");
return false;
}
} while (response.getStatus() == 302);
} else {
System.err.println("Unknown response from server: " + response.getStatus());
return false;
}
}
} catch (Exception ex) {
throw ex;
} finally {
client.close();
}
}
protected ResteasyClient getResteasyClient() {
if (this.resteasyClient == null) {
this.resteasyClient = createResteasyClient();
}
return this.resteasyClient;
}
protected ResteasyClient createResteasyClient() {
return new ResteasyClientBuilder()
.connectionCheckoutTimeout(1, TimeUnit.HOURS)
.connectionTTL(1, TimeUnit.HOURS)
.socketTimeout(1, TimeUnit.HOURS)
.disableTrustManager().build();
}
public String getTokenString() throws VerificationException, IOException, ServerRequest.HttpFailure {
return tokenString;
}
@@ -308,7 +525,7 @@ public class KeycloakInstalled {
parseAccessToken(tokenResponse);
}
public void refreshToken(String refreshToken) throws IOException, ServerRequest.HttpFailure, VerificationException {
public void refreshToken(String refreshToken) throws IOException, ServerRequest.HttpFailure, VerificationException {
AccessTokenResponse tokenResponse = ServerRequest.invokeRefresh(deployment, refreshToken);
parseAccessToken(tokenResponse);
@@ -360,7 +577,6 @@ public class KeycloakInstalled {
}
private void processCode(String code, String redirectUri) throws IOException, ServerRequest.HttpFailure, VerificationException {
AccessTokenResponse tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, redirectUri, null);
parseAccessToken(tokenResponse);
@@ -435,11 +651,26 @@ public class KeycloakInstalled {
OutputStreamWriter out = new OutputStreamWriter(socket.getOutputStream());
PrintWriter pw = new PrintWriter(out);
if (writer != null) {
System.err.println("Using a writer is deprecated. Please remove its usage. This is now handled by endpoint on server");
}
if (error == null) {
writer.success(pw, KeycloakInstalled.this);
if (writer != null) {
writer.success(pw, KeycloakInstalled.this);
} else {
pw.println("HTTP/1.1 302 Found");
pw.println("Location: " + deployment.getTokenUrl().replace("/token", "/delegated"));
}
} else {
writer.failure(pw, KeycloakInstalled.this);
if (writer != null) {
writer.failure(pw, KeycloakInstalled.this);
} else {
pw.println("HTTP/1.1 302 Found");
pw.println("Location: " + deployment.getTokenUrl().replace("/token", "/delegated?error=true"));
}
}
pw.flush();
socket.close();
+2 -2
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.0.Final</version>
<version>4.4.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -33,7 +33,7 @@
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
<artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
<artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
@@ -27,6 +27,9 @@ import javax.security.cert.X509Certificate;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.SecurityContext;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
@@ -51,6 +54,8 @@ public class JaxrsHttpFacade implements OIDCHttpFacade {
protected class RequestFacade implements OIDCHttpFacade.Request {
private InputStream inputStream;
@Override
public String getFirstParam(String param) {
throw new RuntimeException("NOT IMPLEMENTED");
@@ -108,6 +113,19 @@ public class JaxrsHttpFacade implements OIDCHttpFacade {
@Override
public InputStream getInputStream() {
return getInputStream(false);
}
@Override
public InputStream getInputStream(boolean buffered) {
if (inputStream != null) {
return inputStream;
}
if (buffered) {
return inputStream = new BufferedInputStream(requestContext.getEntityStream());
}
return requestContext.getEntityStream();
}
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.0.Final</version>
<version>4.4.0.Final</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.0.Final</version>
<version>4.4.0.Final</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.0.Final</version>
<version>4.4.0.Final</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.0.Final</version>
<version>4.4.0.Final</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.0.Final</version>
<version>4.4.0.Final</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.0.Final</version>
<version>4.4.0.Final</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.0.Final</version>
<version>4.4.0.Final</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.0.Final</version>
<version>4.4.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
+73 -10
View File
@@ -18,7 +18,7 @@
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import * as Keycloak from 'keycloak';
import * as Keycloak from './keycloak';
export as namespace KeycloakAuthorization;
@@ -35,6 +35,64 @@ declare namespace KeycloakAuthorization {
then(onGrant: (rpt: string) => void, onDeny: () => void, onError: () => void): void;
}
interface AuthorizationRequest {
/**
* An array of objects representing the resource and scopes.
*/
permissions?:ResourcePermission[],
/**
* A permission ticket obtained from a resource server when using UMA authorization protocol.
*/
ticket?:string,
/**
* A boolean value indicating whether the server should create permission requests to the resources
* and scopes referenced by a permission ticket. This parameter will only take effect when used together
* with the ticket parameter as part of a UMA authorization process.
*/
submitRequest?:boolean,
/**
* Defines additional information about this authorization request in order to specify how it should be processed
* by the server.
*/
metadata?:AuthorizationRequestMetadata,
/**
* Defines whether or not this authorization request should include the current RPT. If set to true, the RPT will
* be sent and permissions in the current RPT will be included in the new RPT. Otherwise, only the permissions referenced in this
* authorization request will be granted in the new RPT.
*/
incrementalAuthorization?:boolean
}
interface AuthorizationRequestMetadata {
/**
* A boolean value indicating to the server if resource names should be included in the RPT’s permissions.
* If false, only the resource identifier is included.
*/
responseIncludeResourceName?:any,
/**
* An integer N that defines a limit for the amount of permissions an RPT can have. When used together with
* rpt parameter, only the last N requested permissions will be kept in the RPT.
*/
response_permissions_limit?:number
}
interface ResourcePermission {
/**
* The id or name of a resource.
*/
id:string,
/**
* An array of strings where each value is the name of a scope associated with the resource.
*/
scopes?:string[]
}
interface KeycloakAuthorizationInstance {
rpt: any;
config: { rpt_endpoint: string };
@@ -42,18 +100,23 @@ declare namespace KeycloakAuthorization {
init(): void;
/**
* This method enables client applications to better integrate with resource servers protected by a Keycloak
* policy enforcer.
*
* In this case, the resource server will respond with a 401 status code and a WWW-Authenticate header holding the
* 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.
*/
authorize(wwwAuthenticateHeader: string): KeycloakAuthorizationPromise;
* This method enables client applications to better integrate with resource servers protected by a Keycloak
* policy enforcer using UMA protocol.
*
* The authorization request must be provided with a ticket.
*
* @param authorizationRequest An AuthorizationRequest instance with a valid permission ticket set.
* @returns A promise to set functions to be invoked on grant, deny or error.
*/
authorize(authorizationRequest: AuthorizationRequest): KeycloakAuthorizationPromise;
/**
* Obtains all entitlements from a Keycloak server based on a given resourceServerId.
*
* @param resourceServerId The id (client id) of the resource server to obtain permissions from.
* @param authorizationRequest An AuthorizationRequest instance.
* @returns A promise to set functions to be invoked on grant, deny or error.
*/
entitlement(resourceServerId: string, entitlementRequest: {}): KeycloakAuthorizationPromise;
entitlement(resourceServerId: string, authorizationRequest?: AuthorizationRequest): KeycloakAuthorizationPromise;
}
}
@@ -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) {
@@ -41,74 +41,65 @@
/**
* This method enables client applications to better integrate with resource servers protected by a Keycloak
* policy enforcer.
* policy enforcer using UMA protocol.
*
* In this case, the resource server will respond with a 401 status code and a WWW-Authenticate header holding the
* 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.
* The authorization request must be provided with a ticket.
*/
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 +107,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,25 +141,70 @@
}
};
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;
};
this.init(this);
return this;
};
if ( typeof module === "object" && module && typeof module.exports === "object" ) {
+511 -478
View File
@@ -1,478 +1,511 @@
/*
* MIT License
*
* Copyright 2017 Brett Epps <https://github.com/eppsilon>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
export as namespace Keycloak;
export = Keycloak;
/**
* Creates a new Keycloak client instance.
* @param config Path to a JSON config file or a plain config object.
*/
declare function Keycloak(config?: string|{}): Keycloak.KeycloakInstance;
declare namespace Keycloak {
type KeycloakAdapterName = 'cordova'|'default';
type KeycloakOnLoad = 'login-required'|'check-sso';
type KeycloakResponseMode = 'query'|'fragment';
type KeycloakResponseType = 'code'|'id_token token'|'code id_token token';
type KeycloakFlow = 'standard'|'implicit'|'hybrid';
interface KeycloakInitOptions {
/**
* @private Undocumented.
*/
adapter?: KeycloakAdapterName;
/**
* Specifies an action to do on load.
*/
onLoad?: KeycloakOnLoad;
/**
* Set an initial value for the token.
*/
token?: string;
/**
* Set an initial value for the refresh token.
*/
refreshToken?: string;
/**
* Set an initial value for the id token (only together with `token` or
* `refreshToken`).
*/
idToken?: string;
/**
* Set an initial value for skew between local time and Keycloak server in
* seconds (only together with `token` or `refreshToken`).
*/
timeSkew?: number;
/**
* Set to enable/disable monitoring login state.
* @default true
*/
checkLoginIframe?: boolean;
/**
* Set the interval to check login state (in seconds).
* @default 5
*/
checkLoginIframeInterval?: boolean;
/**
* Set the OpenID Connect response mode to send to Keycloak upon login.
* @default fragment After successful authentication Keycloak will redirect
* to JavaScript application with OpenID Connect parameters
* added in URL fragment. This is generally safer and
* recommended over query.
*/
responseMode?: KeycloakResponseMode;
/**
* Set the OpenID Connect flow.
* @default standard
*/
flow?: KeycloakFlow;
}
interface KeycloakLoginOptions {
/**
* @private Undocumented.
*/
scope?: string;
/**
* Specifies the uri to redirect to after login.
*/
redirectUri?: string;
/**
* By default the login screen is displayed if the user is not logged into
* Keycloak. To only authenticate to the application if the user is already
* logged in and not display the login page if the user is not logged in, set
* this option to `'none'`. To always require re-authentication and ignore
* SSO, set this option to `'login'`.
*/
prompt?: 'none'|'login';
/**
* If value is `'register'` then user is redirected to registration page,
* otherwise to login page.
*/
action?: 'register';
/**
* Used just if user is already authenticated. Specifies maximum time since
* the authentication of user happened. If user is already authenticated for
* longer time than `'maxAge'`, the SSO is ignored and he will need to
* authenticate again.
*/
maxAge?: number;
/**
* Used to pre-fill the username/email field on the login form.
*/
loginHint?: string;
/**
* Used to tell Keycloak which IDP the user wants to authenticate with.
*/
idpHint?: string;
/**
* Specifies the desired locale for the UI.
*/
locale?: string;
}
type KeycloakPromiseCallback<T> = (result: T) => void;
interface KeycloakPromise<TSuccess, TError> {
/**
* Function to call if the promised action succeeds.
*/
success(callback: KeycloakPromiseCallback<TSuccess>): KeycloakPromise<TSuccess, TError>;
/**
* Function to call if the promised action throws an error.
*/
error(callback: KeycloakPromiseCallback<TError>): KeycloakPromise<TSuccess, TError>;
}
interface KeycloakError {
error: string;
error_description: string;
}
interface KeycloakAdapter {
login(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
logout(options?: any): KeycloakPromise<void, void>;
register(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
accountManagement(): KeycloakPromise<void, void>;
redirectUri(options: { redirectUri: string; }, encodeHash: boolean): string;
}
interface KeycloakProfile {
id?: string;
username?: string;
email?: string;
firstName?: string;
lastName?: string;
enabled?: boolean;
emailVerified?: boolean;
totp?: boolean;
createdTimestamp?: number;
}
// export interface KeycloakUserInfo {}
/**
* A client for the Keycloak authentication server.
* @see {@link https://keycloak.gitbooks.io/securing-client-applications-guide/content/topics/oidc/javascript-adapter.html|Keycloak JS adapter documentation}
*/
interface KeycloakInstance {
/**
* Is true if the user is authenticated, false otherwise.
*/
authenticated?: boolean;
/**
* The user id.
*/
subject?: string;
/**
* Response mode passed in init (default value is `'fragment'`).
*/
responseMode?: KeycloakResponseMode;
/**
* Response type sent to Keycloak with login requests. This is determined
* based on the flow value used during initialization, but can be overridden
* by setting this value.
*/
responseType?: KeycloakResponseType;
/**
* Flow passed in init.
*/
flow?: KeycloakFlow;
/**
* The realm roles associated with the token.
*/
realmAccess?: { roles: string[] };
/**
* The resource roles associated with the token.
*/
resourceAccess?: string[];
/**
* The base64 encoded token that can be sent in the Authorization header in
* requests to services.
*/
token?: string;
/**
* The parsed token as a JavaScript object.
*/
tokenParsed?: {
exp?: number;
iat?: number;
nonce?: string;
sub?: string;
session_state?: string;
realm_access?: { roles: string[] };
resource_access?: string[];
};
/**
* The base64 encoded refresh token that can be used to retrieve a new token.
*/
refreshToken?: string;
/**
* The parsed refresh token as a JavaScript object.
*/
refreshTokenParsed?: { nonce?: string };
/**
* The base64 encoded ID token.
*/
idToken?: string;
/**
* The parsed id token as a JavaScript object.
*/
idTokenParsed?: { nonce?: string };
/**
* The estimated time difference between the browser time and the Keycloak
* server in seconds. This value is just an estimation, but is accurate
* enough when determining if a token is expired or not.
*/
timeSkew?: number;
/**
* @private Undocumented.
*/
loginRequired?: boolean;
/**
* @private Undocumented.
*/
authServerUrl?: string;
/**
* @private Undocumented.
*/
realm?: string;
/**
* @private Undocumented.
*/
clientId?: string;
/**
* @private Undocumented.
*/
clientSecret?: string;
/**
* @private Undocumented.
*/
redirectUri?: string;
/**
* @private Undocumented.
*/
sessionId?: string;
/**
* @private Undocumented.
*/
profile?: KeycloakProfile;
/**
* @private Undocumented.
*/
userInfo?: {}; // KeycloakUserInfo;
/**
* Called when the adapter is initialized.
*/
onReady?(authenticated?: boolean): void;
/**
* Called when a user is successfully authenticated.
*/
onAuthSuccess?(): void;
/**
* Called if there was an error during authentication.
*/
onAuthError?(errorData: KeycloakError): void;
/**
* Called when the token is refreshed.
*/
onAuthRefreshSuccess?(): void;
/**
* Called if there was an error while trying to refresh the token.
*/
onAuthRefreshError?(): void;
/**
* Called if the user is logged out (will only be called if the session
* status iframe is enabled, or in Cordova mode).
*/
onAuthLogout?(): void;
/**
* Called when the access token is expired. If a refresh token is available
* the token can be refreshed with Keycloak#updateToken, or in cases where
* it's not (ie. with implicit flow) you can redirect to login screen to
* obtain a new access token.
*/
onTokenExpired?(): void;
/**
* Called to initialize the adapter.
* @param initOptions Initialization options.
* @returns A promise to set functions to be invoked on success or error.
*/
init(initOptions: KeycloakInitOptions): KeycloakPromise<boolean, KeycloakError>;
/**
* Redirects to login form.
* @param options Login options.
*/
login(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
/**
* Redirects to logout.
* @param options Logout options.
* @param options.redirectUri Specifies the uri to redirect to after logout.
*/
logout(options?: any): KeycloakPromise<void, void>;
/**
* Redirects to registration form.
* @param options Supports same options as Keycloak#login but `action` is
* set to `'register'`.
*/
register(options?: any): KeycloakPromise<void, void>;
/**
* Redirects to the Account Management Console.
*/
accountManagement(): KeycloakPromise<void, void>;
/**
* Returns the URL to login form.
* @param options Supports same options as Keycloak#login.
*/
createLoginUrl(options?: KeycloakLoginOptions): string;
/**
* Returns the URL to logout the user.
* @param options Logout options.
* @param options.redirectUri Specifies the uri to redirect to after logout.
*/
createLogoutUrl(options?: any): string;
/**
* Returns the URL to registration page.
* @param options Supports same options as Keycloak#createLoginUrl but
* `action` is set to `'register'`.
*/
createRegisterUrl(options?: KeycloakLoginOptions): string;
/**
* Returns the URL to the Account Management Console.
*/
createAccountUrl(): string;
/**
* Returns true if the token has less than `minValidity` seconds left before
* it expires.
* @param minValidity If not specified, `0` is used.
*/
isTokenExpired(minValidity?: number): boolean;
/**
* If the token expires within `minValidity` seconds, the token is refreshed.
* If the session status iframe is enabled, the session status is also
* checked.
* @returns A promise to set functions that can be invoked if the token is
* still valid, or if the token is no longer valid.
* @example
* ```js
* keycloak.updateToken(5).success(function(refreshed) {
* if (refreshed) {
* alert('Token was successfully refreshed');
* } else {
* alert('Token is still valid');
* }
* }).error(function() {
* alert('Failed to refresh the token, or the session has expired');
* });
*/
updateToken(minValidity: number): KeycloakPromise<boolean, boolean>;
/**
* Clears authentication state, including tokens. This can be useful if
* the application has detected the session was expired, for example if
* updating token fails. Invoking this results in Keycloak#onAuthLogout
* callback listener being invoked.
*/
clearToken(): void;
/**
* Returns true if the token has the given realm role.
* @param role A realm role name.
*/
hasRealmRole(role: string): boolean;
/**
* Returns true if the token has the given role for the resource.
* @param role A role name.
* @param resource If not specified, `clientId` is used.
*/
hasResourceRole(role: string, resource?: string): boolean;
/**
* Loads the user's profile.
* @returns A promise to set functions to be invoked on success or error.
*/
loadUserProfile(): KeycloakPromise<KeycloakProfile, void>;
/**
* @private Undocumented.
*/
loadUserInfo(): KeycloakPromise<{}, void>;
}
}
/*
* MIT License
*
* Copyright 2017 Brett Epps <https://github.com/eppsilon>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
export as namespace Keycloak;
export = Keycloak;
/**
* Creates a new Keycloak client instance.
* @param config Path to a JSON config file or a plain config object.
*/
declare function Keycloak(config?: string|{}): Keycloak.KeycloakInstance;
declare namespace Keycloak {
type KeycloakAdapterName = 'cordova' | 'cordova-native' |'default' | any;
type KeycloakOnLoad = 'login-required'|'check-sso';
type KeycloakResponseMode = 'query'|'fragment';
type KeycloakResponseType = 'code'|'id_token token'|'code id_token token';
type KeycloakFlow = 'standard'|'implicit'|'hybrid';
interface KeycloakInitOptions {
/**
* @private Undocumented.
*/
useNonce?: boolean;
/**
* Allows to use different adapter:
*
* - {string} default - using browser api for redirects
* - {string} cordova - using cordova plugins
* - {function} - allows to provide custom function as adapter.
*/
adapter?: KeycloakAdapterName;
/**
* Specifies an action to do on load.
*/
onLoad?: KeycloakOnLoad;
/**
* Set an initial value for the token.
*/
token?: string;
/**
* Set an initial value for the refresh token.
*/
refreshToken?: string;
/**
* Set an initial value for the id token (only together with `token` or
* `refreshToken`).
*/
idToken?: string;
/**
* Set an initial value for skew between local time and Keycloak server in
* seconds (only together with `token` or `refreshToken`).
*/
timeSkew?: number;
/**
* Set to enable/disable monitoring login state.
* @default true
*/
checkLoginIframe?: boolean;
/**
* Set the interval to check login state (in seconds).
* @default 5
*/
checkLoginIframeInterval?: number;
/**
* Set the OpenID Connect response mode to send to Keycloak upon login.
* @default fragment After successful authentication Keycloak will redirect
* to JavaScript application with OpenID Connect parameters
* added in URL fragment. This is generally safer and
* recommended over query.
*/
responseMode?: KeycloakResponseMode;
/**
* Specifies a default uri to redirect to after login or logout.
* This is currently supported for adapter 'cordova-native' and 'default'
*/
redirectUri?: string;
/**
* Set the OpenID Connect flow.
* @default standard
*/
flow?: KeycloakFlow;
}
interface KeycloakLoginOptions {
/**
* @private Undocumented.
*/
scope?: string;
/**
* Specifies the uri to redirect to after login.
*/
redirectUri?: string;
/**
* By default the login screen is displayed if the user is not logged into
* Keycloak. To only authenticate to the application if the user is already
* logged in and not display the login page if the user is not logged in, set
* this option to `'none'`. To always require re-authentication and ignore
* SSO, set this option to `'login'`.
*/
prompt?: 'none'|'login';
/**
* If value is `'register'` then user is redirected to registration page,
* otherwise to login page.
*/
action?: 'register';
/**
* Used just if user is already authenticated. Specifies maximum time since
* the authentication of user happened. If user is already authenticated for
* longer time than `'maxAge'`, the SSO is ignored and he will need to
* authenticate again.
*/
maxAge?: number;
/**
* Used to pre-fill the username/email field on the login form.
*/
loginHint?: string;
/**
* Used to tell Keycloak which IDP the user wants to authenticate with.
*/
idpHint?: string;
/**
* Sets the 'ui_locales' query param in compliance with section 3.1.2.1
* of the OIDC 1.0 specification.
*/
locale?: string;
/**
* Specifies the desired Keycloak locale for the UI. This differs from
* the locale param in that it tells the Keycloak server to set a cookie and update
* the user's profile to a new preferred locale.
*/
kcLocale?: string;
/**
* Specifies arguments that are passed to the Cordova in-app-browser (if applicable).
* Options 'hidden' and 'location' are not affected by these arguments.
* All available options are defined at https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-inappbrowser/.
* Example of use: { zoom: "no", hardwareback: "yes" }
*/
cordovaOptions?: { [optionName: string]: string };
}
type KeycloakPromiseCallback<T> = (result: T) => void;
interface KeycloakPromise<TSuccess, TError> {
/**
* Function to call if the promised action succeeds.
*/
success(callback: KeycloakPromiseCallback<TSuccess>): KeycloakPromise<TSuccess, TError>;
/**
* Function to call if the promised action throws an error.
*/
error(callback: KeycloakPromiseCallback<TError>): KeycloakPromise<TSuccess, TError>;
}
interface KeycloakError {
error: string;
error_description: string;
}
interface KeycloakAdapter {
login(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
logout(options?: any): KeycloakPromise<void, void>;
register(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
accountManagement(): KeycloakPromise<void, void>;
redirectUri(options: { redirectUri: string; }, encodeHash: boolean): string;
}
interface KeycloakProfile {
id?: string;
username?: string;
email?: string;
firstName?: string;
lastName?: string;
enabled?: boolean;
emailVerified?: boolean;
totp?: boolean;
createdTimestamp?: number;
}
interface KeycloakTokenParsed {
exp?: number;
iat?: number;
nonce?: string;
sub?: string;
session_state?: string;
realm_access?: { roles: string[] };
resource_access?: string[];
}
// export interface KeycloakUserInfo {}
/**
* A client for the Keycloak authentication server.
* @see {@link https://keycloak.gitbooks.io/securing-client-applications-guide/content/topics/oidc/javascript-adapter.html|Keycloak JS adapter documentation}
*/
interface KeycloakInstance {
/**
* Is true if the user is authenticated, false otherwise.
*/
authenticated?: boolean;
/**
* The user id.
*/
subject?: string;
/**
* Response mode passed in init (default value is `'fragment'`).
*/
responseMode?: KeycloakResponseMode;
/**
* Response type sent to Keycloak with login requests. This is determined
* based on the flow value used during initialization, but can be overridden
* by setting this value.
*/
responseType?: KeycloakResponseType;
/**
* Flow passed in init.
*/
flow?: KeycloakFlow;
/**
* The realm roles associated with the token.
*/
realmAccess?: { roles: string[] };
/**
* The resource roles associated with the token.
*/
resourceAccess?: string[];
/**
* The base64 encoded token that can be sent in the Authorization header in
* requests to services.
*/
token?: string;
/**
* The parsed token as a JavaScript object.
*/
tokenParsed?: KeycloakTokenParsed;
/**
* The base64 encoded refresh token that can be used to retrieve a new token.
*/
refreshToken?: string;
/**
* The parsed refresh token as a JavaScript object.
*/
refreshTokenParsed?: KeycloakTokenParsed;
/**
* The base64 encoded ID token.
*/
idToken?: string;
/**
* The parsed id token as a JavaScript object.
*/
idTokenParsed?: KeycloakTokenParsed;
/**
* The estimated time difference between the browser time and the Keycloak
* server in seconds. This value is just an estimation, but is accurate
* enough when determining if a token is expired or not.
*/
timeSkew?: number;
/**
* @private Undocumented.
*/
loginRequired?: boolean;
/**
* @private Undocumented.
*/
authServerUrl?: string;
/**
* @private Undocumented.
*/
realm?: string;
/**
* @private Undocumented.
*/
clientId?: string;
/**
* @private Undocumented.
*/
clientSecret?: string;
/**
* @private Undocumented.
*/
redirectUri?: string;
/**
* @private Undocumented.
*/
sessionId?: string;
/**
* @private Undocumented.
*/
profile?: KeycloakProfile;
/**
* @private Undocumented.
*/
userInfo?: {}; // KeycloakUserInfo;
/**
* Called when the adapter is initialized.
*/
onReady?(authenticated?: boolean): void;
/**
* Called when a user is successfully authenticated.
*/
onAuthSuccess?(): void;
/**
* Called if there was an error during authentication.
*/
onAuthError?(errorData: KeycloakError): void;
/**
* Called when the token is refreshed.
*/
onAuthRefreshSuccess?(): void;
/**
* Called if there was an error while trying to refresh the token.
*/
onAuthRefreshError?(): void;
/**
* Called if the user is logged out (will only be called if the session
* status iframe is enabled, or in Cordova mode).
*/
onAuthLogout?(): void;
/**
* Called when the access token is expired. If a refresh token is available
* the token can be refreshed with Keycloak#updateToken, or in cases where
* it's not (ie. with implicit flow) you can redirect to login screen to
* obtain a new access token.
*/
onTokenExpired?(): void;
/**
* Called to initialize the adapter.
* @param initOptions Initialization options.
* @returns A promise to set functions to be invoked on success or error.
*/
init(initOptions: KeycloakInitOptions): KeycloakPromise<boolean, KeycloakError>;
/**
* Redirects to login form.
* @param options Login options.
*/
login(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
/**
* Redirects to logout.
* @param options Logout options.
* @param options.redirectUri Specifies the uri to redirect to after logout.
*/
logout(options?: any): KeycloakPromise<void, void>;
/**
* Redirects to registration form.
* @param options Supports same options as Keycloak#login but `action` is
* set to `'register'`.
*/
register(options?: any): KeycloakPromise<void, void>;
/**
* Redirects to the Account Management Console.
*/
accountManagement(): KeycloakPromise<void, void>;
/**
* Returns the URL to login form.
* @param options Supports same options as Keycloak#login.
*/
createLoginUrl(options?: KeycloakLoginOptions): string;
/**
* Returns the URL to logout the user.
* @param options Logout options.
* @param options.redirectUri Specifies the uri to redirect to after logout.
*/
createLogoutUrl(options?: any): string;
/**
* Returns the URL to registration page.
* @param options Supports same options as Keycloak#createLoginUrl but
* `action` is set to `'register'`.
*/
createRegisterUrl(options?: KeycloakLoginOptions): string;
/**
* Returns the URL to the Account Management Console.
*/
createAccountUrl(): string;
/**
* Returns true if the token has less than `minValidity` seconds left before
* it expires.
* @param minValidity If not specified, `0` is used.
*/
isTokenExpired(minValidity?: number): boolean;
/**
* If the token expires within `minValidity` seconds, the token is refreshed.
* If the session status iframe is enabled, the session status is also
* checked.
* @returns A promise to set functions that can be invoked if the token is
* still valid, or if the token is no longer valid.
* @example
* ```js
* keycloak.updateToken(5).success(function(refreshed) {
* if (refreshed) {
* alert('Token was successfully refreshed');
* } else {
* alert('Token is still valid');
* }
* }).error(function() {
* alert('Failed to refresh the token, or the session has expired');
* });
*/
updateToken(minValidity: number): KeycloakPromise<boolean, boolean>;
/**
* Clears authentication state, including tokens. This can be useful if
* the application has detected the session was expired, for example if
* updating token fails. Invoking this results in Keycloak#onAuthLogout
* callback listener being invoked.
*/
clearToken(): void;
/**
* Returns true if the token has the given realm role.
* @param role A realm role name.
*/
hasRealmRole(role: string): boolean;
/**
* Returns true if the token has the given role for the resource.
* @param role A role name.
* @param resource If not specified, `clientId` is used.
*/
hasResourceRole(role: string, resource?: string): boolean;
/**
* Loads the user's profile.
* @returns A promise to set functions to be invoked on success or error.
*/
loadUserProfile(): KeycloakPromise<KeycloakProfile, void>;
/**
* @private Undocumented.
*/
loadUserInfo(): KeycloakPromise<{}, void>;
}
}
+435 -173
View File
@@ -40,15 +40,18 @@
}
}
var useNonce = true;
kc.init = function (initOptions) {
kc.authenticated = false;
callbackStorage = createCallbackStorage();
var adapters = ['default', 'cordova', 'cordova-native'];
if (initOptions && initOptions.adapter === 'cordova') {
adapter = loadAdapter('cordova');
} else if (initOptions && initOptions.adapter === 'default') {
adapter = loadAdapter();
if (initOptions && adapters.indexOf(initOptions.adapter) > -1) {
adapter = loadAdapter(initOptions.adapter);
} else if (initOptions && typeof initOptions.adapter === "object") {
adapter = initOptions.adapter;
} else {
if (window.Cordova || window.cordova) {
adapter = loadAdapter('cordova');
@@ -58,6 +61,10 @@
}
if (initOptions) {
if (typeof initOptions.useNonce !== 'undefined') {
useNonce = initOptions.useNonce;
}
if (typeof initOptions.checkLoginIframe !== 'undefined') {
loginIframe.enable = initOptions.checkLoginIframe;
}
@@ -98,6 +105,10 @@
if (initOptions.timeSkew != null) {
kc.timeSkew = initOptions.timeSkew;
}
if(initOptions.redirectUri) {
kc.redirectUri = initOptions.redirectUri;
}
}
if (!kc.responseMode) {
@@ -159,10 +170,15 @@
var callback = parseCallback(window.location.href);
if (callback) {
setupCheckLoginIframe();
window.history.replaceState({}, null, callback.newUrl);
processCallback(callback, initPromise);
return;
}
if (callback && callback.valid) {
return setupCheckLoginIframe().success(function() {
processCallback(callback, initPromise);
}).error(function (e) {
initPromise.setError();
});
} else if (initOptions) {
if (initOptions.token && initOptions.refreshToken) {
setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken);
@@ -230,22 +246,34 @@
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 scope;
if (options && options.scope) {
if (options.scope.indexOf("openid") != -1) {
scope = options.scope;
} else {
scope = "openid " + options.scope;
}
} else {
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);
@@ -266,6 +294,10 @@
if (options && options.locale) {
url += '&ui_locales=' + encodeURIComponent(options.locale);
}
if (options && options.kcLocale) {
url += '&kc_locale=' + encodeURIComponent(options.kcLocale);
}
return url;
}
@@ -275,8 +307,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;
@@ -295,11 +326,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;
}
@@ -347,7 +381,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');
@@ -412,7 +446,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);
@@ -448,6 +482,10 @@
} else {
console.warn('[KEYCLOAK] Failed to refresh token');
if (req.status == 400) {
kc.clearToken();
}
kc.onAuthRefreshError && kc.onAuthRefreshError();
for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
p.setError(true);
@@ -486,10 +524,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;
}
}
@@ -523,7 +565,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);
@@ -560,9 +602,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();
@@ -587,6 +629,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);
@@ -601,7 +702,7 @@
kc.realm = config['realm'];
kc.clientId = config['resource'];
kc.clientSecret = (config['credentials'] || {})['secret'];
setupOidcEndoints(null);
promise.setSuccess();
} else {
promise.setError();
@@ -611,30 +712,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;
@@ -753,23 +886,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', 'token_type', 'id_token', 'state', 'session_state', 'expires_in'];
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;
@@ -826,22 +1073,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';
@@ -921,7 +1164,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;
},
@@ -935,12 +1183,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;
}
}
};
@@ -956,24 +1199,54 @@
return window.open(loginUrl, target, options);
}
};
var shallowCloneCordovaOptions = function (userOptions) {
if (userOptions && userOptions.cordovaOptions) {
return Object.keys(userOptions.cordovaOptions).reduce(function (options, optionName) {
options[optionName] = userOptions.cordovaOptions[optionName];
return options;
}, {});
} else {
return {};
}
};
var formatCordovaOptions = function (cordovaOptions) {
return Object.keys(cordovaOptions).reduce(function (options, optionName) {
options.push(optionName+"="+cordovaOptions[optionName]);
return options;
}, []).join(",");
};
var createCordovaOptions = function (userOptions) {
var cordovaOptions = shallowCloneCordovaOptions(userOptions);
cordovaOptions.location = 'no';
if (userOptions && userOptions.prompt == 'none') {
cordovaOptions.hidden = 'yes';
}
return formatCordovaOptions(cordovaOptions);
};
return {
login: function(options) {
var promise = createPromise();
var o = 'location=no';
if (options && options.prompt == 'none') {
o += ',hidden=yes';
}
var cordovaOptions = createCordovaOptions(options);
var loginUrl = kc.createLoginUrl(options);
var ref = cordovaOpenWindowWrapper(loginUrl, '_blank', o);
var ref = cordovaOpenWindowWrapper(loginUrl, '_blank', cordovaOptions);
var completed = false;
var closed = false;
var closeBrowser = function() {
closed = true;
ref.close();
};
ref.addEventListener('loadstart', function(event) {
if (event.url.indexOf('http://localhost') == 0) {
var callback = parseCallback(event.url);
processCallback(callback, promise);
ref.close();
closeBrowser();
completed = true;
}
});
@@ -983,21 +1256,29 @@
if (event.url.indexOf('http://localhost') == 0) {
var callback = parseCallback(event.url);
processCallback(callback, promise);
ref.close();
closeBrowser();
completed = true;
} else {
promise.setError();
ref.close();
closeBrowser();
}
}
});
ref.addEventListener('exit', function(event) {
if (!closed) {
promise.setError({
reason: "closed_by_user"
});
}
});
return promise.promise;
},
logout: function(options) {
var promise = createPromise();
var logoutUrl = kc.createLogoutUrl(options);
var ref = cordovaOpenWindowWrapper(logoutUrl, '_blank', 'location=no,hidden=yes');
@@ -1032,7 +1313,8 @@
register : function() {
var registerUrl = kc.createRegisterUrl();
var ref = cordovaOpenWindowWrapper(registerUrl, '_blank', 'location=no');
var cordovaOptions = createCordovaOptions(options);
var ref = cordovaOpenWindowWrapper(registerUrl, '_blank', cordovaOptions);
ref.addEventListener('loadstart', function(event) {
if (event.url.indexOf('http://localhost') == 0) {
ref.close();
@@ -1042,12 +1324,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) {
@@ -1056,6 +1342,75 @@
}
}
if (type == 'cordova-native') {
loginIframe.enable = false;
return {
login: function(options) {
var promise = createPromise();
var loginUrl = kc.createLoginUrl(options);
universalLinks.subscribe('keycloak', function(event) {
universalLinks.unsubscribe('keycloak');
window.cordova.plugins.browsertab.close();
var oauth = parseCallback(event.url);
processCallback(oauth, promise);
});
window.cordova.plugins.browsertab.openUrl(loginUrl);
return promise.promise;
},
logout: function(options) {
var promise = createPromise();
var logoutUrl = kc.createLogoutUrl(options);
universalLinks.subscribe('keycloak', function(event) {
universalLinks.unsubscribe('keycloak');
window.cordova.plugins.browsertab.close();
kc.clearToken();
promise.setSuccess();
});
window.cordova.plugins.browsertab.openUrl(logoutUrl);
return promise.promise;
},
register : function(options) {
var promise = createPromise();
var registerUrl = kc.createRegisterUrl(options);
universalLinks.subscribe('keycloak' , function(event) {
universalLinks.unsubscribe('keycloak');
window.cordova.plugins.browsertab.close();
var oauth = parseCallback(event.url);
processCallback(oauth, promise);
});
window.cordova.plugins.browsertab.openUrl(registerUrl);
return promise.promise;
},
accountManagement : function() {
var accountUrl = kc.createAccountUrl();
if (typeof accountUrl !== 'undefined') {
window.cordova.plugins.browsertab.openUrl(accountUrl);
} else {
throw "Not supported by the OIDC server";
}
},
redirectUri: function(options) {
if (options && options.redirectUri) {
return options.redirectUri;
} else if (kc.redirectUri) {
return kc.redirectUri;
} else {
return "http://localhost";
}
}
}
}
throw 'invalid adapter type: ' + type;
}
@@ -1177,99 +1532,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" ) {
@@ -36,7 +36,7 @@
req.onreadystatechange = function () {
if (req.readyState === 4) {
if (req.status === 204) {
if (req.status === 204 || req.status === 1223) {
init = {
clientId: clientId,
origin: origin
@@ -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) {
File renamed without changes.
+84
View File
@@ -0,0 +1,84 @@
<?xml version="1.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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.4.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>kcinit</artifactId>
<name>Keycloak CLI SSO Framework</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-installed-adapter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.keycloak.adapters.KcinitMain</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
+26
View File
@@ -0,0 +1,26 @@
#!/bin/bash
case "`uname`" in
CYGWIN*)
CFILE = `cygpath "$0"`
RESOLVED_NAME=`readlink -f "$CFILE"`
;;
Darwin*)
RESOLVED_NAME=`readlink "$0"`
;;
FreeBSD)
RESOLVED_NAME=`readlink -f "$0"`
;;
Linux)
RESOLVED_NAME=`readlink -f "$0"`
;;
esac
if [ "x$RESOLVED_NAME" = "x" ]; then
RESOLVED_NAME="$0"
fi
SCRIPTPATH=`dirname "$RESOLVED_NAME"`
JAR=$SCRIPTPATH/kcinit-${project.version}.jar
java -jar $JAR $@
+8
View File
@@ -0,0 +1,8 @@
@echo off
if "%OS%" == "Windows_NT" (
set "DIRNAME=%~dp0%"
) else (
set DIRNAME=.\
)
java -jar %DIRNAME%\kcinit-${project.version}.jar %*
@@ -0,0 +1,30 @@
/*
* 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;
import org.keycloak.adapters.installed.KcinitDriver;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KcinitMain extends KcinitDriver {
public static void main(String[] args) throws Exception {
new KcinitMain().mainCmd(args);
}
}
+8 -7
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.0.Final</version>
<version>4.4.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -36,7 +36,7 @@
org.keycloak.adapters.osgi.*
</keycloak.osgi.export>
<keycloak.osgi.import>
org.ops4j.pax.web.*;version="[3.0,5)",
org.ops4j.pax.web.*;version="[3.0,8)",
javax.servlet.*;version="[2.5,4)";resolution:=optional,
org.eclipse.jetty.*;version="[8.1,10)";resolution:=optional,
org.keycloak.*;version="${project.version}",
@@ -77,11 +77,6 @@
<artifactId>org.osgi.enterprise</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-runtime</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-api</artifactId>
@@ -99,6 +94,12 @@
<version>${cxf.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@@ -0,0 +1,89 @@
/*
* 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.osgi;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.jboss.logging.Logger;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade;
/**
* This {@link KeycloakConfigResolver} tries to resolve most specific configuration for given URI path. If not found,
* <em>parent</em> path is checked up to top-level path.
*/
public class HierarchicalPathBasedKeycloakConfigResolver extends PathBasedKeycloakConfigResolver {
protected static final Logger log = Logger.getLogger(HierarchicalPathBasedKeycloakConfigResolver.class);
public HierarchicalPathBasedKeycloakConfigResolver() {
prepopulateCache();
}
@Override
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
// we cached all available deployments initially and now we'll try to check them from
// most specific to most general
URI uri = URI.create(request.getURI());
String path = uri.getPath();
if (path != null) {
while (path.startsWith("/")) {
path = path.substring(1);
}
String[] segments = path.split("/");
List<String> paths = collectPaths(segments);
for (String pathFragment: paths) {
KeycloakDeployment cachedDeployment = super.getCachedDeployment(pathFragment);
if (cachedDeployment != null) {
return cachedDeployment;
}
}
}
throw new IllegalStateException("Can't find Keycloak configuration related to URI path " + uri);
}
/**
* <p>For segments like "a, b, c, d", returns:<ul>
* <li>"a-b-c-d"</li>
* <li>"a-b-c"</li>
* <li>"a-b"</li>
* <li>"a"</li>
* <li>""</li>
* </ul></p>
* @param segments
* @return
*/
private List<String> collectPaths(String[] segments) {
List<String> result = new ArrayList<>(segments.length + 1);
for (int idx = segments.length; idx >= 0; idx--) {
StringBuilder sb = null;
for (int i = 0; i < idx; i++) {
if (sb == null) {
sb = new StringBuilder();
}
sb.append("-").append(segments[i]);
}
result.add(sb == null ? "" : sb.toString().substring(1));
}
return result;
}
}
@@ -16,56 +16,169 @@
*/
package org.keycloak.adapters.osgi;
import org.jboss.logging.Logger;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.HttpFacade;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class PathBasedKeycloakConfigResolver implements KeycloakConfigResolver {
protected static final Logger log = Logger.getLogger(PathBasedKeycloakConfigResolver.class);
private final Map<String, KeycloakDeployment> cache = new ConcurrentHashMap<String, KeycloakDeployment>();
private File keycloakConfigLocation = null;
public PathBasedKeycloakConfigResolver() {
String location = null;
String keycloakConfig = (String) System.getProperties().get("keycloak.config");
if (keycloakConfig != null && !"".equals(keycloakConfig.trim())) {
location = keycloakConfig;
} else {
String karafEtc = (String) System.getProperties().get("karaf.etc");
if (karafEtc != null && !"".equals(karafEtc.trim())) {
location = karafEtc;
}
}
if (location != null) {
File loc = new File(location);
if (loc.isDirectory()) {
keycloakConfigLocation = loc;
}
}
}
@Override
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
String path = request.getURI();
String[] urlTokens = path.split("/");
if (urlTokens.length < 4) {
throw new IllegalStateException("Not able to determine the web-context to load the correspondent keycloak.json file");
}
String webContext = getDeploymentKeyForURI(request);
String webContext = urlTokens[3];
return getOrCreateDeployment(webContext);
}
KeycloakDeployment deployment = cache.get(webContext);
/**
* {@code pathFragment} is a key for {@link KeycloakDeployment deployments}. The key is used to construct
* a path relative to {@code keycloak.config} or {@code karaf.etc} system properties.
* For given key, {@code <key>-keycloak.json} file is checked.
* @param pathFragment
* @return
*/
protected synchronized KeycloakDeployment getOrCreateDeployment(String pathFragment) {
KeycloakDeployment deployment = getCachedDeployment(pathFragment);
if (null == deployment) {
// not found on the simple cache, try to load it from the file system
String keycloakConfig = (String) System.getProperties().get("keycloak.config");
if(keycloakConfig == null || "".equals(keycloakConfig.trim())){
String karafEtc = (String) System.getProperties().get("karaf.etc");
if(karafEtc == null || "".equals(karafEtc.trim())){
throw new IllegalStateException("Neither \"keycloak.config\" nor \"karaf.etc\" java properties are set. Please set one of them.");
}
keycloakConfig = karafEtc;
if (keycloakConfigLocation == null) {
throw new IllegalStateException("Neither \"keycloak.config\" nor \"karaf.etc\" java properties are set." +
" Please set one of them.");
}
String absolutePath = keycloakConfig + File.separator + webContext + "-keycloak.json";
InputStream is = null;
try {
is = new FileInputStream(absolutePath);
} catch (FileNotFoundException e){
throw new IllegalStateException("Not able to find the file " + absolutePath);
File configuration = new File(keycloakConfigLocation, pathFragment + ("".equals(pathFragment) ? "" : "-")
+ "keycloak.json");
if (!cacheConfiguration(pathFragment, configuration)) {
throw new IllegalStateException("Not able to read the file " + configuration);
}
deployment = KeycloakDeploymentBuilder.build(is);
cache.put(webContext, deployment);
deployment = getCachedDeployment(pathFragment);
}
return deployment;
}
protected synchronized KeycloakDeployment getCachedDeployment(String pathFragment) {
return cache.get(pathFragment);
}
/**
* If there's a need, we can pre populate the cache of deployments.
*/
protected void prepopulateCache() {
if (keycloakConfigLocation == null || !keycloakConfigLocation.isDirectory()) {
log.warn("Can't cache Keycloak configurations. No configuration storage is accessible." +
" Please set either \"keycloak.config\" or \"karaf.etc\" system properties");
return;
}
File[] configs = keycloakConfigLocation.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isFile() && pathname.getName().endsWith("keycloak.json");
}
});
if (configs != null) {
for (File config: configs) {
String pathFragment = null;
if ("keycloak.json".equals(config.getName())) {
pathFragment = "";
} else if (config.getName().endsWith("-keycloak.json")) {
pathFragment = config.getName()
.substring(0, config.getName().length() - "-keycloak.json".length());
}
cacheConfiguration(pathFragment, config);
}
}
}
private boolean cacheConfiguration(String key, File config) {
try {
InputStream is = new FileInputStream(config);
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(is);
cache.put(key, deployment);
return true;
} catch (FileNotFoundException | RuntimeException e) {
log.warn("Can't cache " + config + ": " + e.getMessage(), e);
return false;
}
}
/**
* Finds a context path from given {@link HttpFacade.Request}. For default context, first path segment
* is returned.
* @param request
* @return
*/
private String getDeploymentKeyForURI(HttpFacade.Request request) {
String uri = request.getURI();
String relativePath = request.getRelativePath();
String webContext = null;
if (relativePath == null || !uri.contains(relativePath)) {
String[] urlTokens = uri.split("/");
if (urlTokens.length < 4) {
throw new IllegalStateException("Not able to determine the web-context to load the correspondent keycloak.json file");
}
webContext = urlTokens[3];
} else {
URI parsedURI = URI.create(uri);
String path = parsedURI.getPath();
if (path.contains(relativePath)) {
path = path.substring(0, path.indexOf(relativePath));
}
while (path.startsWith("/")) {
path = path.substring(1);
}
webContext = path;
if ("".equals(webContext)) {
path = relativePath;
while (path.startsWith("/")) {
path = path.substring(1);
}
if (path.contains("/")) {
path = path.substring(0, path.indexOf("/"));
}
webContext = path;
}
}
return webContext;
}
}
@@ -29,11 +29,12 @@ import org.osgi.util.tracker.ServiceTrackerCustomizer;
import java.net.URL;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Integration with pax-web, which allows to inject custom jetty-web.xml configuration from current bundle classpath into {@link WebContainer}
* Integration with pax-web in Fuse 6.3, which allows to inject custom jetty-web.xml configuration from current bundle classpath into {@link WebContainer}
* and allows to inject custom security constraint for securing resources by Keycloak.
*
* <p>It assumes that pax-web {@link WebContainer} is used as implementation of OSGI {@link org.osgi.service.http.HttpService}, which
@@ -47,7 +48,7 @@ public class PaxWebIntegrationService {
private BundleContext bundleContext;
private String jettyWebXmlLocation;
private List<ConstraintMapping> constraintMappings; // Using jetty constraint mapping just because of compatibility with other fuse services
private List<Object> constraintMappings;
private ServiceTracker webContainerTracker;
private HttpContext httpContext;
@@ -68,11 +69,11 @@ public class PaxWebIntegrationService {
this.jettyWebXmlLocation = jettyWebXmlLocation;
}
public List<ConstraintMapping> getConstraintMappings() {
public List<Object> getConstraintMappings() {
return constraintMappings;
}
public void setConstraintMappings(List<ConstraintMapping> constraintMappings) {
public void setConstraintMappings(List<Object> constraintMappings) {
this.constraintMappings = constraintMappings;
}
@@ -120,8 +121,25 @@ public class PaxWebIntegrationService {
if (constraintMappings == null) {
throw new IllegalStateException("constraintMappings was null!");
}
for (ConstraintMapping constraintMapping : constraintMappings) {
addConstraintMapping(service, constraintMapping);
List<ConstraintHandler> handlers = new ArrayList<>();
try {
handlers.add(new JettyConstraintHandler());
} catch (Throwable t) {
// Ignore
}
try {
handlers.add(new PaxWebConstraintHandler());
} catch (Throwable t) {
// Ignore
}
for (Object constraintMapping : constraintMappings) {
boolean handled = false;
for (ConstraintHandler handler : handlers) {
handled |= handler.addConstraintMapping(httpContext, service, constraintMapping);
}
if (!handled) {
log.warnv("Unable to add constraint mapping for constraint of type " + constraintMapping.getClass().toString());
}
}
service.registerLoginConfig("BASIC", "does-not-matter", null, null, httpContext);
@@ -146,6 +164,16 @@ public class PaxWebIntegrationService {
}
}
protected void addConstraintMapping(WebContainer service, PaxWebSecurityConstraintMapping constraintMapping) {
String name = constraintMapping.getConstraintName();
if (name == null) {
name = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
}
log.debug("Adding security constraint name=" + name + ", url=" + constraintMapping.getUrl() + ", dataConstraint=" + constraintMapping.getDataConstraint() + ", canAuthenticate="
+ constraintMapping.isAuthentication() + ", roles=" + constraintMapping.getRoles());
service.registerConstraintMapping(name, constraintMapping.getUrl(), constraintMapping.getMapping(), constraintMapping.getDataConstraint(), constraintMapping.isAuthentication(), constraintMapping.getRoles(), httpContext);
}
protected void addConstraintMapping(WebContainer service, ConstraintMapping constraintMapping) {
Constraint constraint = constraintMapping.getConstraint();
String[] roles = constraint.getRoles();
@@ -178,4 +206,71 @@ public class PaxWebIntegrationService {
service.unregisterConstraintMapping(httpContext);
}
}
}
private interface ConstraintHandler {
boolean addConstraintMapping(HttpContext httpContext, WebContainer service, Object cm);
}
private static class PaxWebConstraintHandler implements ConstraintHandler {
@Override
public boolean addConstraintMapping(HttpContext httpContext, WebContainer service, Object cm) {
if (cm instanceof PaxWebSecurityConstraintMapping) {
PaxWebSecurityConstraintMapping constraintMapping = (PaxWebSecurityConstraintMapping) cm;
String name = constraintMapping.getConstraintName();
if (name == null) {
name = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
}
log.debug("Adding security constraint name=" + name + ", url=" + constraintMapping.getUrl() + ", dataConstraint=" + constraintMapping.getDataConstraint() + ", canAuthenticate="
+ constraintMapping.isAuthentication() + ", roles=" + constraintMapping.getRoles());
service.registerConstraintMapping(name, constraintMapping.getUrl(), constraintMapping.getMapping(), constraintMapping.getDataConstraint(), constraintMapping.isAuthentication(), constraintMapping.getRoles(), httpContext);
return true;
}
return false;
}
}
private static class JettyConstraintHandler implements ConstraintHandler {
@Override
public boolean addConstraintMapping(HttpContext httpContext, WebContainer service, Object cm) {
if (cm instanceof ConstraintMapping) {
ConstraintMapping constraintMapping = (ConstraintMapping) cm;
Constraint constraint = constraintMapping.getConstraint();
String[] roles = constraint.getRoles();
// name property is unavailable on constraint object :/
String name = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
int dataConstraint = constraint.getDataConstraint();
String dataConstraintStr;
switch (dataConstraint) {
case Constraint.DC_UNSET:
dataConstraintStr = null;
break;
case Constraint.DC_NONE:
dataConstraintStr = "NONE";
break;
case Constraint.DC_CONFIDENTIAL:
dataConstraintStr = "CONFIDENTIAL";
break;
case Constraint.DC_INTEGRAL:
dataConstraintStr = "INTEGRAL";
break;
default:
log.warnv("Unknown data constraint: " + dataConstraint);
dataConstraintStr = "CONFIDENTIAL";
}
List<String> rolesList = Arrays.asList(roles);
log.debug("Adding security constraint name=" + name + ", url=" + constraintMapping.getPathSpec() + ", dataConstraint=" + dataConstraintStr + ", canAuthenticate="
+ constraint.getAuthenticate() + ", roles=" + rolesList);
service.registerConstraintMapping(name, constraintMapping.getPathSpec(), null, dataConstraintStr, constraint.getAuthenticate(), rolesList, httpContext);
return true;
}
return false;
}
}
}
@@ -0,0 +1,73 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.keycloak.adapters.osgi;
import java.security.SecureRandom;
import java.util.LinkedList;
import java.util.List;
/**
*
* @author hmlnarik
*/
public class PaxWebSecurityConstraintMapping {
private String constraintName = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
private String mapping;
private String url;
private String dataConstraint = "NONE";
private boolean authentication = true;
private List<String> roles = new LinkedList<>();
public String getConstraintName() {
return constraintName;
}
public void setConstraintName(String constraintName) {
this.constraintName = constraintName;
}
public String getMapping() {
return mapping;
}
public void setMapping(String mapping) {
this.mapping = mapping;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDataConstraint() {
return dataConstraint;
}
public void setDataConstraint(String dataConstraint) {
this.dataConstraint = dataConstraint;
}
public boolean isAuthentication() {
return authentication;
}
public void setAuthentication(boolean authentication) {
this.authentication = authentication;
}
public List<String> getRoles() {
return roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
}
@@ -0,0 +1,162 @@
/*
* 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.osgi;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.AuthenticationError;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.LogoutError;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
public class HierarchicalPathBasedKeycloakConfigResolverTest {
@Test
public void genericAndSpecificConfigurations() throws Exception {
HierarchicalPathBasedKeycloakConfigResolver resolver = new HierarchicalPathBasedKeycloakConfigResolver();
populate(resolver, true);
assertThat(resolver.resolve(new MockRequest("http://localhost/a/b/c/d/e?a=b")).getRealm(), equalTo("a-b-c-d-e"));
assertThat(resolver.resolve(new MockRequest("http://localhost/a/b/c/d/x?a=b")).getRealm(), equalTo("a-b-c-d"));
assertThat(resolver.resolve(new MockRequest("http://localhost/a/b/c/x/x?a=b")).getRealm(), equalTo("a-b-c"));
assertThat(resolver.resolve(new MockRequest("http://localhost/a/b/x/x/x?a=b")).getRealm(), equalTo("a-b"));
assertThat(resolver.resolve(new MockRequest("http://localhost/a/x/x/x/x?a=b")).getRealm(), equalTo("a"));
assertThat(resolver.resolve(new MockRequest("http://localhost/x/x/x/x/x?a=b")).getRealm(), equalTo(""));
populate(resolver, false);
try {
resolver.resolve(new MockRequest("http://localhost/x/x/x/x/x?a=b"));
fail("Expected java.lang.IllegalStateException: Can't find Keycloak configuration ...");
} catch (IllegalStateException expected) {
}
}
@SuppressWarnings("unchecked")
private PathBasedKeycloakConfigResolver populate(PathBasedKeycloakConfigResolver resolver, boolean fallback)
throws Exception {
Field f = PathBasedKeycloakConfigResolver.class.getDeclaredField("cache");
f.setAccessible(true);
Map<String, KeycloakDeployment> cache = (Map<String, KeycloakDeployment>) f.get(resolver);
cache.clear();
cache.put("a-b-c-d-e", newKeycloakDeployment("a-b-c-d-e"));
cache.put("a-b-c-d", newKeycloakDeployment("a-b-c-d"));
cache.put("a-b-c", newKeycloakDeployment("a-b-c"));
cache.put("a-b", newKeycloakDeployment("a-b"));
cache.put("a", newKeycloakDeployment("a"));
if (fallback) {
cache.put("", newKeycloakDeployment(""));
}
return resolver;
}
private KeycloakDeployment newKeycloakDeployment(String realm) {
KeycloakDeployment deployment = new KeycloakDeployment();
deployment.setRealm(realm);
return deployment;
}
private class MockRequest implements OIDCHttpFacade.Request {
private String uri;
public MockRequest(String uri) {
this.uri = uri;
}
@Override
public String getMethod() {
return null;
}
@Override
public String getURI() {
return this.uri;
}
@Override
public String getRelativePath() {
return null;
}
@Override
public boolean isSecure() {
return false;
}
@Override
public String getFirstParam(String param) {
return null;
}
@Override
public String getQueryParamValue(String param) {
return null;
}
@Override
public HttpFacade.Cookie getCookie(String cookieName) {
return null;
}
@Override
public String getHeader(String name) {
return null;
}
@Override
public List<String> getHeaders(String name) {
return null;
}
@Override
public InputStream getInputStream() {
return null;
}
@Override
public InputStream getInputStream(boolean buffered) {
return null;
}
@Override
public String getRemoteAddr() {
return null;
}
@Override
public void setError(AuthenticationError error) {
}
@Override
public void setError(LogoutError error) {
}
}
}
@@ -0,0 +1,149 @@
/*
* 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.osgi;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.AuthenticationError;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.LogoutError;
import static org.junit.Assert.assertNotNull;
public class PathBasedKeycloakConfigResolverTest {
@Test
public void relativeURIsAndContexts() throws Exception {
PathBasedKeycloakConfigResolver resolver = new PathBasedKeycloakConfigResolver();
assertNotNull(populate(resolver, "test")
.resolve(new MockRequest("http://localhost/test/a/b/c?d=e", "/a/b/c")));
assertNotNull(populate(resolver, "test")
.resolve(new MockRequest("http://localhost/test/a/b/c?d=e", "/a/b")));
// means default context and actually we use first segment
assertNotNull(populate(resolver, "test")
.resolve(new MockRequest("http://localhost/test/a/b/c?d=e", "/test/a/b/c")));
assertNotNull(populate(resolver, "test/a")
.resolve(new MockRequest("http://localhost/test/a/b/c?d=e", "/b/c")));
assertNotNull(populate(resolver, "")
.resolve(new MockRequest("http://localhost/", "/")));
}
@SuppressWarnings("unchecked")
private PathBasedKeycloakConfigResolver populate(PathBasedKeycloakConfigResolver resolver, String context)
throws Exception {
Field f = PathBasedKeycloakConfigResolver.class.getDeclaredField("cache");
f.setAccessible(true);
Map<String, KeycloakDeployment> cache = (Map<String, KeycloakDeployment>) f.get(resolver);
cache.clear();
cache.put(context, new KeycloakDeployment());
return resolver;
}
private class MockRequest implements OIDCHttpFacade.Request {
private String uri;
private String relativePath;
public MockRequest(String uri, String relativePath) {
this.uri = uri;
this.relativePath = relativePath;
}
@Override
public String getMethod() {
return null;
}
@Override
public String getURI() {
return this.uri;
}
@Override
public String getRelativePath() {
return this.relativePath;
}
@Override
public boolean isSecure() {
return false;
}
@Override
public String getFirstParam(String param) {
return null;
}
@Override
public String getQueryParamValue(String param) {
return null;
}
@Override
public HttpFacade.Cookie getCookie(String cookieName) {
return null;
}
@Override
public String getHeader(String name) {
return null;
}
@Override
public List<String> getHeaders(String name) {
return null;
}
@Override
public InputStream getInputStream() {
return null;
}
@Override
public InputStream getInputStream(boolean buffered) {
return null;
}
@Override
public String getRemoteAddr() {
return null;
}
@Override
public void setError(AuthenticationError error) {
}
@Override
public void setError(LogoutError error) {
}
}
}
+5 -2
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.0.Final</version>
<version>4.4.0.Final</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<name>Keycloak OIDC Client Adapter Modules</name>
@@ -34,7 +34,8 @@
<module>adapter-core</module>
<module>as7-eap6</module>
<module>installed</module>
<module>cli-sso</module>
<module>fuse7</module>
<module>kcinit</module>
<module>jaxrs-oauth-client</module>
<module>jetty</module>
<module>js</module>
@@ -42,6 +43,8 @@
<module>servlet-filter</module>
<module>servlet-oauth-client</module>
<module>spring-boot</module>
<module>spring-boot2</module>
<module>spring-boot-adapter-core</module>
<module>spring-boot-container-bundle</module>
<module>spring-security</module>
<module>tomcat</module>
+45 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>3.4.0.Final</version>
<version>4.4.0.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -30,6 +30,17 @@
<name>Keycloak Servlet Filter Adapter Integration</name>
<description/>
<properties>
<keycloak.osgi.export>
org.keycloak.adapters.servlet.*
</keycloak.osgi.export>
<keycloak.osgi.import>
javax.servlet.*;version="[2.5,4)";resolution:=optional,
org.keycloak.*;version="${project.version}",
*;resolution:=optional
</keycloak.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
@@ -94,6 +105,39 @@
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-ClassPath>.</Bundle-ClassPath>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
Loaded 100 of 6431 files, more files were not shown because too many files have changed in this diff. Show more