Compare commits

..
294 Commits
Author SHA1 Message Date
keycloak-bot eb68ef47b1 Set version to 1.9.2.Final 2016-04-14 13:33:01 +00:00
Stian Thorgersen 7f7fb23f32 Merge pull request #2633 from stianst/1.9.x
KEYCLOAK-2825 KEYCLOAK-2826 Remove private for org.keycloak.keycloak-…
2016-04-14 15:30:10 +02:00
Stian Thorgersen 846bc2ca35 KEYCLOAK-2827 ModuleLoadError: org.keycloak.keycloak-core-public:main 2016-04-14 15:21:58 +02:00
Stian Thorgersen ed77589a43 KEYCLOAK-2825 KEYCLOAK-2826 Remove private for org.keycloak.keycloak-adapter-core and org.keycloak.keycloak-adapter-spi modules 2016-04-14 14:40:05 +02:00
Marek Posolda 6ab94ce4e8 Merge pull request #2629 from mposolda/1.9.x
KEYCLOAK-2823 Password policy 'hashAlgorithm' value not correctly sho…
2016-04-14 13:56:06 +02:00
mposolda a5179907ad KEYCLOAK-2823 Password policy 'hashAlgorithm' value not correctly shown in admin console 2016-04-14 13:15:23 +02:00
Marek Posolda 77bbf2afb5 Merge pull request #2626 from mposolda/1.9.x
Migration and mongo fix
2016-04-14 11:48:13 +02:00
mposolda 85b95c5432 KEYCLOAK-2820 Mongo doesn't start on wildfly 2016-04-14 10:41:31 +02:00
mposolda 613a26cd1c KEYCLOAK-2819 Fix realm security header setup after migration 2016-04-14 10:22:14 +02:00
Stian Thorgersen 18cc0fdaf3 Merge pull request #2625 from stianst/1.9.x
KEYCLOAK-2818
2016-04-14 09:14:33 +02:00
Stian Thorgersen 39c0dfeb8f KEYCLOAK-2818
Fix poms not updated by versions plugin
2016-04-14 08:17:05 +02:00
Stian Thorgersen 524ae68224 Fix to cors-example-fix 2016-04-14 07:10:50 +02:00
Bruno Oliveira e63726d51c Fix the broken logout URL 2016-04-14 07:10:33 +02:00
Bruno Oliveira 29c839602f Make the client URL match exactly the same configured at the realm 2016-04-14 07:10:33 +02:00
Stian Thorgersen 1e20cad2ae Merge pull request #2616 from abstractj/minor-fix
There's no such profile for this example
2016-04-14 07:02:47 +02:00
Stian Thorgersen 7feeca8273 Merge pull request #2615 from abstractj/cordova
inappbrowser plugin returns 404 with the current namespace
2016-04-14 06:59:54 +02:00
Bill Burke 9c8d9ceb28 Merge pull request #2622 from patriot1burke/1.9.x
merge adapter-spi and adapter-spi-public
2016-04-13 22:52:22 -04:00
Bill Burke 07a7035398 merge adapter-spi and adapter-spi-public 2016-04-13 22:18:26 -04:00
Stian Thorgersen 6c2f8a8f65 Merge pull request #2612 from abstractj/angular2-example
Example points to wrong KC version
2016-04-13 19:35:06 +02:00
Bruno Oliveira d1bf3c3be6 There's no such profile for this example 2016-04-13 14:10:22 -03:00
Bruno Oliveira edef864ffc inappbrowser plugin returns 404 with the current namespace 2016-04-13 14:05:45 -03:00
Marek Posolda 3effc265c4 Merge pull request #2610 from mposolda/1.9.x
KEYCLOAK-2812 Make testsuite-arquillian working with -Pauth-server-wi…
2016-04-13 16:41:22 +02:00
Bruno Oliveira 41775bf8ef Example points to wrong KC version 2016-04-13 11:07:48 -03:00
mposolda 6c2464e399 KEYCLOAK-2812 Make testsuite-arquillian working with -Pauth-server-wildfly 2016-04-13 16:04:20 +02:00
Marek Posolda 17a7d04de9 Merge pull request #2609 from mposolda/1.9.x
KEYCLOAK-2811 FederationProvidersIntegrationTest.testReadOnly failing…
2016-04-13 13:50:07 +02:00
mposolda b630c5b447 KEYCLOAK-2811 FederationProvidersIntegrationTest.testReadOnly failing on MSAD 2016-04-13 12:40:10 +02:00
Marek Posolda ab69b98766 Merge pull request #2605 from mposolda/1.9.x
KEYCLOAK-2809 NPE when removing role, which is in scope of some Clien…
2016-04-13 12:39:07 +02:00
Stian Thorgersen d78450137e Merge pull request #2603 from stianst/KEYCLOAK-2810-1.9
KEYCLOAK-2810 Added robots.txt and robots meta header
2016-04-13 11:58:23 +02:00
mposolda 1e47e28f20 KEYCLOAK-2809 NPE when removing role, which is in scope of some ClientTemplate 2016-04-13 11:48:04 +02:00
Stian Thorgersen 80a8961878 KEYCLOAK-2810 Added robots.txt and robots meta header 2016-04-13 11:24:19 +02:00
Marek Posolda 95d555ef39 Merge pull request #2598 from mposolda/1.9.x
Fix testsuite with Mongo
2016-04-13 11:06:49 +02:00
Stian Thorgersen 1a8f56f3d1 Merge pull request #2601 from stianst/KEYCLOAK-2804-1.9
KEYCLOAK-2804 Prevent browser from prefilling username/password into …
2016-04-13 10:39:48 +02:00
Stian Thorgersen 4e1059740a KEYCLOAK-2804 Prevent browser from prefilling username/password into non-login form 2016-04-13 10:01:44 +02:00
mposolda 7e225d60fd Fix testsuite with Mongo 2016-04-13 09:54:36 +02:00
Bill Burke b55449b739 Merge pull request #2597 from patriot1burke/1.9.x
KEYCLOAK-2790
2016-04-12 19:45:22 -04:00
Bill Burke 5b298cf43c KEYCLOAK-2790 2016-04-12 18:25:01 -04:00
Bill Burke b56fe48277 Merge pull request #2586 from mhajas/secureDeployment-1.9.x-PR
KEYCLOAK-2199 Add tests for secure deployment
2016-04-12 17:53:34 -04:00
Bill Burke 4c438de595 Merge pull request #2568 from cpitman/proxy-headers-1.9
KEYCLOAK-2791 - Proxy handling for X-Forwarded headers
2016-04-12 17:53:07 -04:00
Bill Burke ea006f1d77 Merge pull request #2588 from vramik/KEYCLOAK-2794-1.9.x
KEYCLOAK-2794 Change undertow-embedded version
2016-04-12 17:52:39 -04:00
Bill Burke cb4e0a9e75 Merge pull request #2595 from patriot1burke/1.9.x
public private api
2016-04-12 17:51:49 -04:00
Bill Burke 7df32ba4cd public/private fixes 2016-04-12 15:19:31 -04:00
Bill Burke 89098b3726 Merge remote-tracking branch 'remote19/1.9.x' into b_1_9 2016-04-12 12:10:04 -04:00
Bill Burke 9e45c2ac4d public/private modules 2016-04-12 12:09:46 -04:00
Stian Thorgersen d4f8f833a2 Merge pull request #2592 from stianst/KEYCLOAK-2807-1.9
KEYCLOAK-2807 Fix server info providers page
2016-04-12 16:18:20 +02:00
Stian Thorgersen e159254cd2 KEYCLOAK-2807 Fix server info providers page 2016-04-12 15:40:22 +02:00
Stian Thorgersen ee090149b9 Merge pull request #2590 from stianst/KEYCLOAK-2799-1.9
KEYCLOAK-2799 Show error for identity brokering login if user is disa…
2016-04-12 14:11:21 +02:00
Stian Thorgersen a22c8c1d8e KEYCLOAK-2799 Show error for identity brokering login if user is disabled 2016-04-12 13:15:21 +02:00
Stian Thorgersen 83ff45ce04 Merge pull request #2584 from stianst/KEYCLOAK-2803-1.9
KEYCLOAK-2803 Fix failure to add execution to client flow
2016-04-12 13:05:07 +02:00
Vlasta Ramik 815e650448 Change undertow-embedded version from snapshot to released version 1.0.0.Alpha2 2016-04-12 09:48:20 +02:00
mhajas 777bdc908f Add tests for secure deployment 2016-04-12 09:16:28 +02:00
Stian Thorgersen 45231b69a7 KEYCLOAK-2803 Fix failure to add execution to client flow 2016-04-12 08:04:54 +02:00
Stian Thorgersen b01f522ace Merge pull request #2582 from stianst/KEYCLOAK-2770-1.9
KEYCLOAK-2770 Close mail transport after sending message
2016-04-12 07:43:58 +02:00
Stian Thorgersen e840766b1f Merge pull request #2571 from pdrozd/1.9.x
KEYCLOAK-2794 - changed version of undertow-embedded.
2016-04-12 07:42:10 +02:00
Stian Thorgersen 4298916013 Merge pull request #2574 from stianst/KEYCLOAK-2700-1.9
KEYCLOAK-2700 Import existing realm fails due to can't delete group
2016-04-12 07:20:03 +02:00
Stian Thorgersen 788bdfb29b KEYCLOAK-2770 Close mail transport after sending message 2016-04-12 07:07:53 +02:00
Marek Posolda 3ec8672a75 Merge pull request #2580 from mposolda/1.9.x
Identity brokering error handling fixes
2016-04-12 00:08:55 +02:00
mposolda fc95067193 KEYCLOAK-2802 NPE during identity broker cancelled from account mgmt 2016-04-11 23:28:59 +02:00
mposolda d14867f785 KEYCLOAK-2801 Redirected to login theme error page after failed social linking from account management 2016-04-11 22:51:05 +02:00
Marek Posolda ffe483ae2d Merge pull request #2578 from mposolda/1.9.x
KEYCLOAK-2769 Better error handling of expired code in IdentityBroker…
2016-04-11 18:56:32 +02:00
mposolda dc2d71489d KEYCLOAK-2769 Better error handling of expired code in IdentityBrokerService 2016-04-11 18:08:10 +02:00
Marek Posolda 42402ee6f4 Merge pull request #2576 from mposolda/1.9.x
KEYCLOAK-2795 Testsuite fails sometimes with NPE in LockServiceFactor…
2016-04-11 10:50:59 +02:00
mposolda f4f95cd096 KEYCLOAK-2795 Testsuite fails sometimes with NPE in LockServiceFactory.getLockService 2016-04-11 10:08:26 +02:00
Stian Thorgersen dafea0cad6 KEYCLOAK-2700 Import existing realm fails due to can't delete group 2016-04-11 09:19:20 +02:00
Stian Thorgersen b5fe63af22 Merge pull request #2570 from stianst/KEYCLOAK-2766-1.9
KEYCLOAK-2766 Add missing id to AuthenticatorConfigRepresentation
2016-04-11 08:35:34 +02:00
Pavel Drozd a48ace4d70 KEYCLOAK-2794 - changed version of undertow-embedded. 2016-04-11 08:34:54 +02:00
Stian Thorgersen 5440688dd9 KEYCLOAK-2766 Add missing id to AuthenticatorConfigRepresentation 2016-04-11 07:43:45 +02:00
Stian Thorgersen b21b548b9c Merge pull request #2560 from vramik/KEYCLOAK-2783
KEYCLOAK-2783 added nexus-staging-maven-plugin
2016-04-11 07:14:50 +02:00
Stian Thorgersen fe44483e60 Merge pull request #2563 from guusdk/1.9.x
KEYCLOAK-2785: Admin client should be able to delete a user.
2016-04-11 07:14:13 +02:00
Stian Thorgersen 12be32f6dc Merge pull request #2559 from mhajas/js-console-stability-1.9.x-PR
KEYCLOAK-2780 Better stability in JSConsole test
2016-04-11 07:12:27 +02:00
Stian Thorgersen 524080b0ae Merge pull request #2557 from mhajas/secure-portal-1.9.x-PR
KEYCLOAK-2779 Fix DemoServletAdpater tests
2016-04-11 07:10:57 +02:00
Stian Thorgersen d8dfc1ca0b Merge pull request #2555 from mhajas/eap6URLs-1.9.x-PR
KEYCLOAK-2778 Fix urls on eap6
2016-04-11 07:09:57 +02:00
Chris Pitman bee7c077b7 KEYCLOAK-2791 - Proxy handling for X-Forwarded headers 2016-04-09 02:22:15 -04:00
Marek Posolda a3abc0296d Merge pull request #2566 from mposolda/1.9.x
KEYCLOAK-2769 Fix NPE during 'Identity Broker cancelled' and instead …
2016-04-08 22:14:22 +02:00
mposolda 2d3580e2d4 KEYCLOAK-2769 Fix NPE during 'Identity Broker cancelled' and instead show keycloak 'we are sorry' page 2016-04-08 19:19:52 +02:00
Guus der Kinderen 3c5fcebce9 KEYCLOAK-2785: Admin client should be able to delete a user.
The delete user service should be exposed in the admin client.
2016-04-08 16:36:54 +02:00
Vlasta Ramik 00bb2de032 added nexus-staging-maven-plugin 2016-04-08 15:37:21 +02:00
mhajas 013af68d74 Better stability in JSConsole test 2016-04-08 13:57:57 +02:00
mhajas 8209c74918 Fix DemoServlet test 2016-04-08 13:50:16 +02:00
mhajas 4ed2c4e5a8 Fix timeouts in DemoServlets tests 2016-04-08 13:50:03 +02:00
mhajas 34e125ea80 Fix urls on eap6 2016-04-08 13:31:08 +02:00
Marek Posolda 135071b3aa Merge pull request #2552 from mposolda/1.9.x
KEYCLOAK-2614 Refactor database lock to use 'SELECT FOR UPDATE' pessi…
2016-04-08 13:18:43 +02:00
mposolda 38952e5b3d KEYCLOAK-2614 Refactor database lock to use 'SELECT FOR UPDATE' pessimistic locking 2016-04-08 11:48:43 +02:00
Stian Thorgersen 3eefe2438b Merge pull request #2550 from stianst/KEYCLOAK-2683-1.9
KEYCLOAK-2683 Remove QRCodeResource and embed QR code in image
2016-04-08 09:53:24 +02:00
Stian Thorgersen faaf20de95 KEYCLOAK-2683 Remove QRCodeResource and embed QR code in image 2016-04-08 09:01:28 +02:00
Stian Thorgersen 7d5cedc6eb Merge pull request #2549 from stianst/1.9.x
KEYCLOAK-2774
2016-04-08 07:00:45 +02:00
Stian Thorgersen 51efc3c184 KEYCLOAK-2774
Adapters logout section points to removed tokens endpoint
2016-04-08 07:00:32 +02:00
Stian Thorgersen cddba23dcf Merge pull request #2545 from vmuzikar/1.9.x
KEYCLOAK-2772 Add deletion test to GroupTest
2016-04-08 06:51:36 +02:00
Stian Thorgersen 5c693246ba Merge pull request #2542 from stianst/KEYCLOAK-2646-1.9
KEYCLOAK-2646 Admin console shows 'Page not found' after password reset
2016-04-07 19:36:58 +02:00
Stian Thorgersen ae0aa61b3c Merge pull request #2547 from stianst/1.9.x
Revert accidentally removed ResteasyClient configuration in admin
2016-04-07 19:36:49 +02:00
Konstantin Gribov 1cf10f1f11 Revert accidentally removed ResteasyClient configuration in admin
Fixes accidentally removed in PR #2449 ResteasyClient pool size parameter in
`org.keycloak.admin.client.Keycloak`.
2016-04-07 19:36:25 +02:00
Vaclav Muzikar b576703533 KEYCLOAK-2772 Add deletion test to GroupTest 2016-04-07 18:05:12 +02:00
Stian Thorgersen 850de683db Merge pull request #2541 from stianst/admin-client-prs-1.9
Add service account support to Keycloak admin client
2016-04-07 16:53:01 +02:00
Stian Thorgersen 59fd461563 Merge pull request #2537 from stianst/KEYCLOAK-2751-1.9
KEYCLOAK-2751
2016-04-07 16:27:09 +02:00
Stian Thorgersen ddd93df503 KEYCLOAK-2646 Admin console shows 'Page not found' after password reset 2016-04-07 16:23:34 +02:00
Konstantin Gribov 74f98f9eb2 Add service account support to Keycloak admin client
Added grant_type=client_credentials support to keycloak-admin-client
so `keycloak-admin-client` can be used with service client account.

