quarkus
quarkus copied to clipboard
Make it possible to inject Keycloak admin client
closes: #26837
Keycloak admin client can be configured in application.properties and injected as a CDI bean. Also the client can work with Dev Services for Keycloak without any configuration at all (analogous to bob and alice) and OIDC token providers are leveraged as default authorization provider in case user want to access Keycloak Admin REST API with access token from request.
I wrote security-keycloak-admin-client.adoc that is part of this PR and explains "What?" and "Why?" has been done.
cc @sberyozkin
:waning_crescent_moon: This workflow status is outdated as a new workflow run has been triggered.
Failing Jobs - Building 7c44defb9d19f4072ff993413d4592eb0cb3bcae
| Status | Name | Step | Failures | Logs | Raw logs |
|---|---|---|---|---|---|
| ✖ | Initial JDK 11 Build | Verify extension dependencies |
:warning: Check → | Logs | Raw logs |
:waning_crescent_moon: This workflow status is outdated as a new workflow run has been triggered.
Failing Jobs - Building c647c8571c194cb50ac35804d6a6b2385aad7c01
| Status | Name | Step | Failures | Logs | Raw logs |
|---|---|---|---|---|---|
| :heavy_check_mark: | JVM Tests - JDK 11 | ||||
| ✖ | JVM Tests - JDK 11 Windows | Build |
Failures | Logs | Raw logs |
| :heavy_check_mark: | JVM Tests - JDK 17 | ||||
| :heavy_check_mark: | JVM Tests - JDK 18 |
Full information is available in the Build summary check run.
Failures
:gear: JVM Tests - JDK 11 Windows #
- Failing: extensions/keycloak-admin-client-aggregator/keycloak-admin-client-reactive/deployment extensions/keycloak-admin-client-aggregator/keycloak-admin-client/deployment
! Skipped: extensions/keycloak-authorization/deployment integration-tests/keycloak-authorization integration-tests/oidc
:package: extensions/keycloak-admin-client-aggregator/keycloak-admin-client-reactive/deployment
✖ io.quarkus.keycloak.admin.client.reactive.AutoconfigurationForKeycloakDevServicesTest.testGetRealms - More details - Source on GitHub
java.lang.RuntimeException:
java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[error]: Build step io.quarkus.oidc.deployment.devservices.keycloak.KeycloakDevServicesProcessor#startKeycloakContainer threw an exception: java.lang.RuntimeException: java.lang.IllegalStateException: Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration
✖ io.quarkus.keycloak.admin.client.reactive.UserConfigurationForKeycloakDevServicesTest.testGetRealm - More details - Source on GitHub
java.lang.RuntimeException:
java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[error]: Build step io.quarkus.oidc.deployment.devservices.keycloak.KeycloakDevServicesProcessor#startKeycloakContainer threw an exception: java.lang.RuntimeException: java.lang.IllegalStateException: Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration
✖ io.quarkus.keycloak.admin.client.reactive.UserConfigurationForKeycloakDevServicesTest.testGetRoles - More details - Source on GitHub
java.lang.RuntimeException:
java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[error]: Build step io.quarkus.oidc.deployment.devservices.keycloak.KeycloakDevServicesProcessor#startKeycloakContainer threw an exception: java.lang.RuntimeException: java.lang.IllegalStateException: Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration
✖ io.quarkus.keycloak.admin.client.reactive.UserConfigurationForKeycloakDevServicesTest.testGetUsers - More details - Source on GitHub
java.lang.RuntimeException:
java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[error]: Build step io.quarkus.oidc.deployment.devservices.keycloak.KeycloakDevServicesProcessor#startKeycloakContainer threw an exception: java.lang.RuntimeException: java.lang.IllegalStateException: Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration
✖ io.quarkus.keycloak.admin.client.reactive.UserConfigurationForKeycloakDevServicesTest.testGetClients - More details - Source on GitHub
java.lang.RuntimeException:
java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[error]: Build step io.quarkus.oidc.deployment.devservices.keycloak.KeycloakDevServicesProcessor#startKeycloakContainer threw an exception: java.lang.RuntimeException: java.lang.IllegalStateException: Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration
:package: extensions/keycloak-admin-client-aggregator/keycloak-admin-client/deployment
✖ io.quarkus.keycloak.admin.client.reactive.AutoconfigurationForKeycloakDevServicesTest.testGetRealms - More details - Source on GitHub
java.lang.RuntimeException:
java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[error]: Build step io.quarkus.oidc.deployment.devservices.keycloak.KeycloakDevServicesProcessor#startKeycloakContainer threw an exception: java.lang.RuntimeException: java.lang.IllegalStateException: Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration
✖ io.quarkus.keycloak.admin.client.reactive.UserConfigurationForKeycloakDevServicesTest.testGetRealm - More details - Source on GitHub
java.lang.RuntimeException:
java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[error]: Build step io.quarkus.oidc.deployment.devservices.keycloak.KeycloakDevServicesProcessor#startKeycloakContainer threw an exception: java.lang.RuntimeException: java.lang.IllegalStateException: Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration
✖ io.quarkus.keycloak.admin.client.reactive.UserConfigurationForKeycloakDevServicesTest.testGetRoles - More details - Source on GitHub
java.lang.RuntimeException:
java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[error]: Build step io.quarkus.oidc.deployment.devservices.keycloak.KeycloakDevServicesProcessor#startKeycloakContainer threw an exception: java.lang.RuntimeException: java.lang.IllegalStateException: Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration
✖ io.quarkus.keycloak.admin.client.reactive.UserConfigurationForKeycloakDevServicesTest.testGetUsers - More details - Source on GitHub
java.lang.RuntimeException:
java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[error]: Build step io.quarkus.oidc.deployment.devservices.keycloak.KeycloakDevServicesProcessor#startKeycloakContainer threw an exception: java.lang.RuntimeException: java.lang.IllegalStateException: Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration
✖ io.quarkus.keycloak.admin.client.reactive.UserConfigurationForKeycloakDevServicesTest.testGetClients - More details - Source on GitHub
java.lang.RuntimeException:
java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[error]: Build step io.quarkus.oidc.deployment.devservices.keycloak.KeycloakDevServicesProcessor#startKeycloakContainer threw an exception: java.lang.RuntimeException: java.lang.IllegalStateException: Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration
Tests seems related, but are passing locally for me, I'll have a look what's different.
Update: that was test containers running on Windows, fixed.
Good idea about starting doc-ing it, please add the links to it from security.adoc, and I think keycloak-authorization docs which mention the admin client. This new doc should also link back to Quarkus Security (security.doc) in its References section, sane way it is done for other security docs, thanks
FYI - @sberyozkin I reflected both of your comments, but I'll push the changes tomorrow afternoon as I need re-test it after refactoring. Thank you for the feedback.
:waning_crescent_moon: This workflow status is outdated as a new workflow run has been triggered.
Failing Jobs - Building b79e016138d5ab76ae5aaf91f5160d2e2aeeb826
| Status | Name | Step | Failures | Logs | Raw logs |
|---|---|---|---|---|---|
| :hourglass: | JVM Tests - JDK 11 | Build |
Failures | Logs | Raw logs |
| ✖ | JVM Tests - JDK 11 Windows | Build |
Failures | Logs | Raw logs |
| ✖ | JVM Tests - JDK 17 | Build |
Failures | Logs | Raw logs |
| ✖ | JVM Tests - JDK 18 | Build |
Failures | Logs | Raw logs |
| ✖ | Native Tests - HTTP | Build |
Failures | Logs | Raw logs |
Full information is available in the Build summary check run.
Failures
:gear: JVM Tests - JDK 11 #
- Failing: integration-tests/rest-client
:package: integration-tests/rest-client
✖ io.quarkus.it.rest.client.selfsigned.ExternalSelfSignedTestCase.should_accept_self_signed_certs line 20 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
✖ io.quarkus.it.rest.client.selfsigned.ExternalSelfSignedTestCase.should_accept_self_signed_certs_java_url line 29 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
✖ io.quarkus.it.rest.client.trustall.ExternalTlsTrustAllTestCase.restClient line 20 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
✖ io.quarkus.it.rest.client.wronghost.ExternalWrongHostTestCase.restClient line 22 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
:gear: JVM Tests - JDK 11 Windows #
- Failing: integration-tests/rest-client
:package: integration-tests/rest-client
✖ io.quarkus.it.rest.client.selfsigned.ExternalSelfSignedTestCase.should_accept_self_signed_certs line 20 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
✖ io.quarkus.it.rest.client.selfsigned.ExternalSelfSignedTestCase.should_accept_self_signed_certs_java_url line 29 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
✖ io.quarkus.it.rest.client.trustall.ExternalTlsTrustAllTestCase.restClient line 20 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
✖ io.quarkus.it.rest.client.wronghost.ExternalWrongHostTestCase.restClient line 22 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
:gear: JVM Tests - JDK 17 #
- Failing: integration-tests/rest-client
:package: integration-tests/rest-client
✖ io.quarkus.it.rest.client.selfsigned.ExternalSelfSignedTestCase.should_accept_self_signed_certs line 20 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
✖ io.quarkus.it.rest.client.selfsigned.ExternalSelfSignedTestCase.should_accept_self_signed_certs_java_url line 29 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
✖ io.quarkus.it.rest.client.trustall.ExternalTlsTrustAllTestCase.restClient line 20 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
✖ io.quarkus.it.rest.client.wronghost.ExternalWrongHostTestCase.restClient line 22 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
:gear: JVM Tests - JDK 18 #
- Failing: integration-tests/rest-client
:package: integration-tests/rest-client
✖ io.quarkus.it.rest.client.selfsigned.ExternalSelfSignedTestCase.should_accept_self_signed_certs line 20 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
✖ io.quarkus.it.rest.client.selfsigned.ExternalSelfSignedTestCase.should_accept_self_signed_certs_java_url line 29 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
✖ io.quarkus.it.rest.client.trustall.ExternalTlsTrustAllTestCase.restClient line 20 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
✖ io.quarkus.it.rest.client.wronghost.ExternalWrongHostTestCase.restClient line 22 - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
:gear: Native Tests - HTTP #
- Failing: integration-tests/rest-client
:package: integration-tests/rest-client
✖ io.quarkus.it.rest.client.selfsigned.ExternalSelfSignedITCase.should_accept_self_signed_certs - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
✖ io.quarkus.it.rest.client.selfsigned.ExternalSelfSignedITCase.should_accept_self_signed_certs_java_url - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
✖ io.quarkus.it.rest.client.trustall.ExternalTlsTrustAllIT.restClient - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
✖ io.quarkus.it.rest.client.wronghost.ExternalWrongHostIT.restClient - More details - Source on GitHub
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
I checked test failures - not related.
@michalvavrik Hi, I'm sorry, but I'm still not understanding the need for the SPI module as well a few other things in the PR, why can't we just have a synthetic bean produced in the recorder, which will do:
KeycloakBuilder.builder()
.serverUrl(KEYCLOAK_SERVER_URL)
.realm("master")
.clientId("admin-cli")
.username("admin")
.password("admin")
.build();
where serverUrl, realm, clientId, username and password are configured ?
Thank you for feedback as always. I will need confirmation that you mean your comments and I should drop it all.
- It makes no sense to me and plenty (plenty) of work went into this, but I respect your opinion and will do whatever you want. It's all showed in tests.
- I just want to be sure you really read the PR before I drop it, that's all...
Just write I am sure.
Also, how else would you configure Keycloak admin client with Keycloak Dev Services without SPI? SPI is also used for OIDC to register default authorization provider. It takes access token from the request and uses it to access the Keycloak Admin REST API (as showed in docs I wrote, please have a look). Again, it would seems more rational for me to explain why do you believe we should just use pwd/username/client id/secret, authorization looks to me like a major case (existing SSO account is temporarily assigned with certain roles, as manage-realm). We have it for free, user don't have to configure anything as the access token is provided by OIDC. I added test to OIDC integration test module that shows it. Please have a look.
The example you showed with serverurl/pwd/username/client id validates only when client is created, which because you can't keep client open for long, is when request starts. Is that really the time you want to validate inputs?
@michalvavrik Let me take a quick stab asap at what I meant, as it feels like we might be talking about different things :-) Of course, your effort is appreciated, I'm sorry if it will end up that some of your code may need to be changed - but it has happened to me a lot, including having to completely rewrite an original DevServices for KC module structure which I was convinced was best and I was not excited about having to rewrite, but then eventually I realized my original idea was not good :-)
I appreciate the response. I understand my ideas were bad and I'm happy to drop them (even though I'd really appreciate your comment/response on authorization as these thoughts I had came from my previous practice were we used Keycloak, after all, you just suggest to ignore KeycloakBuilder#authorization method... I'll drop it anyway!).
As I'm OOTO and very busy, I'll move the PR to draft and rewrite it on 29.8.. Thank you for your time.
@michalvavrik @sberyozkin maybe you both could have a talk so that you agree on a common plan?
@michalvavrik
I understand my ideas were bad
No, I don't think I even implied it, and I do appreciate you were trying to innovate here :-). But IMHO it makes it much simpler to handle it step by step, right now, I believe it looks much closer to the original goal of #26837. We have a much simpler way to deal with initializing Keycloak admin object.
I'm not sure about the config of the grants though yet, I'll look asap.
Re some of your earlier points:
Keycloak admin client with Keycloak Dev Services without SPI
I'm not sure why we need to do it. Keycloak Dev Services work right now without Keycloak admin client. Does it have to use it ? May be but lets open an enhancement request and discuss the reason why we need to bring this dependency, I'm not sure we need to but may be I'm missing something.
Please allow me to also provide one else point of view - allowing to configure one client looks good in "quick start"/docs, but in production you will hardly ever have one almighty client with all the powers.
Well, it makes sense, in general. We have OidcClient able to support many targets. Do we need the same for Keycloak admin client, given that a single one can setup everything withing a given Keycloak instance, I'm not sure, but if we do need, than may be we should do it similarly to the multi-tenant setup of the oidc.
All of this can be useful for Quarkus users, but my point is, lets handle it step by step, with the dedicated issues
Thanks for your help so far
@michalvavrik FYI: https://github.com/quarkusio/quarkus/blob/main/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientsConfig.java, and there is a support for injecting named clients in the deployment module. Have a look at in the next step please.
Sounds good, I can see three issues (topics): authorization, dev services, multiple clients. Once this PR is done, I'll create an issue for Dev Services for Keycloak where it can be discussed whether it makes sense. If so, I'll provide impl. Thank you