diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/EnvironmentVariables.java b/operator/controller/src/main/java/io/apicurio/registry/operator/EnvironmentVariables.java index aeaeee269f..2496fbfe97 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/EnvironmentVariables.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/EnvironmentVariables.java @@ -20,4 +20,35 @@ public class EnvironmentVariables { public static final String KAFKASQL_SSL_TRUSTSTORE_TYPE = KAFKA_PREFIX + "SSL_TRUSTSTORE_TYPE"; public static final String KAFKASQL_SSL_TRUSTSTORE_LOCATION = KAFKA_PREFIX + "SSL_TRUSTSTORE_LOCATION"; public static final String KAFKASQL_SSL_TRUSTSTORE_PASSWORD = KAFKA_PREFIX + "SSL_TRUSTSTORE_PASSWORD"; + + // Auth related environment variables + public static final String APICURIO_REGISTRY_AUTH_ENABLED = "QUARKUS_OIDC_TENANT_ENABLED"; + public static final String APICURIO_REGISTRY_APP_CLIENT_ID = "QUARKUS_OIDC_CLIENT_ID"; + public static final String APICURIO_REGISTRY_UI_CLIENT_ID = "APICURIO_UI_AUTH_OIDC_CLIENT_ID"; + public static final String APICURIO_UI_AUTH_OIDC_REDIRECT_URI = "APICURIO_UI_AUTH_OIDC_REDIRECT_URI"; + public static final String APICURIO_UI_AUTH_OIDC_LOGOUT_URL = "APICURIO_UI_AUTH_OIDC_LOGOUT_URL"; + public static final String APICURIO_REGISTRY_AUTH_SERVER_URL = "QUARKUS_OIDC_AUTH_SERVER_URL"; + public static final String OIDC_TLS_VERIFICATION = "QUARKUS_OIDC_TLS_VERIFICATION"; + public static final String OIDC_TLS_TRUSTSTORE_LOCATION = "QUARKUS_OIDC_TLS_TRUST_STORE_FILE"; + public static final String OIDC_TLS_TRUSTSTORE_PASSWORD = "QUARKUS_OIDC_TLS_TRUST_STORE_PASSWORD"; + + public static final String APICURIO_AUTHN_BASIC_CLIENT_CREDENTIALS_ENABLED = "APICURIO_AUTHN_BASIC_CLIENT_CREDENTIALS_ENABLED"; + public static final String APICURIO_AUTHN_BASIC_CLIENT_CREDENTIALS_CACHE_EXPIRATION = "APICURIO_AUTHN_BASIC_CLIENT_CREDENTIALS_CACHE_EXPIRATION"; + public static final String APICURIO_AUTH_ANONYMOUS_READ_ACCESS_ENABLED = "APICURIO_AUTH_ANONYMOUS_READ_ACCESS_ENABLED"; + + // Authz related environment variables + public static final String APICURIO_AUTH_ROLE_BASED_AUTHORIZATION = "APICURIO_AUTH_ROLE_BASED_AUTHORIZATION"; + public static final String APICURIO_AUTH_AUTHENTICATED_READ_ACCESS_ENABLED = "APICURIO_AUTH_AUTHENTICATED_READ_ACCESS_ENABLED"; + public static final String APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION_LIMIT_GROUP_ACCESS = "APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION_LIMIT_GROUP_ACCESS"; + public static final String APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION = "APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION"; + public static final String APICURIO_AUTH_ROLE_SOURCE = "APICURIO_AUTH_ROLE_SOURCE"; + public static final String APICURIO_AUTH_ROLES_ADMIN = "APICURIO_AUTH_ROLES_ADMIN"; + public static final String APICURIO_AUTH_ROLES_DEVELOPER = "APICURIO_AUTH_ROLES_DEVELOPER"; + public static final String APICURIO_AUTH_ROLES_READONLY = "APICURIO_AUTH_ROLES_READONLY"; + public static final String APICURIO_AUTH_ADMIN_OVERRIDE_ENABLED = "APICURIO_AUTH_ADMIN_OVERRIDE_ENABLED"; + public static final String APICURIO_AUTH_ADMIN_OVERRIDE_ROLE = "APICURIO_AUTH_ADMIN_OVERRIDE_ROLE"; + public static final String APICURIO_AUTH_ADMIN_OVERRIDE_FROM = "APICURIO_AUTH_ADMIN_OVERRIDE_FROM"; + public static final String APICURIO_AUTH_ADMIN_OVERRIDE_TYPE = "APICURIO_AUTH_ADMIN_OVERRIDE_TYPE"; + public static final String APICURIO_AUTH_ADMIN_OVERRIDE_CLAIM = "APICURIO_AUTH_ADMIN_OVERRIDE_CLAIM"; + public static final String APICURIO_AUTH_ADMIN_OVERRIDE_CLAIM_VALUE = "APICURIO_AUTH_ADMIN_OVERRIDE_CLAIM_VALUE"; } diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/AdminOverride.java b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/AdminOverride.java new file mode 100644 index 0000000000..35eb782d8a --- /dev/null +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/AdminOverride.java @@ -0,0 +1,47 @@ +package io.apicurio.registry.operator.feat.security; + +import io.apicurio.registry.operator.EnvironmentVariables; +import io.apicurio.registry.operator.api.v1.spec.auth.AdminOverrideSpec; +import io.fabric8.kubernetes.api.model.EnvVar; + +import java.util.Map; + +import static io.apicurio.registry.operator.utils.Utils.createEnvVar; +import static io.apicurio.registry.operator.utils.Utils.putIfNotBlank; + +/** + * Helper class used to handle Admin Overide related configuration. + */ +public class AdminOverride { + + /** + * Configures admin-override-related environment variables for the Apicurio Registry. + * + * @param env The map of environment variables to be configured. + * @param adminOverrideSpec The adminOverride specification containing required admin override settings. + * If null, no changes will be made to envVars. + */ + public static void configureAdminOverride(AdminOverrideSpec adminOverrideSpec, Map env) { + if (adminOverrideSpec == null) { + return; + } + + if (adminOverrideSpec.getEnabled() != null && adminOverrideSpec.getEnabled()) { + env.put(EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_ENABLED, + createEnvVar(EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_ENABLED, + adminOverrideSpec.getEnabled().toString())); + + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_ROLE, + adminOverrideSpec.getRole()); + + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_FROM, + adminOverrideSpec.getFrom()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_TYPE, + adminOverrideSpec.getType()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_CLAIM, + adminOverrideSpec.getClaimName()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_CLAIM_VALUE, + adminOverrideSpec.getClaimValue()); + } + } +} diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/Auth.java b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/Auth.java new file mode 100644 index 0000000000..c57a993a2d --- /dev/null +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/Auth.java @@ -0,0 +1,59 @@ +package io.apicurio.registry.operator.feat.security; + +import io.apicurio.registry.operator.EnvironmentVariables; +import io.apicurio.registry.operator.api.v1.spec.auth.AuthSpec; +import io.fabric8.kubernetes.api.model.EnvVar; +import io.fabric8.kubernetes.api.model.apps.Deployment; + +import java.util.Map; +import java.util.Optional; + +import static io.apicurio.registry.operator.utils.Utils.createEnvVar; +import static io.apicurio.registry.operator.utils.Utils.putIfNotBlank; + +/** + * Helper class used to handle AUTH related configuration. + */ +public class Auth { + + /** + * Configures authentication-related environment variables for the Apicurio Registry. + * + * @param env The map of environment variables to be configured. + * @param deployment The application deployment to configure TLS. + * @param authSpec The authentication specification containing required auth settings. If null, no changes + * will be made to envVars. + */ + public static void configureAuth(AuthSpec authSpec, Deployment deployment, Map env) { + if (authSpec == null) { + return; + } + + env.put(EnvironmentVariables.APICURIO_REGISTRY_AUTH_ENABLED, + createEnvVar(EnvironmentVariables.APICURIO_REGISTRY_AUTH_ENABLED, + Optional.ofNullable(authSpec.getEnabled()).orElse(Boolean.FALSE).toString())); + + putIfNotBlank(env, EnvironmentVariables.APICURIO_REGISTRY_APP_CLIENT_ID, authSpec.getAppClientId()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_REGISTRY_UI_CLIENT_ID, authSpec.getUiClientId()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_UI_AUTH_OIDC_REDIRECT_URI, + authSpec.getRedirectURI()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_UI_AUTH_OIDC_LOGOUT_URL, authSpec.getLogoutURL()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL, + authSpec.getAuthServerUrl()); + + if (authSpec.getAnonymousReads() != null && authSpec.getAnonymousReads()) { + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_ANONYMOUS_READ_ACCESS_ENABLED, + authSpec.getAnonymousReads().toString()); + } + + if (authSpec.getBasicAuth() != null && authSpec.getBasicAuth().getEnabled()) { + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTHN_BASIC_CLIENT_CREDENTIALS_ENABLED, + authSpec.getBasicAuth().getEnabled().toString()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTHN_BASIC_CLIENT_CREDENTIALS_CACHE_EXPIRATION, + authSpec.getBasicAuth().getCacheExpiration()); + } + + AuthTLS.configureAuthTLS(authSpec, deployment, env); + Authz.configureAuthz(authSpec.getAuthz(), env); + } +} diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/AuthTLS.java b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/AuthTLS.java new file mode 100644 index 0000000000..40f1911501 --- /dev/null +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/AuthTLS.java @@ -0,0 +1,51 @@ +package io.apicurio.registry.operator.feat.security; + +import io.apicurio.registry.operator.EnvironmentVariables; +import io.apicurio.registry.operator.api.v1.spec.auth.AuthSpec; +import io.apicurio.registry.operator.api.v1.spec.auth.AuthTLSSpec; +import io.apicurio.registry.operator.utils.SecretKeyRefTool; +import io.fabric8.kubernetes.api.model.EnvVar; +import io.fabric8.kubernetes.api.model.apps.Deployment; + +import java.util.Map; +import java.util.Optional; + +import static io.apicurio.registry.operator.EnvironmentVariables.*; +import static io.apicurio.registry.operator.api.v1.ContainerNames.REGISTRY_APP_CONTAINER_NAME; +import static io.apicurio.registry.operator.resource.app.AppDeploymentResource.addEnvVar; +import static io.apicurio.registry.operator.utils.Utils.putIfNotBlank; +import static java.util.Optional.ofNullable; + +public class AuthTLS { + + /** + * Configure TLS for OIDC authentication + */ + public static void configureAuthTLS(AuthSpec authSpec, Deployment deployment, Map env) { + + putIfNotBlank(env, EnvironmentVariables.OIDC_TLS_VERIFICATION, + authSpec.getTls().getTlsVerificationType()); + + // spotless:off + var truststore = new SecretKeyRefTool(getAuthTLSSpec(authSpec) + .map(AuthTLSSpec::getTruststoreSecretRef) + .orElse(null), "ca.p12"); + + var truststorePassword = new SecretKeyRefTool(getAuthTLSSpec(authSpec) + .map(AuthTLSSpec::getTruststorePasswordSecretRef) + .orElse(null), "ca.password"); + // spotless:on + if (truststore.isValid() && truststorePassword.isValid()) { + truststore.applySecretVolume(deployment, REGISTRY_APP_CONTAINER_NAME); + addEnvVar(env, OIDC_TLS_TRUSTSTORE_LOCATION, truststore.getSecretVolumeKeyPath()); + truststorePassword.applySecretEnvVar(env, OIDC_TLS_TRUSTSTORE_PASSWORD); + } + } + + private static Optional getAuthTLSSpec(AuthSpec primary) { + // spotless:off + return ofNullable(primary) + .map(AuthSpec::getTls); + // spotless:on + } +} diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/Authz.java b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/Authz.java new file mode 100644 index 0000000000..67aeba2817 --- /dev/null +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/Authz.java @@ -0,0 +1,60 @@ +package io.apicurio.registry.operator.feat.security; + +import io.apicurio.registry.operator.EnvironmentVariables; +import io.apicurio.registry.operator.api.v1.spec.auth.AuthzSpec; +import io.fabric8.kubernetes.api.model.EnvVar; + +import java.util.Map; + +import static io.apicurio.registry.operator.utils.Utils.createEnvVar; +import static io.apicurio.registry.operator.utils.Utils.putIfNotBlank; + +/** + * Helper class used to handle AUTHZ related configuration. + */ +public class Authz { + + /** + * Configures authorization-related environment variables for the Apicurio Registry. + * + * @param env The map of environment variables to be configured. + * @param authzSpec The auhtorization specification containing required authz settings. If null, no + * changes will be made to envVars. + */ + public static void configureAuthz(AuthzSpec authzSpec, Map env) { + if (authzSpec == null) { + return; + } + + if (authzSpec.getEnabled()) { + env.put(EnvironmentVariables.APICURIO_AUTH_ROLE_BASED_AUTHORIZATION, + createEnvVar(EnvironmentVariables.APICURIO_AUTH_ROLE_BASED_AUTHORIZATION, + authzSpec.getEnabled().toString())); + + if (authzSpec.getGroupAccess() != null && authzSpec.getGroupAccess()) { + putIfNotBlank(env, + EnvironmentVariables.APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION_LIMIT_GROUP_ACCESS, + authzSpec.getGroupAccess().toString()); + } + + if (authzSpec.getOwnerOnly() != null && authzSpec.getOwnerOnly()) { + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION, + authzSpec.getOwnerOnly().toString()); + } + + if (authzSpec.getReadAccess() != null && authzSpec.getReadAccess()) { + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_AUTHENTICATED_READ_ACCESS_ENABLED, + authzSpec.getReadAccess().toString()); + } + + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_ROLE_SOURCE, authzSpec.getRoleSource()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_ROLES_ADMIN, authzSpec.getAdminRole()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_ROLES_DEVELOPER, + authzSpec.getDeveloperRole()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_ROLES_READONLY, + authzSpec.getReadOnlyRole()); + + AdminOverride.configureAdminOverride(authzSpec.getAdminOverride(), env); + } + } +} diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/app/AppDeploymentResource.java b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/app/AppDeploymentResource.java index 8262960615..5001fb2864 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/app/AppDeploymentResource.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/app/AppDeploymentResource.java @@ -7,9 +7,11 @@ import io.apicurio.registry.operator.api.v1.spec.AppFeaturesSpec; import io.apicurio.registry.operator.api.v1.spec.AppSpec; import io.apicurio.registry.operator.api.v1.spec.StorageSpec; +import io.apicurio.registry.operator.api.v1.spec.auth.AuthSpec; import io.apicurio.registry.operator.feat.Cors; import io.apicurio.registry.operator.feat.KafkaSql; import io.apicurio.registry.operator.feat.PostgresSql; +import io.apicurio.registry.operator.feat.security.Auth; import io.fabric8.kubernetes.api.model.Container; import io.fabric8.kubernetes.api.model.EnvVar; import io.fabric8.kubernetes.api.model.EnvVarBuilder; @@ -60,12 +62,26 @@ protected Deployment desired(ApicurioRegistry3 primary, Context envVars, String name, String value) { + if (!Utils.isBlank(value)) { + envVars.put(name, createEnvVar(name, value)); + } + } + + /** + * Creates an environment variable using the given name and value. + * + * @param name The name of the environment variable. + * @param value The value of the environment variable. + * @return An {@link EnvVar} instance with the specified name and value. + */ + public static EnvVar createEnvVar(String name, String value) { + return new EnvVarBuilder().withName(name).withValue(value).build(); + } } diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthITTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthITTest.java new file mode 100644 index 0000000000..a36b24a683 --- /dev/null +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthITTest.java @@ -0,0 +1,80 @@ +package io.apicurio.registry.operator.it; + +import io.apicurio.registry.operator.EnvironmentVariables; +import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; +import io.apicurio.registry.operator.api.v1.spec.auth.AuthSpec; +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static io.apicurio.registry.operator.api.v1.ContainerNames.REGISTRY_APP_CONTAINER_NAME; +import static io.apicurio.registry.operator.resource.ResourceFactory.COMPONENT_APP; +import static io.apicurio.registry.operator.resource.ResourceFactory.COMPONENT_UI; +import static io.apicurio.registry.operator.resource.app.AppDeploymentResource.getContainerFromDeployment; +import static org.assertj.core.api.Assertions.assertThat; + +@QuarkusTest +public class AuthITTest extends BaseAuthTest { + + /** + * In this test, Keycloak is deployed using a self-signed certificate with the hostname set to the ingress + * value. TLS verification is disabled at the Apicurio Registry level, so even in that case the deployment + * works. + */ + @Test + void testAuthTlsNoVerification() { + // Preparation, deploy Keycloak + ApicurioRegistry3 registry = prepareInfra("/k8s/examples/auth/keycloak.yaml", + "k8s/examples/auth/simple-with_keycloak.apicurioregistry3.yaml"); + AuthSpec authSpec = registry.getSpec().getApp().getAuth(); + + Assertions.assertEquals("registry-api", authSpec.getAppClientId()); + Assertions.assertEquals("apicurio-registry", authSpec.getUiClientId()); + Assertions.assertEquals(true, authSpec.getEnabled()); + Assertions.assertEquals("https://simple-keycloak.apps.cluster.example/realms/registry", + authSpec.getAuthServerUrl()); + Assertions.assertEquals("https://simple-ui.apps.cluster.example", authSpec.getRedirectURI()); + Assertions.assertEquals("https://simple-ui.apps.cluster.example", authSpec.getLogoutURL()); + + Assertions.assertEquals("https://simple-ui.apps.cluster.example", authSpec.getLogoutURL()); + + client.resource(registry).create(); + + // Assertions, checks registry deployments exist + checkDeploymentExists(registry, COMPONENT_APP, 1); + checkDeploymentExists(registry, COMPONENT_UI, 1); + + // App deployment auth related assertions + var appEnv = getContainerFromDeployment( + client.apps().deployments().inNamespace(namespace) + .withName(registry.getMetadata().getName() + "-app-deployment").get(), + REGISTRY_APP_CONTAINER_NAME).getEnv(); + + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_REGISTRY_AUTH_ENABLED + "=" + "true"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.OIDC_TLS_VERIFICATION + "=" + "none"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_REGISTRY_APP_CLIENT_ID + "=" + "registry-api"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_REGISTRY_UI_CLIENT_ID + "=" + "apicurio-registry"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL + "=" + + "https://simple-keycloak.apps.cluster.example/realms/registry"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_UI_AUTH_OIDC_REDIRECT_URI + "=" + + "https://simple-ui.apps.cluster.example"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_UI_AUTH_OIDC_LOGOUT_URL + "=" + + "https://simple-ui.apps.cluster.example"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.OIDC_TLS_VERIFICATION + "=" + "none"); + + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()).contains( + EnvironmentVariables.APICURIO_AUTHN_BASIC_CLIENT_CREDENTIALS_ENABLED + "=" + "true"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()).contains( + EnvironmentVariables.APICURIO_AUTHN_BASIC_CLIENT_CREDENTIALS_CACHE_EXPIRATION + "=" + "25"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_AUTH_ANONYMOUS_READ_ACCESS_ENABLED + "=" + "true"); + } +} diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthTLSITTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthTLSITTest.java new file mode 100644 index 0000000000..078e622a08 --- /dev/null +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthTLSITTest.java @@ -0,0 +1,83 @@ +package io.apicurio.registry.operator.it; + +import io.apicurio.registry.operator.EnvironmentVariables; +import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; +import io.apicurio.registry.operator.api.v1.spec.auth.AuthSpec; +import io.quarkus.test.junit.QuarkusTest; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Duration; + +import static io.apicurio.registry.operator.api.v1.ContainerNames.REGISTRY_APP_CONTAINER_NAME; +import static io.apicurio.registry.operator.resource.ResourceFactory.COMPONENT_APP; +import static io.apicurio.registry.operator.resource.ResourceFactory.COMPONENT_UI; +import static io.apicurio.registry.operator.resource.app.AppDeploymentResource.getContainerFromDeployment; +import static org.assertj.core.api.Assertions.assertThat; + +@QuarkusTest +public class AuthTLSITTest extends BaseAuthTest { + + private static final Logger log = LoggerFactory.getLogger(AuthTLSITTest.class); + + @BeforeAll + public static void init() { + Awaitility.setDefaultTimeout(Duration.ofSeconds(60)); + } + + /** + * In this test, Keycloak is deployed using a self-signed certificate with the hostname set to the ingress + * value. TLS verification is enabled at the Apicurio Registry level, mounting the trustore into the + * Quarkus application using the custom resource. + */ + @Test + void testAuthTlsVerification() { + ApicurioRegistry3 registry = prepareInfra("/k8s/examples/auth/keycloak.yaml", + "k8s/examples/auth/tls/simple-with_keycloak.apicurioregistry3.yaml"); + AuthSpec authSpec = registry.getSpec().getApp().getAuth(); + + Assertions.assertEquals("registry-api", authSpec.getAppClientId()); + Assertions.assertEquals("apicurio-registry", authSpec.getUiClientId()); + Assertions.assertEquals(true, authSpec.getEnabled()); + Assertions.assertEquals("https://simple-keycloak.apps.cluster.example/realms/registry", + authSpec.getAuthServerUrl()); + Assertions.assertEquals("https://simple-ui.apps.cluster.example", authSpec.getRedirectURI()); + Assertions.assertEquals("https://simple-ui.apps.cluster.example", authSpec.getLogoutURL()); + + client.resource(registry).create(); + + // Assertions, checks registry deployments exist + checkDeploymentExists(registry, COMPONENT_APP, 1); + checkDeploymentExists(registry, COMPONENT_UI, 1); + + // App deployment auth related assertions + var appEnv = getContainerFromDeployment( + client.apps().deployments().inNamespace(namespace) + .withName(registry.getMetadata().getName() + "-app-deployment").get(), + REGISTRY_APP_CONTAINER_NAME).getEnv(); + + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_REGISTRY_AUTH_ENABLED + "=" + "true"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.OIDC_TLS_VERIFICATION + "=" + "required"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_REGISTRY_APP_CLIENT_ID + "=" + "registry-api"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_REGISTRY_UI_CLIENT_ID + "=" + "apicurio-registry"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL + "=" + + "https://simple-keycloak.apps.cluster.example/realms/registry"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_UI_AUTH_OIDC_REDIRECT_URI + "=" + + "https://simple-ui.apps.cluster.example"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_UI_AUTH_OIDC_LOGOUT_URL + "=" + + "https://simple-ui.apps.cluster.example"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.OIDC_TLS_VERIFICATION + "=" + "required"); + } +} diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthzITTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthzITTest.java new file mode 100644 index 0000000000..41a6a7e6a6 --- /dev/null +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthzITTest.java @@ -0,0 +1,131 @@ +package io.apicurio.registry.operator.it; + +import io.apicurio.registry.operator.EnvironmentVariables; +import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; +import io.apicurio.registry.operator.api.v1.spec.auth.AdminOverrideSpec; +import io.apicurio.registry.operator.api.v1.spec.auth.AuthSpec; +import io.apicurio.registry.operator.api.v1.spec.auth.AuthzSpec; +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static io.apicurio.registry.operator.api.v1.ContainerNames.REGISTRY_APP_CONTAINER_NAME; +import static io.apicurio.registry.operator.resource.ResourceFactory.COMPONENT_APP; +import static io.apicurio.registry.operator.resource.ResourceFactory.COMPONENT_UI; +import static io.apicurio.registry.operator.resource.app.AppDeploymentResource.getContainerFromDeployment; +import static org.assertj.core.api.Assertions.assertThat; + +@QuarkusTest +public class AuthzITTest extends BaseAuthTest { + + /** + * In this test, Keycloak is deployed using a self-signed certificate with the hostname set to the ingress + * value. TLS verification is disabled at the Apicurio Registry level, so even in that case the deployment + * works. + */ + @Test + void testAuthz() { + // Preparation, deploy Keycloak + ApicurioRegistry3 registry = prepareInfra("/k8s/examples/auth/keycloak.yaml", + "k8s/examples/auth/authz-with_keycloak.apicurioregistry3.yaml"); + AuthSpec authSpec = registry.getSpec().getApp().getAuth(); + + Assertions.assertEquals("registry-api", authSpec.getAppClientId()); + Assertions.assertEquals("apicurio-registry", authSpec.getUiClientId()); + Assertions.assertEquals(true, authSpec.getEnabled()); + Assertions.assertEquals("https://simple-keycloak.apps.cluster.example/realms/registry", + authSpec.getAuthServerUrl()); + Assertions.assertEquals("https://simple-ui.apps.cluster.example", authSpec.getRedirectURI()); + Assertions.assertEquals("https://simple-ui.apps.cluster.example", authSpec.getLogoutURL()); + + AuthzSpec authzSpec = authSpec.getAuthz(); + + // Authz exclusive assertions + Assertions.assertEquals(true, authzSpec.getEnabled()); + Assertions.assertEquals(true, authzSpec.getOwnerOnly()); + Assertions.assertEquals(true, authzSpec.getGroupAccess()); + Assertions.assertEquals(true, authzSpec.getReadAccess()); + Assertions.assertEquals("token", authzSpec.getRoleSource()); + Assertions.assertEquals("admin", authzSpec.getAdminRole()); + Assertions.assertEquals("dev", authzSpec.getDeveloperRole()); + Assertions.assertEquals("read", authzSpec.getReadOnlyRole()); + + // Admin Override assertions + AdminOverrideSpec adminOverrideSpec = authzSpec.getAdminOverride(); + Assertions.assertEquals(true, adminOverrideSpec.getEnabled()); + Assertions.assertEquals("token", adminOverrideSpec.getFrom()); + Assertions.assertEquals("claim", adminOverrideSpec.getType()); + Assertions.assertEquals("admin", adminOverrideSpec.getRole()); + Assertions.assertEquals("test", adminOverrideSpec.getClaimName()); + Assertions.assertEquals("test", adminOverrideSpec.getClaimValue()); + + client.resource(registry).create(); + + // Assertions, checks registry deployments exist + checkDeploymentExists(registry, COMPONENT_APP, 1); + checkDeploymentExists(registry, COMPONENT_UI, 1); + + // App deployment auth related assertions + var appEnv = getContainerFromDeployment( + client.apps().deployments().inNamespace(namespace) + .withName(registry.getMetadata().getName() + "-app-deployment").get(), + REGISTRY_APP_CONTAINER_NAME).getEnv(); + + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_REGISTRY_AUTH_ENABLED + "=" + "true"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.OIDC_TLS_VERIFICATION + "=" + "required"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_REGISTRY_APP_CLIENT_ID + "=" + "registry-api"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_REGISTRY_UI_CLIENT_ID + "=" + "apicurio-registry"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL + "=" + + "https://simple-keycloak.apps.cluster.example/realms/registry"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_UI_AUTH_OIDC_REDIRECT_URI + "=" + + "https://simple-ui.apps.cluster.example"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_UI_AUTH_OIDC_LOGOUT_URL + "=" + + "https://simple-ui.apps.cluster.example"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.OIDC_TLS_VERIFICATION + "=" + "required"); + + // Authz exclusive assertions + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_AUTH_ROLE_BASED_AUTHORIZATION + "=" + "true"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()).contains( + EnvironmentVariables.APICURIO_AUTH_AUTHENTICATED_READ_ACCESS_ENABLED + "=" + "true"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION_LIMIT_GROUP_ACCESS + "=" + + "true"); + + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION + "=" + "true"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_AUTH_ROLE_SOURCE + "=" + "token"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_AUTH_ROLES_ADMIN + "=" + "admin"); + + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_AUTH_ROLES_DEVELOPER + "=" + "dev"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_AUTH_ROLES_READONLY + "=" + "read"); + + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_ENABLED + "=" + "true"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_FROM + "=" + "token"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_TYPE + "=" + "claim"); + + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_ROLE + "=" + "admin"); + + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_CLAIM + "=" + "test"); + + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_CLAIM_VALUE + "=" + "test"); + } +} diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/BaseAuthTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/BaseAuthTest.java new file mode 100644 index 0000000000..f92da4a9f8 --- /dev/null +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/BaseAuthTest.java @@ -0,0 +1,44 @@ +package io.apicurio.registry.operator.it; + +import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.utils.Serialization; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.BeforeAll; + +import java.time.Duration; +import java.util.List; + +import static io.apicurio.registry.operator.resource.ResourceFactory.deserialize; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public abstract class BaseAuthTest extends ITBase { + + @BeforeAll + public static void init() { + Awaitility.setDefaultTimeout(Duration.ofSeconds(60)); + } + + protected static ApicurioRegistry3 prepareInfra(String keycloakResource, String apicurioResource) { + List resources = Serialization + .unmarshal(AuthITTest.class.getResourceAsStream(keycloakResource)); + + createResources(resources, "Keycloak"); + + await().ignoreExceptions().untilAsserted(() -> { + assertThat(client.apps().deployments().withName("keycloak").get().getStatus().getReadyReplicas()) + .isEqualTo(1); + }); + + createKeycloakDNSResolution("simple-keycloak.apps.cluster.example", + "keycloak." + namespace + ".svc.cluster.local"); + + // Deploy Registry + var registry = deserialize(apicurioResource, ApicurioRegistry3.class); + + registry.getMetadata().setNamespace(namespace); + + return registry; + } +} diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/ITBase.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/ITBase.java index ba385a3972..2d476ecfcf 100644 --- a/operator/controller/src/test/java/io/apicurio/registry/operator/it/ITBase.java +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/ITBase.java @@ -2,6 +2,8 @@ import io.apicurio.registry.operator.Constants; import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.NamespaceBuilder; import io.fabric8.kubernetes.api.model.Pod; @@ -236,6 +238,41 @@ private static void createOperator() { }); } + static void createKeycloakDNSResolution(String ingressHostname, String keycloakService) { + String configMapName = "coredns"; + String systemNamespace = "kube-system"; + + // Step 1: Fetch the existing CoreDNS ConfigMap + ConfigMap existingConfigMap = client.configMaps().inNamespace(systemNamespace).withName(configMapName) + .get(); + + if (existingConfigMap == null) { + throw new IllegalStateException("Error: CoreDNS ConfigMap not found!"); + } + + // Step 2: Modify the CoreDNS configuration + String corefile = existingConfigMap.getData().get("Corefile"); + + // Step 3: Append the rewrite rule to Corefile + String newCorefile = corefile.replaceFirst("\\.:53 \\{", + ".:53 {\n rewrite name " + ingressHostname + " " + keycloakService); + + // Step 4: Create the updated ConfigMap, ensuring resourceVersion is included + ConfigMap updatedConfigMap = new ConfigMapBuilder().withMetadata(existingConfigMap.getMetadata()) // Preserve + // metadata + // (including + // UID) + .addToData("Corefile", newCorefile).build(); + + // Step 5: Apply the updated ConfigMap + client.configMaps().inNamespace(systemNamespace).resource(updatedConfigMap).update(); + + log.info("CoreDNS ConfigMap updated successfully!"); + + // Step 6: Restart CoreDNS to apply changes + client.apps().deployments().inNamespace(systemNamespace).withName("coredns").rolling().restart(); + } + static void createNamespace(KubernetesClient client, String namespace) { log.info("Creating Namespace {}", namespace); client.resource( @@ -253,6 +290,17 @@ static void setDefaultAwaitilityTimings() { Awaitility.setDefaultTimeout(Duration.ofSeconds(5 * 60)); } + static void createResources(List resources, String resourceType) { + resources.forEach(r -> { + log.info("Creating {} resource kind {} in namespace {}", resourceType, r.getKind(), namespace); + client.resource(r).inNamespace(namespace).createOrReplace(); + await().ignoreExceptions().until(() -> { + assertThat(client.resource(r).inNamespace(namespace).get()).isNotNull(); + return true; + }); + }); + } + @AfterEach public void cleanup() { if (cleanup) { diff --git a/operator/controller/src/test/resources/k8s/examples/auth/authz-with_keycloak.apicurioregistry3.yaml b/operator/controller/src/test/resources/k8s/examples/auth/authz-with_keycloak.apicurioregistry3.yaml new file mode 100644 index 0000000000..9c4313a96c --- /dev/null +++ b/operator/controller/src/test/resources/k8s/examples/auth/authz-with_keycloak.apicurioregistry3.yaml @@ -0,0 +1,42 @@ +apiVersion: registry.apicur.io/v1 +kind: ApicurioRegistry3 +metadata: + name: simple +spec: + app: + ingress: + host: simple-app.apps.cluster.example + auth: + enabled: true + appClientId: registry-api + uiClientId: apicurio-registry + authServerUrl: https://simple-keycloak.apps.cluster.example/realms/registry + redirectURI: https://simple-ui.apps.cluster.example + logoutURL: https://simple-ui.apps.cluster.example + authz: + enabled: true + ownerOnly: true + groupAccess: true + readAccess: true + roleSource: token + adminRole: admin + developerRole: dev + readOnlyRole: read + adminOverride: + enabled: true + from: token + type: claim + role: admin + claimName: test + claimValue: test + tls: + tlsVerificationType: required + truststoreSecretRef: + name: keycloak-truststore + key: truststore + truststorePasswordSecretRef: + name: keycloak-truststore + key: password + ui: + ingress: + host: simple-ui.apps.cluster.example diff --git a/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml b/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml new file mode 100644 index 0000000000..75a6bd0a50 --- /dev/null +++ b/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml @@ -0,0 +1,3263 @@ +apiVersion: v1 +data: + realm.json: | + { + "id": "registry", + "realm": "registry", + "notBefore": 1600933598, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": true, + "registrationEmailAsUsername": false, + "rememberMe": true, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": true, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "91363f21-2a57-4d65-954b-b3cd45d9f69c", + "name": "sr-admin", + "composite": false, + "clientRole": false, + "containerId": "registry", + "attributes": {} + }, + { + "id": "b2a0eee7-c761-49a1-9426-441d467f3f98", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "registry", + "attributes": {} + }, + { + "id": "2c0937ba-7b1f-424c-927c-33cd2ccfdc62", + "name": "sr-developer", + "composite": false, + "clientRole": false, + "containerId": "registry", + "attributes": {} + }, + { + "id": "1ffdda65-2476-4ad5-b1e8-9f8af44a897b", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "registry", + "attributes": {} + }, + { + "id": "a06b0184-e5bc-44c2-ae5b-4315754405f1", + "name": "sr-readonly", + "composite": false, + "clientRole": false, + "containerId": "registry", + "attributes": {} + }, + { + "id": "33a21208-808d-444f-9aa7-fdf3dc9536ce", + "name": "default-roles-registry", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": [ + "User", + "offline_access", + "uma_authorization" + ], + "client": { + "account": [ + "view-applications", + "view-profile", + "manage-account" + ] + } + }, + "clientRole": false, + "containerId": "registry", + "attributes": {} + }, + { + "id": "43dd3a54-4447-4703-ad0e-cd558f78c803", + "name": "User", + "composite": false, + "clientRole": false, + "containerId": "registry", + "attributes": {} + } + ], + "client": { + "admin-client": [], + "realm-management": [ + { + "id": "dcd9e6f5-3158-4e90-ba06-e5e80e0fa05c", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "0b4d5891-c2d9-409d-b4bd-b5daa11e059e", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "2fee03d5-f4da-418f-843e-0a70cba351c7", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-clients" + ] + } + }, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "82e5542a-8ec4-4e76-aa2f-75c0eb707aad", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "4d0d41a7-ac3f-47c6-a227-de0dc1c861de", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "4d69772c-08d4-4735-954c-4115788cecbc", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "8a79c5f9-2d14-44e4-b2bc-7e4cb955b721", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "0b54c521-9672-49b5-89ab-603fe4da9693", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "3fa4a674-d449-4576-b207-f89e556ba617", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "7a9c60b9-f2e6-4fec-ac85-7b3633a0f116", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-users", + "view-identity-providers", + "view-clients", + "manage-realm", + "query-groups", + "view-events", + "query-realms", + "manage-events", + "manage-authorization", + "view-users", + "view-authorization", + "manage-identity-providers", + "impersonation", + "query-clients", + "manage-clients", + "view-realm", + "create-client", + "manage-users" + ] + } + }, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "f7c5550f-8233-4347-967d-4905cbd36cec", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-users", + "query-groups" + ] + } + }, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "05c8c44f-8554-4a91-b9cb-be6995144040", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "e91f859e-d5c3-48d6-b0b0-5ff2710db3c9", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "a0c6b281-3915-45c2-9af7-d7d4f669419a", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "e6e6d63d-d161-444f-a58d-30875b076d16", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "4c592e93-ba00-4661-af7d-bc50b800c060", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "e50b557e-c79e-4e5a-92ee-a56b9c3925e1", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "34b1cafe-ee16-42d0-9ec1-c6829e01f78c", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + }, + { + "id": "7d1d9c1e-6c1c-41eb-b8d9-6460e4d243f0", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "attributes": {} + } + ], + "apicurio-registry": [], + "readonly-client": [], + "security-admin-console": [], + "admin-cli": [], + "account-console": [], + "developer-client": [], + "wrong-client": [], + "broker": [ + { + "id": "1ec1b34a-682d-44e7-b1fb-1d235b27b9d0", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "717c272b-ed48-4d5a-a3cb-da5d3d3ba528", + "attributes": {} + } + ], + "account": [ + { + "id": "e81eb004-b1ac-49a7-84ab-d2b86228c89d", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "d7c9d8ec-d826-4979-970e-4d5c4d9e358b", + "attributes": {} + }, + { + "id": "a49e7c09-b2df-4468-96f0-65b8591774ad", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "d7c9d8ec-d826-4979-970e-4d5c4d9e358b", + "attributes": {} + }, + { + "id": "b6e07306-8781-4538-800b-32b2ffc5d57c", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "d7c9d8ec-d826-4979-970e-4d5c4d9e358b", + "attributes": {} + }, + { + "id": "aa763dea-9699-4899-95bc-f2cef6692b1d", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": [ + "view-consent" + ] + } + }, + "clientRole": true, + "containerId": "d7c9d8ec-d826-4979-970e-4d5c4d9e358b", + "attributes": {} + }, + { + "id": "6b67af9e-5c96-4944-8189-7d6c7ecc1f80", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "d7c9d8ec-d826-4979-970e-4d5c4d9e358b", + "attributes": {} + }, + { + "id": "aad246c6-eb26-4cfd-b840-e113021f5b9b", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "d7c9d8ec-d826-4979-970e-4d5c4d9e358b", + "attributes": {} + }, + { + "id": "de065b56-c31c-41df-b110-186a798d7f17", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": [ + "manage-account-links" + ] + } + }, + "clientRole": true, + "containerId": "d7c9d8ec-d826-4979-970e-4d5c4d9e358b", + "attributes": {} + } + ], + "registry-api": [] + } + }, + "groups": [], + "defaultRole": { + "id": "33a21208-808d-444f-9aa7-fdf3dc9536ce", + "name": "default-roles-registry", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "registry" + }, + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpSupportedApplications": [ + "FreeOTP", + "Google Authenticator" + ], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "users": [ + { + "id": "5d3833aa-2aba-4fc4-a546-00dfe1d56bb5", + "createdTimestamp": 1687335757025, + "username": "admin-client", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "admin-client", + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "sr-admin", + "default-roles-registry" + ], + "notBefore": 0, + "groups": [] + }, + { + "id": "0f1c31e8-a357-4f05-90cc-3374973b6088", + "createdTimestamp": 1608543288931, + "username": "developer-client", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "developer-client", + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "offline_access", + "uma_authorization", + "sr-developer", + "User" + ], + "clientRoles": { + "account": [ + "view-applications", + "view-profile", + "manage-account" + ] + }, + "notBefore": 0, + "groups": [] + }, + { + "id": "0f1d31e8-a358-4f05-90cc-3374453b7088", + "createdTimestamp": 1608543288931, + "username": "service-account-wrong-client", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "wrong-client", + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "offline_access", + "uma_authorization", + "sr-developer", + "User" + ], + "clientRoles": { + "account": [ + "view-applications", + "view-profile", + "manage-account" + ] + }, + "notBefore": 0, + "groups": [] + }, + { + "id": "06fec980-2e11-4c7f-a8fb-3d285f30b0b0", + "createdTimestamp": 1608543552621, + "username": "readonly-client", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "readonly-client", + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "offline_access", + "uma_authorization", + "sr-readonly", + "User" + ], + "clientRoles": { + "account": [ + "view-applications", + "view-profile", + "manage-account" + ] + }, + "notBefore": 0, + "groups": [] + }, + { + "id": "70dbaf4a-fcef-449c-9184-121b6cedb115", + "createdTimestamp": 1607594319706, + "username": "service-account-registry-api", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "registry-api", + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "sr-admin", + "offline_access", + "uma_authorization", + "User" + ], + "clientRoles": { + "account": [ + "view-applications", + "view-profile", + "manage-account" + ] + }, + "notBefore": 0, + "groups": [] + } + ], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": [ + "manage-account" + ] + } + ] + }, + "clients": [ + { + "id": "d7c9d8ec-d826-4979-970e-4d5c4d9e358b", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/registry/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "test1", + "redirectUris": [ + "/realms/registry/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "ae8d8aa5-5991-42a1-8ca8-eec12b6717f7", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/registry/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "test1", + "redirectUris": [ + "/realms/registry/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "b852b0a0-768e-4f42-badd-14cf7d2d227a", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "80b79538-8665-4188-921d-f4d0ad1f3113", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "test1", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "21c5668a-4fba-46e9-8ac3-831e5d907eb6", + "clientId": "admin-client", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "test1", + "redirectUris": [ + "*" + ], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "frontchannel.logout.session.required": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature.keyinfo.ext": "false", + "use.refresh.tokens": "true", + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "true", + "client_credentials.use_refresh_token": "false", + "require.pushed.authorization.requests": "false", + "saml.client.signature": "false", + "saml.allow.ecp.flow": "false", + "id.token.as.detached.signature": "false", + "saml.assertion.signature": "false", + "client.secret.creation.time": "1687335786", + "saml.encrypt": "false", + "login_theme": "keycloak", + "saml.server.signature": "false", + "exclude.session.state.from.auth.response": "false", + "saml.artifact.binding": "false", + "saml_force_name_id_format": "false", + "acr.loa.map": "{}", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "token.response.type.bearer.lower-case": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "ef8fe948-b172-4d59-bffe-4ba46d0c0b09", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "id": "7e443600-bf35-49df-b0ea-5b5c51f9c590", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" + } + }, + { + "id": "605d578d-5532-4f83-bd97-fe6bf72c5fee", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "1fa15256-6427-47fa-a144-b0b784e834c6", + "clientId": "apicurio-registry", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "test1", + "redirectUris": [ + "*" + ], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "717c272b-ed48-4d5a-a3cb-da5d3d3ba528", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "test1", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "af889033-76c2-41ae-a368-67d5789f4b87", + "clientId": "developer-client", + "rootUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "test1", + "redirectUris": [ + "*" + ], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "frontchannel.logout.session.required": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature.keyinfo.ext": "false", + "use.refresh.tokens": "true", + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "false", + "client_credentials.use_refresh_token": "false", + "require.pushed.authorization.requests": "false", + "saml.client.signature": "false", + "saml.allow.ecp.flow": "false", + "id.token.as.detached.signature": "false", + "saml.assertion.signature": "false", + "saml.encrypt": "false", + "login_theme": "keycloak", + "saml.server.signature": "false", + "exclude.session.state.from.auth.response": "false", + "saml.artifact.binding": "false", + "saml_force_name_id_format": "false", + "acr.loa.map": "{}", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "token.response.type.bearer.lower-case": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "aeb67b03-0bbd-432c-a329-0a04363e974a", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" + } + }, + { + "id": "7c29a433-d724-4b5b-86fe-d9cf9f6b7ba1", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "id": "d2bda4a4-e2cc-498e-838e-60a7840ccad9", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "af779833-76c2-41ae-a368-67d5789f4b87", + "clientId": "developer-2-client", + "rootUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "test2", + "redirectUris": [ + "*" + ], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "frontchannel.logout.session.required": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature.keyinfo.ext": "false", + "use.refresh.tokens": "true", + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "false", + "client_credentials.use_refresh_token": "false", + "require.pushed.authorization.requests": "false", + "saml.client.signature": "false", + "saml.allow.ecp.flow": "false", + "id.token.as.detached.signature": "false", + "saml.assertion.signature": "false", + "saml.encrypt": "false", + "login_theme": "keycloak", + "saml.server.signature": "false", + "exclude.session.state.from.auth.response": "false", + "saml.artifact.binding": "false", + "saml_force_name_id_format": "false", + "acr.loa.map": "{}", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "token.response.type.bearer.lower-case": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "aeb67a03-0bbd-432c-a329-0a04363e974a", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" + } + }, + { + "id": "7c28a433-d724-4b5b-86fe-d9cf9f6b7ba1", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "id": "d2bda474-e2cc-498e-838e-60a7840ccad9", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "af669833-76c2-41ae-a368-67d5789f4b87", + "clientId": "no-role-client", + "rootUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "test1", + "redirectUris": [ + "*" + ], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "frontchannel.logout.session.required": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature.keyinfo.ext": "false", + "use.refresh.tokens": "true", + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "false", + "client_credentials.use_refresh_token": "false", + "require.pushed.authorization.requests": "false", + "saml.client.signature": "false", + "saml.allow.ecp.flow": "false", + "id.token.as.detached.signature": "false", + "saml.assertion.signature": "false", + "saml.encrypt": "false", + "login_theme": "keycloak", + "saml.server.signature": "false", + "exclude.session.state.from.auth.response": "false", + "saml.artifact.binding": "false", + "saml_force_name_id_format": "false", + "acr.loa.map": "{}", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "token.response.type.bearer.lower-case": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "aeb67c03-0bbd-432c-a329-0a04363e974a", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" + } + }, + { + "id": "7c27a433-d724-4b5b-86fe-d9cf9f6b7ba1", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "id": "d2bda374-e2cc-498e-838e-60a7840ccad9", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "af774034-77c2-41le-a368-67d5789f4b87", + "clientId": "wrong-client", + "rootUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "test1", + "redirectUris": [ + "*" + ], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "frontchannel.logout.session.required": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature.keyinfo.ext": "false", + "use.refresh.tokens": "true", + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "false", + "client_credentials.use_refresh_token": "false", + "require.pushed.authorization.requests": "false", + "saml.client.signature": "false", + "saml.allow.ecp.flow": "false", + "id.token.as.detached.signature": "false", + "saml.assertion.signature": "false", + "saml.encrypt": "false", + "login_theme": "keycloak", + "saml.server.signature": "false", + "exclude.session.state.from.auth.response": "false", + "saml.artifact.binding": "false", + "saml_force_name_id_format": "false", + "acr.loa.map": "{}", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "token.response.type.bearer.lower-case": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "ytb67b03-0yyi-432c-a329-0a04363e974a", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" + } + }, + { + "id": "4c29a234-d731-4b5b-84fe-d9cf9f6b7ba1", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "id": "d2bda9a3-e2cc-498e-838e-60a7840hhod5", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "57e9899-9c1a-498d-96fd-c9bcbd3f3121", + "clientId": "readonly-client", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "test1", + "redirectUris": [ + "*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "login_theme": "keycloak", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "574cef06-3ccf-4ed0-9001-edd83606ff57", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + }, + { + "id": "eae693c7-5b00-4c10-bf8c-c8959925659a", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" + } + }, + { + "id": "c1861851-a5ae-4da0-9110-469abc9bd64d", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "ff7205a6-6580-4cc3-9e97-7c4a39c7562e", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "test1", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "69afe60b-7329-440a-9b4c-0ebec33d8902", + "clientId": "registry-api", + "rootUrl": "", + "adminUrl": "http://localhost:8080", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "test1", + "redirectUris": [ + "*" + ], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "frontchannel.logout.session.required": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature.keyinfo.ext": "false", + "use.refresh.tokens": "true", + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "false", + "client_credentials.use_refresh_token": "false", + "require.pushed.authorization.requests": "false", + "saml.client.signature": "false", + "saml.allow.ecp.flow": "false", + "id.token.as.detached.signature": "false", + "saml.assertion.signature": "false", + "saml.encrypt": "false", + "login_theme": "keycloak", + "saml.server.signature": "false", + "exclude.session.state.from.auth.response": "false", + "saml.artifact.binding": "false", + "saml_force_name_id_format": "false", + "acr.loa.map": "{}", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "token.response.type.bearer.lower-case": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "83a4a269-207b-4818-bbbb-a04abebb2997", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "id": "20241caf-ebb6-467f-acae-7543b143c289", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" + } + }, + { + "id": "eb3a14a2-5c4c-4dd7-99dc-e2734c8c5d98", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "f1beca1a-0756-4dc6-9719-9234cd9b779f", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/registry/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "test1", + "redirectUris": [ + "/admin/registry/console/*" + ], + "webOrigins": [ + "+" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "cb9b9e1a-63a0-460e-a42f-14d1ace49da3", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + } + ], + "clientScopes": [ + { + "id": "db64f4f6-e562-4e43-9f17-255b8a21c5ca", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "bfdfc98c-59eb-4a05-9a74-dd1bb4dea691", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "e285b5a4-2854-46d8-bd06-6ca2911dde8a", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "c6da4bb5-fc32-4e34-88ac-72ca671afd3f", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "id": "5539a2b5-6e75-4533-aaaa-800101d0df4d", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "f7c31e38-1dda-4dcc-82b6-c7d958416962", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "ab66a766-4d9d-40ac-bcc9-264cd5471b92", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "1b1867ba-1a17-412d-ab97-0ae88963d5b5", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "id": "a2766301-ac7f-42f4-bfbe-87d2fdcef382", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "35bbe3f0-d5a3-4785-b3dc-d187330b699d", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "d439a3d1-c6da-4138-87df-c1b851e7b9c8", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "90c61a9b-39ac-4dbd-af49-9123739f98f9", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "9170cc7f-5624-4a15-b50d-cd1b9a0ffb6f", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "f2d44f21-de2f-47d8-b0d5-4e46f9de581b", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "9f0f742e-6202-4543-80e3-80fb2fbfc042", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "033dab17-488c-400d-b603-f75ebe1949b7", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "c87aca2f-23dd-4fc7-b79f-a9b061c165e4", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "4361eae6-7021-49e1-a387-3d8bc4f50247", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "35c03d95-ce9d-4fd2-823b-23e896dbb105", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "0ff4cd3c-6567-4935-bbe8-0191a9bcdd72", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "8e087dfd-2978-4ea5-9ed2-32efca9a98c5", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "31a3c477-3aab-46cd-81e3-2b76b2c8c21c", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "a3606512-379b-4bcb-999d-29cf17812012", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "2f535da7-ea99-4f8d-acbe-988a3691034b", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "2fbfe26c-b175-4b16-97ea-edea4072181a", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "17e5a7d4-2e77-4ff4-8c71-b93e41e4e1bb", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "ca64060a-1d26-43ca-aae7-23c62ed805d4", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "beaddf45-8040-42cd-9236-55fe2134f5df", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "043c162e-e0ba-418b-b965-157f6a52db46", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "5074f873-23f3-4d03-9b75-6c6af91fb7ed", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "ab3d0106-d086-4813-930b-5a27ddfe038b", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "65eabc09-c5a1-4a0e-830c-44a430c129f0", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "f25b9036-55b3-4cc3-bc37-bc0be5405432", + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "e5051472-d2aa-4658-9d95-01064efcca13", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "d6f896d6-b31b-487e-b4a8-af921d3d04eb", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "c7831e59-070e-4d77-8382-d5ef4f60ddd4", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + }, + { + "id": "3456b5e1-4852-48ac-8420-79568ffd9a89", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "userinfo.token.claim": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + } + ] + } + ], + "defaultDefaultClientScopes": [ + "role_list", + "profile", + "email", + "web-origins", + "roles", + "acr" + ], + "defaultOptionalClientScopes": [ + "address", + "offline_access", + "phone", + "microprofile-jwt" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "loginTheme": "keycloak", + "accountTheme": "keycloak", + "adminTheme": "keycloak", + "emailTheme": "keycloak", + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "6b7d0997-665b-4567-b955-f41029b1f7c6", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "b2062994-7819-4b5b-bcaf-bd565e0261f7", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "id": "e28102af-287e-40d3-a64d-636f9d0af6c0", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "7ea991c6-db4b-4989-87c1-2cf0b2e2b371", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "c2aa0c17-6405-441a-96d8-7174001b1f6e", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-usermodel-property-mapper", + "oidc-address-mapper", + "oidc-full-name-mapper", + "saml-user-property-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-sha256-pairwise-sub-mapper", + "saml-user-attribute-mapper", + "saml-role-list-mapper" + ] + } + }, + { + "id": "f1d9cee9-fa10-451a-a96f-486c1abc9184", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "398d86a9-4247-4923-b3bf-a94ef18974f2", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "id": "fd8139d0-82ee-453e-a9aa-0bbd10538eff", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-sha256-pairwise-sub-mapper", + "saml-user-attribute-mapper", + "saml-user-property-mapper", + "oidc-usermodel-property-mapper", + "oidc-full-name-mapper", + "oidc-address-mapper", + "saml-role-list-mapper", + "oidc-usermodel-attribute-mapper" + ] + } + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "9ec415cf-5494-4823-a83d-cc38441b088c", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "cd2dc9b1-a252-4f70-93c0-5bdb51532236", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS256" + ] + } + }, + { + "id": "2dc91aa5-3084-4ceb-973e-24d78e78777a", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [ + "" + ], + "authenticationFlows": [ + { + "id": "e5691b91-7a97-482c-9ba4-fef8933be469", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false + } + ] + }, + { + "id": "c6f47ebc-143a-471b-9f93-0b7faf267717", + "alias": "Authentication Options", + "description": "Authentication options.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "basic-auth", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "basic-auth-otp", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "09cbd3e3-0784-4af0-a279-4fd95216a37f", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "016d5ed7-b797-4d0c-8a2a-da422a1c0b07", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "2122d0ae-d429-43ca-82aa-b2a8ebf265f4", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "7dcbbd10-cfcb-4146-a080-d829ed4bbda8", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Account verification options", + "userSetupAllowed": false + } + ] + }, + { + "id": "f575338f-7ff4-4f6c-9230-2d5a93c01d8e", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "8b96e8ee-9bd6-44a5-9ce6-e12bcb1fca11", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false + } + ] + }, + { + "id": "20339a98-7cbd-4a95-8d8f-f5b60f512524", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "43e87d99-f6c4-495b-a175-35fec32291c4", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "forms", + "userSetupAllowed": false + } + ] + }, + { + "id": "09f61a35-e173-40a8-9bbf-4554586112f0", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "30361549-be4b-4a06-ae79-b60cb3d6ac68", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "ba07e315-0671-4691-af1e-beed84da42ab", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "3ff3f29e-260a-43df-b71b-75a945b220b1", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "User creation or linking", + "userSetupAllowed": false + } + ] + }, + { + "id": "11efa214-3d69-470c-92e1-2ea6cd2f1133", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "b1ee124e-1648-42c2-a268-2db3251b3e44", + "alias": "http challenge", + "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "no-cookie-redirect", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Authentication Options", + "userSetupAllowed": false + } + ] + }, + { + "id": "e5c21eef-7736-42bf-83f7-219f11579be9", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "registration form", + "userSetupAllowed": false + } + ] + }, + { + "id": "51b02897-de8e-47d9-872b-ee06ff1ffd8f", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-profile-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "f3d3919d-e759-49c4-8eb9-e85c16f314a5", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "autheticatorFlow": true, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "84316c99-bba7-42d5-aeeb-2f56b4ce657d", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "43cf7bce-acd0-4c1f-a08e-cac4a1f32a6c", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "6c176502-376d-46fc-835f-0e0352158326", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "terms_and_conditions", + "name": "Terms and Conditions", + "providerId": "terms_and_conditions", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaExpiresIn": "120", + "cibaAuthRequestedUserHint": "login_hint", + "oauth2DeviceCodeLifespan": "600", + "oauth2DevicePollingInterval": "5", + "clientSessionIdleTimeout": "0", + "parRequestUriLifespan": "60", + "clientSessionMaxLifespan": "0", + "cibaInterval": "5" + }, + "keycloakVersion": "25.1.0", + "userManagedAccessAllowed": true, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + } + } +kind: ConfigMap +metadata: + name: keycloak-configmap +--- +apiVersion: v1 +kind: Secret +metadata: + name: keycloak-tls +type: kubernetes.io/tls +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQvakNDQXVhZ0F3SUJBZ0lVVHNkUjVKQ1JMWng4Z05uNS9IYzRMbGU5cEI4d0RRWUpLb1pJaHZjTkFRRUwKQlFBd2RERUxNQWtHQTFVRUJoTUNWVk14Q3pBSkJnTlZCQWdNQWtOQk1SWXdGQVlEVlFRSERBMVRZVzRnUm5KaApibU5wYzJOdk1SRXdEd1lEVlFRS0RBaExaWGxqYkc5aGF6RXRNQ3NHQTFVRUF3d2tjMmx0Y0d4bExXdGxlV05zCmIyRnJMbUZ3Y0hNdVkyeDFjM1JsY2k1bGVHRnRjR3hsTUI0WERUSTFNREV6TVRBM01qSTFNVm9YRFRJMk1ERXoKTVRBM01qSTFNVm93ZERFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVJZd0ZBWURWUVFIREExVApZVzRnUm5KaGJtTnBjMk52TVJFd0R3WURWUVFLREFoTFpYbGpiRzloYXpFdE1Dc0dBMVVFQXd3a2MybHRjR3hsCkxXdGxlV05zYjJGckxtRndjSE11WTJ4MWMzUmxjaTVsZUdGdGNHeGxNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUYKQUFPQ0FROEFNSUlCQ2dLQ0FRRUFxMUtvdXk3UlRtZmlnWDhwZmdJdERvcUFadEJ4UW1YbDh5cUxzZk5JYWJjeQptRldEcHRBZVpRRXJHTU9qRG96U1BiZlBhY1RCUnIvQ0J0T2hOcEd4b2pNTjdGOWdIMGFaRFFmTm14RDVsQkpiClJJS0Q2cGN2U2VTNWZrUCtHS3RwMHd0ZmpURzI3a0E0MStLMHVZdVZPQUhERHdlRU9qUUlpdVVYNDB5Wmlsa3gKeGs5VGF0ejBCZ3B0VVc2V0xZMU1ZWWZqdTB3SGRmTWYxb0diREJ1WDhsQ0dGK3ZIV3B5RUFSamZZQnJ4S0dZYQpVVys5SXN1aTJZbk9YSUR5Q0RabmdQMWN0bXQramM3SlpDUFpkQWtQUzJJOHcvV0xMbVFldHJ5dm5TZGQrUnVuCnZJUGhhRXZhV1YwMTh0dk9BMjRqOHNSVG0xVDdOUGp2RlFvREphY2I5UUlEQVFBQm80R0hNSUdFTUdNR0ExVWQKRVFSY01GcUNKSE5wYlhCc1pTMXJaWGxqYkc5aGF5NWhjSEJ6TG1Oc2RYTjBaWEl1WlhoaGJYQnNaWUlJYTJWNQpZMnh2WVd1Q0RHdGxlV05zYjJGckxuTjJZNElhYTJWNVkyeHZZV3N1YzNaakxtTnNkWE4wWlhJdWJHOWpZV3d3CkhRWURWUjBPQkJZRUZLbng1SGM2RGZVcUlhWGMyZnZxNkZiVkc2V1BNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUIKQVFBTUVjWUFnTDdUMUlRZEcxMnBudndEUDFyY05lSUdkUGdabFZsbVh2dk01N01HSFZQMDZEMlNrYnlBaVFsUQpyNk9oemU0b1MrOVZjcmtxQXFvbzJIMy9mUFQvZEZMeXdxclhXNDdicy83YUV0RDdiR25md0NrZ1MybWhuNFpLCjl2cFdScnJLUTRaQUc4WUc0ckpGYnNhTU5BaE1CL2pvVWpqTGg2MFJvUW5EZFdteWhBYmwwN0FSekNRd3V2OE0KRHZwenJNaS9VOG5CRmx2VVBSZUlwc2VqajlIQXl5ZlJCdjJkSndTWkdMeEZPYjJmbFNJeFNDOGFhYUo4TlFtRApUWmVaSW54dFV2SHdFbm5IMHU1cEIzd0UrdThOa2xHU3U3WG8zY3RLKzlxNENiNTF2R2Fwd2Q2ZGh0am5tbG14CmdnODJTdGFMMW45azAxUmh1OWtlWEEzSgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ3JVcWk3THRGT1orS0IKZnlsK0FpME9pb0JtMEhGQ1plWHpLb3V4ODBocHR6S1lWWU9tMEI1bEFTc1l3Nk1Pak5JOXQ4OXB4TUZHdjhJRwowNkUya2JHaU13M3NYMkFmUnBrTkI4MmJFUG1VRWx0RWdvUHFseTlKNUxsK1EvNFlxMm5UQzErTk1iYnVRRGpYCjRyUzVpNVU0QWNNUEI0UTZOQWlLNVJmalRKbUtXVEhHVDFOcTNQUUdDbTFSYnBZdGpVeGhoK083VEFkMTh4L1cKZ1pzTUc1ZnlVSVlYNjhkYW5JUUJHTjlnR3ZFb1pocFJiNzBpeTZMWmljNWNnUElJTm1lQS9WeTJhMzZOenNsawpJOWwwQ1E5TFlqekQ5WXN1WkI2MnZLK2RKMTM1RzZlOGcrRm9TOXBaWFRYeTI4NERiaVB5eEZPYlZQczArTzhWCkNnTWxweHYxQWdNQkFBRUNnZ0VBQVdiL1A3RTVKZWJpYnRjMzFIYUtnaXNiNGFpOVRZaWdaektsS1ZGSm4wNEYKQmExc3p6M1VzSExKWWRoTi9VblN6SDd3L3o4RlFTbFNYam4rOFoyNlZCM0RDeGN1UzY2Sk9ZZnU2SldsZkdXMAp4akNVbUg1c2ZIWmlqeUozM3hKNFd2Vmg5a2pDY3o1RXpZejl4UzdxenhjY1hkWGN4S2hmSkR3azhROG01UTczCnhMRkNLWHl6aEV2VS9iWEkxYnBJaUhVUjI1OXU4R3FWU2RBLzlsVEhxQ1J2OUh0R0w4bWZaNjZXbmNtdnVJYTgKQWg4RThmbFIxMFRvdGFyeDE2MFhoZVZ2SWZHMHpJWHNqRU9ydWZpU09VZzZNTkI3Ukt6UGxyemxwemRNSXhndwpFbXcwR0t1MlZpRFY0YmhGeWpJVmF2Und3TnpNbFF0Q2hja0k3NlBKRVFLQmdRRFpIcmlsQ25WUHVCTVgwOWd4Cm8vdy9Wb0RubEd2VjhGcVdnTmxEaEQ2bUdQdEVSd3JWc0lGc0xCMzBOUGZKQ2JxL1k0NlFXRkFEWE1lUVV6MFcKVnV4OHZtYkhIYW53QzNJUmdzTEVJZWlBYVNyalNvd0dJM25ScWdXRDIyUXNxUElWSE1nMTA1VDFEVlFoUFRVNQo1eG52NEpxVHpwY2ZNYllKaWw4TzVleXVMUUtCZ1FES0FINHpyeXRNd0FZRXl0bUUwR1FxaXp6bWtqSCtNYndvCitMeHNCc2s2NW8xUDNNaTFRTFFNVk9ESUp1aU5paVkxWldWUnB3QUttOVBCVWF3dWkyL3hnTVEzR0dCM2lReVUKN09rYjhzQjVWcU9RbllPekFXclQ1WjVMbEE2bU9pNTlkaGo4VGhRK3A3dmhBTThxYUdFaDUzbytaaVI4NlJOYQpmclc1NnJFSjZRS0JnQU50eThhM2VwRE92R1owN3dZaHE5c29ONUx3c2g5SDAzWnJCSU5mZEVuVlBTcWY0Smp0ClY2M29xSFJ4M2JQc0ZtbnRYWlFFQVFkOGtKNUQ0a1FYRFZjTy9ycUlTbVhER0lNVVNKVW1YL0NmT0pFc1hUZS8KbWEzby9IdFBuSzVqNEtiYXRJSzlHNjRmRHNRVG5tS1N0c01tRGVybHFWZFJ4cWt4NlR4bi95dmxBb0dBSTRPRwpySXVGYjA3clAvaHgyWm5Sb25Fc1dkVW1HT3loaWdxQkJmVURkK21FdXArMFlibG9iaXFXQWxwazFFMlBmaCsvCmVtV0Q2bG1QRnhQRzA4QU1CRWVCTjdaL2IwU1hBNi9MSS9DL3loVmpXRTk1RWdXVHhJRkFIVVdBZGRVUXkxbngKOXBtN2tYQm41WjBBenhIZTBka2ljc24rYkQrRitZa0JaaGhPMjlFQ2dZRUFwTlhxRVZ5NUdsa0pOWjBKZS9YVwp6ZHpMdkl0eEZObWxKMkpuaGtWdmMxRXJ3RlovQ3l0SGdwMnQ1aUZQTXFoYThhSnRsdzVkVDJBZno4bWtHWnc5CmRmMmozaTljWU00cDdXbm9SMkZ0TERsTjdEY1lrK0tDSHYvMTlycjdOVXhOZ0NJc3Q5SHhod0FGeEt4djBmSEoKRUVJdjRiNkQwU25JOTFRQnk0T1JIVEk9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= +--- +apiVersion: v1 +kind: Secret +metadata: + name: keycloak-truststore +data: + truststore: MIILIAIBAzCCCtYGCSqGSIb3DQEHAaCCCscEggrDMIIKvzCCBQoGCSqGSIb3DQEHBqCCBPswggT3AgEAMIIE8AYJKoZIhvcNAQcBMF8GCSqGSIb3DQEFDTBSMDEGCSqGSIb3DQEFDDAkBBDn4CIMajvaNozhj/sL9kKiAgIIADAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQOglKxdNL07e7TsHpFzm+ooCCBIA58+CNnYjvMf+A1QgOXKAhmPahQ+r801Z4j5fYejxXzahxdxNqdmWoalOqyn44b1QzuyRILta79ZKzivGnON9KQo6GhvoIjTi45qqxYt4Lju+4UJiTdVpS8WH29sVIWdtoOGNZ1RvjqBI4Y4a/F7k+606MCaK+oNUtyoT3sl84bzebICxdfackE26ePg7z5d8wnFLozQUpydz1X9B2r3iPgBlnTep6BWQ1/nQ+VzwmXGkIpFtlfYFgeyAyzcYeVbnd1OsHpH/rLbCwZ+xW/aP0CBWzdJ2aXImr0lePjvi6SPM+wiKrPU1JWjIWswbcpjhFS3Bpkj6j5UXAP7dx67tMvoqruQJ8OnhI6rFg8vvJkla6cndeG8dkQtOYVBnl3RSEUggyXr8QGGdNRHDsjfF/RmQaAM3ozrOTCGylr7BzvG7xH+dYEJ1bVSSsXcQsYMplDkhfH7sLtOJLD+4PqOpHDxmCqQxGt+tLupKiDgHj1AMkpxzwBpgwjGFPO3jhH8NYkAaLfDt+ES8iCuBszhZejYtMDN6HS8Dvm+tPcn5uIpUrwfCZ59yu6UqNWMDVv65yvaneGTfnAjGdDvOdPyvCBpgZ/5iQofpwcUcuN7ZVK8X9+CrCVNVvKKNX10MJz0MuqG2AolbrQPW3W6G0KrYps1P1QiGcmbOx8QoxSDbMVWigwRiAprE0/YFFwLpgNm5Bl6vjfEyjUtdTJVaiI26iH2IXoR9qUBXczic83Lpo30ySrvBWr6g77y+Ib35VdxMBEKZoTtqO6q/HnsqK5tPsAostQkWsLtuyLIwVC4WnaaaXcAPAQs/6Z8R4TCSxdyjqoqouRB1aE0Loe7eoDDhHsik0C3GBa9MX2tzSs9CTih+2EvOQ6WBC0XWTfkICXMduZBg4FbuB4jlqRnm9Adi6Eu3Laa0LeQe+8FUN+cQcFu3G14XgHVtkpj65OXqYZpsUcxyRVsZ/QgbNpTV6bUimj8NaMMnshdMXp22bgH0qFXa0iKIgUG9xQdSuutKokcbuQK9M8IF7UtO/A38gPxRf2FJHN/l8nKaaYH68vfFNFpYlfgm5vY/E5OHATIqPZmP3he0Bj8oHx1Zzj/HaNM092c8amHtWgrKWJ76gq734yBTFp7cBkUhwoThCtIC4RNRwbQcL2lsTYp7iAJ1Oh++cg+70E/eGJSdgwIDhJxDgnp3CiR8TPYL3yTvqozpYSikLsBd3LPCkZobYq/K5rXQCB3y5bY+8LI2e4/GzqTR4q1tvyQ+7yP9Em7MdA5ohXycrYOTIRwpe8I4oEZ/aybfHQixy3N00YEehpbMePms3F4OQDNEJMrE/u0Gqg9T4xX196MMIFFth4o42N5dDi9997mxeLPWmGV1k82GNvJYCAgM4W+QUbqUbtNFvUaK/w60KPPkHZK3A/N91B0HX6OMOF++2/owJJwytyZgWZY6Fp01NFbag7H6K4QbArqsiyNT8DSymmsR76BeDLkyQI5gddZ2plbDPX1JGzcom7/ANjjLuiJ++/JDMxWE1XJvbQC4wggWtBgkqhkiG9w0BBwGgggWeBIIFmjCCBZYwggWSBgsqhkiG9w0BDAoBAqCCBTkwggU1MF8GCSqGSIb3DQEFDTBSMDEGCSqGSIb3DQEFDDAkBBDetqzZxOvqJumSDz2EX/2/AgIIADAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQtEVpQcFjXX1X05ALfJ8rCASCBNBgvZ5M3otctDoekXJFiCGXioYrmotnO4LV0lvjlsMBCjiI3mB65WoZT+HX7yL0qU62deoi3quZLNg3BsTzZ2sVeNddxRuE+EC0J3CvNlq1b3xGLNmsQ7OGgWkOBY26HJWJgxqlJzhhvNeU1kXRpPRKn/flzLGSOMi68O5ou+et0prxYmqusmkQy5KYLJRkgg44kSq5HU92vpW/T2Bp30sbHpNfq6wz7Ik9LIxSAlVC+N6nw/9MbpC8EVzIeGYnpdsijxsE+U3gEjT3f+P715UaOMu9YpTAp2OCGGuNUUsMAIUXe9oYpb1Tb5eDPhJTPaOxL6wyRfr2RIsvDE2+Ch2t0XrF8b2Snpknu44KzuxX66yIDWTUp8ihraew0T0p3qZApHtMbAFAzjooDOohog2BSLKQ7j16YRXh5EonzHGNG319uuPk9mXSS7TPosn4hNJlu7q0QSzo6t51LfkcTVngtjZfTox3qJE3wbjEceMdVXNe1Y92LCG8ioDJgjy1VfKexs85Gn8C9+WFcfckBaPGnbGaD1Wbdm5Dz+kqu27s0VJYdF1XIAPt+lrVHtkBfS+JucbEz8yLhJ+axJCFHdTFfOq4LwGHzPOmmLDLVen1Q8EbrHnjOY0JXusvpZbSPXW4LwKw6YFC8gWrZXOY1XCF6e/mZltkZZIcuAn6ZJKn3d8JuvMffoPpvt7XLGwD5dm4riuzeZ8JU2S5HzEVYS/o3+G/XDTgxWkgAiqC9lI4Wz1o3kcQMSeuVqhcs5mmOEChLauExWGlQia4siGjqCJvrXSldhihwK+7Vg8+EuneznqdMhvkXgdAKXMyupwZIVLhTafR21YeyCF4vezxGOUsrrtGEJU/Cdu0ND8NG1OLY/ZXqzxuGfkCaMlPuu2C1xHvI45E/WyyQKEWWWHTTjo1Fu/t1o0JgWN6F6HJno9zcLctq3USxVZrfblvVXljBF0OtptboPHVrOH98kd5FSK3QHDOtgMoyVhjBKHaM6Ut0MYannmJ0YBZghtgzZ18s9bYoQoVbDJtPAV55dhrKJp6gWdDCbyNBLTZ0E3aDx7vupGZch/pUKrnQ2m4BzuJABBtSlV4VnqllbHt6obOCezf7gVaWCwBIewl/T8BoQ5qMbixLkZ1G2jtcA2pZo1QSxGlQJGd1wfcy07Xu29yFrbh9hQLiX1beJo8UiN5C+P0aQ0Xbg/b9JtxNOdAzOpyYIgaAcPTE4D5dhqSa5Oe86XJy5FGX7S7RpAH4O9fs0mo6RWPw+cN8gI66/vCJN0+Ym2CzG9//Kr9f5mXCqMDJy7MHO+FfFEiR2IekiDu1VZl/tPohWusR0yCqM0Zz29cW7jtMFwFrPTpwFIgUh5ZXpIJem80CKeLgW4eWqQr0QTc4ghutOT50GK+PDrzrs/sp3JD1K0INphbvVn6V0SHeJLrLuQnT9+o0d2T9ep6gko0G+UgoI+xCybcmgipQH6MCxnPUoy7voKrRYGijgU2YBKSbaW+uYngL+S5zwd9gwE4XccmOTRjEcPaCOTd7jOgGxGkeXvouxNwGTIX9oc5SUbfzd8Fs2bW5omC64MaFzMKCXhdbT0+RNGyjYH58uL7+2h7zuWNjoosU7ODeAsN/35rEDAwDODrp+I18U7FSMAA/TFGMB8GCSqGSIb3DQEJFDESHhAAawBlAHkAYwBsAG8AYQBrMCMGCSqGSIb3DQEJFTEWBBTa7p1A+fn/spfdXFP1QSRoRetsEDBBMDEwDQYJYIZIAWUDBAIBBQAEIE0bqSV/+ruRpyhTLVyeuKwFEK5s5AyJl6aPdGIudg1SBAg7YpTb884DsgICCAA= + password: YXBpY3VyaW8= +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: keycloak + labels: + app: keycloak +spec: + replicas: 1 + selector: + matchLabels: + app: keycloak + template: + metadata: + labels: + app: keycloak + spec: + containers: + - name: keycloak + image: quay.io/keycloak/keycloak:26.1.0 + args: ["start-dev", "--import-realm"] + env: + - name: KC_HOSTNAME + value: "simple-keycloak.apps.cluster.example" + - name: KC_BOOTSTRAP_ADMIN_USERNAME + value: "admin" + - name: KC_BOOTSTRAP_ADMIN_PASSWORD + value: "admin" + - name: KC_PROXY + value: "edge" + - name: KC_HEALTH_ENABLED + value: "true" + - name: KC_HTTPS_PORT + value: "8443" + - name: PROXY_ADDRESS_FORWARDING + value: "true" + - name: KEYCLOAK_FRONTEND_URL + value: "https://simple-keycloak.apps.cluster.example" + - name: KC_HTTPS_CERTIFICATE_FILE + value: "/etc/x509/https/tls.crt" + - name: KC_HTTPS_CERTIFICATE_KEY_FILE + value: "/etc/x509/https/tls.key" + ports: + - name: http + containerPort: 8443 + - name: health + containerPort: 9000 + readinessProbe: + httpGet: + path: /health/ready + port: 9000 + scheme: HTTPS + volumeMounts: + - name: keycloak-tls + mountPath: /etc/x509/https + readOnly: true + - name: keycloak-volume + mountPath: /opt/keycloak/data/import + volumes: + - name: keycloak-volume + configMap: + name: keycloak-configmap + - name: keycloak-tls + secret: + secretName: keycloak-tls +--- +apiVersion: v1 +kind: Service +metadata: + name: keycloak + labels: + app: keycloak +spec: + ports: + - name: https + port: 443 + targetPort: 8443 + selector: + app: keycloak + type: LoadBalancer +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: keycloak +spec: + tls: + - hosts: + - simple-keycloak.apps.cluster.example + secretName: keycloak-tls + rules: + - host: simple-keycloak.apps.cluster.example + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: keycloak + port: + number: 443 \ No newline at end of file diff --git a/operator/controller/src/test/resources/k8s/examples/auth/simple-with_keycloak.apicurioregistry3.yaml b/operator/controller/src/test/resources/k8s/examples/auth/simple-with_keycloak.apicurioregistry3.yaml new file mode 100644 index 0000000000..81a2d74418 --- /dev/null +++ b/operator/controller/src/test/resources/k8s/examples/auth/simple-with_keycloak.apicurioregistry3.yaml @@ -0,0 +1,24 @@ +apiVersion: registry.apicur.io/v1 +kind: ApicurioRegistry3 +metadata: + name: simple +spec: + app: + ingress: + host: simple-app.apps.cluster.example + auth: + enabled: true + appClientId: registry-api + uiClientId: apicurio-registry + authServerUrl: https://simple-keycloak.apps.cluster.example/realms/registry + redirectURI: https://simple-ui.apps.cluster.example + logoutURL: https://simple-ui.apps.cluster.example + anonymousReads: true + basicAuth: + enabled: true + cacheExpiration: 25 + tls: + tlsVerificationType: none + ui: + ingress: + host: simple-ui.apps.cluster.example diff --git a/operator/controller/src/test/resources/k8s/examples/auth/tls/simple-with_keycloak.apicurioregistry3.yaml b/operator/controller/src/test/resources/k8s/examples/auth/tls/simple-with_keycloak.apicurioregistry3.yaml new file mode 100644 index 0000000000..fc5a4b0d9c --- /dev/null +++ b/operator/controller/src/test/resources/k8s/examples/auth/tls/simple-with_keycloak.apicurioregistry3.yaml @@ -0,0 +1,29 @@ +apiVersion: registry.apicur.io/v1 +kind: ApicurioRegistry3 +metadata: + name: simple +spec: + app: + ingress: + host: simple-app.apps.cluster.example + auth: + enabled: true + appClientId: registry-api + uiClientId: apicurio-registry + authServerUrl: https://simple-keycloak.apps.cluster.example/realms/registry + redirectURI: https://simple-ui.apps.cluster.example + logoutURL: https://simple-ui.apps.cluster.example + tls: + tlsVerificationType: required + truststoreSecretRef: + name: keycloak-truststore + key: truststore + truststorePasswordSecretRef: + name: keycloak-truststore + key: password + ui: + ingress: + host: simple-ui.apps.cluster.example + env: + - name: REGISTRY_API_URL + value: https://simple-app.apps.cluster.example/apis/registry/v3 diff --git a/operator/install/install.yaml b/operator/install/install.yaml index a1be0c3854..b4261e716f 100644 --- a/operator/install/install.yaml +++ b/operator/install/install.yaml @@ -27,6 +27,158 @@ spec: app: description: Configure Apicurio Registry backend (app) component. properties: + auth: + description: | + Configure authentication and authorization of Apicurio Registry. + properties: + anonymousReads: + description: To allow anonymous users, such as REST API calls + with no authentication credentials, to make read-only calls + to the REST API, set the following option to true. + type: boolean + appClientId: + description: |- + Apicurio Registry backend clientId used for OIDC authentication. + In Identity providers like Keycloak, this is the client id used for the Quarkus backend application + type: string + authServerUrl: + description: URL of the identity server. + type: string + authz: + description: Authorization configuration. + properties: + adminOverride: + description: Admin override configuration + properties: + claimName: + description: The name of a JWT token claim to use + for determining admin-override. + type: string + claimValue: + description: The value that the JWT token claim indicated + by the CLAIM variable must be for the user to be + granted admin-override. + type: string + enabled: + description: Auth admin override enabled. + type: boolean + from: + description: Where to look for admin-override information. + Only token is currently supported. + type: string + role: + description: The name of the role that indicates a + user is an admin. + type: string + type: + description: The type of information used to determine + if a user is an admin. Values depend on the value + of the FROM variable, for example, role or claim + when FROM is token. + type: string + type: object + adminRole: + description: The name of the role that indicates a user + is an admin. + type: string + developerRole: + description: The name of the role that indicates a user + is a developer. + type: string + enabled: + description: Enabled role-based authorization. + type: boolean + groupAccess: + description: When owner-only authorization and group owner-only + authorization are both enabled, only the user who created + an artifact group has write access to that artifact + group, for example, to add or remove artifacts in that + group. + type: boolean + ownerOnly: + description: When owner-only authorization is enabled, + only the user who created an artifact can modify or + delete that artifact. + type: boolean + readAccess: + description: When the authenticated read access option + is enabled, Apicurio Registry grants at least read-only + access to requests from any authenticated user in the + same organization, regardless of their user role. + type: boolean + readOnlyRole: + description: The name of the role that indicates a user + has read-only access. + type: string + roleSource: + description: When set to token, user roles are taken from + the authentication token. + type: string + type: object + basicAuth: + description: Client credentials basic auth configuration. + properties: + cacheExpiration: + description: Client credentials token expiration time. + type: string + enabled: + description: Enabled client credentials. + type: boolean + type: object + enabled: + description: |- + Enable Apicurio Registry Authentication. + In Identity providers like Keycloak, this is the client id used for the Quarkus backend application + type: boolean + logoutURL: + description: Apicurio Registry UI redirect URI used for redirection + after logout. + type: string + redirectURI: + description: Apicurio Registry UI redirect URI used for redirection + after successful authentication. + type: string + tls: + description: |- + OIDC TLS configuration. + When custom certificates are used, this is the field to be used to configure the trustore + properties: + tlsVerificationType: + description: Verify the identity server certificate. + type: string + truststorePasswordSecretRef: + description: Name of a Secret that contains the TLS truststore + password. Key `ca.password` is assumed by default. + properties: + key: + description: Name of the key in the referenced Secret + that contain the target data. This field might be + optional if a default value has been defined. + type: string + name: + description: Name of a Secret that is being referenced. + type: string + type: object + truststoreSecretRef: + description: Name of a Secret that contains the TLS truststore + (in PKCS12 format). Key `ca.p12` is assumed by default. + properties: + key: + description: Name of the key in the referenced Secret + that contain the target data. This field might be + optional if a default value has been defined. + type: string + name: + description: Name of a Secret that is being referenced. + type: string + type: object + type: object + uiClientId: + description: |- + Apicurio Registry UI clientId used for OIDC authentication. + In Identity providers like Keycloak, this is the client id used for the frontend React application + type: string + type: object env: description: Configure a list of environment variables that will be passed to this components' container. diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppSpec.java index bf06cf5e09..d98e1a0565 100644 --- a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppSpec.java +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppSpec.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.JsonDeserializer.None; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import io.apicurio.registry.operator.api.v1.spec.auth.AuthSpec; import lombok.*; import lombok.experimental.SuperBuilder; @@ -12,7 +13,7 @@ @JsonDeserialize(using = None.class) @JsonInclude(NON_NULL) -@JsonPropertyOrder({ "env", "ingress", "podTemplateSpec", "storage", "sql", "kafkasql", "features" }) +@JsonPropertyOrder({ "env", "ingress", "podTemplateSpec", "storage", "sql", "kafkasql", "features", "auth" }) @NoArgsConstructor @AllArgsConstructor(access = PRIVATE) @SuperBuilder(toBuilder = true) @@ -42,6 +43,16 @@ Configure features of the Apicurio Registry backend (app). @JsonSetter(nulls = SKIP) private AppFeaturesSpec features; + /** + * Configure features of the Apicurio Registry application. + */ + @JsonProperty("auth") + @JsonPropertyDescription(""" + Configure authentication and authorization of Apicurio Registry. + """) + @JsonSetter(nulls = SKIP) + private AuthSpec auth; + /** * DEPRECATED: Use the `app.storage.type` and `app.storage.sql` fields instead. The operator will attempt * to update the fields automatically. diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AdminOverrideSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AdminOverrideSpec.java new file mode 100644 index 0000000000..d68f3bca61 --- /dev/null +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AdminOverrideSpec.java @@ -0,0 +1,69 @@ +package io.apicurio.registry.operator.api.v1.spec.auth; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; +import static lombok.AccessLevel.PRIVATE; + +@JsonDeserialize(using = JsonDeserializer.None.class) +@JsonInclude(NON_NULL) +@JsonPropertyOrder({ "enabled", "from", "type", "role", "claimName", "claimValue" }) +@NoArgsConstructor +@AllArgsConstructor(access = PRIVATE) +@SuperBuilder(toBuilder = true) +@Getter +@Setter +@EqualsAndHashCode +@ToString +public class AdminOverrideSpec { + + @JsonProperty("enabled") + @JsonPropertyDescription(""" + Auth admin override enabled.""") + @JsonSetter(nulls = Nulls.SKIP) + private Boolean enabled; + + @JsonProperty("from") + @JsonPropertyDescription(""" + Where to look for admin-override information. Only token is currently supported.""") + @JsonSetter(nulls = Nulls.SKIP) + private String from; + + @JsonProperty("type") + @JsonPropertyDescription(""" + The type of information used to determine if a user is an admin. Values depend on the value of the FROM variable, for example, role or claim when FROM is token.""") + @JsonSetter(nulls = Nulls.SKIP) + private String type; + + @JsonProperty("role") + @JsonPropertyDescription(""" + The name of the role that indicates a user is an admin.""") + @JsonSetter(nulls = Nulls.SKIP) + private String role; + + @JsonProperty("claimName") + @JsonPropertyDescription(""" + The name of a JWT token claim to use for determining admin-override.""") + @JsonSetter(nulls = Nulls.SKIP) + private String claimName; + + @JsonProperty("claimValue") + @JsonPropertyDescription(""" + The value that the JWT token claim indicated by the CLAIM variable must be for the user to be granted admin-override.""") + @JsonSetter(nulls = Nulls.SKIP) + private String claimValue; +} diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthSpec.java new file mode 100644 index 0000000000..5e16f6b07e --- /dev/null +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthSpec.java @@ -0,0 +1,98 @@ +package io.apicurio.registry.operator.api.v1.spec.auth; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; +import static lombok.AccessLevel.PRIVATE; + +@JsonDeserialize(using = JsonDeserializer.None.class) +@JsonInclude(NON_NULL) +@JsonPropertyOrder({ "enabled", "appClientId", "uiClientId", "redirectURI", "authServerUrl", "logoutURL", + "anonymousReads", "basicAuth", "tls", "authz" }) +@NoArgsConstructor +@AllArgsConstructor(access = PRIVATE) +@SuperBuilder(toBuilder = true) +@Getter +@Setter +@EqualsAndHashCode +@ToString +public class AuthSpec { + + @JsonProperty("enabled") + @JsonPropertyDescription(""" + Enable Apicurio Registry Authentication. + In Identity providers like Keycloak, this is the client id used for the Quarkus backend application""") + @JsonSetter(nulls = Nulls.SKIP) + private Boolean enabled; + + @JsonProperty("appClientId") + @JsonPropertyDescription(""" + Apicurio Registry backend clientId used for OIDC authentication. + In Identity providers like Keycloak, this is the client id used for the Quarkus backend application""") + @JsonSetter(nulls = Nulls.SKIP) + private String appClientId; + + @JsonProperty("uiClientId") + @JsonPropertyDescription(""" + Apicurio Registry UI clientId used for OIDC authentication. + In Identity providers like Keycloak, this is the client id used for the frontend React application""") + @JsonSetter(nulls = Nulls.SKIP) + private String uiClientId; + + @JsonProperty("redirectURI") + @JsonPropertyDescription(""" + Apicurio Registry UI redirect URI used for redirection after successful authentication.""") + @JsonSetter(nulls = Nulls.SKIP) + private String redirectURI; + + @JsonProperty("authServerUrl") + @JsonPropertyDescription(""" + URL of the identity server.""") + @JsonSetter(nulls = Nulls.SKIP) + private String authServerUrl; + + @JsonProperty("logoutURL") + @JsonPropertyDescription(""" + Apicurio Registry UI redirect URI used for redirection after logout.""") + @JsonSetter(nulls = Nulls.SKIP) + private String logoutURL; + + @JsonProperty("anonymousReads") + @JsonPropertyDescription(""" + To allow anonymous users, such as REST API calls with no authentication credentials, to make read-only calls to the REST API, set the following option to true.""") + @JsonSetter(nulls = Nulls.SKIP) + private Boolean anonymousReads; + + @JsonProperty("basicAuth") + @JsonPropertyDescription(""" + Client credentials basic auth configuration.""") + @JsonSetter(nulls = Nulls.SKIP) + private BasicAuthSpec basicAuth; + + @JsonProperty("tls") + @JsonPropertyDescription(""" + OIDC TLS configuration. + When custom certificates are used, this is the field to be used to configure the trustore""") + @JsonSetter(nulls = Nulls.SKIP) + private AuthTLSSpec tls; + + @JsonProperty("authz") + @JsonPropertyDescription(""" + Authorization configuration.""") + @JsonSetter(nulls = Nulls.SKIP) + private AuthzSpec authz; +} diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthTLSSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthTLSSpec.java new file mode 100644 index 0000000000..4a21d864c1 --- /dev/null +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthTLSSpec.java @@ -0,0 +1,55 @@ +package io.apicurio.registry.operator.api.v1.spec.auth; + +import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.databind.JsonDeserializer.None; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import io.apicurio.registry.operator.api.v1.spec.SecretKeyRef; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import static lombok.AccessLevel.PRIVATE; + +@JsonDeserialize(using = None.class) +@JsonInclude(Include.NON_NULL) +@JsonPropertyOrder({ "tlsVerificationType", "truststoreSecretRef", "truststorePasswordSecretRef" }) +@NoArgsConstructor +@AllArgsConstructor(access = PRIVATE) +@SuperBuilder(toBuilder = true) +@Getter +@Setter +@EqualsAndHashCode +@ToString +public class AuthTLSSpec { + + /** + * Type of TLS verification. + */ + @JsonProperty("tlsVerificationType") + @JsonPropertyDescription(""" + Verify the identity server certificate.""") + @JsonSetter(nulls = Nulls.SKIP) + private String tlsVerificationType; + + /** + * Name of a Secret that contains the TLS truststore (in PKCS12 format). Key ca.p12 is + * assumed by default. + */ + @JsonProperty("truststoreSecretRef") + @JsonPropertyDescription(""" + Name of a Secret that contains the TLS truststore (in PKCS12 format). \ + Key `ca.p12` is assumed by default.""") + @JsonSetter(nulls = Nulls.SKIP) + private SecretKeyRef truststoreSecretRef; + + /** + * Name of a Secret that contains the TLS truststore password. Key ca.password is assumed by + * default. + */ + @JsonProperty("truststorePasswordSecretRef") + @JsonPropertyDescription(""" + Name of a Secret that contains the TLS truststore password. \ + Key `ca.password` is assumed by default.""") + @JsonSetter(nulls = Nulls.SKIP) + private SecretKeyRef truststorePasswordSecretRef; +} diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthzSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthzSpec.java new file mode 100644 index 0000000000..805921b1ed --- /dev/null +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthzSpec.java @@ -0,0 +1,87 @@ +package io.apicurio.registry.operator.api.v1.spec.auth; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import static lombok.AccessLevel.PRIVATE; + +@JsonDeserialize(using = JsonDeserializer.None.class) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ "enabled", "ownerOnly", "groupAccess", "readAccess", "roleSource", "adminRole", + "developerRole", "readOnlyRole", "adminOverride" }) +@NoArgsConstructor +@AllArgsConstructor(access = PRIVATE) +@SuperBuilder(toBuilder = true) +@Getter +@Setter +@EqualsAndHashCode +@ToString +public class AuthzSpec { + + @JsonProperty("enabled") + @JsonPropertyDescription(""" + Enabled role-based authorization.""") + @JsonSetter(nulls = Nulls.SKIP) + private Boolean enabled; + + @JsonProperty("ownerOnly") + @JsonPropertyDescription(""" + When owner-only authorization is enabled, only the user who created an artifact can modify or delete that artifact.""") + @JsonSetter(nulls = Nulls.SKIP) + private Boolean ownerOnly; + + @JsonProperty("groupAccess") + @JsonPropertyDescription(""" + When owner-only authorization and group owner-only authorization are both enabled, only the user who created an artifact group has write access to that artifact group, for example, to add or remove artifacts in that group.""") + @JsonSetter(nulls = Nulls.SKIP) + private Boolean groupAccess; + + @JsonProperty("readAccess") + @JsonPropertyDescription(""" + When the authenticated read access option is enabled, Apicurio Registry grants at least read-only access to requests from any authenticated user in the same organization, regardless of their user role.""") + @JsonSetter(nulls = Nulls.SKIP) + private Boolean readAccess; + + @JsonProperty("roleSource") + @JsonPropertyDescription(""" + When set to token, user roles are taken from the authentication token.""") + @JsonSetter(nulls = Nulls.SKIP) + private String roleSource; + + @JsonProperty("adminRole") + @JsonPropertyDescription(""" + The name of the role that indicates a user is an admin.""") + @JsonSetter(nulls = Nulls.SKIP) + private String adminRole; + + @JsonProperty("developerRole") + @JsonPropertyDescription(""" + The name of the role that indicates a user is a developer.""") + @JsonSetter(nulls = Nulls.SKIP) + private String developerRole; + + @JsonProperty("readOnlyRole") + @JsonPropertyDescription(""" + The name of the role that indicates a user has read-only access.""") + @JsonSetter(nulls = Nulls.SKIP) + private String readOnlyRole; + + @JsonProperty("adminOverride") + @JsonPropertyDescription(""" + Admin override configuration""") + @JsonSetter(nulls = Nulls.SKIP) + private AdminOverrideSpec adminOverride; +} \ No newline at end of file diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/BasicAuthSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/BasicAuthSpec.java new file mode 100644 index 0000000000..aa781df86f --- /dev/null +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/BasicAuthSpec.java @@ -0,0 +1,45 @@ +package io.apicurio.registry.operator.api.v1.spec.auth; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; +import static lombok.AccessLevel.PRIVATE; + +@JsonDeserialize(using = JsonDeserializer.None.class) +@JsonInclude(NON_NULL) +@JsonPropertyOrder({ "enabled", "cacheExpiration" }) +@NoArgsConstructor +@AllArgsConstructor(access = PRIVATE) +@SuperBuilder(toBuilder = true) +@Getter +@Setter +@EqualsAndHashCode +@ToString +public class BasicAuthSpec { + + @JsonProperty("enabled") + @JsonPropertyDescription(""" + Enabled client credentials.""") + @JsonSetter(nulls = Nulls.SKIP) + private Boolean enabled; + + @JsonProperty("cacheExpiration") + @JsonPropertyDescription(""" + Client credentials token expiration time.""") + @JsonSetter(nulls = Nulls.SKIP) + private String cacheExpiration; +}