Fixes #KEYCLOAK-2236
2016-04-07 15:45:48 +02:00
Stian Thorgersen b32d50df2d Merge pull request #2540 from guusdk/1.9.x
KEYCLOAK-2767: Should return a primitive if possible.
2016-04-07 15:41:36 +02:00
Stian Thorgersen 61d1c498ef KEYCLOAK-2751
Separate HTTP status codes for REST API errors
2016-04-07 15:39:55 +02:00
Guus der Kinderen 7bbc618e1b KEYCLOAK-2767: Should return a primitive if possible.
A JSON primitive is valid JSON. There is no need to construct a JSON object
just for the sake of being JSON complient. This keeps things nice and simple.
2016-04-07 13:59:17 +02:00
Stian Thorgersen 865371b58f Merge pull request #2536 from stianst/1.9.x
KEYCLOAK-2763
2016-04-07 09:37:36 +02:00
Stian Thorgersen 16b1f88546 KEYCLOAK-2763
Document requirement to use a secure private network for clustering
2016-04-07 09:37:17 +02:00
Stian Thorgersen 0f43e2b6f2 Merge pull request #2533 from stianst/KEYCLOAK-2762-1.9
KEYCLOAK-2762 Renaming a realm without saving the form affects all li…
2016-04-07 09:34:38 +02:00
Stian Thorgersen 949ecf60d9 Merge pull request #2534 from stianst/admin-client-prs-1.9
Add admin client PRs from master
2016-04-07 09:06:59 +02:00
Guus der Kinderen 0024162121 KEYCLOAK-2731: Improve thread safety of TokenManager
This commit guards access to the non-final fields of TokenManager by its intrinsic lock.
2016-04-07 07:36:36 +02:00
Guus der Kinderen 958471b081 KEYCLOAK-2746: By default, allow for concurrent usage
The nature of Keycloak makes it very plausible that it is used in
a concurrent setting. With that in mind, it would make sense to,
by default, allow for more than one concurrent Resteasy connection
in the admin client code.
2016-04-07 07:35:53 +02:00
Guus der Kinderen de80133ea2 KEYCLOAK-2726: Invalidate token upon failure
When a token managed by TokenManager is known to be invalid, it should no
longer be used. This commit adds a response listener to the only filter
using TokenManager, which causes, upon authentication failure, to
invalidate the token that was used.
2016-04-07 07:24:21 +02:00
Guus der Kinderen 13104750f6 KEYCLOAK-2721: Do not recreate TokenService proxy
By re-using the service proxy, classloading issues can be prevented.
2016-04-07 07:20:58 +02:00
Stian Thorgersen 2f575af3d2 Merge pull request #2531 from stianst/KEYCLOAK-2761-1.9
KEYCLOAK-2761 Broken on/off switch at Client Mappers with specific wi…
2016-04-07 07:13:37 +02:00
Stian Thorgersen 56f99ed640 KEYCLOAK-2762 Renaming a realm without saving the form affects all links in Console 2016-04-07 06:59:48 +02:00
Stian Thorgersen a42e7f226f Merge pull request #2529 from stianst/KEYCLOAK-2760-1.9
KEYCLOAK-2760
2016-04-07 06:51:26 +02:00
Stian Thorgersen d6e513f6c3 Merge pull request #2526 from stianst/KEYCLOAK-2759-1.9
KEYCLOAK-2759 Fix error message when renaming realm to name that exists
2016-04-07 06:50:52 +02:00
Stian Thorgersen 1500fff93e Merge pull request #2525 from stianst/KEYCLOAK-2758-1.9
KEYCLOAK-2758
2016-04-07 06:50:07 +02:00
Stian Thorgersen c37437e82b KEYCLOAK-2761 Broken on/off switch at Client Mappers with specific window width 2016-04-07 06:19:48 +02:00
Stian Thorgersen 7d0bb05aaa KEYCLOAK-2760
Cannot delete a client template mapper with a little trash icon in heading
2016-04-07 06:07:32 +02:00
Stian Thorgersen 35297a149d KEYCLOAK-2759 Fix error message when renaming realm to name that exists 2016-04-07 06:00:57 +02:00
Stian Thorgersen e68b71ba01 KEYCLOAK-2758
Weird behavior of add/remove button for 'Valid Redirect URIs' in a client settings form
2016-04-07 05:43:36 +02:00
Stian Thorgersen 900be13423 Merge pull request #2521 from stianst/KEYCLOAK-2756-1.9
KEYCLOAK-2756
2016-04-06 19:25:56 +02:00
Stian Thorgersen 02b8cc7ad3 KEYCLOAK-2756
Renaming a realm breaks down the Clients
2016-04-06 15:19:36 +02:00
Marek Posolda 0371c53d07 Merge pull request #2516 from mposolda/1.9.x
KEYCLOAK-1982 Some builtin objects might be missing when import JSON exported from previous versions
2016-04-06 13:24:29 +02:00
Stian Thorgersen 364a54f792 Merge pull request #2518 from stianst/1.9.x
KEYCLOAK-2752
2016-04-06 13:19:30 +02:00
Stian Thorgersen 0016bd8744 KEYCLOAK-2752
Update documentation re directory for providers
2016-04-06 13:18:56 +02:00
Stian Thorgersen 36baf38d09 Merge pull request #2515 from stianst/KEYCLOAK-2590-1.9
KEYCLOAK-2590 Add custom rest endpoint to set time offset
2016-04-06 12:58:18 +02:00
mposolda 437244f61e KEYCLOAK-1982 Some builtin objects might be missing when import JSON exported from old versions 2016-04-06 11:36:42 +02:00
mposolda 70c85af7e2 Ensure KeycloakSessionFactory.close is called before shutdown of auth-server-undertow 2016-04-06 11:36:42 +02:00
mposolda d97dbdac36 KEYCLOAK-2737 Handle the databaseSchema: 'validate' against empty database 2016-04-06 11:36:41 +02:00
Stian Thorgersen 89013c8f6e KEYCLOAK-2590 Add custom rest endpoint to set time offset 2016-04-06 11:16:28 +02:00
Stian Thorgersen ff80f613d3 Merge pull request #2513 from stianst/KEYCLOAK-2262-1.9
KEYCLOAK-2262
2016-04-06 11:12:04 +02:00
Stian Thorgersen 15cbddba13 KEYCLOAK-2262 Refactor realm resource provider, remove admin resource provider and added example 2016-04-06 10:29:40 +02:00
Pedro Igor 7c11bd2214 [KEYCLOAK-2262] - New SPIs to extend both Realm and Admin RESTful APIs 2016-04-06 10:26:59 +02:00
Stian Thorgersen 7280eb3cd9 Merge pull request #2510 from stianst/KEYCLOAK-2748-1.9
KEYCLOAK-2748
2016-04-06 09:10:21 +02:00
Stian Thorgersen 4d38c1f95d KEYCLOAK-2748
Mark modules as private
2016-04-06 08:07:52 +02:00
Stian Thorgersen 8d196500d8 Merge pull request #2504 from stianst/KEYCLOAK-2707-1.9
KEYCLOAK-2707 Fix account management layout for medium size screens
2016-04-05 13:26:05 +02:00
Marek Posolda a0e21c8bf6 Merge pull request #2506 from mposolda/1.9.x
KEYCLOAK-2623 Remove auth-server-url-for-backend-requests from adapters
2016-04-05 13:09:57 +02:00
mposolda 6b85ff0d77 Removed docker-cluster as integration-arquillian has support for clustering tests now 2016-04-05 11:41:53 +02:00
mposolda 89b622e6a0 KEYCLOAK-2623 Remove auth-server-url-for-backend-requests from adapters 2016-04-05 11:41:53 +02:00
Stian Thorgersen 53e2e352fd Merge pull request #2414 from velias/KEYCLOAK-2670
KEYCLOAK-2670 - client app is able to push additional HTTP GET
2016-04-05 11:20:04 +02:00
Stian Thorgersen fda48fc537 Merge pull request #2501 from vmuzikar/1.9.x
KEYCLOAK-2745 More UI tests stabilization
2016-04-05 11:19:07 +02:00
Stian Thorgersen 3d8721246a Merge pull request #2499 from stianst/KEYCLOAK-2722-1.9
KEYCLOAK-2722 Check user session in token introspection endpoint
2016-04-05 11:18:34 +02:00
Stian Thorgersen 0b3847eb5d KEYCLOAK-2707 Fix account management layout for medium size screens 2016-04-05 10:31:16 +02:00
Vaclav Muzikar 96606fe3da KEYCLOAK-2745 More UI tests stabilization
(cherry picked from commit 3dbf5ee)
2016-04-05 10:14:41 +02:00
Stian Thorgersen b061288d1c KEYCLOAK-2722 Check user session in token introspection endpoint 2016-04-05 09:32:14 +02:00
Stian Thorgersen 89080af339 Merge pull request #2497 from stianst/KEYCLOAK-2652-1.9
KEYCLOAK-2652 Added fasterxml modules to as7 adapter dist
2016-04-05 08:57:56 +02:00
Stian Thorgersen 7bfe51c925 Merge pull request #2495 from stianst/KEYCLOAK-2711-1.9
KEYCLOAK-2711
2016-04-05 08:54:36 +02:00
Stian Thorgersen 07b43f11e2 Merge pull request #2493 from stianst/KEYCLOAK-2704-1.9
KEYCLOAK-2704
2016-04-05 08:54:20 +02:00
Marek Posolda 8b4fe15aeb Merge pull request #2486 from mposolda/1.9.x
databaseSchema option - proper support for "validate" for both JPA and Mongo
2016-04-05 08:48:37 +02:00
Stian Thorgersen 95486ef962 KEYCLOAK-2652 Added fasterxml modules to as7 adapter dist 2016-04-05 08:02:59 +02:00
Stian Thorgersen 5a799d4c33 KEYCLOAK-2711
Button for 'impersonate user' is visible when I am creating new user
2016-04-05 07:54:31 +02:00
Stian Thorgersen 5da2fad0a2 KEYCLOAK-2704
User count missing in REST admin endpoint
2016-04-05 07:48:51 +02:00
Bill Burke 6c84520021 Merge pull request #2491 from patriot1burke/1.9.x
KEYCLOAK-2669
2016-04-04 21:14:21 -04:00
Bill Burke 2955c814b3 KEYCLOAK-2669 2016-04-04 21:13:14 -04:00
Bill Burke aad3b77996 Merge pull request #2489 from patriot1burke/1.9.x
KEYCLOAK-2738
2016-04-04 18:54:51 -04:00
Bill Burke 109191f248 KEYCLOAK-2738 2016-04-04 18:48:22 -04:00
mposolda 749130b689 KEYCLOAK-2737 connectionsMongo: Support for 'databaseSchema: validate' 2016-04-04 22:23:24 +02:00
mposolda a8732aa83c KEYCLOAK-2744 connectionsJpa: the databaseSchema 'validate' should check if database schema is updated to latest version 2016-04-04 22:23:24 +02:00
Bill Burke 6b9aa49f79 Merge pull request #2482 from stianst/KEYCLOAK-2742-1.9.x
KEYCLOAK-2742
2016-04-04 12:54:27 -04:00
Stian Thorgersen 50b701ccf0 Merge pull request #2483 from stianst/1.9.x
Minor update to themes README.txt
2016-04-04 15:26:25 +02:00
Stian Thorgersen 7a67217c87 Minor update to themes README.txt 2016-04-04 15:24:40 +02:00
Stian Thorgersen 4f861dcbec KEYCLOAK-2742
Broken navigation links while creating/editing a Client Mapper
2016-04-04 15:18:32 +02:00
Stian Thorgersen 4be5d27ada Merge pull request #2480 from stianst/KEYCLOAK-2651-1.9.x
KEYCLOAK-2651
2016-04-04 10:05:04 +02:00
Stian Thorgersen 71b0ffa3ca Merge pull request #2478 from stianst/1.9.x
KEYCLOAK-2608
2016-04-04 10:04:47 +02:00
Stian Thorgersen 7ff35067aa KEYCLOAK-2651
No CSRF protection or general security headers on welcome page
2016-04-04 09:12:01 +02:00
Stian Thorgersen 05ee844bf6 KEYCLOAK-2608
Timestamp resolution of 1s for Event.time is inappropriate for use with tests
2016-04-04 08:31:04 +02:00
Stian Thorgersen d05a139a5f Merge pull request #2472 from winfinit/1.9.x
fixed typo in javascript example
2016-04-04 08:16:12 +02:00
Bill Burke b8da04814b Merge pull request #2476 from patriot1burke/1.9.x
KEYCLOAK-2662
2016-04-01 17:55:46 -04:00
Bill Burke 5482a80fe3 KEYCLOAK-2662 2016-04-01 16:43:16 -04:00
Bill Burke 2fd2812c94 Merge pull request #2459 from vmuzikar/1.9.x
KEYCLOAK-2732
2016-04-01 16:03:08 -04:00
Bill Burke bd0cd93c90 Merge pull request #2474 from patriot1burke/1.9.x
KEYCLOAK-2512
2016-04-01 16:01:58 -04:00
Bill Burke 90efb232a4 KEYCLOAK-2512 2016-04-01 14:50:55 -04:00
Roman Jurkov 431bf08c83 fixed typo in javascript example 2016-04-01 14:28:06 -04:00
Marek Posolda d6a70fb31e Merge pull request #2471 from mposolda/1.9.x
KEYCLOAK-2737 Remove 'databaseSchema' property from connectionsMongo
2016-04-01 18:48:40 +02:00
mposolda a049cfefe8 KEYCLOAK-2737 Remove 'databaseSchema' property from connectionsMongo 2016-04-01 16:55:46 +02:00
Marek Posolda 88725f68dd Merge pull request #2469 from mposolda/1.9.x
KEYCLOAK-2613 KEYCLOAK-2413 Export/import fixes and improvements
2016-04-01 16:46:13 +02:00
mposolda e07aefdcfd KEYCLOAK-2613 Add version to RealmRepresentation in JSON exports 2016-04-01 15:49:35 +02:00
mposolda 878b724ce1 KEYCLOAK-2413 Very slow export/import of realms with large users count 2016-04-01 15:33:39 +02:00
Marek Posolda d0bcb6b6aa Merge pull request #2467 from mposolda/1.9.x
Fix LDAPGroupMapperTest with MSAD and Mongo
2016-04-01 11:22:58 +02:00
mposolda e6489b3bc1 Fix LDAPGroupMapperTest with MSAD and Mongo 2016-04-01 10:43:36 +02:00
Marek Posolda aebc473071 Merge pull request #2465 from mposolda/1.9.x
KEYCLOAK-2661 Broken migration from Keycloak 1.5 with Mongo 3
2016-04-01 09:28:32 +02:00
mposolda fdf43f058a KEYCLOAK-2661 Broken migration from Keycloak 1.5 with Mongo 3 2016-03-31 22:29:46 +02:00
Bill Burke 6886510e32 Merge pull request #2463 from patriot1burke/1.9.x
KEYCLOAK-2621
2016-03-31 14:34:47 -04:00
Bill Burke dafa2e3401 KEYCLOAK-2621 2016-03-31 14:33:58 -04:00
Bill Burke e67d37e9d8 Merge pull request #2461 from patriot1burke/1.9.x
KEYCLOAK-2516
2016-03-31 13:58:01 -04:00
Bill Burke 9422542635 KEYCLOAK-2516 2016-03-31 13:57:13 -04:00
Vaclav Muzikar f5bed87eec KEYCLOAK-2732 2016-03-31 18:31:06 +02:00
Marek Posolda 7d2f0fd5cf Merge pull request #2456 from mposolda/1.9.x
KEYCLOAK-2610 Performance improve - add more indexes to database
2016-03-31 13:36:02 +02:00
mposolda cfd8770499 KEYCLOAK-2610 Add more indexes to database 2016-03-31 11:48:41 +02:00
mposolda a540e9a0da KEYCLOAK-2610 More operations added to ManyUsersTest 2016-03-31 11:48:41 +02:00
Bill Burke 028059a668 Merge pull request #2455 from patriot1burke/1.9.x
KEYCLOAK-2716
2016-03-30 20:26:48 -04:00
Bill Burke 1031d6b6f5 resolve conflict 2016-03-30 18:23:18 -04:00
Bill Burke c6a0243c92 KEYCLOAK-2716 2016-03-30 18:14:47 -04:00
Bill Burke 9736f56d3b Merge pull request #2453 from patriot1burke/1.9.x
KEYCLOAK-2490
2016-03-30 15:49:40 -04:00
Bill Burke 662d50eeea Merge pull request #2451 from patriot1burke/1.9.x
KEYCLOAK-2691
2016-03-30 15:47:57 -04:00
Bill Burke bfbbd5375f KEYCLOAK-2490 2016-03-30 15:42:05 -04:00
Bill Burke b735106750 Merge pull request #2445 from mhajas/js-console-1.9.x-PR
KEYCLOAK-2725 Run js-console example on non-relative scanario + tests of implicit flow
2016-03-30 15:16:55 -04:00
Bill Burke 1857c0c0b4 Merge pull request #2431 from mstruk/assert-events-b
KEYCLOAK-2589 KEYCLOAK-2607 KEYCLOAK-2597 Port AssertEvents to integration-arquillian
2016-03-30 15:16:34 -04:00
Bill Burke 157bb84bce Merge pull request #2448 from tkyjovsk/KEYCLOAK-2641-fix_1.9.x
KEYCLOAK-2641 fix 1.9.x
2016-03-30 15:15:36 -04:00
Bill Burke 52617e6ef1 Merge remote-tracking branch 'remote19/1.9.x' into b_1_9 2016-03-30 15:13:51 -04:00
Bill Burke 239816f4f5 KEYCLOAK-2691 2016-03-30 15:13:39 -04:00
Tomas Kyjovsky e4ee88a492 KEYCLOAK-2641 updated READMEs 2016-03-30 18:15:56 +02:00
Tomas Kyjovsky 353958538c KEYCLOAK-2641 Fixed location of module.xsl 2016-03-30 18:15:36 +02:00
mhajas 47a91ccf33 Run js-console example on non-relative scanario + tests of implicit flow 2016-03-30 12:20:40 +02:00
Bill Burke 85baeeb34e Merge pull request #2442 from ssilvert/1.9.x
KEYCLOAK-2619: Partial Import doesn't support groups
2016-03-28 22:58:33 -04:00
Stan Silvert ef1c8199a5 KEYCLOAK-2619: Partial Import doesn't support groups 2016-03-28 15:56:06 -04:00
Bill Burke 89da594735 Merge pull request #2440 from patriot1burke/1.9.x
KEYCLOAK-2718
2016-03-28 12:01:28 -04:00
Bill Burke 811085f9bd KEYCLOAK-2718 2016-03-28 11:13:50 -04:00
Bill Burke b8904bc018 Merge pull request #2438 from patriot1burke/1.9.x
KEYCLOAK-2698
2016-03-28 10:22:13 -04:00
Bill Burke 937d234964 KEYCLOAK-2698 2016-03-28 10:20:50 -04:00
Bill Burke b5bc4f15f8 Merge pull request #2427 from tkyjovsk/fail-if-no-tests-false-1.9.x
Arq. TestSuite Surefire config: failIfNoTests=false
2016-03-28 08:48:09 -04:00
Bill Burke 16863f2ed1 Merge pull request #2434 from patriot1burke/1.9.x
KEYCLOAK-2584
2016-03-25 00:08:47 -04:00
Bill Burke 805afbb592 KEYCLOAK-2584 2016-03-24 18:56:24 -04:00
Bill Burke 977b61f91c Merge pull request #2432 from patriot1burke/1.9.x
KEYCLOAK-2624
2016-03-24 16:24:57 -04:00
Bill Burke 91815c65a3 KEYCLOAK-2624 2016-03-24 15:43:08 -04:00
Marko Strukelj 13cb6f481f KEYCLOAK-2597 Invalid children group location header response 2016-03-24 17:57:09 +01:00
Marko Strukelj dc254ff0f7 KEYCLOAK-2607 Migrate Group tests to ARQ testsuite 2016-03-24 17:57:08 +01:00
Marko Strukelj a633241d2f KEYCLOAK-2589 Copy AssertEvents to Arquillian testsuite and modify to pull events from admin endpoints 2016-03-24 17:57:00 +01:00
Vlastimil Elias 97b38f4ccd KEYCLOAK-2670 - client app is able to push additional HTTP GET
parameters in initial OpenID auth request for use in Auth flows
2016-03-24 16:19:13 +01:00
Marek Posolda 416e4948d7 Merge pull request #2428 from mposolda/1.9.x
KEYCLOAK-2713 Fix error at startup
2016-03-24 16:12:04 +01:00
mposolda 60dff589d4 KEYCLOAK-2713 Fix error at startup 2016-03-24 15:30:11 +01:00
Tomas Kyjovsky 61656c6eba Arq. TestSuite Surefire config: failIfNoTests=false 2016-03-24 15:26:20 +01:00
Bill Burke 2affc4d847 Merge pull request #2422 from tkyjovsk/KEYCLOAK-2641-1.9.x
KEYCLOAK-2641 moved app-server configurations from adapter tests
2016-03-24 09:31:36 -04:00
Bill Burke 09179159e7 Merge pull request #2423 from patriot1burke/1.9.x
KEYCLOAK-2543
2016-03-24 09:30:51 -04:00
Bill Burke b43dc8348d KEYCLOAK-2543 2016-03-24 08:44:31 -04:00
Tomas Kyjovsky f3357c2b5b KEYCLOAK-2641 moved app-server configurations from adapter tests to servers/app-server module 2016-03-24 12:10:37 +01:00
Marek Posolda 79c308166a Merge pull request #2419 from mposolda/1.9.x
Fixes and performance improvements for "create user"
2016-03-24 11:58:41 +01:00
mposolda 2ea985fd30 KEYCLOAK-2610 Ommit checks for default roles and groups during creation of user 2016-03-24 10:47:18 +01:00
mposolda 385cfd5161 KEYCLOAK-2712 Default required actions not added when user created through admin console 2016-03-24 10:47:18 +01:00
mposolda db3cd543df KEYCLOAK-2710 Improve hibernate statistics report 2016-03-24 10:47:17 +01:00
Stian Thorgersen e139fadb02 Merge pull request #2412 from vmuzikar/1.9.x
[KEYCLOAK-2697] Fix some UI tests stability
2016-03-24 08:19:40 +00:00
Stian Thorgersen 10809b47ad Merge pull request #2408 from vmuzikar/1.9.x-KEYCLOAK-2694
[KEYCLOAK-2694] Add system property for test constants location
2016-03-24 08:18:35 +00:00
Stian Thorgersen dd449ffed0 Merge pull request #2406 from vramik/1.9.x-KEYCLOAK-2676
KEYCLOAK-2676 - Review clients tests
2016-03-24 08:17:36 +00:00
Marek Posolda 1f5124fc8a Merge pull request #2416 from mposolda/1.9.x
DB tuning - step 1
2016-03-23 23:29:23 +01:00
mposolda 741a251d89 KEYCLOAK-2610 Improve performance of creating users with MySQL 2016-03-23 22:34:00 +01:00
mposolda 1012611785 KEYCLOAK-2710 Possibility to log some hibernate statistics 2016-03-23 22:33:50 +01:00
Marek Posolda 465f828d73 Merge pull request #2404 from mposolda/1.9.x-idpcache
KEYCLOAK-2632 Caching of identity provider links
2016-03-22 21:35:26 +01:00
mposolda 038294d5c1 KEYCLOAK-2632 Remove flag from UserCacheSession.invalidateUsers 2016-03-22 18:04:59 +01:00
Vaclav Muzikar d1b33e1638 [KEYCLOAK-2697] Fix some UI tests stability 2016-03-22 14:29:35 +01:00
Marek Posolda d863a4b143 Merge pull request #2409 from mposolda/1.9.x
LDAP fixes
2016-03-22 13:43:18 +01:00
mposolda 4a073b6e46 KEYCLOAK-2696 Unexpected error when trying to delete role mappings from read-only LDAP role mapper 2016-03-22 13:01:40 +01:00
mposolda 30b0c35442 KEYCLOAK-2682 NPE when LDAP groups points to non-existent user 2016-03-22 12:26:29 +01:00
Vaclav Muzikar ef5bb6d447 Add system property for test constants location
(cherry picked from commit f9afa13)
2016-03-22 12:21:29 +01:00
Vlasta Ramik 6aa0c7ca39 KEYCLOAK-2676 - Review clients tests 2016-03-22 11:52:20 +01:00
mposolda 70b3091097 KEYCLOAK-2632 Caching of identity provider links 2016-03-22 10:20:22 +01:00
Stian Thorgersen 210a79a575 Merge pull request #2403 from stianst/1.9.x
KEYCLOAK-2657 Remove copyright.txt and README.txt from server dist
2016-03-22 08:34:41 +00:00
Stian Thorgersen a4fb4fd530 KEYCLOAK-2657 Remove copyright.txt and README.txt from server dist 2016-03-22 07:28:32 +00:00
Marek Posolda 6f56320177 Merge pull request #2400 from mposolda/1.9.x
Caching of user consents and serviceAccount links
2016-03-22 08:03:36 +01:00
mposolda 4d13b5105e KEYCLOAK-2631 Caching of service account links 2016-03-22 07:02:18 +01:00
mposolda 97a398790f KEYCLOAK-2630 Add caching for user consents 2016-03-22 07:01:59 +01:00
mposolda c09da5cac8 KEYCLOAK-2687 ZIPException during import big number of users 2016-03-22 07:01:46 +01:00
Stian Thorgersen 9521e71840 Merge pull request #2397 from vramik/KEYCLOAK-2690
KEYCLOAK-2690 - create page source file when test fails
2016-03-21 15:26:26 +01:00
Stian Thorgersen e5d09e1b04 Merge pull request #2396 from mhajas/mellon-realm-1.9.x-PR
KEYCLOAK-2689 Modify mod_auth_mellon realm for rhel7
2016-03-21 15:21:30 +01:00
Stian Thorgersen ebe6d5c5c1 Merge pull request #2394 from mhajas/mellon-timeout-1.9.x-PR
KEYCLOAK-2688 Modified session length in mod_auth_mellon test
2016-03-21 15:21:03 +01:00
Stian Thorgersen 0320884c72 Merge pull request #2387 from pdrozd/1.9.x
KEYCLOAK-2685 - Updated EAP7 namespaces
2016-03-21 15:20:20 +01:00
Stian Thorgersen a254d2120d Merge pull request #2390 from stianst/1.9.x
KEYCLOAK-2686 - Fix failing migration from 1.9.1 to 1.9.2.
2016-03-21 15:12:21 +01:00
Vlasta Ramik 4157907358 create page source file when test fails 2016-03-21 14:16:55 +01:00
mhajas dfc8708808 Modify realm for rhel7 2016-03-21 12:44:43 +01:00
mhajas 67c40eb212 Modified session length 2016-03-21 12:38:00 +01:00
Thomas Darimont bafaef7060 KEYCLOAK-2664 - QRCodeResource should prohibit caching of the generated image
Added cache-control headers to avoid caching for generated
QRCode images.

