Skip to content

Commit

Permalink
Merge pull request quarkusio#39342 from michalvavrik/feature/migrate-…
Browse files Browse the repository at this point in the history
…keycloak-admin-client-config-classes

Migrate Keycloak Admin Client from config classes to the `@ConfigMapping`
  • Loading branch information
sberyozkin authored Mar 12, 2024
2 parents 5a5fdc6 + 36b0bbd commit 926db7c
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 55 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
package io.quarkus.keycloak.admin.client.common;

import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;

/**
* Keycloak Admin Client
*/
@ConfigRoot(phase = ConfigPhase.BUILD_TIME, name = "keycloak.admin-client")
public class KeycloakAdminClientBuildTimeConfig {
@ConfigMapping(prefix = "quarkus.keycloak.admin-client")
@ConfigRoot(phase = ConfigPhase.BUILD_TIME)
public interface KeycloakAdminClientBuildTimeConfig {

/**
* Set to true if Keycloak Admin Client injection is supported.
*/
@ConfigItem(defaultValue = "true")
public boolean enabled = true;
@WithDefault("true")
boolean enabled();

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ public class KeycloakAdminClientInjectionEnabled implements BooleanSupplier {

@Override
public boolean getAsBoolean() {
return config.enabled;
return config.enabled();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public void passwordGrantTypeTest() {

// username is required
Assertions.assertThrows(KeycloakAdminClientConfigUtil.KeycloakAdminClientException.class, () -> {
KeycloakAdminClientConfig config = createConfig();
KeycloakAdminClientConfigImpl config = createConfig();
config.username = Optional.empty();
validate(config);
});

// password is required
Assertions.assertThrows(KeycloakAdminClientConfigUtil.KeycloakAdminClientException.class, () -> {
KeycloakAdminClientConfig config = createConfig();
KeycloakAdminClientConfigImpl config = createConfig();
config.password = Optional.empty();
validate(config);
});
Expand All @@ -36,14 +36,14 @@ public void clientCredentialsGrantTypeTest() {

// client secret is required
Assertions.assertThrows(KeycloakAdminClientConfigUtil.KeycloakAdminClientException.class, () -> {
KeycloakAdminClientConfig config = createClientCredentialsConfig();
KeycloakAdminClientConfigImpl config = createClientCredentialsConfig();
config.clientSecret = Optional.empty();
validate(config);
});
}

private KeycloakAdminClientConfig createConfig() {
final KeycloakAdminClientConfig config = new KeycloakAdminClientConfig();
private KeycloakAdminClientConfigImpl createConfig() {
final KeycloakAdminClientConfigImpl config = new KeycloakAdminClientConfigImpl();
config.serverUrl = Optional.of("https://localhost:8081");
config.grantType = KeycloakAdminClientConfig.GrantType.PASSWORD;
config.clientId = "client id";
Expand All @@ -55,13 +55,65 @@ private KeycloakAdminClientConfig createConfig() {
return config;
}

private KeycloakAdminClientConfig createClientCredentialsConfig() {
final KeycloakAdminClientConfig config = createConfig();
private KeycloakAdminClientConfigImpl createClientCredentialsConfig() {
final KeycloakAdminClientConfigImpl config = createConfig();
config.grantType = KeycloakAdminClientConfig.GrantType.CLIENT_CREDENTIALS;
config.password = Optional.empty();
config.username = Optional.empty();
config.clientSecret = Optional.of("client secret");
return config;
}

private static final class KeycloakAdminClientConfigImpl implements KeycloakAdminClientConfig {

private Optional<String> password;
private Optional<String> username;
private Optional<String> clientSecret;
private Optional<String> scope;
private Optional<String> serverUrl;
private String realm;
private String clientId;
private KeycloakAdminClientConfig.GrantType grantType;

@Override
public Optional<String> serverUrl() {
return serverUrl;
}

@Override
public String realm() {
return realm;
}

@Override
public String clientId() {
return clientId;
}

@Override
public Optional<String> clientSecret() {
return clientSecret;
}

@Override
public Optional<String> username() {
return username;
}

@Override
public Optional<String> password() {
return password;
}

@Override
public Optional<String> scope() {
return scope;
}

@Override
public GrantType grantType() {
return grantType;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,68 +2,67 @@

import java.util.Optional;

import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;

/**
* Keycloak Admin Client
*/
@ConfigRoot(phase = ConfigPhase.RUN_TIME, name = "keycloak.admin-client")
public class KeycloakAdminClientConfig {
@ConfigMapping(prefix = "quarkus.keycloak.admin-client")
@ConfigRoot(phase = ConfigPhase.RUN_TIME)
public interface KeycloakAdminClientConfig {

/**
* Keycloak server URL, for example, `https://host:port`.
* If this property is not set then the Keycloak Admin Client injection will fail - use
* {@linkplain org.keycloak.admin.client.KeycloakBuilder}
* to create it instead.
*/
@ConfigItem
public Optional<String> serverUrl;
Optional<String> serverUrl();

/**
* Realm.
*/
@ConfigItem(defaultValue = "master")
public String realm;
@WithDefault("master")
String realm();

/**
* Client id.
*/
@ConfigItem(defaultValue = "admin-cli")
public String clientId;
@WithDefault("admin-cli")
String clientId();

/**
* Client secret. Required with a `client_credentials` grant type.
*/
@ConfigItem
public Optional<String> clientSecret;
Optional<String> clientSecret();

/**
* Username. Required with a `password` grant type.
*/
@ConfigItem(defaultValue = "admin")
public Optional<String> username;
@WithDefault("admin")
Optional<String> username();

/**
* Password. Required with a `password` grant type.
*/
@ConfigItem(defaultValue = "admin")
public Optional<String> password;
@WithDefault("admin")
Optional<String> password();

/**
* OAuth 2.0 <a href="https://datatracker.ietf.org/doc/html/rfc6749#section-3.3">Access Token Scope</a>.
*/
@ConfigItem
public Optional<String> scope;
Optional<String> scope();

/**
* OAuth Grant Type.
*/
@ConfigItem(defaultValue = "PASSWORD")
public GrantType grantType;
@WithDefault("PASSWORD")
GrantType grantType();

public enum GrantType {
enum GrantType {
PASSWORD,
CLIENT_CREDENTIALS;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,20 @@ public class KeycloakAdminClientConfigUtil {
*/
public static void validate(KeycloakAdminClientConfig config) {

if (config.serverUrl.isEmpty()) {
if (config.serverUrl().isEmpty()) {
LOG.debug(
"Configuration property 'server-url' is not set, 'Keycloak' admin client injection will fail, "
+ "use org.keycloak.admin.client.KeycloakBuilder to create it instead");
return;
}

// client id is also required in both cases, but since it's not nullable, we can skip its validation
if (config.grantType == PASSWORD) {
if (config.password.isEmpty() || config.username.isEmpty()) {
if (config.grantType() == PASSWORD) {
if (config.password().isEmpty() || config.username().isEmpty()) {
throw new KeycloakAdminClientException("grant type 'password' requires username and password");
}
} else {
if (config.clientSecret.isEmpty()) {
if (config.clientSecret().isEmpty()) {
throw new KeycloakAdminClientException("grant type 'client_credentials' requires client secret");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public Supplier<Keycloak> createAdminClient() {

final KeycloakAdminClientConfig config = keycloakAdminClientConfigRuntimeValue.getValue();
validate(config);
if (config.serverUrl.isEmpty()) {
if (config.serverUrl().isEmpty()) {
return new Supplier<>() {
@Override
public Keycloak get() {
Expand All @@ -40,14 +40,14 @@ public Keycloak get() {
}
final KeycloakBuilder keycloakBuilder = KeycloakBuilder
.builder()
.clientId(config.clientId)
.clientSecret(config.clientSecret.orElse(null))
.grantType(config.grantType.asString())
.username(config.username.orElse(null))
.password(config.password.orElse(null))
.realm(config.realm)
.serverUrl(config.serverUrl.get())
.scope(config.scope.orElse(null));
.clientId(config.clientId())
.clientSecret(config.clientSecret().orElse(null))
.grantType(config.grantType().asString())
.username(config.username().orElse(null))
.password(config.password().orElse(null))
.realm(config.realm())
.serverUrl(config.serverUrl().get())
.scope(config.scope().orElse(null));
return new Supplier<Keycloak>() {
@Override
public Keycloak get() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public Supplier<Keycloak> createAdminClient() {

final KeycloakAdminClientConfig config = keycloakAdminClientConfigRuntimeValue.getValue();
validate(config);
if (config.serverUrl.isEmpty()) {
if (config.serverUrl().isEmpty()) {
return new Supplier<>() {
@Override
public Keycloak get() {
Expand All @@ -46,14 +46,14 @@ public Keycloak get() {
}
final KeycloakBuilder keycloakBuilder = KeycloakBuilder
.builder()
.clientId(config.clientId)
.clientSecret(config.clientSecret.orElse(null))
.grantType(config.grantType.asString())
.username(config.username.orElse(null))
.password(config.password.orElse(null))
.realm(config.realm)
.serverUrl(config.serverUrl.get())
.scope(config.scope.orElse(null));
.clientId(config.clientId())
.clientSecret(config.clientSecret().orElse(null))
.grantType(config.grantType().asString())
.username(config.username().orElse(null))
.password(config.password().orElse(null))
.realm(config.realm())
.serverUrl(config.serverUrl().get())
.scope(config.scope().orElse(null));
return new Supplier<Keycloak>() {
@Override
public Keycloak get() {
Expand Down

0 comments on commit 926db7c

Please sign in to comment.