Adjusted CacheControlUtil#noCache() to avoid caching more aggressively.
2016-03-21 10:39:53 +01:00
Stian Thorgersen ee06eda05c Merge pull request #2379 from vmuzikar/1.9.x
Add properties file for some "constants" used in integration tests
2016-03-21 10:37:31 +01:00
Thomas Darimont a3525dd073 KEYCLOAK-2686 - Fix failing migration from 1.9.1 to 1.9.2.
Since the `Map` returned by `realm.getBrowserSecurityHeaders()` is unmodifiable
we need to add the new header setting to a copy of the map which we then set
on the realm.
2016-03-21 10:31:43 +01:00
Pavel Drozd f97f0e4e1b KEYCLOAK-2685 - Updated EAP7 namespaces 2016-03-21 08:59:05 +01:00
Marek Posolda c62e93577b Merge pull request #2385 from mposolda/1.9.x
KEYCLOAK-2681 JS adapter init function with initOptions argument does…
2016-03-18 14:16:47 +01:00
ratcashdev bcebd3fb83 KEYCLOAK-2679: Fix when importing empty uniqueMember attributes 2016-03-18 12:20:37 +01:00
mposolda 0589c82242 KEYCLOAK-2681 JS adapter init function with initOptions argument doesn't call success callback 2016-03-18 12:08:33 +01:00
Vaclav Muzikar 4ddf4d1c99 Add support for testing default theme name in prop file
(cherry picked from commit 2d7424c)
2016-03-14 16:44:00 +01:00
Vaclav Muzikar a98148f19b Add LDAP vendors test
(cherry picked from commit cd063c6)
2016-03-14 16:43:39 +01:00
Vaclav Muzikar d1072e8942 Add support for some test constants in props file
(cherry picked from commit a35aa47)
2016-03-14 16:43:24 +01:00
Marek Posolda 5b0254f0c7 Merge pull request #2376 from mposolda/1.9.x
KEYCLOAK-2634 Better error reporting if password update failed due to…
2016-03-14 14:32:24 +01:00
mposolda c2b406e7d6 KEYCLOAK-2634 Better error reporting if password update failed due to MSAD password policy 2016-03-14 13:35:30 +01:00
Marek Posolda 4821290c32 Merge pull request #2375 from mposolda/1.9.x
A bit more details about Kerberos and LDAP mappers in docs
2016-03-14 11:52:59 +01:00
mposolda d3bb68c2b0 A bit more details about Kerberos and LDAP mappers in docs 2016-03-14 11:51:13 +01:00
Marek Posolda 9560364a1c Merge pull request #2372 from mposolda/1.9.x
1.9.x
2016-03-14 10:32:32 +01:00
mposolda 4b49bc7140 KEYCLOAK-2659 Allow sync all roles even if there are more than 1000 2016-03-14 09:37:31 +01:00
mposolda 88bac57cad KEYCLOAK-2644 Delete user with a READ_ONLY LDAP federation provider just from Keycloak DB 2016-03-14 09:27:21 +01:00
Marek Posolda 9630921d91 Merge pull request #2371 from mposolda/1.9.x
LDAP fixes
2016-03-11 23:12:10 +01:00
Bill Burke 84e216c2ac Merge pull request #2369 from patriot1burke/1.9.x
refactor user cache
2016-03-11 16:57:15 -05:00
mposolda 0bc72e908f KEYCLOAK-2655 Support of lazy sync LDAP groups, which user is member of 2016-03-11 22:29:43 +01:00
mposolda 6910572bd2 KEYCLOAK-2643 Added write-only property to LDAP full-name attribute mapper 2016-03-11 22:29:43 +01:00
mposolda a4dd8a38b3 KEYCLOAK-2629 LDAP Federation provider - input fields validation 2016-03-11 22:29:42 +01:00
mposolda f0b4127567 KEYCLOAK-2640 LDAP group sync does not sync more than 1000 groups 2016-03-11 22:29:42 +01:00
Bill Burke c4f3a4e68e refactor user cache
KEYCLOAK-2612 Set default eviction strategy for users cache
2016-03-11 11:43:06 -05:00
Stian Thorgersen c0aa4289e2 Merge pull request #2367 from stianst/KEYCLOAK-2612
KEYCLOAK-2612 Set default eviction strategy for users cache
2016-03-11 16:22:51 +01:00
Stian Thorgersen 9be600cffe KEYCLOAK-2593 Character set missing from responses and no content sniffing defense in place 2016-03-11 15:33:09 +01:00
Stian Thorgersen c998f6eb53 KEYCLOAK-2612 Set default eviction strategy for users cache 2016-03-11 15:29:30 +01:00
Stian Thorgersen c901b38858 Merge pull request #2366 from stianst/KEYCLOAK-2637
KEYCLOAK-2637
2016-03-11 15:23:08 +01:00
Stian Thorgersen 4bd09cf14b KEYCLOAK-2637
ModelExceptionMapper uses AdminMessagesProvider which loads messages outside of themes
2016-03-11 13:01:18 +01:00
Stian Thorgersen 3edadb01c5 Merge pull request #2363 from stianst/1.9.x
KEYCLOAK-2636
2016-03-11 12:33:17 +01:00
Stian Thorgersen b738764442 KEYCLOAK-2636
Tooltip for redirect uri is wrong
2016-03-11 11:12:53 +01:00
Marek Posolda 660fd94e7f Merge pull request #2362 from mposolda/1.9.x
KEYCLOAK-2628 protected method to IdpCreateUserIfUniqueAuthenticator …
2016-03-11 11:08:23 +01:00
mposolda a3138b8514 KEYCLOAK-2628 protected method to IdpCreateUserIfUniqueAuthenticator after user is registered 2016-03-11 10:05:07 +01:00
helio-frota 627e434b73 minor update javascript adapter docs 2016-03-11 06:03:40 +01:00
Stian Thorgersen 406754f31b Merge branch 'master' of https://github.com/keycloak/keycloak into 1.9.x 2016-03-10 08:09:40 +01:00
Stian Thorgersen b371521b33 Merge pull request #2350 from mposolda/1.9.x
1.9.x
2016-03-09 06:13:52 +01:00
mposolda f151933d91 Log DB info in arquillian testsuite 2016-03-08 22:08:45 +01:00
mposolda 5ea93822e7 Disable DBLockTest for now 2016-03-08 22:08:33 +01:00
mposolda cafd2d80d2 Added exclusion of old commons-io dependency as it's causing issues in some environments 2016-03-08 22:08:22 +01:00
mposolda c9dcd7973a Fix DB2 2016-03-08 22:08:08 +01:00
9300 changed files with 220735 additions and 1965896 deletions

No files matched your search

-10
View File
@@ -14,7 +14,6 @@
nbactions.xml
nb-configuration.xml
catalog.xml
nbproject
# Compiled source #
###################
@@ -46,12 +45,3 @@ nbproject
#########
target
# Maven shade
#############
*dependency-reduced-pom.xml
# nodejs #
##########
# 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
+7 -25
View File
@@ -1,38 +1,20 @@
language: java
dist: precise
cache:
cache: false
env:
global:
- MAVEN_SKIP_RC=true
- MAVEN_OPTS="-Xms512m -Xmx1536m"
matrix:
- TESTS=unit
- TESTS=server-group1
- TESTS=server-group2
- TESTS=server-group3
- TESTS=server-group4
- TESTS=old
- TESTS=crossdc
- MAVEN_OPTS="-Xms512m -Xmx2048m"
jdk:
- oraclejdk8
install: true
before_script:
- export MAVEN_SKIP_RC=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"
install:
- mvn install -Pdistribution -DskipTests=true -B -V -q
script:
- ./travis-run-tests.sh $TESTS
script:
- mvn test -B
sudo: false
Regular → Executable
+8 -13
View File
@@ -9,7 +9,7 @@ For more information about Keycloak visit [Keycloak homepage](http://keycloak.or
Building
--------
Ensure you have JDK 8 (or newer), Maven 3.1.1 (or newer) and Git installed
Ensure you have JDK 8 (or newer), Maven 3.2.1 (or newer) and Git installed
java -version
mvn -version
@@ -36,27 +36,23 @@ Once completed you will find distribution archives in `distribution`.
Starting Keycloak
-----------------
To start Keycloak during development first build as specified above, then run:
To start Keycloak during development first build as specficied above, then run:
mvn -f testsuite/utils/pom.xml exec:java -Pkeycloak-server
mvn -f testsuite/integration/pom.xml exec:java -Pkeycloak-server
To start Keycloak from the server distribution first build the distribution it as specified above, then run:
To start Keycloak from the appliance distribution first build the distribution it as specified above, then run:
tar xfz distribution/server-dist/target/keycloak-<VERSION>.tar.gz
cd keycloak-<VERSION>
tar xfz distribution/appliance-dist/target/keycloak-appliance-dist-all-<VERSION>.tar.gz
cd keycloak-appliance-dist-all-<VERSION>/keycloak
bin/standalone.sh
To stop the server press `Ctrl + C`.
Reporting security vulnerabilities
----------------------------------
If you've found a security vulnerability, please look at the [instructions on how to properly report it](http://www.keycloak.org/security.html)
Help and Documentation
----------------------
* [Documentation](http://www.keycloak.org/documentation.html) - User Guide, Admin REST API and Javadocs
* [Documentation](http://keycloak.jboss.org/docs) - User Guide, Admin REST API and Javadocs
* [User Mailing List](https://lists.jboss.org/mailman/listinfo/keycloak-user) - Mailing list to ask for help and general questions about Keycloak
* [JIRA](https://issues.jboss.org/projects/KEYCLOAK) - Issue tracker for bugs and feature requests
@@ -69,11 +65,10 @@ Contributing
* [Testsuite](misc/Testsuite.md) - Details about testsuite, but also how to quickly run Keycloak during development and a few test tools (OTP generation, LDAP server, Mail server)
* [Database Testing](misc/DatabaseTesting.md) - How to do testing of Keycloak on different databases
* [Updating Database](misc/UpdatingDatabaseSchema.md) - How to change the Keycloak database
* [Changing the Default keycloak-subsystem Configuration](misc/UpdatingServerConfig.md) - How to update the default keycloak-subsystem config
* [Developer Mailing List](https://lists.jboss.org/mailman/listinfo/keycloak-dev) - Mailing list to discuss development of Keycloak
License
-------
* [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)
* [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)
+1 -6
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version>
<version>1.9.2.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -65,11 +65,6 @@
<artifactId>keycloak-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-authz-client</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
@@ -17,21 +17,26 @@
package org.keycloak.adapters;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.jboss.logging.Logger;
import org.keycloak.adapters.authentication.ClientCredentialsProvider;
import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.adapters.rotation.PublicKeyLocator;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.common.enums.RelativeUrlsUsed;
import org.keycloak.common.enums.SslRequired;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.enums.TokenStore;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.representations.idm.PublishedRealmRepresentation;
import org.keycloak.util.JsonSerialization;
import org.keycloak.common.util.KeycloakUriBuilder;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.security.PublicKey;
import java.util.Map;
/**
@@ -52,7 +57,7 @@ public class AdapterDeploymentContext {
* during the application deployment's life cycle.
*
* @param deployment A KeycloakConfigResolver, possibly missing the Auth
* Server URL
* Server URL and/or Realm Public Key
*/
public AdapterDeploymentContext(KeycloakDeployment deployment) {
this.deployment = deployment;
@@ -74,6 +79,7 @@ public class AdapterDeploymentContext {
/**
* For single-tenant deployments, it complements KeycloakDeployment
* by resolving a relative Auth Server's URL based on the current request
* and, if needed, will lazily resolve the Realm's Public Key.
*
* For multi-tenant deployments, defers the resolution of KeycloakDeployment
* to the KeycloakConfigResolver .
@@ -92,8 +98,8 @@ public class AdapterDeploymentContext {
if (deployment.getAuthServerBaseUrl() == null) return deployment;
KeycloakDeployment resolvedDeployment = resolveUrls(deployment, facade);
if (resolvedDeployment.getPublicKeyLocator() == null) {
throw new RuntimeException("KeycloakDeployment was never initialized through appropriate SPIs");
if (resolvedDeployment.getRealmKey() == null) {
resolveRealmKey(resolvedDeployment);
}
return resolvedDeployment;
}
@@ -109,6 +115,45 @@ public class AdapterDeploymentContext {
}
}
public void resolveRealmKey(KeycloakDeployment deployment) {
if (deployment.getClient() == null) {
throw new RuntimeException("KeycloakDeployment was never initialized through appropriate SPIs");
}
HttpGet get = new HttpGet(deployment.getRealmInfoUrl());
try {
HttpResponse response = deployment.getClient().execute(get);
int status = response.getStatusLine().getStatusCode();
if (status != 200) {
close(response);
throw new RuntimeException("Unable to resolve realm public key remotely, status = " + status);
}
HttpEntity entity = response.getEntity();
if (entity == null) {
throw new RuntimeException("Unable to resolve realm public key remotely. There was no entity.");
}
InputStream is = entity.getContent();
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
int c;
while ((c = is.read()) != -1) {
os.write(c);
}
byte[] bytes = os.toByteArray();
String json = new String(bytes);
PublishedRealmRepresentation rep = JsonSerialization.readValue(json, PublishedRealmRepresentation.class);
deployment.setRealmKey(rep.getPublicKey());
} finally {
try {
is.close();
} catch (IOException ignored) {
}
}
} catch (IOException e) {
throw new RuntimeException("Unable to resolve realm public key remotely", e);
}
}
/**
* This delegate is used to store temporary, per-request metadata like request resolved URLs.
* Ever method is delegated except URL get methods and isConfigured()
@@ -162,11 +207,6 @@ public class AdapterDeploymentContext {
return (this.unregisterNodeUrl != null) ? this.unregisterNodeUrl : delegate.getUnregisterNodeUrl();
}
@Override
public String getJwksUrl() {
return (this.jwksUrl != null) ? this.jwksUrl : delegate.getJwksUrl();
}
@Override
public String getResourceName() {
return delegate.getResourceName();
@@ -183,13 +223,13 @@ public class AdapterDeploymentContext {
}
@Override
public void setPublicKeyLocator(PublicKeyLocator publicKeyLocator) {
delegate.setPublicKeyLocator(publicKeyLocator);
public PublicKey getRealmKey() {
return delegate.getRealmKey();
}
@Override
public PublicKeyLocator getPublicKeyLocator() {
return delegate.getPublicKeyLocator();
public void setRealmKey(PublicKey realmKey) {
delegate.setRealmKey(realmKey);
}
@Override
@@ -206,16 +246,6 @@ 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() {
@@ -287,16 +317,6 @@ 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();
@@ -362,12 +382,6 @@ public class AdapterDeploymentContext {
return delegate.getNotBefore();
}
@Override
public void updateNotBefore(int notBefore) {
delegate.setNotBefore(notBefore);
getPublicKeyLocator().reset(this);
}
@Override
public void setExposeToken(boolean exposeToken) {
delegate.setExposeToken(exposeToken);
@@ -442,46 +456,6 @@ public class AdapterDeploymentContext {
public void setTurnOffChangeSessionIdOnLogin(boolean turnOffChangeSessionIdOnLogin) {
delegate.setTurnOffChangeSessionIdOnLogin(turnOffChangeSessionIdOnLogin);
}
@Override
public int getTokenMinimumTimeToLive() {
return delegate.getTokenMinimumTimeToLive();
}
@Override
public void setTokenMinimumTimeToLive(final int tokenMinimumTimeToLive) {
delegate.setTokenMinimumTimeToLive(tokenMinimumTimeToLive);
}
@Override
public PolicyEnforcer getPolicyEnforcer() {
return delegate.getPolicyEnforcer();
}
@Override
public void setPolicyEnforcer(PolicyEnforcer policyEnforcer) {
delegate.setPolicyEnforcer(policyEnforcer);
}
@Override
public void setMinTimeBetweenJwksRequests(int minTimeBetweenJwksRequests) {
delegate.setMinTimeBetweenJwksRequests(minTimeBetweenJwksRequests);
}
@Override
public int getMinTimeBetweenJwksRequests() {
return delegate.getMinTimeBetweenJwksRequests();
}
@Override
public int getPublicKeyCacheTtl() {
return delegate.getPublicKeyCacheTtl();
}
@Override
public void setPublicKeyCacheTtl(int publicKeyCacheTtl) {
delegate.setPublicKeyCacheTtl(publicKeyCacheTtl);
}
}
protected KeycloakUriBuilder getBaseBuilder(HttpFacade facade, String base) {
@@ -19,7 +19,9 @@ package org.keycloak.adapters;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.representations.AccessToken;
import org.keycloak.common.util.UriUtils;
import java.util.Collections;
import java.util.Set;
@@ -18,12 +18,10 @@
package org.keycloak.adapters;
import org.jboss.logging.Logger;
import org.keycloak.AuthorizationContext;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.common.util.UriUtils;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.representations.AccessToken;
import org.keycloak.common.util.UriUtils;
import java.io.IOException;
import java.util.Set;
@@ -57,9 +55,6 @@ public class AuthenticatedActionsHandler {
queryBearerToken();
return true;
}
if (!isAuthorized()) {
return true;
}
return false;
}
@@ -101,7 +96,6 @@ public class AuthenticatedActionsHandler {
if (!deployment.isCors()) return false;
KeycloakSecurityContext securityContext = facade.getSecurityContext();
String origin = facade.getRequest().getHeader(CorsHeaders.ORIGIN);
String exposeHeaders = deployment.getCorsExposedHeaders();
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)) {
@@ -125,36 +119,9 @@ public class AuthenticatedActionsHandler {
facade.getResponse().setStatus(200);
facade.getResponse().setHeader(CorsHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
facade.getResponse().setHeader(CorsHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
if (exposeHeaders != null) {
facade.getResponse().setHeader(CorsHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, exposeHeaders);
}
} else {
log.debugv("cors validation not needed as we're not a secure session or origin header was null: {0}", facade.getRequest().getURI());
}
return false;
}
private boolean isAuthorized() {
PolicyEnforcer policyEnforcer = this.deployment.getPolicyEnforcer();
if (policyEnforcer == null) {
log.debugv("Policy enforcement is disabled.");
return true;
}
try {
OIDCHttpFacade facade = (OIDCHttpFacade) this.facade;
AuthorizationContext authorizationContext = policyEnforcer.enforce(facade);
RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext) facade.getSecurityContext();
if (session != null) {
session.setAuthorizationContext(authorizationContext);
return authorizationContext.isGranted();
}
return true;
} catch (Exception e) {
throw new RuntimeException("Failed to enforce policy decisions.", e);
}
}
}
@@ -24,17 +24,16 @@ import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.common.util.Base64;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.constants.ServiceUrlConstants;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.util.JsonSerialization;
import org.keycloak.common.util.KeycloakUriBuilder;
import java.util.List;
@@ -71,10 +70,9 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
AccessTokenResponse atr=null;
try {
String userpw=new String(Base64.decode(tokenString));
int seperatorIndex = userpw.indexOf(":");
String user = userpw.substring(0, seperatorIndex);
String pw = userpw.substring(seperatorIndex + 1);
atr = getToken(user, pw);
String[] parts=userpw.split(":");
atr = getToken(parts[0], parts[1]);
tokenString = atr.getToken();
} catch (Exception e) {
log.debug("Failed to obtain token", e);
@@ -83,8 +81,8 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
}
return authenticateToken(exchange, atr.getToken());
}
}
private AccessTokenResponse getToken(String username, String password) throws Exception {
AccessTokenResponse tokenResponse=null;
HttpClient client = deployment.getClient();
@@ -106,7 +104,6 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
int status = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
if (status != 200) {
EntityUtils.consumeQuietly(entity);
throw new java.io.IOException("Bad status: " + status);
}
if (entity == null) {
@@ -18,13 +18,11 @@
package org.keycloak.adapters;
import org.jboss.logging.Logger;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.RSATokenVerifier;
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;
@@ -85,18 +83,8 @@ 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);
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());
} catch (VerificationException e) {
log.error("Failed to verify token", e);
challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.INVALID_TOKEN, "invalid_token", e.getMessage());
@@ -136,7 +124,6 @@ public class BearerTokenRequestAuthenticator {
}
surrogate = chain[0].getSubjectDN().getName();
}
log.debug("successful authorized");
return AuthOutcome.AUTHENTICATED;
}
@@ -174,19 +161,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);
if(deployment.isDelegateBearerErrorResponseSending()){
facade.getResponse().setStatus(401);
}
else {
facade.getResponse().sendError(401);
}
facade.getResponse().sendError(401);
return true;
}
};
@@ -17,17 +17,19 @@
package org.keycloak.adapters;
import java.io.IOException;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.RSATokenVerifier;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import org.keycloak.common.util.KeycloakUriBuilder;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -71,7 +73,7 @@ public class CookieTokenStore {
try {
// Skip check if token is active now. It's supposed to be done later by the caller
AccessToken accessToken = AdapterRSATokenVerifier.verifyToken(accessTokenString, deployment, false, true);
AccessToken accessToken = RSATokenVerifier.verifyToken(accessTokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl(), false, true);
IDToken idToken;
if (idTokenString != null && idTokenString.length() > 0) {
try {
@@ -30,5 +30,4 @@ public interface CorsHeaders {
String ORIGIN = "Origin";
String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";
String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
}
@@ -1,72 +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.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.keycloak.util.JsonSerialization;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class HttpAdapterUtils {
public static <T> T sendJsonHttpRequest(KeycloakDeployment deployment, HttpRequestBase httpRequest, Class<T> clazz) throws HttpClientAdapterException {
try {
HttpResponse response = deployment.getClient().execute(httpRequest);
int status = response.getStatusLine().getStatusCode();
if (status != 200) {
close(response);
throw new HttpClientAdapterException("Unexpected status = " + status);
}
HttpEntity entity = response.getEntity();
if (entity == null) {
throw new HttpClientAdapterException("There was no entity.");
}
InputStream is = entity.getContent();
try {
return JsonSerialization.readValue(is, clazz);
} finally {
try {
is.close();
} catch (IOException ignored) {
}
}
} catch (IOException e) {
throw new HttpClientAdapterException("IO error", e);
}
}
private static void close(HttpResponse response) {
if (response.getEntity() != null) {
try {
response.getEntity().getContent().close();
} catch (IOException e) {
}
}
}
}
@@ -1,32 +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;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class HttpClientAdapterException extends Exception {
public HttpClientAdapterException(String message) {
super(message);
}
public HttpClientAdapterException(String message, Throwable t) {
super(message, t);
}
}
@@ -17,11 +17,9 @@
package org.keycloak.adapters;
import org.apache.http.HttpHost;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
@@ -36,9 +34,9 @@ import org.apache.http.impl.conn.SingleClientConnManager;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.common.util.EnvUtil;
import org.keycloak.common.util.KeystoreUtil;
import org.keycloak.representations.adapters.config.AdapterHttpClientConfig;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
@@ -48,7 +46,6 @@ import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.URI;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
@@ -57,8 +54,6 @@ 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.
@@ -117,7 +112,6 @@ public class HttpClientBuilder {
protected TimeUnit socketTimeoutUnits = TimeUnit.MILLISECONDS;
protected long establishConnectionTimeout = -1;
protected TimeUnit establishConnectionTimeoutUnits = TimeUnit.MILLISECONDS;
protected HttpHost proxyHost;
/**
@@ -127,7 +121,8 @@ public class HttpClientBuilder {
* @param unit
* @return
*/
public HttpClientBuilder socketTimeout(long timeout, TimeUnit unit) {
public HttpClientBuilder socketTimeout(long timeout, TimeUnit unit)
{
this.socketTimeout = timeout;
this.socketTimeoutUnits = unit;
return this;
@@ -140,7 +135,8 @@ public class HttpClientBuilder {
* @param unit
* @return
*/
public HttpClientBuilder establishConnectionTimeout(long timeout, TimeUnit unit) {
public HttpClientBuilder establishConnectionTimeout(long timeout, TimeUnit unit)
{
this.establishConnectionTimeout = timeout;
this.establishConnectionTimeoutUnits = unit;
return this;
@@ -172,8 +168,8 @@ public class HttpClientBuilder {
return this;
}
public HttpClientBuilder disableCookieCache(boolean disable) {
this.disableCookieCache = disable;
public HttpClientBuilder disableCookieCache() {
this.disableCookieCache = true;
return this;
}
@@ -291,21 +287,16 @@ 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);
}
if (socketTimeout > -1) {
if (socketTimeout > -1)
{
HttpConnectionParams.setSoTimeout(params, (int) socketTimeoutUnits.toMillis(socketTimeout));
}
if (establishConnectionTimeout > -1) {
HttpConnectionParams.setConnectionTimeout(params, (int) establishConnectionTimeoutUnits.toMillis(establishConnectionTimeout));
if (establishConnectionTimeout > -1)
{
HttpConnectionParams.setConnectionTimeout(params, (int)establishConnectionTimeoutUnits.toMillis(establishConnectionTimeout));
}
DefaultHttpClient client = new DefaultHttpClient(cm, params);
if (disableCookieCache) {
client.setCookieStore(new CookieStore() {
@Override
@@ -336,8 +327,8 @@ public class HttpClientBuilder {
}
}
public HttpClient build(AdapterHttpClientConfig adapterConfig) {
disableCookieCache(true); // disable cookie cache as we don't want sticky sessions for load balancing
public HttpClient build(AdapterConfig adapterConfig) {
disableCookieCache(); // disable cookie cache as we don't want sticky sessions for load balancing
String truststorePath = adapterConfig.getTruststore();
if (truststorePath != null) {
@@ -373,28 +364,6 @@ public class HttpClientBuilder {
} else {
trustStore(truststore);
}
configureProxyForAuthServerIfProvided(adapterConfig);
return build();
}
/**
* Configures a the proxy to use for auth-server requests if provided.
* <p>
* If the given {@link AdapterHttpClientConfig} contains the attribute {@code proxy-url} we use the
* given URL as a proxy server, otherwise the proxy configuration is ignored.
* </p>
*
* @param adapterConfig
*/
private void configureProxyForAuthServerIfProvided(AdapterHttpClientConfig adapterConfig) {
if (adapterConfig == null || adapterConfig.getProxyUrl() == null || adapterConfig.getProxyUrl().trim().isEmpty()) {
return;
}
URI uri = URI.create(adapterConfig.getProxyUrl());
this.proxyHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
}
}
@@ -20,23 +20,20 @@ package org.keycloak.adapters;
import org.apache.http.client.HttpClient;
import org.jboss.logging.Logger;
import org.keycloak.adapters.authentication.ClientCredentialsProvider;
import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.adapters.rotation.PublicKeyLocator;
import org.keycloak.constants.ServiceUrlConstants;
import org.keycloak.common.enums.RelativeUrlsUsed;
import org.keycloak.common.enums.SslRequired;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.constants.ServiceUrlConstants;
import org.keycloak.enums.TokenStore;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.common.util.KeycloakUriBuilder;
import java.net.URI;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @author <a href="mailto:brad.culley@spartasystems.com">Brad Culley</a>
* @author <a href="mailto:john.ament@spartasystems.com">John D. Ament</a>
* @version $Revision: 1 $
*/
public class KeycloakDeployment {
@@ -45,7 +42,7 @@ public class KeycloakDeployment {
protected RelativeUrlsUsed relativeUrls;
protected String realm;
protected PublicKeyLocator publicKeyLocator;
protected volatile PublicKey realmKey;
protected String authServerBaseUrl;
protected String realmInfoUrl;
protected KeycloakUriBuilder authUrl;
@@ -54,12 +51,10 @@ public class KeycloakDeployment {
protected String accountUrl;
protected String registerNodeUrl;
protected String unregisterNodeUrl;
protected String jwksUrl;
protected String principalAttribute = "sub";
protected String resourceName;
protected boolean bearerOnly;
protected boolean autodetectBearerOnly;
protected boolean enableBasicAuth;
protected boolean publicClient;
protected Map<String, Object> resourceCredentials = new HashMap<>();
@@ -68,7 +63,6 @@ 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;
@@ -76,7 +70,6 @@ public class KeycloakDeployment {
protected int corsMaxAge = -1;
protected String corsAllowedHeaders;
protected String corsAllowedMethods;
protected String corsExposedHeaders;
protected boolean exposeToken;
protected boolean alwaysRefreshToken;
protected boolean registerNodeAtStartup;
@@ -84,24 +77,12 @@ public class KeycloakDeployment {
protected boolean turnOffChangeSessionIdOnLogin;
protected volatile int notBefore;
protected int tokenMinimumTimeToLive;
protected int minTimeBetweenJwksRequests;
protected int publicKeyCacheTtl;
private PolicyEnforcer policyEnforcer;
// https://tools.ietf.org/html/rfc7636
protected boolean pkce = false;
protected boolean ignoreOAuthQueryParameter;
protected Map<String, String> redirectRewriteRules;
protected boolean delegateBearerErrorResponseSending = false;
public KeycloakDeployment() {
}
public boolean isConfigured() {
return getRealm() != null && getPublicKeyLocator() != null && (isBearerOnly() || getAuthServerBaseUrl() != null);
return getRealm() != null && getRealmKey() != null && (isBearerOnly() || getAuthServerBaseUrl() != null);
}
public String getResourceName() {
@@ -116,12 +97,12 @@ public class KeycloakDeployment {
this.realm = realm;
}
public PublicKeyLocator getPublicKeyLocator() {
return publicKeyLocator;
public PublicKey getRealmKey() {
return realmKey;
}
public void setPublicKeyLocator(PublicKeyLocator publicKeyLocator) {
this.publicKeyLocator = publicKeyLocator;
public void setRealmKey(PublicKey realmKey) {
this.realmKey = realmKey;
}
public String getAuthServerBaseUrl() {
@@ -154,8 +135,6 @@ 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();
@@ -165,7 +144,6 @@ public class KeycloakDeployment {
accountUrl = authUrlBuilder.clone().path(ServiceUrlConstants.ACCOUNT_SERVICE_PATH).build(getRealm()).toString();
registerNodeUrl = authUrlBuilder.clone().path(ServiceUrlConstants.CLIENTS_MANAGEMENT_REGISTER_NODE_PATH).build(getRealm()).toString();
unregisterNodeUrl = authUrlBuilder.clone().path(ServiceUrlConstants.CLIENTS_MANAGEMENT_UNREGISTER_NODE_PATH).build(getRealm()).toString();
jwksUrl = authUrlBuilder.clone().path(ServiceUrlConstants.JWKS_URL).build(getRealm()).toString();
}
public RelativeUrlsUsed getRelativeUrls() {
@@ -200,10 +178,6 @@ public class KeycloakDeployment {
return unregisterNodeUrl;
}
public String getJwksUrl() {
return jwksUrl;
}
public void setResourceName(String resourceName) {
this.resourceName = resourceName;
}
@@ -216,14 +190,6 @@ public class KeycloakDeployment {
this.bearerOnly = bearerOnly;
}
public boolean isAutodetectBearerOnly() {
return autodetectBearerOnly;
}
public void setAutodetectBearerOnly(boolean autodetectBearerOnly) {
this.autodetectBearerOnly = autodetectBearerOnly;
}
public boolean isEnableBasicAuth() {
return enableBasicAuth;
}
@@ -280,14 +246,6 @@ public class KeycloakDeployment {
this.sslRequired = sslRequired;
}
public int getConfidentialPort() {
return confidentialPort;
}
public void setConfidentialPort(int confidentialPort) {
this.confidentialPort = confidentialPort;
}
public TokenStore getTokenStore() {
return tokenStore;
}
@@ -344,14 +302,6 @@ public class KeycloakDeployment {
this.corsAllowedMethods = corsAllowedMethods;
}
public String getCorsExposedHeaders() {
return corsExposedHeaders;
}
public void setCorsExposedHeaders(String corsExposedHeaders) {
this.corsExposedHeaders = corsExposedHeaders;
}
public boolean isExposeToken() {
return exposeToken;
}
@@ -368,11 +318,6 @@ public class KeycloakDeployment {
this.notBefore = notBefore;
}
public void updateNotBefore(int notBefore) {
this.notBefore = notBefore;
getPublicKeyLocator().reset(this);
}
public boolean isAlwaysRefreshToken() {
return alwaysRefreshToken;
}
@@ -412,69 +357,4 @@ public class KeycloakDeployment {
public void setTurnOffChangeSessionIdOnLogin(boolean turnOffChangeSessionIdOnLogin) {
this.turnOffChangeSessionIdOnLogin = turnOffChangeSessionIdOnLogin;
}
public int getTokenMinimumTimeToLive() {
return tokenMinimumTimeToLive;
}
public void setTokenMinimumTimeToLive(final int tokenMinimumTimeToLive) {
this.tokenMinimumTimeToLive = tokenMinimumTimeToLive;
}
public int getMinTimeBetweenJwksRequests() {
return minTimeBetweenJwksRequests;
}
public void setMinTimeBetweenJwksRequests(int minTimeBetweenJwksRequests) {
this.minTimeBetweenJwksRequests = minTimeBetweenJwksRequests;
}
public int getPublicKeyCacheTtl() {
return publicKeyCacheTtl;
}
public void setPublicKeyCacheTtl(int publicKeyCacheTtl) {
this.publicKeyCacheTtl = publicKeyCacheTtl;
}
public void setPolicyEnforcer(PolicyEnforcer policyEnforcer) {
this.policyEnforcer = policyEnforcer;
}
public PolicyEnforcer getPolicyEnforcer() {
return policyEnforcer;
}
// https://tools.ietf.org/html/rfc7636
public boolean isPkce() {
return pkce;
}
public void setPkce(boolean pkce) {
this.pkce = pkce;
}
public void setIgnoreOAuthQueryParameter(boolean ignoreOAuthQueryParameter) {
this.ignoreOAuthQueryParameter = ignoreOAuthQueryParameter;
}
public boolean isOAuthQueryParameterEnabled() {
return !this.ignoreOAuthQueryParameter;
}
public Map<String, String> getRedirectRewriteRules() {
return redirectRewriteRules;
}
public void setRewriteRedirectRules(Map<String, String> redirectRewriteRules) {
this.redirectRewriteRules = redirectRewriteRules;
}
public boolean isDelegateBearerErrorResponseSending() {
return delegateBearerErrorResponseSending;
}
public void setDelegateBearerErrorResponseSending(boolean delegateBearerErrorResponseSending) {
this.delegateBearerErrorResponseSending = delegateBearerErrorResponseSending;
}
}
@@ -21,14 +21,10 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.jboss.logging.Logger;
import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.adapters.rotation.HardcodedPublicKeyLocator;
import org.keycloak.adapters.rotation.JWKPublicKeyLocator;
import org.keycloak.common.enums.SslRequired;
import org.keycloak.common.util.PemUtils;
import org.keycloak.enums.TokenStore;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.common.util.PemUtils;
import org.keycloak.util.SystemPropertiesJsonParserFactory;
import java.io.IOException;
@@ -37,8 +33,6 @@ import java.security.PublicKey;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @author <a href="mailto:brad.culley@spartasystems.com">Brad Culley</a>
* @author <a href="mailto:john.ament@spartasystems.com">John D. Ament</a>
* @version $Revision: 1 $
*/
public class KeycloakDeploymentBuilder {
@@ -63,26 +57,16 @@ public class KeycloakDeploymentBuilder {
PublicKey realmKey;
try {
realmKey = PemUtils.decodePublicKey(realmKeyPem);
HardcodedPublicKeyLocator pkLocator = new HardcodedPublicKeyLocator(realmKey);
deployment.setPublicKeyLocator(pkLocator);
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
JWKPublicKeyLocator pkLocator = new JWKPublicKeyLocator();
deployment.setPublicKeyLocator(pkLocator);
deployment.setRealmKey(realmKey);
}
if (adapterConfig.getSslRequired() != null) {
deployment.setSslRequired(SslRequired.valueOf(adapterConfig.getSslRequired().toUpperCase()));
} 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 {
@@ -103,25 +87,13 @@ public class KeycloakDeploymentBuilder {
deployment.setCorsMaxAge(adapterConfig.getCorsMaxAge());
deployment.setCorsAllowedHeaders(adapterConfig.getCorsAllowedHeaders());
deployment.setCorsAllowedMethods(adapterConfig.getCorsAllowedMethods());
deployment.setCorsExposedHeaders(adapterConfig.getCorsExposedHeaders());
}
// https://tools.ietf.org/html/rfc7636
if (adapterConfig.isPkce()) {
deployment.setPkce(true);
}
deployment.setBearerOnly(adapterConfig.isBearerOnly());
deployment.setAutodetectBearerOnly(adapterConfig.isAutodetectBearerOnly());
deployment.setEnableBasicAuth(adapterConfig.isEnableBasicAuth());
deployment.setAlwaysRefreshToken(adapterConfig.isAlwaysRefreshToken());
deployment.setRegisterNodeAtStartup(adapterConfig.isRegisterNodeAtStartup());
deployment.setRegisterNodePeriod(adapterConfig.getRegisterNodePeriod());
deployment.setTokenMinimumTimeToLive(adapterConfig.getTokenMinimumTimeToLive());
deployment.setMinTimeBetweenJwksRequests(adapterConfig.getMinTimeBetweenJwksRequests());
deployment.setPublicKeyCacheTtl(adapterConfig.getPublicKeyCacheTtl());
deployment.setIgnoreOAuthQueryParameter(adapterConfig.isIgnoreOAuthQueryParameter());
deployment.setRewriteRedirectRules(adapterConfig.getRedirectRewriteRules());
if (realmKeyPem == null && adapterConfig.isBearerOnly() && adapterConfig.getAuthServerUrl() == null) {
throw new IllegalArgumentException("For bearer auth, you must set the realm-public-key or auth-server-url");
@@ -130,19 +102,13 @@ public class KeycloakDeploymentBuilder {
deployment.setClient(new HttpClientBuilder().build(adapterConfig));
}
if (adapterConfig.getAuthServerUrl() == null && (!deployment.isBearerOnly() || realmKeyPem == null)) {
throw new RuntimeException("You must specify auth-server-url");
throw new RuntimeException("You must specify auth-url");
}
deployment.setAuthServerBaseUrl(adapterConfig);
if (adapterConfig.getTurnOffChangeSessionIdOnLogin() != null) {
deployment.setTurnOffChangeSessionIdOnLogin(adapterConfig.getTurnOffChangeSessionIdOnLogin());
}
PolicyEnforcerConfig policyEnforcerConfig = adapterConfig.getPolicyEnforcerConfig();
if (policyEnforcerConfig != null) {
deployment.setPolicyEnforcer(new PolicyEnforcer(deployment, adapterConfig));
}
log.debug("Use authServerUrl: " + deployment.getAuthServerBaseUrl() + ", tokenUrl: " + deployment.getTokenUrl() + ", relativeUrls: " + deployment.getRelativeUrls());
return deployment;
}
@@ -17,10 +17,6 @@
package org.keycloak.adapters;
import org.jboss.logging.Logger;
import org.keycloak.common.util.HostUtils;
import org.keycloak.common.util.Time;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
@@ -28,6 +24,10 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import org.jboss.logging.Logger;
import org.keycloak.common.util.HostUtils;
import org.keycloak.common.util.Time;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@@ -19,14 +19,12 @@ package org.keycloak.adapters;
import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.RSATokenVerifier;
import org.keycloak.adapters.spi.AdapterSessionStore;
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.common.util.KeycloakUriBuilder;
import org.keycloak.common.util.UriUtils;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.enums.TokenStore;
import org.keycloak.jose.jws.JWSInput;
@@ -34,13 +32,11 @@ import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken;
import org.keycloak.util.TokenUtil;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.common.util.UriUtils;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.logging.Level;
import java.util.concurrent.atomic.AtomicLong;
/**
@@ -66,7 +62,7 @@ public class OAuthRequestAuthenticator {
this.reqAuthenticator = requestAuthenticator;
this.facade = facade;
this.deployment = deployment;
this.sslRedirectPort = deployment.getConfidentialPort() != -1 ? deployment.getConfidentialPort() : sslRedirectPort;
this.sslRedirectPort = sslRedirectPort;
this.tokenStore = tokenStore;
}
@@ -143,7 +139,6 @@ public class OAuthRequestAuthenticator {
protected String getRedirectUri(String state) {
String url = getRequestUrl();
log.debugf("callback uri: %s", url);
if (!facade.getRequest().isSecure() && deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr())) {
int port = sslRedirectPort();
if (port < 0) {
@@ -164,19 +159,10 @@ public class OAuthRequestAuthenticator {
String scope = getQueryParamValue(OAuth2Constants.SCOPE);
url = UriUtils.stripQueryParam(url, OAuth2Constants.SCOPE);
String prompt = getQueryParamValue(OAuth2Constants.PROMPT);
url = UriUtils.stripQueryParam(url, OAuth2Constants.PROMPT);
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())
.queryParam(OAuth2Constants.REDIRECT_URI, rewrittenRedirectUri(url))
.queryParam(OAuth2Constants.REDIRECT_URI, url)
.queryParam(OAuth2Constants.STATE, state)
.queryParam("login", "true");
if(loginHint != null && loginHint.length() > 0){
@@ -185,18 +171,9 @@ public class OAuthRequestAuthenticator {
if (idpHint != null && idpHint.length() > 0) {
redirectUriBuilder.queryParam(AdapterConstants.KC_IDP_HINT,idpHint);
}
if (prompt != null && prompt.length() > 0) {
redirectUriBuilder.queryParam(OAuth2Constants.PROMPT, prompt);
if (scope != null && scope.length() > 0) {
redirectUriBuilder.queryParam(OAuth2Constants.SCOPE, scope);
}
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);
return redirectUriBuilder.build().toString();
}
@@ -205,13 +182,15 @@ public class OAuthRequestAuthenticator {
return sslRedirectPort;
}
protected static final AtomicLong counter = new AtomicLong();
protected String getStateCode() {
return AdapterUtils.generateId();
return counter.getAndIncrement() + "/" + AdapterUtils.generateId();
}
protected AuthChallenge loginRedirect() {
final String state = getStateCode();
final String redirect = getRedirectUri(state);
final String redirect = getRedirectUri(state);
if (redirect == null) {
return challenge(403, OIDCAuthenticationError.Reason.NO_REDIRECT_URI, null);
}
@@ -329,15 +308,14 @@ public class OAuthRequestAuthenticator {
AccessTokenResponse tokenResponse = null;
strippedOauthParametersRequestUri = stripOauthParametersFromRedirect();
try {
// For COOKIE store we don't have httpSessionId and single sign-out won't be available
String httpSessionId = deployment.getTokenStore() == TokenStore.SESSION ? reqAuthenticator.changeHttpSessionId(true) : null;
tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, rewrittenRedirectUri(strippedOauthParametersRequestUri), httpSessionId);
tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, strippedOauthParametersRequestUri, httpSessionId);
} catch (ServerRequest.HttpFailure failure) {
log.error("failed to turn code into token");
log.error("status from server: " + failure.getStatus());
if (failure.getError() != null) {
if (failure.getStatus() == 400 && failure.getError() != null) {
log.error(" " + failure.getError());
}
return challenge(403, OIDCAuthenticationError.Reason.CODE_TO_TOKEN_FAILURE, null);
@@ -350,16 +328,8 @@ 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);
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());
if (idTokenString != null) {
try {
JWSInput input = new JWSInput(idTokenString);
@@ -374,7 +344,7 @@ public class OAuthRequestAuthenticator {
return challenge(403, OIDCAuthenticationError.Reason.INVALID_TOKEN, null);
}
if (tokenResponse.getNotBeforePolicy() > deployment.getNotBefore()) {
deployment.updateNotBefore(tokenResponse.getNotBeforePolicy());
deployment.setNotBefore(tokenResponse.getNotBeforePolicy());
}
if (token.getIssuedAt() < deployment.getNotBefore()) {
log.error("Stale token");
@@ -390,36 +360,9 @@ public class OAuthRequestAuthenticator {
protected String stripOauthParametersFromRedirect() {
KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(facade.getRequest().getURI())
.replaceQueryParam(OAuth2Constants.CODE, null)
.replaceQueryParam(OAuth2Constants.STATE, null)
.replaceQueryParam(OAuth2Constants.SESSION_STATE, null);
.replaceQueryParam(OAuth2Constants.STATE, null);
return builder.build().toString();
}
private String rewrittenRedirectUri(String originalUri) {
Map<String, String> rewriteRules = deployment.getRedirectRewriteRules();
if(rewriteRules != null && !rewriteRules.isEmpty()) {
try {
URL url = new URL(originalUri);
Map.Entry<String, String> rule = rewriteRules.entrySet().iterator().next();
StringBuilder redirectUriBuilder = new StringBuilder(url.getProtocol());
redirectUriBuilder.append("://"+ url.getAuthority());
redirectUriBuilder.append(url.getPath().replaceFirst(rule.getKey(), rule.getValue()));
return redirectUriBuilder.toString();
} catch (MalformedURLException ex) {
log.error("Not a valid request url");
throw new RuntimeException(ex);
}
}
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);
}
}
}
@@ -35,8 +35,7 @@ public class OIDCAuthenticationError implements AuthenticationError {
CODE_TO_TOKEN_FAILURE,
INVALID_TOKEN,
STALE_TOKEN,
NO_AUTHORIZATION_HEADER,
NO_QUERY_PARAMETER_ACCESS_TOKEN
NO_AUTHORIZATION_HEADER
}
private Reason reason;
@@ -17,32 +17,20 @@
package org.keycloak.adapters;
import java.security.PublicKey;
import org.jboss.logging.Logger;
import org.keycloak.adapters.authentication.ClientCredentialsProvider;
import org.keycloak.adapters.authentication.JWTClientCredentialsProvider;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.UserSessionManagement;
import org.keycloak.common.util.StreamUtil;
import org.keycloak.jose.jwk.JSONWebKeySet;
import org.keycloak.jose.jwk.JWK;
import org.keycloak.jose.jwk.JWKBuilder;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.representations.VersionRepresentation;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.representations.VersionRepresentation;
import org.keycloak.representations.adapters.action.AdminAction;
import org.keycloak.representations.adapters.action.LogoutAction;
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
import org.keycloak.representations.adapters.action.TestAvailabilityAction;
import org.keycloak.util.JsonSerialization;
import java.security.PublicKey;
import org.keycloak.common.util.StreamUtil;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -94,10 +82,6 @@ public class PreAuthActionsHandler {
if (!resolveDeployment()) return true;
handleTestAvailable();
return true;
} else if (requestUri.endsWith(AdapterConstants.K_JWKS)) {
if (!resolveDeployment()) return true;
handleJwksRequest();
return true;
}
return false;
}
@@ -155,7 +139,7 @@ public class PreAuthActionsHandler {
} else {
log.debugf("logout of all sessions for application '%s'", action.getResource());
if (action.getNotBefore() > deployment.getNotBefore()) {
deployment.updateNotBefore(action.getNotBefore());
deployment.setNotBefore(action.getNotBefore());
}
userSessionManagement.logoutAll();
}
@@ -177,7 +161,7 @@ public class PreAuthActionsHandler {
}
PushNotBeforeAction action = JsonSerialization.readValue(token.getContent(), PushNotBeforeAction.class);
if (!validateAction(action)) return;
deployment.updateNotBefore(action.getNotBefore());
deployment.setNotBefore(action.getNotBefore());
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -214,8 +198,7 @@ public class PreAuthActionsHandler {
try {
JWSInput input = new JWSInput(token);
PublicKey publicKey = AdapterRSATokenVerifier.getPublicKey(input.getHeader().getKeyId(), deployment);
if (RSAProvider.verify(input, publicKey)) {
if (RSAProvider.verify(input, deployment.getRealmKey())) {
return input;
}
} catch (JWSInputException ignore) {
@@ -257,26 +240,4 @@ public class PreAuthActionsHandler {
}
}
protected void handleJwksRequest() {
try {
JSONWebKeySet jwks = new JSONWebKeySet();
ClientCredentialsProvider clientCredentialsProvider = deployment.getClientAuthenticator();
// For now, just get signature key from JWT provider. We can add more if we support encryption etc.
if (clientCredentialsProvider instanceof JWTClientCredentialsProvider) {
PublicKey publicKey = ((JWTClientCredentialsProvider) clientCredentialsProvider).getPublicKey();
JWK jwk = JWKBuilder.create().rs256(publicKey);
jwks.setKeys(new JWK[] { jwk });
} else {
jwks.setKeys(new JWK[] {});
}
facade.getResponse().setStatus(200);
facade.getResponse().setHeader("Content-Type", "application/json");
JsonSerialization.writeValueToStream(facade.getResponse().getOutputStream(), jwks);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@@ -1,59 +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.jboss.logging.Logger;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.HttpFacade;
/**
* @author <a href="mailto:froehlich.ch@gmail.com">Christian Froehlich</a>
* @author <a href="mailto:brad.culley@spartasystems.com">Brad Culley</a>
* @author <a href="mailto:john.ament@spartasystems.com">John D. Ament</a>
* @version $Revision: 1 $
*/
public class QueryParamterTokenRequestAuthenticator extends BearerTokenRequestAuthenticator {
public static final String ACCESS_TOKEN = "access_token";
protected Logger log = Logger.getLogger(QueryParamterTokenRequestAuthenticator.class);
public QueryParamterTokenRequestAuthenticator(KeycloakDeployment deployment) {
super(deployment);
}
public AuthOutcome authenticate(HttpFacade exchange) {
if(!deployment.isOAuthQueryParameterEnabled()) {
return AuthOutcome.NOT_ATTEMPTED;
}
tokenString = null;
tokenString = getAccessTokenFromQueryParamter(exchange);
if (tokenString == null || tokenString.trim().isEmpty()) {
challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.NO_QUERY_PARAMETER_ACCESS_TOKEN, null, null);
return AuthOutcome.NOT_ATTEMPTED;
}
return (authenticateToken(exchange, tokenString));
}
String getAccessTokenFromQueryParamter(HttpFacade exchange) {
try {
if (exchange != null && exchange.getRequest() != null) {
return exchange.getRequest().getQueryParamValue(ACCESS_TOKEN);
}
} catch (Exception ignore) {
}
return null;
}
}
@@ -18,11 +18,9 @@
package org.keycloak.adapters;
import org.jboss.logging.Logger;
import org.keycloak.AuthorizationContext;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.RSATokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.Time;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken;
@@ -76,11 +74,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
}
public boolean isActive() {
return token != null && this.token.isActive() && deployment!=null && this.token.getIssuedAt() > deployment.getNotBefore();
}
public boolean isTokenTimeToLiveSufficient(AccessToken token) {
return token != null && (token.getExpiration() - this.deployment.getTokenMinimumTimeToLive()) > Time.currentTime();
return token != null && this.token.isActive() && this.token.getIssuedAt() > deployment.getNotBefore();
}
public KeycloakDeployment getDeployment() {
@@ -101,7 +95,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
if (log.isTraceEnabled()) {
log.trace("checking whether to refresh.");
}
if (isActive() && isTokenTimeToLiveSufficient(this.token)) return true;
if (isActive()) return true;
}
if (this.deployment == null || refreshToken == null) return false; // Might be serialized in HttpSession?
@@ -130,21 +124,14 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
String tokenString = response.getToken();
AccessToken token = null;
try {
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());
log.debug("Token Verification succeeded!");
} catch (VerificationException e) {
log.error("failed verification of token");
return false;
}
// If the TTL is greater-or-equal to the expire time on the refreshed token, have to abort or go into an infinite refresh loop
if (!isTokenTimeToLiveSufficient(token)) {
log.error("failed to refresh the token with a longer time-to-live than the minimum");
return false;
}
if (response.getNotBeforePolicy() > deployment.getNotBefore()) {
deployment.updateNotBefore(response.getNotBeforePolicy());
deployment.setNotBefore(response.getNotBeforePolicy());
}
this.token = token;
@@ -155,13 +142,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
this.refreshToken = response.getRefreshToken();
}
this.tokenString = tokenString;
if (tokenStore != null) {
tokenStore.refreshCallback(this);
}
tokenStore.refreshCallback(this);
return true;
}
public void setAuthorizationContext(AuthorizationContext authorizationContext) {
this.authorizationContext = authorizationContext;
}
}
@@ -17,14 +17,11 @@
package org.keycloak.adapters;
import java.util.Collections;
import java.util.List;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.common.enums.SslRequired;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -64,7 +61,7 @@ public abstract class RequestAuthenticator {
if (log.isTraceEnabled()) {
log.trace("try bearer");
}
AuthOutcome outcome = bearer.authenticate(facade);
if (outcome == AuthOutcome.FAILED) {
challenge = bearer.getChallenge();
@@ -77,36 +74,18 @@ public abstract class RequestAuthenticator {
return AuthOutcome.AUTHENTICATED;
}
QueryParamterTokenRequestAuthenticator queryParamAuth = createQueryParamterTokenRequestAuthenticator();
if (log.isTraceEnabled()) {
log.trace("try query paramter auth");
}
outcome = queryParamAuth.authenticate(facade);
if (outcome == AuthOutcome.FAILED) {
challenge = queryParamAuth.getChallenge();
log.debug("QueryParamAuth auth FAILED");
return AuthOutcome.FAILED;
} else if (outcome == AuthOutcome.AUTHENTICATED) {
if (verifySSL()) return AuthOutcome.FAILED;
log.debug("QueryParamAuth AUTHENTICATED");
completeAuthentication(queryParamAuth, "KEYCLOAK");
return AuthOutcome.AUTHENTICATED;
}
if (deployment.isEnableBasicAuth()) {
BasicAuthRequestAuthenticator basicAuth = createBasicAuthAuthenticator();
if (log.isTraceEnabled()) {
log.trace("try basic auth");
}
outcome = basicAuth.authenticate(facade);
if (outcome == AuthOutcome.FAILED) {
challenge = basicAuth.getChallenge();
log.debug("BasicAuth FAILED");
return AuthOutcome.FAILED;
} else if (outcome == AuthOutcome.AUTHENTICATED) {
if (verifySSL()) return AuthOutcome.FAILED;
log.debug("BasicAuth AUTHENTICATED");
completeAuthentication(basicAuth, "BASIC");
return AuthOutcome.AUTHENTICATED;
@@ -119,12 +98,6 @@ public abstract class RequestAuthenticator {
return AuthOutcome.NOT_ATTEMPTED;
}
if (isAutodetectedBearerOnly(facade.getRequest())) {
challenge = bearer.getChallenge();
log.debug("NOT_ATTEMPTED: Treating as bearer only");
return AuthOutcome.NOT_ATTEMPTED;
}
if (log.isTraceEnabled()) {
log.trace("try oauth");
}
@@ -161,43 +134,12 @@ public abstract class RequestAuthenticator {
protected boolean verifySSL() {
if (!facade.getRequest().isSecure() && deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr())) {
log.warnf("SSL is required to authenticate. Remote address %s is secure: %s, SSL required for: %s .",
facade.getRequest().getRemoteAddr(), facade.getRequest().isSecure(), deployment.getSslRequired().name());
log.warn("SSL is required to authenticate");
return true;
}
return false;
}
protected boolean isAutodetectedBearerOnly(HttpFacade.Request request) {
if (!deployment.isAutodetectBearerOnly()) return false;
String headerValue = facade.getRequest().getHeader("X-Requested-With");
if (headerValue != null && headerValue.equalsIgnoreCase("XMLHttpRequest")) {
return true;
}
headerValue = facade.getRequest().getHeader("Faces-Request");
if (headerValue != null && headerValue.startsWith("partial/")) {
return true;
}
headerValue = facade.getRequest().getHeader("SOAPAction");
if (headerValue != null) {
return true;
}
List<String> accepts = facade.getRequest().getHeaders("Accept");
if (accepts == null) accepts = Collections.emptyList();
for (String accept : accepts) {
if (accept.contains("text/html") || accept.contains("text/*") || accept.contains("*/*")) {
return false;
}
}
return true;
}
protected abstract OAuthRequestAuthenticator createOAuthAuthenticator();
protected BearerTokenRequestAuthenticator createBearerTokenAuthenticator() {
@@ -208,10 +150,6 @@ public abstract class RequestAuthenticator {
return new BasicAuthRequestAuthenticator(deployment);
}
protected QueryParamterTokenRequestAuthenticator createQueryParamterTokenRequestAuthenticator() {
return new QueryParamterTokenRequestAuthenticator(deployment);
}
protected void completeAuthentication(OAuthRequestAuthenticator oauth) {
RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, tokenStore, oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), oauth.getRefreshToken());
final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(AdapterUtils.getPrincipalName(deployment, oauth.getToken()), session);
@@ -220,12 +158,10 @@ public abstract class RequestAuthenticator {
}
protected abstract void completeOAuthAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal);
protected abstract void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method);
/**
* After code is received, we change the session id if possible to guard against https://www.owasp.org/index.php/Session_Fixation
*
* @param create
* @return
*/
@@ -26,14 +26,12 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
import org.keycloak.common.util.HostUtils;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.common.util.StreamUtil;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.common.util.HostUtils;
import org.keycloak.util.JsonSerialization;
import org.jboss.logging.Logger;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.common.util.StreamUtil;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -48,8 +46,6 @@ import java.util.List;
*/
public class ServerRequest {
private static Logger logger = Logger.getLogger(ServerRequest.class);
public static class HttpFailure extends Exception {
private int status;
private String error;
@@ -140,62 +136,6 @@ public class ServerRequest {
}
}
// https://tools.ietf.org/html/rfc7636#section-4
public static AccessTokenResponse invokeAccessCodeToToken(KeycloakDeployment deployment, String code, String redirectUri, String sessionId, String codeVerifier) throws IOException, HttpFailure {
List<NameValuePair> formparams = new ArrayList<>();
redirectUri = stripOauthParametersFromRedirect(redirectUri);
formparams.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, "authorization_code"));
formparams.add(new BasicNameValuePair(OAuth2Constants.CODE, code));
formparams.add(new BasicNameValuePair(OAuth2Constants.REDIRECT_URI, redirectUri));
if (sessionId != null) {
formparams.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_STATE, sessionId));
formparams.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_HOST, HostUtils.getHostName()));
}
// https://tools.ietf.org/html/rfc7636#section-4
if (codeVerifier != null) {
logger.debugf("add to POST parameters of Token Request, codeVerifier = %s", codeVerifier);
formparams.add(new BasicNameValuePair(OAuth2Constants.CODE_VERIFIER, codeVerifier));
} else {
logger.debug("add to POST parameters of Token Request without codeVerifier");
}
HttpPost post = new HttpPost(deployment.getTokenUrl());
ClientCredentialsProviderUtils.setClientCredentials(deployment, post, formparams);
UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
post.setEntity(form);
HttpResponse response = deployment.getClient().execute(post);
int status = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
if (status != 200) {
error(status, entity);
}
if (entity == null) {
throw new HttpFailure(status, null);
}
InputStream is = entity.getContent();
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
int c;
while ((c = is.read()) != -1) {
os.write(c);
}
byte[] bytes = os.toByteArray();
String json = new String(bytes);
try {
return JsonSerialization.readValue(json, AccessTokenResponse.class);
} catch (IOException e) {
throw new IOException(json, e);
}
} finally {
try {
is.close();
} catch (IOException ignored) {
}
}
}
public static AccessTokenResponse invokeRefresh(KeycloakDeployment deployment, String refreshToken) throws IOException, HttpFailure {
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
formparams.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.REFRESH_TOKEN));
@@ -17,10 +17,10 @@
package org.keycloak.adapters.authentication;
import org.keycloak.adapters.KeycloakDeployment;
import java.util.Map;
import org.keycloak.adapters.KeycloakDeployment;
/**
* The simple SPI for authenticating clients/applications . It's used by adapter during all OIDC backchannel requests to Keycloak server
* (codeToToken exchange, refresh token or backchannel logout) . You can also use it in your application during direct access grants or service account request
@@ -17,12 +17,6 @@
package org.keycloak.adapters.authentication;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.jboss.logging.Logger;
import org.keycloak.adapters.KeycloakDeployment;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -30,6 +24,12 @@ import java.util.Map;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.jboss.logging.Logger;
import org.keycloak.adapters.KeycloakDeployment;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@@ -17,14 +17,14 @@
package org.keycloak.adapters.authentication;
import java.util.Map;
import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.util.BasicAuthHelper;
import java.util.Map;
/**
* Traditional OAuth2 authentication of clients based on client_id and client_secret
*
@@ -32,7 +32,7 @@ import java.util.Map;
*/
public class ClientIdAndSecretCredentialsProvider implements ClientCredentialsProvider {
private static Logger logger = Logger.getLogger(ClientIdAndSecretCredentialsProvider.class);
private static Logger logger = Logger.getLogger(ClientCredentialsProviderUtils.class);
public static final String PROVIDER_ID = CredentialRepresentation.SECRET;
@@ -17,24 +17,16 @@
package org.keycloak.adapters.authentication;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.PrivateKey;
import java.util.Map;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.jose.jwk.JWK;
import org.keycloak.jose.jwk.JWKBuilder;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.representations.JsonWebToken;
import org.keycloak.common.util.KeystoreUtil;
import org.keycloak.common.util.Time;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.representations.JsonWebToken;
import java.security.PrivateKey;
import java.util.Map;
/**
* Client authentication based on JWT signed by client private key .
@@ -46,9 +38,7 @@ public class JWTClientCredentialsProvider implements ClientCredentialsProvider {
public static final String PROVIDER_ID = "jwt";
private KeyPair keyPair;
private JWK publicKeyJwk;
private PrivateKey privateKey;
private int tokenTimeout;
@Override
@@ -56,23 +46,14 @@ public class JWTClientCredentialsProvider implements ClientCredentialsProvider {
return PROVIDER_ID;
}
public void setupKeyPair(KeyPair keyPair) {
this.keyPair = keyPair;
this.publicKeyJwk = JWKBuilder.create().rs256(keyPair.getPublic());
public void setPrivateKey(PrivateKey privateKey) {
this.privateKey = privateKey;
}
public void setTokenTimeout(int tokenTimeout) {
this.tokenTimeout = tokenTimeout;
}
protected int getTokenTimeout() {
return tokenTimeout;
}
public PublicKey getPublicKey() {
return keyPair.getPublic();
}
@Override
public void init(KeycloakDeployment deployment, Object config) {
if (config == null || !(config instanceof Map)) {
@@ -103,9 +84,7 @@ public class JWTClientCredentialsProvider implements ClientCredentialsProvider {
if (clientKeyAlias == null) {
clientKeyAlias = deployment.getResourceName();
}
KeyPair keyPair = KeystoreUtil.loadKeyPairFromKeystore(clientKeystoreFile, clientKeystorePassword, clientKeyPassword, clientKeyAlias, clientKeystoreFormat);
setupKeyPair(keyPair);
this.privateKey = KeystoreUtil.loadPrivateKeyFromKeystore(clientKeystoreFile, clientKeystorePassword, clientKeyPassword, clientKeyAlias, clientKeystoreFormat);
this.tokenTimeout = asInt(cfg, "token-timeout", 10);
}
@@ -137,9 +116,8 @@ public class JWTClientCredentialsProvider implements ClientCredentialsProvider {
public String createSignedRequestToken(String clientId, String realmInfoUrl) {
JsonWebToken jwt = createRequestToken(clientId, realmInfoUrl);
return new JWSBuilder()
.kid(publicKeyJwk.getKeyId())
.jsonContent(jwt)
.rsa256(keyPair.getPrivate());
.rsa256(privateKey);
}
protected JsonWebToken createRequestToken(String clientId, String realmInfoUrl) {
@@ -1,97 +0,0 @@
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;
}
}
@@ -1,300 +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.Collections;
import java.util.List;
import java.util.Map;
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.Request;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.ClientAuthorizationContext;
import org.keycloak.representations.AccessToken;
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.idm.authorization.Permission;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public abstract class AbstractPolicyEnforcer {
private static Logger LOGGER = Logger.getLogger(AbstractPolicyEnforcer.class);
private final PolicyEnforcerConfig enforcerConfig;
private final PolicyEnforcer policyEnforcer;
private Map<String, PathConfig> paths;
private AuthzClient authzClient;
private PathMatcher pathMatcher;
public 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();
if (EnforcementMode.DISABLED.equals(enforcementMode)) {
return createEmptyAuthorizationContext(true);
}
Request request = httpFacade.getRequest();
String path = getPath(request);
PathConfig pathConfig = this.pathMatcher.matches(path, this.paths);
KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();
if (securityContext == null) {
if (pathConfig != null) {
challenge(pathConfig, getRequiredScopes(pathConfig, request), httpFacade);
}
return createEmptyAuthorizationContext(false);
}
AccessToken accessToken = securityContext.getToken();
if (accessToken != null) {
LOGGER.debugf("Checking permissions for path [%s] with config [%s].", request.getURI(), pathConfig);
if (pathConfig == null) {
if (EnforcementMode.PERMISSIVE.equals(enforcementMode)) {
return createAuthorizationContext(accessToken, null);
}
LOGGER.debugf("Could not find a configuration for path [%s]", path);
if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
return createAuthorizationContext(accessToken, null);
}
handleAccessDenied(httpFacade);
return createEmptyAuthorizationContext(false);
}
if (EnforcementMode.DISABLED.equals(pathConfig.getEnforcementMode())) {
return createEmptyAuthorizationContext(true);
}
MethodConfig methodConfig = getRequiredScopes(pathConfig, request);
if (isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
try {
return createAuthorizationContext(accessToken, pathConfig);
} catch (Exception e) {
throw new RuntimeException("Error processing path [" + pathConfig.getPath() + "].", e);
}
}
LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
if (!challenge(pathConfig, methodConfig, httpFacade)) {
LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
handleAccessDenied(httpFacade);
}
}
return createEmptyAuthorizationContext(false);
}
protected abstract boolean challenge(PathConfig pathConfig, MethodConfig methodConfig, OIDCHttpFacade facade);
protected boolean isAuthorized(PathConfig actualPathConfig, MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
Request request = httpFacade.getRequest();
PolicyEnforcerConfig enforcerConfig = getEnforcerConfig();
if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
return true;
}
AccessToken.Authorization authorization = accessToken.getAuthorization();
if (authorization == null) {
return false;
}
List<Permission> permissions = authorization.getPermissions();
boolean hasPermission = false;
for (Permission permission : permissions) {
if (permission.getResourceId() != null) {
if (isResourcePermission(actualPathConfig, permission)) {
hasPermission = true;
if (actualPathConfig.isInstance() && !matchResourcePermission(actualPathConfig, permission)) {
continue;
}
if (hasResourceScopePermission(methodConfig, permission)) {
LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions);
if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) {
this.paths.remove(actualPathConfig);
}
return true;
}
}
} else {
if (hasResourceScopePermission(methodConfig, permission)) {
hasPermission = true;
return true;
}
}
}
if (!hasPermission && EnforcementMode.PERMISSIVE.equals(actualPathConfig.getEnforcementMode())) {
return true;
}
LOGGER.debugf("Authorization FAILED for path [%s]. Not enough permissions [%s].", actualPathConfig, permissions);
return false;
}
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(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();
}
protected AuthzClient getAuthzClient() {
return this.authzClient;
}
protected PolicyEnforcerConfig getEnforcerConfig() {
return enforcerConfig;
}
protected PolicyEnforcer getPolicyEnforcer() {
return policyEnforcer;
}
private AuthorizationContext createEmptyAuthorizationContext(final boolean granted) {
return new ClientAuthorizationContext(authzClient) {
@Override
public boolean hasPermission(String resourceName, String scopeName) {
return granted;
}
@Override
public boolean hasResourcePermission(String resourceName) {
return granted;
}
@Override
public boolean hasScopePermission(String scopeName) {
return granted;
}
@Override
public List<Permission> getPermissions() {
return Collections.EMPTY_LIST;
}
@Override
public boolean isGranted() {
return granted;
}
};
}
private String getPath(Request request) {
return request.getRelativePath();
}
private MethodConfig getRequiredScopes(PathConfig pathConfig, Request request) {
String method = request.getMethod();
for (MethodConfig methodConfig : pathConfig.getMethods()) {
if (methodConfig.getMethod().equals(method)) {
return methodConfig;
}
}
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);
}
private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) {
// first we try a match using resource id
boolean resourceMatch = matchResourcePermission(actualPathConfig, permission);
// as a fallback, check if the current path is an instance and if so, check if parent's id matches the permission
if (!resourceMatch && actualPathConfig.isInstance()) {
resourceMatch = matchResourcePermission(actualPathConfig.getParentConfig(), permission);
}
return resourceMatch;
}
private boolean matchResourcePermission(PathConfig actualPathConfig, Permission permission) {
return permission.getResourceId().equals(actualPathConfig.getId());
}
}
@@ -1,81 +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.HashSet;
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.resource.PermissionResource;
import org.keycloak.authorization.client.resource.ProtectionResource;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import org.keycloak.representations.idm.authorization.PermissionRequest;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
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, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade facade) {
HttpFacade.Response response = facade.getResponse();
AuthzClient authzClient = getAuthzClient();
String ticket = getPermissionTicket(pathConfig, methodConfig, authzClient);
if (ticket == null) {
response.setStatus(403);
return true;
}
String realm = authzClient.getConfiguration().getRealm();
String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString();
response.setStatus(401);
StringBuilder wwwAuthenticate = new StringBuilder("UMA realm=\"").append(realm).append("\"").append(",as_uri=\"").append(authorizationServerUri).append("\"");
if (ticket != null) {
wwwAuthenticate.append(",ticket=\"").append(ticket).append("\"");
}
response.setHeader("WWW-Authenticate", wwwAuthenticate.toString());
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending UMA challenge");
}
return true;
}
private String getPermissionTicket(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AuthzClient authzClient) {
if (getEnforcerConfig().getUserManagedAccess() != null) {
ProtectionResource protection = authzClient.protection();
PermissionResource permission = protection.permission();
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceId(pathConfig.getId());
permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
return permission.create(permissionRequest).getTicket();
}
return null;
}
}
@@ -1,152 +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.ArrayList;
import java.util.HashSet;
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.representations.AccessToken;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import org.keycloak.representations.idm.authorization.AuthorizationRequest;
import org.keycloak.representations.idm.authorization.AuthorizationResponse;
import org.keycloak.representations.idm.authorization.Permission;
import org.keycloak.representations.idm.authorization.PermissionRequest;
import org.keycloak.representations.idm.authorization.PermissionResponse;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
private static Logger LOGGER = Logger.getLogger(KeycloakAdapterPolicyEnforcer.class);
public KeycloakAdapterPolicyEnforcer(PolicyEnforcer policyEnforcer) {
super(policyEnforcer);
}
@Override
protected boolean isAuthorized(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
AccessToken original = accessToken;
if (super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
return true;
}
accessToken = requestAuthorizationToken(pathConfig, methodConfig, httpFacade);
if (accessToken == null) {
return false;
}
AccessToken.Authorization authorization = original.getAuthorization();
if (authorization == null) {
authorization = new AccessToken.Authorization();
authorization.setPermissions(new ArrayList<Permission>());
}
AccessToken.Authorization newAuthorization = accessToken.getAuthorization();
if (newAuthorization != null) {
authorization.getPermissions().addAll(newAuthorization.getPermissions());
}
original.setAuthorization(authorization);
return super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade);
}
@Override
protected boolean challenge(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade facade) {
handleAccessDenied(facade);
return true;
}
@Override
protected void handleAccessDenied(OIDCHttpFacade facade) {
KeycloakSecurityContext securityContext = facade.getSecurityContext();
if (securityContext == null) {
return;
}
String accessDeniedPath = getEnforcerConfig().getOnDenyRedirectTo();
HttpFacade.Response response = facade.getResponse();
if (accessDeniedPath != null) {
response.setStatus(302);
response.setHeader("Location", accessDeniedPath);
} else {
response.sendError(403);
}
}
private AccessToken requestAuthorizationToken(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade) {
try {
KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();
String accessTokenString = securityContext.getTokenString();
AuthzClient authzClient = getAuthzClient();
KeycloakDeployment deployment = getPolicyEnforcer().getDeployment();
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceId(pathConfig.getId());
permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
AccessToken accessToken = securityContext.getToken();
AuthorizationRequest authzRequest;
if (getEnforcerConfig().getUserManagedAccess() != null) {
PermissionResponse permissionResponse = authzClient.protection().permission().create(permissionRequest);
authzRequest = new AuthorizationRequest();
authzRequest.setTicket(permissionResponse.getTicket());
} else {
authzRequest = new AuthorizationRequest();
if (accessToken.getAuthorization() != null) {
authzRequest.addPermission(pathConfig.getId(), methodConfig.getScopes());
}
}
if (accessToken.getAuthorization() != null) {
authzRequest.setRpt(accessTokenString);
}
LOGGER.debug("Obtaining authorization for authenticated user.");
AuthorizationResponse authzResponse = authzClient.authorization(accessTokenString).authorize(authzRequest);
if (authzResponse != null) {
return AdapterRSATokenVerifier.verifyToken(authzResponse.getToken(), deployment);
}
return null;
} catch (AuthorizationDeniedException e) {
LOGGER.debug("Authorization denied", e);
return null;
} catch (Exception e) {
throw new RuntimeException("Unexpected error during authorization request.", e);
}
}
}
@@ -1,170 +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.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PathCache {
/**
* The load factor.
*/
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
private final Map<String, CacheEntry> cache;
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);
}
/**
* Creates a new instance.
*
* @param maxEntries the maximum number of entries to keep in the cache
* @param maxAge the time in milliseconds that an entry can stay in the cache. If {@code -1}, entries never expire
*/
public PathCache(final int maxEntries, long maxAge) {
cache = new LinkedHashMap<String, CacheEntry>(16, DEFAULT_LOAD_FACTOR, true) {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return cache.size() > maxEntries;
}
};
this.maxAge = maxAge;
}
public void put(String uri, PathConfig newValue) {
try {
if (parkForWriteAndCheckInterrupt()) {
return;
}
CacheEntry cacheEntry = cache.get(uri);
if (cacheEntry == null) {
cache.put(uri, new CacheEntry(uri, newValue, maxAge));
}
} finally {
writing.lazySet(false);
}
}
public PathConfig get(String uri) {
if (parkForReadAndCheckInterrupt()) {
return null;
}
CacheEntry cached = cache.get(uri);
if (cached != null) {
return removeIfExpired(cached);
}
return null;
}
public void remove(String key) {
try {
if (parkForWriteAndCheckInterrupt()) {
return;
}
cache.remove(key);
} finally {
writing.lazySet(false);
}
}
private PathConfig removeIfExpired(CacheEntry cached) {
if (cached == null) {
return null;
}
if (cached.isExpired()) {
remove(cached.key());
return null;
}
return cached.value();
}
private boolean parkForWriteAndCheckInterrupt() {
while (!writing.compareAndSet(false, true)) {
LockSupport.parkNanos(1L);
if (Thread.interrupted()) {
return true;
}
}
return false;
}
private boolean parkForReadAndCheckInterrupt() {
while (writing.get()) {
LockSupport.parkNanos(1L);
if (Thread.interrupted()) {
return true;
}
}
return false;
}
private static final class CacheEntry {
final String key;
final PathConfig value;
final long expiration;
CacheEntry(String key, PathConfig value, long maxAge) {
this.key = key;
this.value = value;
if(maxAge == -1) {
expiration = -1;
} else {
expiration = System.currentTimeMillis() + maxAge;
}
}
String key() {
return key;
}
PathConfig value() {
return value;
}
boolean isExpired() {
return expiration != -1 ? System.currentTimeMillis() > expiration : false;
}
}
}
@@ -1,244 +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.List;
import java.util.Map;
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)) {
if (matchingAnyPath == null || matchingAnyPath.getPath().length() < matchingUri.length()) {
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();
List<ResourceRepresentation> search = resource.findByUri(path);
if (!search.isEmpty()) {
// resource does exist on the server, cache it
ResourceRepresentation targetResource = search.get(0);
PathConfig config = PolicyEnforcer.createPathConfig(targetResource);
config.setScopes(originalConfig.getScopes());
config.setMethods(originalConfig.getMethods());
config.setParentConfig(originalConfig);
config.setEnforcementMode(originalConfig.getEnforcementMode());
return config;
}
}
return originalConfig;
}
}
@@ -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.authorization;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.jboss.logging.Logger;
import org.keycloak.AuthorizationContext;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade;
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.ResourceRepresentation;
import org.keycloak.authorization.client.representation.ScopeRepresentation;
import org.keycloak.authorization.client.resource.ProtectedResource;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import org.keycloak.representations.idm.authorization.Permission;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PolicyEnforcer {
private static Logger LOGGER = Logger.getLogger(PolicyEnforcer.class);
private final KeycloakDeployment deployment;
private final AuthzClient authzClient;
private final PolicyEnforcerConfig enforcerConfig;
private final Map<String, PathConfig> paths;
private final PathMatcher pathMatcher;
public PolicyEnforcer(KeycloakDeployment deployment, AdapterConfig adapterConfig) {
this.deployment = deployment;
this.enforcerConfig = adapterConfig.getPolicyEnforcerConfig();
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(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);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Initialization complete. Path configurations:");
for (PathConfig pathConfig : this.paths.values()) {
LOGGER.debug(pathConfig);
}
}
}
public AuthorizationContext enforce(OIDCHttpFacade facade) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debugv("Policy enforcement is enable. 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);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debugv("Policy enforcement result for path [{0}] is : {1}", facade.getRequest().getURI(), context.isGranted() ? "GRANTED" : "DENIED");
LOGGER.debugv("Returning authorization context with permissions:");
for (Permission permission : context.getPermissions()) {
LOGGER.debug(permission);
}
}
return context;
}
PolicyEnforcerConfig getEnforcerConfig() {
return enforcerConfig;
}
AuthzClient getClient() {
return authzClient;
}
public Map<String, PathConfig> getPaths() {
return paths;
}
void addPath(PathConfig pathConfig) {
paths.put(pathConfig.getPath(), pathConfig);
}
KeycloakDeployment getDeployment() {
return deployment;
}
private Map<String, PathConfig> configurePaths(ProtectedResource protectedResource, PolicyEnforcerConfig enforcerConfig) {
boolean loadPathsFromServer = true;
for (PathConfig pathConfig : enforcerConfig.getPaths()) {
if (!PolicyEnforcerConfig.EnforcementMode.DISABLED.equals(pathConfig.getEnforcementMode())) {
loadPathsFromServer = false;
break;
}
}
if (loadPathsFromServer) {
LOGGER.info("No path provided in configuration.");
return configureAllPathsForResourceServer(protectedResource);
} else {
LOGGER.info("Paths provided in configuration.");
return configureDefinedPaths(protectedResource, enforcerConfig);
}
}
private Map<String, PathConfig> configureDefinedPaths(ProtectedResource protectedResource, PolicyEnforcerConfig enforcerConfig) {
Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>());
for (PathConfig pathConfig : enforcerConfig.getPaths()) {
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);
resource = protectedResource.findByName(resourceName);
} else {
LOGGER.debugf("Trying to find resource with uri [%s] for path [%s].", path, path);
List<ResourceRepresentation> resources = protectedResource.findByUri(path);
if (resources.size() == 1) {
resource = resources.get(0);
} else if (resources.size() > 1) {
throw new RuntimeException("Multiple resources found with the same uri");
} else {
resource = null;
}
}
if (resource == null) {
if (enforcerConfig.isCreateResources()) {
LOGGER.debugf("Creating resource on server for path [%s].", pathConfig);
ResourceRepresentation representation = new ResourceRepresentation();
representation.setName(resourceName);
representation.setType(pathConfig.getType());
representation.setUri(path);
HashSet<ScopeRepresentation> scopes = new HashSet<>();
for (String scopeName : pathConfig.getScopes()) {
ScopeRepresentation scope = new ScopeRepresentation();
scope.setName(scopeName);
scopes.add(scope);
}
representation.setScopes(scopes);
ResourceRepresentation registrationResponse = protectedResource.create(representation);
pathConfig.setId(registrationResponse.getId());
} else {
throw new RuntimeException("Could not find matching resource on server with uri [" + path + "] or name [" + resourceName + "]. Make sure you have created a resource on the server that matches with the path configuration.");
}
} else {
pathConfig.setId(resource.getId());
}
PathConfig existingPath = null;
for (PathConfig current : paths.values()) {
if (current.getId().equals(pathConfig.getId()) && current.getPath().equals(pathConfig.getPath())) {
existingPath = current;
break;
}
}
if (existingPath == null) {
paths.put(pathConfig.getPath(), pathConfig);
} else {
existingPath.getMethods().addAll(pathConfig.getMethods());
existingPath.getScopes().addAll(pathConfig.getScopes());
}
}
return paths;
}
private Map<String, PathConfig> configureAllPathsForResourceServer(ProtectedResource protectedResource) {
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()) {
ResourceRepresentation resourceDescription = protectedResource.findById(id);
if (resourceDescription.getUri() != null) {
PathConfig pathConfig = createPathConfig(resourceDescription);
paths.put(pathConfig.getPath(), pathConfig);
}
}
return paths;
}
static PathConfig createPathConfig(ResourceRepresentation resourceDescription) {
PathConfig pathConfig = new PathConfig();
pathConfig.setId(resourceDescription.getId());
pathConfig.setName(resourceDescription.getName());
String uri = resourceDescription.getUri();
if (uri == null || "".equals(uri.trim())) {
throw new RuntimeException("Failed to configure paths. Resource [" + resourceDescription.getName() + "] has an invalid or empty URI [" + uri + "].");
}
pathConfig.setPath(uri);
List<String> scopeNames = new ArrayList<>();
for (ScopeRepresentation scope : resourceDescription.getScopes()) {
scopeNames.add(scope.getName());
}
pathConfig.setScopes(scopeNames);
pathConfig.setType(resourceDescription.getType());
return pathConfig;
}
public PathMatcher getPathMatcher() {
return pathMatcher;
}
}
@@ -17,17 +17,14 @@
package org.keycloak.adapters.jaas;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.FindFile;
import org.keycloak.common.util.reflections.Reflections;
import org.keycloak.representations.AccessToken;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.security.Principal;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
@@ -38,17 +35,18 @@ import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.Principal;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.RSATokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AdapterUtils;
import org.keycloak.common.util.FindFile;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.representations.AccessToken;
import org.keycloak.common.util.reflections.Reflections;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -57,7 +55,7 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
public static final String KEYCLOAK_CONFIG_FILE_OPTION = "keycloak-config-file";
public static final String ROLE_PRINCIPAL_CLASS_OPTION = "role-principal-class";
public static final String PROFILE_RESOURCE = "profile:";
protected Subject subject;
protected CallbackHandler callbackHandler;
protected Auth auth;
@@ -88,27 +86,17 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
protected KeycloakDeployment resolveDeployment(String keycloakConfigFile) {
try {
InputStream is = null;
if (keycloakConfigFile.startsWith(PROFILE_RESOURCE)) {
try {
is = new URL(keycloakConfigFile).openStream();
} catch (MalformedURLException mfue) {
throw new RuntimeException(mfue);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
} else {
is = FindFile.findFile(keycloakConfigFile);
}
InputStream is = FindFile.findFile(keycloakConfigFile);
KeycloakDeployment kd = KeycloakDeploymentBuilder.build(is);
if (kd.getRealmKey() == null) {
new AdapterDeploymentContext().resolveRealmKey(kd);
}
return kd;
} catch (RuntimeException e) {
getLogger().debug("Unable to find or parse file " + keycloakConfigFile + " due to " + e.getMessage(), e);
throw e;
}
}
@Override
public boolean login() throws LoginException {
@@ -202,7 +190,7 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
protected Auth bearerAuth(String tokenString) throws VerificationException {
AccessToken token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
AccessToken token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());
boolean verifyCaller;
if (deployment.isUseResourceRoleMappings()) {
@@ -17,6 +17,19 @@
package org.keycloak.adapters.jaas;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
@@ -26,25 +39,12 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
import org.keycloak.constants.ServiceUrlConstants;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
import org.keycloak.util.JsonSerialization;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.keycloak.common.util.KeycloakUriBuilder;
/**
* Login module based on Resource Owner password credentials grant from OAuth2 specs. It's supposed to be used in environments. which
@@ -102,9 +102,9 @@ public class DirectAccessGrantsLoginModule extends AbstractKeycloakLoginModule {
StringBuilder errorBuilder = new StringBuilder("Login failed. Invalid status: " + status);
if (entity != null) {
InputStream is = entity.getContent();
OAuth2ErrorRepresentation errorRep = JsonSerialization.readValue(is, OAuth2ErrorRepresentation.class);
errorBuilder.append(", OAuth2 error. Error: " + errorRep.getError())
.append(", Error description: " + errorRep.getErrorDescription());
Map<String, String> errors = (Map<String, String>) JsonSerialization.readValue(is, Map.class);
errorBuilder.append(", OAuth2 error. Error: " + errors.get(OAuth2Constants.ERROR))
.append(", Error description: " + errors.get(OAuth2Constants.ERROR_DESCRIPTION));
}
String error = errorBuilder.toString();
log.warn(error);
@@ -161,9 +161,9 @@ public class DirectAccessGrantsLoginModule extends AbstractKeycloakLoginModule {
if (entity != null) {
InputStream is = entity.getContent();
if (status == 400) {
OAuth2ErrorRepresentation errorRep = JsonSerialization.readValue(is, OAuth2ErrorRepresentation.class);
errorBuilder.append(", OAuth2 error. Error: " + errorRep.getError())
.append(", Error description: " + errorRep.getErrorDescription());
Map<String, String> errors = (Map<String, String>) JsonSerialization.readValue(is, Map.class);
errorBuilder.append(", OAuth2 error. Error: " + errors.get(OAuth2Constants.ERROR))
.append(", Error description: " + errors.get(OAuth2Constants.ERROR_DESCRIPTION));
} else {
if (is != null) is.close();
File mode changed.
@@ -1,58 +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.rotation;
import org.jboss.logging.Logger;
import org.keycloak.RSATokenVerifier;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.common.VerificationException;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.representations.AccessToken;
import java.security.PublicKey;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class AdapterRSATokenVerifier {
private static final Logger log = Logger.getLogger(AdapterRSATokenVerifier.class);
public static AccessToken verifyToken(String tokenString, KeycloakDeployment deployment) throws VerificationException {
return verifyToken(tokenString, deployment, true, true);
}
public static PublicKey getPublicKey(String kid, KeycloakDeployment deployment) throws VerificationException {
PublicKeyLocator pkLocator = deployment.getPublicKeyLocator();
PublicKey publicKey = pkLocator.getPublicKey(kid, deployment);
if (publicKey == null) {
log.errorf("Didn't find publicKey for kid: %s", kid);
throw new VerificationException("Didn't find publicKey for specified kid");
}
return publicKey;
}
public static AccessToken verifyToken(String tokenString, KeycloakDeployment deployment, boolean checkActive, boolean checkTokenType) throws VerificationException {
RSATokenVerifier verifier = RSATokenVerifier.create(tokenString).realmUrl(deployment.getRealmInfoUrl()).checkActive(checkActive).checkTokenType(checkTokenType);
PublicKey publicKey = getPublicKey(verifier.getHeader().getKeyId(), deployment);
return verifier.publicKey(publicKey).verify().getToken();
}
}
@@ -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.rotation;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.jose.jws.JWSInput;
import java.security.PublicKey;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class HardcodedPublicKeyLocator implements PublicKeyLocator {
private PublicKey publicKey;
public HardcodedPublicKeyLocator(PublicKey publicKey) {
this.publicKey = publicKey;
}
@Override
public PublicKey getPublicKey(String kid, KeycloakDeployment deployment) {
return publicKey;
}
@Override
public void reset(KeycloakDeployment deployment) {
}
}
@@ -1,115 +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.rotation;
import org.apache.http.client.methods.HttpGet;
import org.jboss.logging.Logger;
import org.keycloak.adapters.HttpAdapterUtils;
import org.keycloak.adapters.HttpClientAdapterException;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.common.util.Time;
import org.keycloak.jose.jwk.JSONWebKeySet;
import org.keycloak.jose.jwk.JWK;
import org.keycloak.util.JWKSUtils;
import java.security.PublicKey;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* When needed, publicKeys are downloaded by sending request to realm's jwks_url
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class JWKPublicKeyLocator implements PublicKeyLocator {
private static final Logger log = Logger.getLogger(JWKPublicKeyLocator.class);
private Map<String, PublicKey> currentKeys = new ConcurrentHashMap<>();
private volatile int lastRequestTime = 0;
@Override
public PublicKey getPublicKey(String kid, KeycloakDeployment deployment) {
int minTimeBetweenRequests = deployment.getMinTimeBetweenJwksRequests();
int publicKeyCacheTtl = deployment.getPublicKeyCacheTtl();
int currentTime = Time.currentTime();
// Check if key is in cache.
PublicKey publicKey = lookupCachedKey(publicKeyCacheTtl, currentTime, kid);
if (publicKey != null) {
return publicKey;
}
// Check if we are allowed to send request
synchronized (this) {
currentTime = Time.currentTime();
if (currentTime > lastRequestTime + minTimeBetweenRequests) {
sendRequest(deployment);
lastRequestTime = currentTime;
} else {
log.debug("Won't send request to realm jwks url. Last request time was " + lastRequestTime);
}
return lookupCachedKey(publicKeyCacheTtl, currentTime, kid);
}
}
@Override
public void reset(KeycloakDeployment deployment) {
synchronized (this) {
sendRequest(deployment);
lastRequestTime = Time.currentTime();
}
}
private PublicKey lookupCachedKey(int publicKeyCacheTtl, int currentTime, String kid) {
if (lastRequestTime + publicKeyCacheTtl > currentTime && kid != null) {
return currentKeys.get(kid);
} else {
return null;
}
}
private void sendRequest(KeycloakDeployment deployment) {
if (log.isTraceEnabled()) {
log.trace("Going to send request to retrieve new set of realm public keys for client " + deployment.getResourceName());
}
HttpGet getMethod = new HttpGet(deployment.getJwksUrl());
try {
JSONWebKeySet jwks = HttpAdapterUtils.sendJsonHttpRequest(deployment, getMethod, JSONWebKeySet.class);
Map<String, PublicKey> publicKeys = JWKSUtils.getKeysForUse(jwks, JWK.Use.SIG);
if (log.isDebugEnabled()) {
log.debug("Realm public keys successfully retrieved for client " + deployment.getResourceName() + ". New kids: " + publicKeys.keySet().toString());
}
// Update current keys
currentKeys.clear();
currentKeys.putAll(publicKeys);
} catch (HttpClientAdapterException e) {
log.error("Error when sending request to retrieve realm keys", e);
}
}
}
@@ -1,44 +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.rotation;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.jose.jws.JWSInput;
import java.security.PublicKey;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface PublicKeyLocator {
/**
* @param kid
* @param deployment
* @return publicKey, which should be used for verify signature on given "input"
*/
PublicKey getPublicKey(String kid, KeycloakDeployment deployment);
/**
* Reset the state of locator (eg. clear the cached keys)
*
* @param deployment
*/
void reset(KeycloakDeployment deployment);
}
@@ -16,5 +16,4 @@
#
org.keycloak.adapters.authentication.ClientIdAndSecretCredentialsProvider
org.keycloak.adapters.authentication.JWTClientCredentialsProvider
org.keycloak.adapters.authentication.JWTClientSecretCredentialsProvider
org.keycloak.adapters.authentication.JWTClientCredentialsProvider
@@ -21,22 +21,16 @@ 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;
import org.keycloak.common.enums.SslRequired;
import org.keycloak.common.util.PemUtils;
import org.keycloak.enums.TokenStore;
import org.keycloak.common.util.PemUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
* @author <a href="mailto:brad.culley@spartasystems.com">Brad Culley</a>
* @author <a href="mailto:john.ament@spartasystems.com">John D. Ament</a>
*/
public class KeycloakDeploymentBuilderTest {
@@ -45,11 +39,7 @@ public class KeycloakDeploymentBuilderTest {
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getClass().getResourceAsStream("/keycloak.json"));
assertEquals("demo", deployment.getRealm());
assertEquals("customer-portal", deployment.getResourceName());
assertTrue(deployment.getPublicKeyLocator() instanceof HardcodedPublicKeyLocator);
assertEquals(PemUtils.decodePublicKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"),
deployment.getPublicKeyLocator().getPublicKey(null, deployment));
assertEquals(PemUtils.decodePublicKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"), deployment.getRealmKey());
assertEquals("https://localhost:8443/auth/realms/demo/protocol/openid-connect/auth", deployment.getAuthUrl().build().toString());
assertEquals(SslRequired.EXTERNAL, deployment.getSslRequired());
assertTrue(deployment.isUseResourceRoleMappings());
@@ -57,12 +47,10 @@ public class KeycloakDeploymentBuilderTest {
assertEquals(1000, deployment.getCorsMaxAge());
assertEquals("POST, PUT, DELETE, GET", deployment.getCorsAllowedMethods());
assertEquals("X-Custom, X-Custom2", deployment.getCorsAllowedHeaders());
assertEquals("X-Custom3, X-Custom4", deployment.getCorsExposedHeaders());
assertTrue(deployment.isBearerOnly());
assertTrue(deployment.isPublicClient());
assertTrue(deployment.isEnableBasicAuth());
assertTrue(deployment.isExposeToken());
assertFalse(deployment.isOAuthQueryParameterEnabled());
assertEquals("234234-234234-234234", deployment.getResourceCredentials().get("secret"));
assertEquals(ClientIdAndSecretCredentialsProvider.PROVIDER_ID, deployment.getClientAuthenticator().getId());
assertEquals(20, ((ThreadSafeClientConnManager) deployment.getClient().getConnectionManager()).getMaxTotal());
@@ -73,20 +61,12 @@ public class KeycloakDeploymentBuilderTest {
assertEquals(1000, deployment.getRegisterNodePeriod());
assertEquals(TokenStore.COOKIE, deployment.getTokenStore());
assertEquals("email", deployment.getPrincipalAttribute());
assertEquals(10, deployment.getTokenMinimumTimeToLive());
assertEquals(20, deployment.getMinTimeBetweenJwksRequests());
assertEquals(120, deployment.getPublicKeyCacheTtl());
assertEquals("/api/$1", deployment.getRedirectRewriteRules().get("^/wsmaster/api/(.*)$"));
}
@Test
public void loadNoClientCredentials() throws Exception {
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getClass().getResourceAsStream("/keycloak-no-credentials.json"));
assertEquals(ClientIdAndSecretCredentialsProvider.PROVIDER_ID, deployment.getClientAuthenticator().getId());
assertTrue(deployment.getPublicKeyLocator() instanceof JWKPublicKeyLocator);
assertEquals(10, deployment.getMinTimeBetweenJwksRequests());
assertEquals(86400, deployment.getPublicKeyCacheTtl());
}
@Test
@@ -95,9 +75,4 @@ 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());
}
}
@@ -1,68 +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.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;
/**
* @author <a href="mailto:brad.culley@spartasystems.com">Brad Culley</a>
* @author <a href="mailto:john.ament@spartasystems.com">John D. Ament</a>
*/
public class KeycloakDeploymentTest {
@Test
public void shouldNotEnableOAuthQueryParamWhenIgnoreIsTrue() {
KeycloakDeployment keycloakDeployment = new KeycloakDeployment();
keycloakDeployment.setIgnoreOAuthQueryParameter(true);
assertFalse(keycloakDeployment.isOAuthQueryParameterEnabled());
}
@Test
public void shouldEnableOAuthQueryParamWhenIgnoreIsFalse() {
KeycloakDeployment keycloakDeployment = new KeycloakDeployment();
keycloakDeployment.setIgnoreOAuthQueryParameter(false);
assertTrue(keycloakDeployment.isOAuthQueryParameterEnabled());
}
@Test
public void shouldEnableOAuthQueryParamWhenIgnoreNotSet() {
KeycloakDeployment keycloakDeployment = new KeycloakDeployment();
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());
}
}
@@ -1,23 +0,0 @@
package org.keycloak.adapters;
import org.junit.Test;
import org.keycloak.representations.oidc.TokenMetadataRepresentation;
import static org.junit.Assert.assertFalse;
/**
* @author github.com/tubbynl
*
*/
public class RefreshableKeycloakSecurityContextTest {
@Test
public void isActive() {
TokenMetadataRepresentation token = new TokenMetadataRepresentation();
token.setActive(true);
token.issuedNow();
RefreshableKeycloakSecurityContext sut = new RefreshableKeycloakSecurityContext(null,null,null,token,null, null, null);
// verify false if null deployment (KEYCLOAK-3050; yielded a npe)
assertFalse(sut.isActive());
}
}
@@ -1,6 +1,7 @@
{
"realm": "demo",
"resource": "customer-portal",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "https://localhost:8443/auth",
"public-client": true,
"expose-token": true
@@ -1,12 +0,0 @@
{
"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"
}
}
}
@@ -9,7 +9,6 @@
"cors-max-age": 1000,
"cors-allowed-methods": "POST, PUT, DELETE, GET",
"cors-allowed-headers": "X-Custom, X-Custom2",
"cors-exposed-headers": "X-Custom3, X-Custom4",
"bearer-only": true,
"public-client": true,
"enable-basic-auth": true,
@@ -29,12 +28,5 @@
"register-node-at-startup": true,
"register-node-period": 1000,
"token-store": "cookie",
"principal-attribute": "email",
"token-minimum-time-to-live": 10,
"min-time-between-jwks-requests": 20,
"public-key-cache-ttl": 120,
"ignore-oauth-query-parameter": true,
"redirect-rewrite-rules" : {
"^/wsmaster/api/(.*)$" : "/api/$1"
}
"principal-attribute": "email"
}
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-as7-integration-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version>
<version>1.9.2.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>4.0.0.Beta1</version>
<version>1.9.2.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -1,13 +0,0 @@
package org.keycloak.adapters.jbossweb;
import org.apache.catalina.Container;
import org.apache.catalina.Valve;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.tomcat.AbstractAuthenticatedActionsValve;
public class AuthenticatedActionsValve extends AbstractAuthenticatedActionsValve {
public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
super(deploymentContext, next, container);
}
}
@@ -17,20 +17,20 @@
package org.keycloak.adapters.jbossweb;
import org.apache.catalina.Container;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Valve;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.deploy.LoginConfig;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.tomcat.AbstractAuthenticatedActionsValve;
import org.apache.catalina.realm.GenericPrincipal;
import org.keycloak.adapters.tomcat.AbstractKeycloakAuthenticatorValve;
import org.keycloak.adapters.tomcat.GenericPrincipalFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.Principal;
import java.util.List;
/**
* Keycloak authentication valve
@@ -60,6 +60,7 @@ public class KeycloakAuthenticatorValve extends AbstractKeycloakAuthenticatorVal
super.start();
}
public void logout(Request request) {
logoutInternal(request);
}
@@ -68,9 +69,4 @@ public class KeycloakAuthenticatorValve extends AbstractKeycloakAuthenticatorVal
protected GenericPrincipalFactory createPrincipalFactory() {
return new JBossWebPrincipalFactory();
}
@Override
protected AbstractAuthenticatedActionsValve createAuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
return new AuthenticatedActionsValve(deploymentContext, next, container);
}
}
+3 -1
View File
@@ -21,7 +21,7 @@
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-integration-pom</artifactId>
<version>4.0.0.Beta1</version>
<version>1.9.2.Final</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -88,6 +88,7 @@
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.1.0.GA</version>
</dependency>
<dependency>
@@ -103,6 +104,7 @@ projects that depend on this project.-->
<dependency>
<groupId>org.jboss.msc</groupId>
<artifactId>jboss-msc</artifactId>
<version>1.0.2.GA</version>
</dependency>
<dependency>
@@ -49,6 +49,7 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
@Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
String deploymentName = deploymentUnit.getName();
// if it's not a web-app there's nothing to secure
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
@@ -66,25 +67,24 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
// otherwise
LoginConfigMetaData loginConfig = webMetaData.getLoginConfig();
boolean hasSubsystemConfig = service.isSecureDeployment(deploymentUnit);
boolean hasSubsystemConfig = service.isSecureDeployment(deploymentName);
boolean webRequiresKC = loginConfig != null && "KEYCLOAK".equalsIgnoreCase(loginConfig.getAuthMethod());
boolean isConfigured = service.isDeploymentConfigured(deploymentUnit);
if ((hasSubsystemConfig && isConfigured) || webRequiresKC) {
log.debug("Setting up KEYCLOAK auth method for WAR: " + deploymentUnit.getName());
if (hasSubsystemConfig || webRequiresKC) {
log.debug("Setting up KEYCLOAK auth method for WAR: " + deploymentName);
// if secure-deployment configuration exists for web app, we force KEYCLOAK auth method on it
if (hasSubsystemConfig) {
addJSONData(service.getJSON(deploymentUnit), warMetaData);
addJSONData(service.getJSON(deploymentName), warMetaData);
if (loginConfig != null) {
loginConfig.setAuthMethod("KEYCLOAK");
loginConfig.setRealmName(service.getRealmName(deploymentUnit));
loginConfig.setRealmName(service.getRealmName(deploymentName));
} else {
log.warn("Failed to set up KEYCLOAK auth method for WAR: " + deploymentUnit.getName() + " (loginConfig == null)");
log.warn("Failed to set up KEYCLOAK auth method for WAR: " + deploymentName + " (loginConfig == null)");
}
}
addValve(webMetaData);
KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentUnit.getName());
KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentName);
}
}
@@ -17,12 +17,9 @@
package org.keycloak.subsystem.as7;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.web.deployment.WarMetaData;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
import org.jboss.logging.Logger;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
import java.util.HashMap;
import java.util.Map;
@@ -157,23 +154,14 @@ public final class KeycloakAdapterConfigService {
return null;
}
public String getRealmName(DeploymentUnit deploymentUnit) {
ModelNode deployment = getSecureDeployment(deploymentUnit);
public String getRealmName(String deploymentName) {
ModelNode deployment = this.secureDeployments.get(deploymentName);
return deployment.get(RealmDefinition.TAG_NAME).asString();
}
protected boolean isDeploymentConfigured(DeploymentUnit deploymentUnit) {
ModelNode deployment = getSecureDeployment(deploymentUnit);
if (! deployment.isDefined()) {
return false;
}
ModelNode resource = deployment.get(SecureDeploymentDefinition.RESOURCE.getName());
return resource.isDefined();
}
public String getJSON(DeploymentUnit deploymentUnit) {
ModelNode deployment = getSecureDeployment(deploymentUnit);
public String getJSON(String deploymentName) {
ModelNode deployment = this.secureDeployments.get(deploymentName);
String realmName = deployment.get(RealmDefinition.TAG_NAME).asString();
ModelNode realm = this.realms.get(realmName);
@@ -196,36 +184,9 @@ public final class KeycloakAdapterConfigService {
}
}
public boolean isSecureDeployment(DeploymentUnit deploymentUnit) {
public boolean isSecureDeployment(String deploymentName) {
//log.info("********* CHECK KEYCLOAK DEPLOYMENT: deployments.size()" + deployments.size());
String deploymentName = preferredDeploymentName(deploymentUnit);
return this.secureDeployments.containsKey(deploymentName);
}
private ModelNode getSecureDeployment(DeploymentUnit deploymentUnit) {
String deploymentName = preferredDeploymentName(deploymentUnit);
return this.secureDeployments.containsKey(deploymentName)
? this.secureDeployments.get(deploymentName)
: new ModelNode();
}
// KEYCLOAK-3273: prefer module name if available
private String preferredDeploymentName(DeploymentUnit deploymentUnit) {
String deploymentName = deploymentUnit.getName();
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) {
return deploymentName;
}
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) {
return deploymentName;
}
String moduleName = webMetaData.getModuleName();
if (moduleName != null) return moduleName + ".war";
return deploymentName;
}
}
@@ -46,7 +46,8 @@ public abstract class KeycloakDependencyProcessor implements DeploymentUnitProce
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
if (!KeycloakAdapterConfigService.getInstance().isSecureDeployment(deploymentUnit)) {
String deploymentName = deploymentUnit.getName();
if (!KeycloakAdapterConfigService.getInstance().isSecureDeployment(deploymentName)) {
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) {
return;
@@ -25,6 +25,7 @@ import org.jboss.as.server.AbstractDeploymentChainStep;
import org.jboss.as.server.DeploymentProcessorTarget;
import org.jboss.as.server.deployment.Phase;
import org.jboss.dmr.ModelNode;
import org.jboss.msc.service.ServiceController;
import java.util.List;
@@ -86,7 +86,7 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
if (!SharedAttributeDefinitons.validateTruststoreSetIfRequired(addRealm)) {
//TODO: externalize the message
throw new XMLStreamException("truststore and truststore-password must be set if ssl-required is not none and disable-trust-manager is false.");
throw new XMLStreamException("truststore and truststore-password must be set if ssl-required is not none and disable-trust-maanger is false.");
}
list.add(addRealm);
@@ -117,7 +117,7 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
* TODO need to check realm-ref first.
if (!SharedAttributeDefinitons.validateTruststoreSetIfRequired(addSecureDeployment)) {
//TODO: externalize the message
throw new XMLStreamException("truststore and truststore-password must be set if ssl-required is not none and disable-trust-manager is false.");
throw new XMLStreamException("truststore and truststore-password must be set if ssl-required is not none and disable-trust-maanger is false.");
}
*/
@@ -157,7 +157,7 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
credentialsToAdd.add(addCredential);
} else {
for (Map.Entry<String, String> entry : values.entrySet()) {
ModelNode addCredential = getCredentialToAdd(parent, name + "." + entry.getKey(), entry.getValue());
ModelNode addCredential = getCredentialToAdd(parent, name + "" + entry.getKey(), entry.getValue());
credentialsToAdd.add(addCredential);
}
}
@@ -46,7 +46,7 @@ public final class RealmAddHandler extends AbstractAddStepHandler {
if (!SharedAttributeDefinitons.validateTruststoreSetIfRequired(model.clone())) {
//TODO: externalize message
throw new OperationFailedException("truststore and truststore-password must be set if ssl-required is not none and disable-trust-manager is false.");
throw new OperationFailedException("truststore and truststore-password must be set if ssl-required is not none and disable-trust-maanger is false.");
}
}
@@ -23,6 +23,8 @@ import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.dmr.ModelNode;
import java.util.List;
/**
* Update an attribute on a realm.
*
@@ -23,7 +23,6 @@ import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.SimpleResourceDefinition;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
import org.jboss.as.controller.operations.validation.IntRangeValidator;
import org.jboss.as.controller.operations.validation.StringLengthValidator;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.as.controller.registry.OperationEntry;
@@ -80,24 +79,6 @@ class SecureDeploymentDefinition extends SimpleResourceDefinition {
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition TURN_OFF_CHANGE_SESSION =
new SimpleAttributeDefinitionBuilder("turn-off-change-session-id-on-login", ModelType.BOOLEAN, true)
.setXmlName("turn-off-change-session-id-on-login")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition TOKEN_MINIMUM_TIME_TO_LIVE =
new SimpleAttributeDefinitionBuilder("token-minimum-time-to-live", ModelType.INT, true)
.setXmlName("token-minimum-time-to-live")
.setValidator(new IntRangeValidator(-1, true))
.setAllowExpression(true)
.build();
protected static final SimpleAttributeDefinition MIN_TIME_BETWEEN_JWKS_REQUESTS =
new SimpleAttributeDefinitionBuilder("min-time-between-jwks-requests", ModelType.INT, true)
.setXmlName("min-time-between-jwks-requests")
.setValidator(new IntRangeValidator(-1, true))
.setAllowExpression(true)
.build();
protected static final List<SimpleAttributeDefinition> DEPLOYMENT_ONLY_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
static {
@@ -107,9 +88,6 @@ class SecureDeploymentDefinition extends SimpleResourceDefinition {
DEPLOYMENT_ONLY_ATTRIBUTES.add(BEARER_ONLY);
DEPLOYMENT_ONLY_ATTRIBUTES.add(ENABLE_BASIC_AUTH);
DEPLOYMENT_ONLY_ATTRIBUTES.add(PUBLIC_CLIENT);
DEPLOYMENT_ONLY_ATTRIBUTES.add(TURN_OFF_CHANGE_SESSION);
DEPLOYMENT_ONLY_ATTRIBUTES.add(TOKEN_MINIMUM_TIME_TO_LIVE);
DEPLOYMENT_ONLY_ATTRIBUTES.add(MIN_TIME_BETWEEN_JWKS_REQUESTS);
}
protected static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
@@ -80,8 +80,6 @@ keycloak.secure-deployment.register-node-period=how often to re-register node
keycloak.secure-deployment.token-store=cookie or session storage for auth session data
keycloak.secure-deployment.principal-attribute=token attribute to use to set Principal name
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.credential=Credential value
@@ -105,8 +105,6 @@
<xs:element name="principal-attribute" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="enable-basic-auth" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
<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:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
+1 -13
View File
@@ -20,7 +20,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version>
<version>1.9.2.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak AS7 / JBoss EAP 6 Integration</name>
@@ -30,18 +30,6 @@
<artifactId>keycloak-as7-integration-pom</artifactId>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-parent</artifactId>
<version>${jboss.as.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>as7-adapter-spi</module>
<module>as7-adapter</module>
-9
View File
@@ -1,9 +0,0 @@
CLI Single Sign On
===================================
This java-based utility is meant for providing Keycloak integration to
command line applications that are either written in Java or another language. The
idea is that the Java app provided by this utility performs a login for a specific
client, parses responses, and exports an access token as an environment variable
that can be used by the command line utility you are accessing.
-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>4.0.0.Beta1</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);
}
}
+1 -10
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version>
<version>1.9.2.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -63,15 +63,6 @@
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
<artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
</dependency>
</dependencies>
<build>
@@ -1,281 +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("login-cli")) {
loginCli();
}
*/
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(" login-cli - attempt Keycloak proprietary cli protocol. Otherwise do normal login");
System.err.println(" token - print access token if logged in");
System.err.println(" logout - logout.");
System.exit(1);
}
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(boolean outputToken) 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, outputToken);
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(true)) return;
AdapterConfig config = getConfig();
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
installed.login();
processResponse(installed, true);
}
public void loginCli() throws Exception {
if (checkToken(false)) return;
AdapterConfig config = getConfig();
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
if (!installed.loginCommandLine()) installed.login();
processResponse(installed, false);
}
public String getHome() {
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, boolean outputToken) 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();
if (outputToken) System.out.println(tokenResponse.getToken());
}
public void loginManual() throws Exception {
if (checkToken(true)) return;
AdapterConfig config = getConfig();
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config);
KeycloakInstalled installed = new KeycloakInstalled(deployment);
installed.loginManual();
processResponse(installed, true);
}
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,27 +17,19 @@
package org.keycloak.adapters.installed;
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.RSATokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.ServerRequest;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
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;
@@ -46,28 +38,19 @@ import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.PushbackInputStream;
import java.io.Reader;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URI;
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>
*/
public class KeycloakInstalled {
public interface HttpResponseWriter {
void success(PrintWriter pw, KeycloakInstalled ki);
void failure(PrintWriter pw, KeycloakInstalled ki);
}
private static final String KEYCLOAK_JSON = "META-INF/keycloak.json";
private KeycloakDeployment deployment;
@@ -76,21 +59,12 @@ public class KeycloakInstalled {
LOGGED_MANUAL, LOGGED_DESKTOP
}
private AccessTokenResponse tokenResponse;
private String tokenString;
private String idTokenString;
private IDToken idToken;
private AccessToken token;
private String refreshToken;
private Status status;
private Locale locale;
private HttpResponseWriter loginResponseWriter;
private HttpResponseWriter logoutResponseWriter;
Pattern callbackPattern = Pattern.compile("callback\\s*=\\s*\"([^\"]+)\"");
Pattern paramPattern = Pattern.compile("param=\"([^\"]+)\"\\s+label=\"([^\"]+)\"\\s+mask=(\\S+)");
Pattern codePattern = Pattern.compile("code=([^&]+)");
public KeycloakInstalled() {
InputStream config = Thread.currentThread().getContextClassLoader().getResourceAsStream(KEYCLOAK_JSON);
@@ -101,92 +75,6 @@ public class KeycloakInstalled {
deployment = KeycloakDeploymentBuilder.build(config);
}
public KeycloakInstalled(KeycloakDeployment deployment) {
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;
}
}
public HttpResponseWriter getLogoutResponseWriter() {
if (logoutResponseWriter == null) {
return defaultLogoutWriter;
} else {
return logoutResponseWriter;
}
}
public void setLoginResponseWriter(HttpResponseWriter loginResponseWriter) {
this.loginResponseWriter = loginResponseWriter;
}
public void setLogoutResponseWriter(HttpResponseWriter logoutResponseWriter) {
this.logoutResponseWriter = logoutResponseWriter;
}
public Locale getLocale() {
return locale;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
public void login() throws IOException, ServerRequest.HttpFailure, VerificationException, InterruptedException, OAuthErrorException, URISyntaxException {
if (isDesktopSupported()) {
loginDesktop();
@@ -220,22 +108,18 @@ public class KeycloakInstalled {
}
public void loginDesktop() throws IOException, VerificationException, OAuthErrorException, URISyntaxException, ServerRequest.HttpFailure, InterruptedException {
CallbackListener callback = new CallbackListener(getLoginResponseWriter());
CallbackListener callback = new CallbackListener();
callback.start();
String redirectUri = "http://localhost:" + callback.server.getLocalPort();
String state = UUID.randomUUID().toString();
KeycloakUriBuilder builder = deployment.getAuthUrl().clone()
String authUrl = deployment.getAuthUrl().clone()
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
.queryParam(OAuth2Constants.STATE, state)
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID);
if (locale != null) {
builder.queryParam(OAuth2Constants.UI_LOCALES_PARAM, locale.getLanguage());
}
String authUrl = builder.build().toString();
.build().toString();
Desktop.getDesktop().browse(new URI(authUrl));
@@ -259,7 +143,7 @@ public class KeycloakInstalled {
}
private void logoutDesktop() throws IOException, URISyntaxException, InterruptedException {
CallbackListener callback = new CallbackListener(getLogoutResponseWriter());
CallbackListener callback = new CallbackListener();
callback.start();
String redirectUri = "http://localhost:" + callback.server.getLocalPort();
@@ -282,13 +166,15 @@ public class KeycloakInstalled {
}
public void loginManual(PrintStream printer, Reader reader) throws IOException, ServerRequest.HttpFailure, VerificationException {
CallbackListener callback = new CallbackListener();
callback.start();
String redirectUri = "urn:ietf:wg:oauth:2.0:oob";
String authUrl = deployment.getAuthUrl().clone()
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
.build().toString();
printer.println("Open the following URL in a browser. After login copy/paste the code back and press <enter>");
@@ -302,86 +188,6 @@ public class KeycloakInstalled {
status = Status.LOGGED_MANUAL;
}
public boolean loginCommandLine() throws IOException, ServerRequest.HttpFailure, VerificationException {
String redirectUri = "urn:ietf:wg:oauth:2.0:oob";
return loginCommandLine(redirectUri);
}
/**
* Experimental proprietary WWW-Authentication challenge protocol.
* WWW-Authentication: X-Text-Form-Challenge callback="{url}" param="{param-name}" label="{param-display-label}"
*
* @param redirectUri
* @return
* @throws IOException
* @throws ServerRequest.HttpFailure
* @throws VerificationException
*/
public boolean loginCommandLine(String redirectUri) throws IOException, ServerRequest.HttpFailure, VerificationException {
String authUrl = deployment.getAuthUrl().clone()
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
.build().toString();
ResteasyClient client = new ResteasyClientBuilder().disableTrustManager().build();
try {
Response response = client.target(authUrl).request().get();
if (response.getStatus() != 401) {
return false;
}
while (true) {
String authenticationHeader = response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE);
if (authenticationHeader == null) {
return false;
}
if (!authenticationHeader.contains("X-Text-Form-Challenge")) {
return false;
}
if (response.getMediaType() != null) {
String splash = response.readEntity(String.class);
System.console().writer().println(splash);
}
Matcher m = callbackPattern.matcher(authenticationHeader);
if (!m.find()) return false;
String callback = m.group(1);
//System.err.println("callback: " + callback);
m = paramPattern.matcher(authenticationHeader);
Form form = new Form();
while (m.find()) {
String param = m.group(1);
String label = m.group(2);
String mask = m.group(3).trim();
boolean maskInput = mask.equals("true");
String value = null;
if (maskInput) {
char[] txt = System.console().readPassword(label);
value = new String(txt);
} else {
value = System.console().readLine(label);
}
form.param(param, value);
}
response = client.target(callback).request().post(Entity.form(form));
if (response.getStatus() == 401) continue;
if (response.getStatus() != 302) return false;
String location = response.getLocation().toString();
m = codePattern.matcher(location);
if (!m.find()) return false;
String code = m.group(1);
processCode(code, redirectUri);
return true;
}
} finally {
client.close();
}
}
public String getTokenString() throws VerificationException, IOException, ServerRequest.HttpFailure {
return tokenString;
}
@@ -400,19 +206,12 @@ public class KeycloakInstalled {
parseAccessToken(tokenResponse);
}
public void refreshToken(String refreshToken) throws IOException, ServerRequest.HttpFailure, VerificationException {
AccessTokenResponse tokenResponse = ServerRequest.invokeRefresh(deployment, refreshToken);
parseAccessToken(tokenResponse);
}
private void parseAccessToken(AccessTokenResponse tokenResponse) throws VerificationException {
this.tokenResponse = tokenResponse;
tokenString = tokenResponse.getToken();
refreshToken = tokenResponse.getRefreshToken();
idTokenString = tokenResponse.getIdToken();
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());
if (idTokenString != null) {
try {
JWSInput input = new JWSInput(idTokenString);
@@ -439,10 +238,6 @@ public class KeycloakInstalled {
return refreshToken;
}
public AccessTokenResponse getTokenResponse() {
return tokenResponse;
}
public boolean isDesktopSupported() {
return Desktop.isDesktopSupported();
}
@@ -451,8 +246,6 @@ public class KeycloakInstalled {
return deployment;
}
private void processCode(String code, String redirectUri) throws IOException, ServerRequest.HttpFailure, VerificationException {
AccessTokenResponse tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, redirectUri, null);
parseAccessToken(tokenResponse);
@@ -474,87 +267,6 @@ public class KeycloakInstalled {
return sb.toString();
}
public static class MaskingThread extends Thread {
private volatile boolean stop;
private char echochar = '*';
public MaskingThread() {
}
/**
* Begin masking until asked to stop.
*/
public void run() {
int priority = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
try {
stop = true;
while(stop) {
System.out.print("\010" + echochar);
try {
// attempt masking at this rate
Thread.currentThread().sleep(1);
}catch (InterruptedException iex) {
Thread.currentThread().interrupt();
return;
}
}
} finally { // restore the original priority
Thread.currentThread().setPriority(priority);
}
}
/**
* Instruct the thread to stop masking.
*/
public void stopMasking() {
this.stop = false;
}
}
public static String readMasked(Reader reader) {
MaskingThread et = new MaskingThread();
Thread mask = new Thread(et);
mask.start();
BufferedReader in = new BufferedReader(reader);
String password = "";
try {
password = in.readLine();
} catch (IOException ioe) {
ioe.printStackTrace();
}
// stop masking
et.stopMasking();
// return the password entered by the user
return password;
}
private String readLine(Reader reader, boolean mask) throws IOException {
if (mask) {
System.out.print(" ");
return readMasked(reader);
}
StringBuilder sb = new StringBuilder();
char cb[] = new char[1];
while (reader.read(cb) != -1) {
char c = cb[0];
if ((c == '\n') || (c == '\r')) {
break;
} else {
sb.append(c);
}
}
return sb.toString();
}
public class CallbackListener extends Thread {
private ServerSocket server;
@@ -569,19 +281,14 @@ public class KeycloakInstalled {
private String state;
private Socket socket;
private HttpResponseWriter writer;
public CallbackListener(HttpResponseWriter writer) throws IOException {
this.writer = writer;
public CallbackListener() throws IOException {
server = new ServerSocket(0);
}
@Override
public void run() {
try {
socket = server.accept();
Socket socket = server.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String request = br.readLine();
@@ -605,15 +312,10 @@ public class KeycloakInstalled {
}
}
OutputStreamWriter out = new OutputStreamWriter(socket.getOutputStream());
PrintWriter pw = new PrintWriter(out);
if (error == null) {
writer.success(pw, KeycloakInstalled.this);
} else {
writer.failure(pw, KeycloakInstalled.this);
}
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
pw.println("Please close window and return to application");
pw.flush();
socket.close();
} catch (IOException e) {
errorException = e;
@@ -624,8 +326,6 @@ public class KeycloakInstalled {
} catch (IOException e) {
}
}
}
}
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version>
<version>1.9.2.Final</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -20,6 +20,8 @@ package org.keycloak.jaxrs;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.AuthenticatedActionsHandler;
import org.keycloak.adapters.BasicAuthRequestAuthenticator;
import org.keycloak.adapters.BearerTokenRequestAuthenticator;
@@ -29,8 +31,6 @@ import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.NodesRegistrationManagement;
import org.keycloak.adapters.PreAuthActionsHandler;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.UserSessionManagement;
import org.keycloak.common.constants.GenericConstants;
@@ -40,6 +40,7 @@ import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -17,20 +17,21 @@
package org.keycloak.jaxrs;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.AuthenticationError;
import org.keycloak.adapters.spi.LogoutError;
import org.keycloak.common.util.HostUtils;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
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.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.AuthenticationError;
import org.keycloak.adapters.spi.LogoutError;
import org.keycloak.common.util.HostUtils;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -66,11 +67,6 @@ public class JaxrsHttpFacade implements OIDCHttpFacade {
return requestContext.getUriInfo().getRequestUri().toString();
}
@Override
public String getRelativePath() {
return requestContext.getUriInfo().getPath();
}
@Override
public boolean isSecure() {
return securityContext.isSecure();
@@ -20,7 +20,6 @@ package org.keycloak.jaxrs;
import org.keycloak.AbstractOAuthClient;
import org.keycloak.OAuth2Constants;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.util.TokenUtil;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.InternalServerErrorException;
@@ -86,13 +85,14 @@ public class JaxrsOAuthClient extends AbstractOAuthClient {
}
public Response redirect(UriInfo uriInfo, String redirectUri) {
String state = getStateCode();
String scopeParam = TokenUtil.attachOIDCScope(scope);
UriBuilder uriBuilder = UriBuilder.fromUri(authUrl)
.queryParam(OAuth2Constants.CLIENT_ID, clientId)
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
.queryParam(OAuth2Constants.STATE, state)
.queryParam(OAuth2Constants.SCOPE, scopeParam);
.queryParam(OAuth2Constants.STATE, state);
if (scope != null) {
uriBuilder.queryParam(OAuth2Constants.SCOPE, scope);
}
URI url = uriBuilder.build();
@@ -17,18 +17,19 @@
package org.keycloak.jaxrs;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.common.constants.GenericConstants;
import org.osgi.framework.BundleContext;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.PreMatching;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.logging.Logger;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.PreMatching;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.common.constants.GenericConstants;
import org.osgi.framework.BundleContext;
/**
* Variant of JaxrsBearerTokenFilter, which can be used to properly use resources from current osgi bundle
*
+2 -2
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version>
<version>1.9.2.Final</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -60,7 +60,7 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jetty-adapter-spi</artifactId>
</dependency>
<dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
@@ -27,26 +27,28 @@ import org.eclipse.jetty.security.authentication.FormAuthenticator;
import org.eclipse.jetty.security.authentication.LoginAuthenticator;
import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.URIUtil;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.jetty.spi.JettyHttpFacade;
import org.keycloak.adapters.jetty.spi.JettyUserSessionManagement;
import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.AuthenticatedActionsHandler;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.NodesRegistrationManagement;
import org.keycloak.adapters.PreAuthActionsHandler;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.jetty.spi.JettyHttpFacade;
import org.keycloak.adapters.jetty.spi.JettyUserSessionManagement;
import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.enums.TokenStore;
import org.keycloak.representations.adapters.config.AdapterConfig;
@@ -59,6 +61,7 @@ import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
@@ -106,8 +109,6 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
public abstract AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment);
public abstract JettyUserSessionManagement createSessionManagement(Request request);
public void logoutCurrent(Request request) {
AdapterDeploymentContext deploymentContext = (AdapterDeploymentContext) request.getAttribute(AdapterDeploymentContext.class.getName());
KeycloakSecurityContext ksc = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
@@ -289,7 +290,7 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
log.debug("*** deployment isn't configured return false");
return Authentication.UNAUTHENTICATED;
}
PreAuthActionsHandler handler = new PreAuthActionsHandler(createSessionManagement(request), deploymentContext, facade);
PreAuthActionsHandler handler = new PreAuthActionsHandler(new JettyUserSessionManagement(request.getSessionManager()), deploymentContext, facade);
if (handler.handleRequest()) {
return Authentication.SEND_SUCCESS;
}
@@ -339,13 +340,13 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
Authentication authentication = request.getAuthentication();
if (!(authentication instanceof KeycloakAuthentication)) {
UserIdentity userIdentity = createIdentity(principal);
authentication = createAuthentication(userIdentity, request);
authentication = createAuthentication(userIdentity);
request.setAuthentication(authentication);
}
return authentication;
}
protected abstract Authentication createAuthentication(UserIdentity userIdentity, Request request);
protected abstract Authentication createAuthentication(UserIdentity userIdentity);
public static abstract class KeycloakAuthentication extends UserAuthentication {
public KeycloakAuthentication(String method, UserIdentity userIdentity) {
@@ -23,11 +23,11 @@ import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.CookieTokenStore;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OidcKeycloakAccount;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.RequestAuthenticator;
import org.keycloak.adapters.spi.HttpFacade;
/**
* Handle storage of token info in cookie. Per-request object.
@@ -23,12 +23,12 @@ import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OAuthRequestAuthenticator;
import org.keycloak.adapters.OidcKeycloakAccount;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.RequestAuthenticator;
import org.keycloak.adapters.spi.HttpFacade;
import javax.servlet.http.HttpSession;
import java.security.Principal;
@@ -21,13 +21,13 @@ import org.eclipse.jetty.server.Request;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.spi.AdapterSessionStore;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OidcKeycloakAccount;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.RequestAuthenticator;
import org.keycloak.adapters.spi.AdapterSessionStore;
import javax.servlet.http.HttpSession;
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version>
<version>1.9.2.Final</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -1,40 +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.jetty;
import org.eclipse.jetty.server.SessionManager;
import org.keycloak.adapters.jetty.spi.JettySessionManager;
import javax.servlet.http.HttpSession;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class Jetty81SessionManager implements JettySessionManager {
protected SessionManager sessionManager;
public Jetty81SessionManager(SessionManager sessionManager) {
this.sessionManager = sessionManager;
}
@Override
public HttpSession getHttpSession(String id) {
return sessionManager.getHttpSession(id);
}
}
@@ -20,8 +20,8 @@ package org.keycloak.adapters.jetty;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.MultiMap;
import org.keycloak.adapters.jetty.spi.JettyHttpFacade;
import org.keycloak.adapters.spi.AdapterSessionStore;
import org.keycloak.adapters.jetty.spi.JettyHttpFacade;
import org.keycloak.common.util.MultivaluedHashMap;
import javax.servlet.http.HttpSession;
@@ -25,7 +25,6 @@ import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.jetty.core.AbstractKeycloakJettyAuthenticator;
import org.keycloak.adapters.jetty.core.JettySessionTokenStore;
import org.keycloak.adapters.jetty.spi.JettyUserSessionManagement;
import javax.servlet.ServletRequest;
@@ -45,18 +44,13 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
return new JettySessionTokenStore(request, resolvedDeployment, new JettyAdapterSessionStore(request));
}
@Override
public JettyUserSessionManagement createSessionManagement(Request request) {
return new JettyUserSessionManagement(new Jetty81SessionManager(request.getSessionManager()));
}
@Override
protected Request resolveRequest(ServletRequest req) {
return (req instanceof Request)?(Request)req: AbstractHttpConnection.getCurrentConnection().getRequest();
}
@Override
protected Authentication createAuthentication(UserIdentity userIdentity, Request request) {
protected Authentication createAuthentication(UserIdentity userIdentity) {
return new KeycloakAuthentication(getAuthMethod(), userIdentity) {
@Override
public void logout() {
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version>
<version>1.9.2.Final</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -1,40 +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.jetty;
import org.eclipse.jetty.server.SessionManager;
import org.keycloak.adapters.jetty.spi.JettySessionManager;
import javax.servlet.http.HttpSession;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class Jetty91SessionManager implements JettySessionManager {
protected SessionManager sessionManager;
public Jetty91SessionManager(SessionManager sessionManager) {
this.sessionManager = sessionManager;
}
@Override
public HttpSession getHttpSession(String id) {
return sessionManager.getHttpSession(id);
}
}
@@ -21,8 +21,8 @@ import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.MultiMap;
import org.keycloak.adapters.jetty.spi.JettyHttpFacade;
import org.keycloak.adapters.spi.AdapterSessionStore;
import org.keycloak.adapters.jetty.spi.JettyHttpFacade;
import org.keycloak.common.util.MultivaluedHashMap;
import javax.servlet.http.HttpSession;
@@ -27,7 +27,6 @@ import org.keycloak.adapters.jetty.core.AbstractKeycloakJettyAuthenticator;
import org.keycloak.adapters.jetty.core.JettyRequestAuthenticator;
import org.keycloak.adapters.jetty.core.JettySessionTokenStore;
import org.keycloak.adapters.jetty.spi.JettyHttpFacade;
import org.keycloak.adapters.jetty.spi.JettyUserSessionManagement;
import javax.servlet.ServletRequest;
@@ -47,18 +46,13 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
return new JettySessionTokenStore(request, resolvedDeployment, new JettyAdapterSessionStore(request));
}
@Override
public JettyUserSessionManagement createSessionManagement(Request request) {
return new JettyUserSessionManagement(new Jetty91SessionManager(request.getSessionManager()));
}
@Override
protected Request resolveRequest(ServletRequest req) {
return (req instanceof Request) ? (Request)req : HttpChannel.getCurrentHttpChannel().getRequest();
}
@Override
protected Authentication createAuthentication(UserIdentity userIdentity, Request request) {
protected Authentication createAuthentication(UserIdentity userIdentity) {
return new KeycloakAuthentication(getAuthMethod(), userIdentity) {
@Override
public void logout() {
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>4.0.0.Beta1</version>
<version>1.9.2.Final</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -1,40 +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.jetty;
import org.eclipse.jetty.server.SessionManager;
import org.keycloak.adapters.jetty.spi.JettySessionManager;
import javax.servlet.http.HttpSession;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class Jetty92SessionManager implements JettySessionManager {
protected SessionManager sessionManager;
public Jetty92SessionManager(SessionManager sessionManager) {
this.sessionManager = sessionManager;
}
@Override
public HttpSession getHttpSession(String id) {
return sessionManager.getHttpSession(id);
}
}
@@ -21,8 +21,8 @@ import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.MultiMap;
import org.keycloak.adapters.jetty.spi.JettyHttpFacade;
import org.keycloak.adapters.spi.AdapterSessionStore;
import org.keycloak.adapters.jetty.spi.JettyHttpFacade;
import org.keycloak.common.util.MultivaluedHashMap;
import javax.servlet.http.HttpSession;
@@ -27,7 +27,6 @@ import org.keycloak.adapters.jetty.core.AbstractKeycloakJettyAuthenticator;
import org.keycloak.adapters.jetty.core.JettyRequestAuthenticator;
import org.keycloak.adapters.jetty.core.JettySessionTokenStore;
import org.keycloak.adapters.jetty.spi.JettyHttpFacade;
import org.keycloak.adapters.jetty.spi.JettyUserSessionManagement;
import javax.servlet.ServletRequest;
@@ -48,7 +47,7 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
}
@Override
protected Authentication createAuthentication(UserIdentity userIdentity, Request request) {
protected Authentication createAuthentication(UserIdentity userIdentity) {
return new KeycloakAuthentication(getAuthMethod(), userIdentity) {
@Override
public void logout() {
@@ -62,11 +61,6 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
return new JettySessionTokenStore(request, resolvedDeployment, new JettyAdapterSessionStore(request));
}
@Override
public JettyUserSessionManagement createSessionManagement(Request request) {
return new JettyUserSessionManagement(new Jetty92SessionManager(request.getSessionManager()));
}
@Override
protected JettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade,
KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
-156
View File
@@ -1,156 +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>4.0.0.Beta1</version>
<relativePath>../../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-jetty93-adapter</artifactId>
<name>Keycloak Jetty 9.3.x Integration</name>
<properties>
<jetty9.version>9.3.9.v20160517</jetty9.version>
<keycloak.osgi.export>
org.keycloak.adapters.jetty.*
</keycloak.osgi.export>
<keycloak.osgi.import>
org.eclipse.jetty.*;resolution:=optional,
javax.servlet.*;version="[3.0,4)";resolution:=optional,
org.keycloak.*;version="${project.version}",
*;resolution:=optional
</keycloak.osgi.import>
</properties>
<description />
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>${jboss.logging.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jetty-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty9.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${jetty9.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
<version>${jetty9.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-ClassPath>.</Bundle-ClassPath>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -1,47 +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.jetty;
import org.eclipse.jetty.server.Request;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.jetty.core.JettyRequestAuthenticator;
import org.keycloak.adapters.spi.HttpFacade;
import javax.servlet.http.HttpSession;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class Jetty93RequestAuthenticator extends JettyRequestAuthenticator {
public Jetty93RequestAuthenticator(HttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore, int sslRedirectPort, Request request) {
super(facade, deployment, tokenStore, sslRedirectPort, request);
}
@Override
protected String changeHttpSessionId(boolean create) {
Request request = this.request;
HttpSession session = request.getSession(false);
if (session == null) {
return request.getSession(true).getId();
}
if (!deployment.isTurnOffChangeSessionIdOnLogin()) return request.changeSessionId();
else return session.getId();
}
}
@@ -1,40 +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.jetty;
import org.eclipse.jetty.server.SessionManager;
import org.keycloak.adapters.jetty.spi.JettySessionManager;
import javax.servlet.http.HttpSession;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class Jetty93SessionManager implements JettySessionManager {
protected SessionManager sessionManager;
public Jetty93SessionManager(SessionManager sessionManager) {
this.sessionManager = sessionManager;
}
@Override
public HttpSession getHttpSession(String id) {
return sessionManager.getHttpSession(id);
}
}
@@ -1,111 +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.jetty;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.MultiMap;
import org.keycloak.adapters.jetty.spi.JettyHttpFacade;
import org.keycloak.adapters.spi.AdapterSessionStore;
import org.keycloak.common.util.MultivaluedHashMap;
import javax.servlet.http.HttpSession;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JettyAdapterSessionStore implements AdapterSessionStore {
public static final String CACHED_FORM_PARAMETERS = "__CACHED_FORM_PARAMETERS";
protected Request myRequest;
public JettyAdapterSessionStore(Request request) {
this.myRequest = request; // for IDE/compilation purposes
}
protected MultiMap<String> extractFormParameters(Request base_request) {
MultiMap<String> formParameters = new MultiMap<String>();
base_request.extractFormParameters(formParameters);
return formParameters;
}
protected void restoreFormParameters(MultiMap<String> j_post, Request base_request) {
base_request.setContentParameters(j_post);
}
public boolean restoreRequest() {
HttpSession session = myRequest.getSession(false);
if (session == null) return false;
synchronized (session) {
String j_uri = (String) session.getAttribute(FormAuthenticator.__J_URI);
if (j_uri != null) {
// check if the request is for the same url as the original and restore
// params if it was a post
StringBuffer buf = myRequest.getRequestURL();
if (myRequest.getQueryString() != null)
buf.append("?").append(myRequest.getQueryString());
if (j_uri.equals(buf.toString())) {
String method = (String)session.getAttribute(JettyHttpFacade.__J_METHOD);
myRequest.setMethod(method);
MultivaluedHashMap<String, String> j_post = (MultivaluedHashMap<String, String>) session.getAttribute(CACHED_FORM_PARAMETERS);
if (j_post != null) {
myRequest.setContentType("application/x-www-form-urlencoded");
MultiMap<String> map = new MultiMap<String>();
for (String key : j_post.keySet()) {
for (String val : j_post.getList(key)) {
map.add(key, val);
}
}
restoreFormParameters(map, myRequest);
}
session.removeAttribute(FormAuthenticator.__J_URI);
session.removeAttribute(JettyHttpFacade.__J_METHOD);
session.removeAttribute(FormAuthenticator.__J_POST);
}
return true;
}
}
return false;
}
public void saveRequest() {
// remember the current URI
HttpSession session = myRequest.getSession();
synchronized (session) {
// But only if it is not set already, or we save every uri that leads to a login form redirect
if (session.getAttribute(FormAuthenticator.__J_URI) == null) {
StringBuffer buf = myRequest.getRequestURL();
if (myRequest.getQueryString() != null)
buf.append("?").append(myRequest.getQueryString());
session.setAttribute(FormAuthenticator.__J_URI, buf.toString());
session.setAttribute(JettyHttpFacade.__J_METHOD, myRequest.getMethod());
if ("application/x-www-form-urlencoded".equals(myRequest.getContentType()) && "POST".equalsIgnoreCase(myRequest.getMethod())) {
MultiMap<String> formParameters = extractFormParameters(myRequest);
MultivaluedHashMap<String, String> map = new MultivaluedHashMap<String, String>();
for (String key : formParameters.keySet()) {
for (Object value : formParameters.getValues(key)) {
map.add(key, (String) value);
}
}
session.setAttribute(CACHED_FORM_PARAMETERS, map);
}
}
}
}
}
@@ -1,75 +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.jetty;
import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.UserIdentity;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.jetty.core.AbstractKeycloakJettyAuthenticator;
import org.keycloak.adapters.jetty.core.JettyRequestAuthenticator;
import org.keycloak.adapters.jetty.core.JettySessionTokenStore;
import org.keycloak.adapters.jetty.spi.JettyHttpFacade;
import org.keycloak.adapters.jetty.spi.JettyUserSessionManagement;
import javax.servlet.ServletRequest;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticator {
public KeycloakJettyAuthenticator() {
super();
}
@Override
protected Request resolveRequest(ServletRequest req) {
return Request.getBaseRequest(req);
}
@Override
protected Authentication createAuthentication(UserIdentity userIdentity, final Request request) {
return new KeycloakAuthentication(getAuthMethod(), userIdentity) {
@Override
public void logout() {
logoutCurrent(request);
}
};
}
@Override
public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
return new JettySessionTokenStore(request, resolvedDeployment, new JettyAdapterSessionStore(request));
}
@Override
public JettyUserSessionManagement createSessionManagement(Request request) {
return new JettyUserSessionManagement(new Jetty93SessionManager(request.getSessionManager()));
}
@Override
protected JettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade,
KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
return new Jetty93RequestAuthenticator(facade, deployment, tokenStore, -1, request);
}
}
Loaded 100 of 9300 files, more files were not shown because too many files have changed in this diff. Show more