From f0a680894f163dd3be4fb1f8eeffe0a649b83119 Mon Sep 17 00:00:00 2001 From: Carles Arnal Date: Tue, 28 Jan 2025 14:53:29 +0100 Subject: [PATCH 01/11] Add basic testing infrastructure for auth support --- .../registry/operator/it/KeycloakITTest.java | 55 + .../resources/k8s/examples/auth/keycloak.yaml | 3226 +++++++++++++++++ ...imple-with_keycloak.apicurioregistry3.yaml | 11 + .../operator/api/v1/spec/AppAuthSpec.java | 4 + .../operator/api/v1/spec/AppSpec.java | 1 + .../api/v1/spec/StudioUiAuthSpec.java | 4 + .../operator/api/v1/spec/UiAuthSpec.java | 4 + 7 files changed, 3305 insertions(+) create mode 100644 operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java create mode 100644 operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml create mode 100644 operator/controller/src/test/resources/k8s/examples/auth/simple-with_keycloak.apicurioregistry3.yaml create mode 100644 operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppAuthSpec.java create mode 100644 operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/StudioUiAuthSpec.java create mode 100644 operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/UiAuthSpec.java diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java new file mode 100644 index 0000000000..b12f581c68 --- /dev/null +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java @@ -0,0 +1,55 @@ +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.api.model.PodCondition; +import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBinding; +import io.fabric8.kubernetes.api.model.rbac.RoleBinding; +import io.fabric8.kubernetes.client.utils.Serialization; +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.net.URL; +import java.util.List; + +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.ResourceFactory.deserialize; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +@QuarkusTest +public class KeycloakITTest extends ITBase { + + private static final Logger log = LoggerFactory.getLogger(KeycloakITTest.class); + + @Test + void testKeycloakPlain() { + //Preparation, deploy Keycloak + client.load(KeycloakITTest.class + .getResourceAsStream("/k8s/examples/auth/keycloak.yaml")).create(); + await().ignoreExceptions().untilAsserted(() -> { + assertThat(client.apps().deployments() + .withName("keycloak").get() + .getStatus().getReadyReplicas()).isEqualTo(1); + }); + + //Deploy Registry + var registry = deserialize( + "k8s/examples/auth/simple-with_keycloak.apicurioregistry3.yaml", + ApicurioRegistry3.class); + registry.getMetadata().setNamespace(namespace); + + client.resource(registry).create(); + + //Assertions, verify Registry deployment + checkDeploymentExists(registry, COMPONENT_APP, 1); + checkDeploymentExists(registry, COMPONENT_UI, 1); + //FIXME: Check Registry deployment has the expected auth values. + } +} 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..9080e127ab --- /dev/null +++ b/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml @@ -0,0 +1,3226 @@ +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: 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_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_HTTP_PORT + value: "8090" + ports: + - name: http + containerPort: 8090 + readinessProbe: + httpGet: + path: /health/ready + port: 9000 + volumeMounts: + - name: keycloak-volume + mountPath: /opt/keycloak/data/import + volumes: + - name: keycloak-volume + configMap: + name: keycloak-configmap +--- +apiVersion: v1 +kind: Service +metadata: + name: keycloak + labels: + app: keycloak +spec: + ports: + - name: http + port: 8090 + targetPort: 8090 + selector: + app: keycloak + type: LoadBalancer +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: keycloak +spec: + tls: + - hosts: + - simple-keycloak.apps.cluster.example + rules: + - host: simple-keycloak.apps.cluster.example + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: keycloak + port: + number: 8090 \ 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..2cd8be6262 --- /dev/null +++ b/operator/controller/src/test/resources/k8s/examples/auth/simple-with_keycloak.apicurioregistry3.yaml @@ -0,0 +1,11 @@ +apiVersion: registry.apicur.io/v1 +kind: ApicurioRegistry3 +metadata: + name: simple +spec: + app: + ingress: + host: simple-app.apps.cluster.example + ui: + ingress: + host: simple-ui.apps.cluster.example diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppAuthSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppAuthSpec.java new file mode 100644 index 0000000000..c670079740 --- /dev/null +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppAuthSpec.java @@ -0,0 +1,4 @@ +package io.apicurio.registry.operator.api.v1.spec; + +public class AppAuthSpec { +} 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..a7e5a29c04 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 @@ -42,6 +42,7 @@ Configure features of the Apicurio Registry backend (app). @JsonSetter(nulls = SKIP) private AppFeaturesSpec features; + /** * 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/StudioUiAuthSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/StudioUiAuthSpec.java new file mode 100644 index 0000000000..49c76efe03 --- /dev/null +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/StudioUiAuthSpec.java @@ -0,0 +1,4 @@ +package io.apicurio.registry.operator.api.v1.spec; + +public class StudioUiAuthSpec { +} diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/UiAuthSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/UiAuthSpec.java new file mode 100644 index 0000000000..b4fb9d5513 --- /dev/null +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/UiAuthSpec.java @@ -0,0 +1,4 @@ +package io.apicurio.registry.operator.api.v1.spec; + +public class UiAuthSpec { +} From 5733cbf6fd34beb181ae566485b9bd8de8f24223 Mon Sep 17 00:00:00 2001 From: Carles Arnal Date: Wed, 29 Jan 2025 09:26:14 +0100 Subject: [PATCH 02/11] Add initial support for authentication --- .../operator/EnvironmentVariables.java | 7 ++ .../resource/app/AppDeploymentResource.java | 24 ++++++ .../registry/operator/it/KeycloakITTest.java | 78 +++++++++++++++---- .../resources/k8s/examples/auth/keycloak.yaml | 2 +- ...imple-with_keycloak.apicurioregistry3.yaml | 5 ++ .../operator/api/v1/spec/AppAuthSpec.java | 56 +++++++++++++ .../operator/api/v1/spec/AppSpec.java | 9 +++ 7 files changed, 163 insertions(+), 18 deletions(-) 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..fb2263d21c 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,11 @@ 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_REGISTRY_AUTH_SERVER_URL = "QUARKUS_OIDC_AUTH_SERVER_URL"; + } 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 611147fcf9..0d68edae86 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 @@ -4,6 +4,7 @@ import io.apicurio.registry.operator.OperatorException; import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; import io.apicurio.registry.operator.api.v1.ApicurioRegistry3Spec; +import io.apicurio.registry.operator.api.v1.spec.AppAuthSpec; 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; @@ -67,11 +68,22 @@ protected Deployment desired(ApicurioRegistry3 primary, Context envVars, AppAuthSpec appAuthSpec) { + addEnvVar(envVars, new EnvVarBuilder().withName(EnvironmentVariables.APICURIO_REGISTRY_AUTH_ENABLED) + .withValue(appAuthSpec.getAuthEnabled().toString()).build()); + addEnvVar(envVars, new EnvVarBuilder().withName(EnvironmentVariables.APICURIO_REGISTRY_APP_CLIENT_ID) + .withValue(appAuthSpec.getAppClientId()).build()); + addEnvVar(envVars, new EnvVarBuilder().withName(EnvironmentVariables.APICURIO_REGISTRY_UI_CLIENT_ID) + .withValue(appAuthSpec.getUiClientId()).build()); + addEnvVar(envVars, + new EnvVarBuilder().withName(EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL) + .withValue(appAuthSpec.getAuthServerUrl()).build()); + } } diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java index b12f581c68..cb7630936d 100644 --- a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java @@ -1,25 +1,26 @@ package io.apicurio.registry.operator.it; +import io.apicurio.registry.operator.EnvironmentVariables; import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.PodCondition; -import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBinding; -import io.fabric8.kubernetes.api.model.rbac.RoleBinding; import io.fabric8.kubernetes.client.utils.Serialization; 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.io.BufferedInputStream; -import java.io.IOException; -import java.net.URL; +import java.time.Duration; import java.util.List; +import static io.apicurio.registry.operator.api.v1.ContainerNames.REGISTRY_APP_CONTAINER_NAME; +import static io.apicurio.registry.operator.api.v1.ContainerNames.REGISTRY_UI_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.ResourceFactory.deserialize; +import static io.apicurio.registry.operator.resource.app.AppDeploymentResource.getContainerFromDeployment; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; @@ -28,28 +29,71 @@ public class KeycloakITTest extends ITBase { private static final Logger log = LoggerFactory.getLogger(KeycloakITTest.class); + @BeforeAll + public static void init() { + Awaitility.setDefaultTimeout(Duration.ofSeconds(60)); + } + @Test void testKeycloakPlain() { - //Preparation, deploy Keycloak - client.load(KeycloakITTest.class - .getResourceAsStream("/k8s/examples/auth/keycloak.yaml")).create(); + // Preparation, deploy Keycloak + List resources = Serialization + .unmarshal(KeycloakITTest.class.getResourceAsStream("/k8s/examples/auth/keycloak.yaml")); + + resources.forEach(r -> { + log.info("Creating Keycloak resource kind {} in namespace {}", r.getKind(), namespace); + client.resource(r).inNamespace(namespace).createOrReplace(); + await().ignoreExceptions().until(() -> { + assertThat(client.resource(r).inNamespace(namespace).get()).isNotNull(); + return true; + }); + }); + await().ignoreExceptions().untilAsserted(() -> { - assertThat(client.apps().deployments() - .withName("keycloak").get() - .getStatus().getReadyReplicas()).isEqualTo(1); + assertThat(client.apps().deployments().withName("keycloak").get().getStatus().getReadyReplicas()) + .isEqualTo(1); }); - //Deploy Registry - var registry = deserialize( - "k8s/examples/auth/simple-with_keycloak.apicurioregistry3.yaml", + // Deploy Registry + var registry = deserialize("k8s/examples/auth/simple-with_keycloak.apicurioregistry3.yaml", ApicurioRegistry3.class); + registry.getMetadata().setNamespace(namespace); + var appAuthSpec = registry.getSpec().getApp().getAppAuthSpec(); + + Assertions.assertEquals("registry-api", appAuthSpec.getAppClientId()); + Assertions.assertEquals("apicurio-registry", appAuthSpec.getUiClientId()); + Assertions.assertEquals(true, appAuthSpec.getAuthEnabled()); + Assertions.assertEquals("http://keycloak:8090/realms/registry", appAuthSpec.getAuthServerUrl()); + client.resource(registry).create(); - //Assertions, verify Registry deployment + // Assertions, checks registry deployments exist checkDeploymentExists(registry, COMPONENT_APP, 1); checkDeploymentExists(registry, COMPONENT_UI, 1); - //FIXME: Check Registry deployment has the expected auth values. + + // 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.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 + "=" + + "http://keycloak:8090/realms/registry"); + + // App deployment auth related assertions + var uiEnv = getContainerFromDeployment( + client.apps().deployments().inNamespace(namespace) + .withName(registry.getMetadata().getName() + "-ui-deployment").get(), + REGISTRY_UI_CONTAINER_NAME).getEnv(); + } } diff --git a/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml b/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml index 9080e127ab..3a174666b7 100644 --- a/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml +++ b/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml @@ -3223,4 +3223,4 @@ spec: service: name: keycloak port: - number: 8090 \ No newline at end of file + name: http \ 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 index 2cd8be6262..4f55bb183c 100644 --- 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 @@ -6,6 +6,11 @@ spec: app: ingress: host: simple-app.apps.cluster.example + auth: + authEnabled: true + appClientId: registry-api + uiClientId: apicurio-registry + authServerUrl: http://keycloak:8090/realms/registry ui: ingress: host: simple-ui.apps.cluster.example diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppAuthSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppAuthSpec.java index c670079740..cb0bd006cd 100644 --- a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppAuthSpec.java +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppAuthSpec.java @@ -1,4 +1,60 @@ package io.apicurio.registry.operator.api.v1.spec; +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({ "allowDeletes" }) +@NoArgsConstructor +@AllArgsConstructor(access = PRIVATE) +@SuperBuilder(toBuilder = true) +@Getter +@Setter +@EqualsAndHashCode +@ToString public class AppAuthSpec { + + @JsonProperty("authEnabled") + @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 authEnabled; + + @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("authServerUrl") + @JsonPropertyDescription(""" + URL of the identity server.""") + @JsonSetter(nulls = Nulls.SKIP) + private String authServerUrl; } 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 a7e5a29c04..443298adac 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 @@ -42,6 +42,15 @@ 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 AppAuthSpec appAuthSpec; /** * DEPRECATED: Use the `app.storage.type` and `app.storage.sql` fields instead. The operator will attempt From 5792da381c3827a2cbce1ae02cfd8e7c35414568 Mon Sep 17 00:00:00 2001 From: Carles Arnal Date: Wed, 29 Jan 2025 10:26:40 +0100 Subject: [PATCH 03/11] Modify install file for authentication --- operator/install/install.yaml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/operator/install/install.yaml b/operator/install/install.yaml index a1be0c3854..2c0207bc2a 100644 --- a/operator/install/install.yaml +++ b/operator/install/install.yaml @@ -27,6 +27,29 @@ spec: app: description: Configure Apicurio Registry backend (app) component. properties: + auth: + description: | + Configure authentication and authorization of Apicurio Registry. + properties: + 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 + authEnabled: + description: |- + Enable Apicurio Registry Authentication. + In Identity providers like Keycloak, this is the client id used for the Quarkus backend application + type: boolean + authServerUrl: + description: URL of the identity server. + type: string + 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. From 73b45fc664fe277c62c90f7aae60faaa7e59cae1 Mon Sep 17 00:00:00 2001 From: Carles Arnal Date: Wed, 29 Jan 2025 13:41:26 +0100 Subject: [PATCH 04/11] Improve auth structure --- .../apicurio/registry/operator/feat/Auth.java | 74 +++++++++++++++++++ .../resource/app/AppDeploymentResource.java | 24 ++---- .../apicurio/registry/operator/it/ITBase.java | 11 +++ .../registry/operator/it/KeycloakITTest.java | 21 +----- .../operator/api/v1/spec/AppAuthSpec.java | 6 +- .../operator/api/v1/spec/AppSpec.java | 4 +- 6 files changed, 101 insertions(+), 39 deletions(-) create mode 100644 operator/controller/src/main/java/io/apicurio/registry/operator/feat/Auth.java diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/Auth.java b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/Auth.java new file mode 100644 index 0000000000..9ee3148423 --- /dev/null +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/Auth.java @@ -0,0 +1,74 @@ +package io.apicurio.registry.operator.feat; + +import io.apicurio.registry.operator.EnvironmentVariables; +import io.apicurio.registry.operator.api.v1.spec.AppAuthSpec; +import io.apicurio.registry.operator.utils.Utils; +import io.fabric8.kubernetes.api.model.EnvVar; +import io.fabric8.kubernetes.api.model.EnvVarBuilder; + +import java.util.Map; +import java.util.Optional; + +/** + * Helper class used to handle AUTH related configuration. + */ +public class Auth { + + /** + * Configures authentication-related environment variables for the Apicurio Registry. + * + * @param envVars The map of environment variables to be configured. + * @param appAuthSpec The authentication specification containing required auth settings. If null, no + * changes will be made to envVars. + */ + public static void configureAuth(Map envVars, AppAuthSpec appAuthSpec) { + if (appAuthSpec == null) { + return; + } + + envVars.put(EnvironmentVariables.APICURIO_REGISTRY_AUTH_ENABLED, + createEnvVar(EnvironmentVariables.APICURIO_REGISTRY_AUTH_ENABLED, + Optional.ofNullable(appAuthSpec.getEnabled()).orElse(Boolean.FALSE).toString())); + + envVars.put(EnvironmentVariables.APICURIO_REGISTRY_APP_CLIENT_ID, createEnvVar( + EnvironmentVariables.APICURIO_REGISTRY_APP_CLIENT_ID, appAuthSpec.getAppClientId())); + + envVars.put(EnvironmentVariables.APICURIO_REGISTRY_UI_CLIENT_ID, createEnvVar( + EnvironmentVariables.APICURIO_REGISTRY_UI_CLIENT_ID, appAuthSpec.getUiClientId())); + + envVars.put(EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL, createEnvVar( + EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL, appAuthSpec.getAuthServerUrl())); + + putIfNotBlank(envVars, EnvironmentVariables.APICURIO_REGISTRY_APP_CLIENT_ID, + appAuthSpec.getAppClientId()); + putIfNotBlank(envVars, EnvironmentVariables.APICURIO_REGISTRY_UI_CLIENT_ID, + appAuthSpec.getUiClientId()); + putIfNotBlank(envVars, EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL, + appAuthSpec.getAuthServerUrl()); + } + + /** + * Adds an environment variable to the map only if the value is not null or blank. + * + * @param envVars The environment variables map. + * @param name The name of the environment variable. + * @param value The value to be set (ignored if null or blank). + */ + private static void putIfNotBlank(Map 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. If the value is null, it will be + * replaced with an empty string. + * + * @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. + */ + private static EnvVar createEnvVar(String name, String value) { + return new EnvVarBuilder().withName(name).withValue(value).build(); + } +} 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 0d68edae86..684577c54d 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 @@ -8,6 +8,7 @@ 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.feat.Auth; import io.apicurio.registry.operator.feat.Cors; import io.apicurio.registry.operator.feat.KafkaSql; import io.apicurio.registry.operator.feat.PostgresSql; @@ -74,15 +75,17 @@ protected Deployment desired(ApicurioRegistry3 primary, Context envVars, AppAuthSpec appAuthSpec) { - addEnvVar(envVars, new EnvVarBuilder().withName(EnvironmentVariables.APICURIO_REGISTRY_AUTH_ENABLED) - .withValue(appAuthSpec.getAuthEnabled().toString()).build()); - addEnvVar(envVars, new EnvVarBuilder().withName(EnvironmentVariables.APICURIO_REGISTRY_APP_CLIENT_ID) - .withValue(appAuthSpec.getAppClientId()).build()); - addEnvVar(envVars, new EnvVarBuilder().withName(EnvironmentVariables.APICURIO_REGISTRY_UI_CLIENT_ID) - .withValue(appAuthSpec.getUiClientId()).build()); - addEnvVar(envVars, - new EnvVarBuilder().withName(EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL) - .withValue(appAuthSpec.getAuthServerUrl()).build()); - } } 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 46abde619e..1ec7dc9d64 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 @@ -234,6 +234,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/java/io/apicurio/registry/operator/it/KeycloakITTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java index cb7630936d..a402af87da 100644 --- a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java @@ -16,7 +16,6 @@ import java.util.List; import static io.apicurio.registry.operator.api.v1.ContainerNames.REGISTRY_APP_CONTAINER_NAME; -import static io.apicurio.registry.operator.api.v1.ContainerNames.REGISTRY_UI_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.ResourceFactory.deserialize; @@ -40,14 +39,7 @@ void testKeycloakPlain() { List resources = Serialization .unmarshal(KeycloakITTest.class.getResourceAsStream("/k8s/examples/auth/keycloak.yaml")); - resources.forEach(r -> { - log.info("Creating Keycloak resource kind {} in namespace {}", r.getKind(), namespace); - client.resource(r).inNamespace(namespace).createOrReplace(); - await().ignoreExceptions().until(() -> { - assertThat(client.resource(r).inNamespace(namespace).get()).isNotNull(); - return true; - }); - }); + createResources(resources, "Keycloak"); await().ignoreExceptions().untilAsserted(() -> { assertThat(client.apps().deployments().withName("keycloak").get().getStatus().getReadyReplicas()) @@ -60,11 +52,11 @@ void testKeycloakPlain() { registry.getMetadata().setNamespace(namespace); - var appAuthSpec = registry.getSpec().getApp().getAppAuthSpec(); + var appAuthSpec = registry.getSpec().getApp().getAuth(); Assertions.assertEquals("registry-api", appAuthSpec.getAppClientId()); Assertions.assertEquals("apicurio-registry", appAuthSpec.getUiClientId()); - Assertions.assertEquals(true, appAuthSpec.getAuthEnabled()); + Assertions.assertEquals(true, appAuthSpec.getEnabled()); Assertions.assertEquals("http://keycloak:8090/realms/registry", appAuthSpec.getAuthServerUrl()); client.resource(registry).create(); @@ -88,12 +80,5 @@ void testKeycloakPlain() { assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) .contains(EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL + "=" + "http://keycloak:8090/realms/registry"); - - // App deployment auth related assertions - var uiEnv = getContainerFromDeployment( - client.apps().deployments().inNamespace(namespace) - .withName(registry.getMetadata().getName() + "-ui-deployment").get(), - REGISTRY_UI_CONTAINER_NAME).getEnv(); - } } diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppAuthSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppAuthSpec.java index cb0bd006cd..d83a3bc8a4 100644 --- a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppAuthSpec.java +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppAuthSpec.java @@ -21,7 +21,7 @@ @JsonDeserialize(using = JsonDeserializer.None.class) @JsonInclude(NON_NULL) -@JsonPropertyOrder({ "allowDeletes" }) +@JsonPropertyOrder({ "enabled", "appClientId", "uiClientId", "authServerUrl" }) @NoArgsConstructor @AllArgsConstructor(access = PRIVATE) @SuperBuilder(toBuilder = true) @@ -31,12 +31,12 @@ @ToString public class AppAuthSpec { - @JsonProperty("authEnabled") + @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 authEnabled; + private Boolean enabled; @JsonProperty("appClientId") @JsonPropertyDescription(""" 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 443298adac..ee30450ae6 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 @@ -12,7 +12,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) @@ -50,7 +50,7 @@ Configure features of the Apicurio Registry backend (app). Configure authentication and authorization of Apicurio Registry. """) @JsonSetter(nulls = SKIP) - private AppAuthSpec appAuthSpec; + private AppAuthSpec auth; /** * DEPRECATED: Use the `app.storage.type` and `app.storage.sql` fields instead. The operator will attempt From a8d5f621ff776e748ca541e8c065dcea8e1b8397 Mon Sep 17 00:00:00 2001 From: Carles Arnal Date: Wed, 29 Jan 2025 16:36:44 +0100 Subject: [PATCH 05/11] Fix test resource --- .../operator/resource/app/AppDeploymentResource.java | 1 + .../auth/simple-with_keycloak.apicurioregistry3.yaml | 2 +- operator/install/install.yaml | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) 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 684577c54d..103bc3b1d9 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 @@ -75,6 +75,7 @@ protected Deployment desired(ApicurioRegistry3 primary, Context Date: Thu, 30 Jan 2025 13:41:23 +0100 Subject: [PATCH 06/11] Improve auth configuration and test example --- .../operator/EnvironmentVariables.java | 11 + .../apicurio/registry/operator/feat/Auth.java | 37 +- .../registry/operator/feat/AuthTLS.java | 71 + .../resource/app/AppDeploymentResource.java | 6 +- .../apicurio/registry/operator/it/ITBase.java | 37 + .../registry/operator/it/KeycloakITTest.java | 18 +- .../registry/operator/unit/CorsTest.java | 2 +- .../resources/k8s/examples/auth/keycloak.yaml | 22 +- ...imple-with_keycloak.apicurioregistry3.yaml | 5 +- .../k8s/examples/auth/tls/keycloak.yaml | 3244 +++++++++++++++++ .../operator/api/v1/spec/AppSpec.java | 1 + .../api/v1/spec/StudioUiAuthSpec.java | 4 - .../operator/api/v1/spec/UiAuthSpec.java | 4 - .../api/v1/spec/{ => auth}/AppAuthSpec.java | 30 +- .../api/v1/spec/auth/AuthTLSSpec.java | 69 + 15 files changed, 3520 insertions(+), 41 deletions(-) create mode 100644 operator/controller/src/main/java/io/apicurio/registry/operator/feat/AuthTLS.java create mode 100644 operator/controller/src/test/resources/k8s/examples/auth/tls/keycloak.yaml delete mode 100644 operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/StudioUiAuthSpec.java delete mode 100644 operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/UiAuthSpec.java rename operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/{ => auth}/AppAuthSpec.java (65%) create mode 100644 operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthTLSSpec.java 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 fb2263d21c..9141e7cf7f 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 @@ -25,6 +25,17 @@ public class EnvironmentVariables { 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_KEYSTORE_TYPE = "QUARKUS_OIDC_TLS_KEY_STORE_FILE_TYPE"; + public static final String OIDC_TLS_KEYSTORE_LOCATION = "QUARKUS_OIDC_TLS_KEY_STORE_LOCATION"; + public static final String OIDC_TLS_KEYSTORE_PASSWORD = "QUARKUS_OIDC_TLS_KEY_STORE_PASSWORD"; + public static final String OIDC_TLS_TRUSTSTORE_TYPE = "QUARKUS_OIDC_TLS_TRUST_STORE_FILE_TYPE"; + public static final String OIDC_TLS_TRUSTSTORE_LOCATION = "QUARKUS_OIDC_TLS_TRUST_STORE_NAME"; + public static final String OIDC_TLS_TRUSTSTORE_PASSWORD = "QUARKUS_OIDC_TLS_TRUST_STORE_PASWORD"; } diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/Auth.java b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/Auth.java index 9ee3148423..b923108b68 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/Auth.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/Auth.java @@ -1,10 +1,11 @@ package io.apicurio.registry.operator.feat; import io.apicurio.registry.operator.EnvironmentVariables; -import io.apicurio.registry.operator.api.v1.spec.AppAuthSpec; +import io.apicurio.registry.operator.api.v1.spec.auth.AppAuthSpec; import io.apicurio.registry.operator.utils.Utils; import io.fabric8.kubernetes.api.model.EnvVar; import io.fabric8.kubernetes.api.model.EnvVarBuilder; +import io.fabric8.kubernetes.api.model.apps.Deployment; import java.util.Map; import java.util.Optional; @@ -17,34 +18,31 @@ public class Auth { /** * Configures authentication-related environment variables for the Apicurio Registry. * - * @param envVars The map of environment variables to be configured. + * @param env The map of environment variables to be configured. * @param appAuthSpec The authentication specification containing required auth settings. If null, no * changes will be made to envVars. */ - public static void configureAuth(Map envVars, AppAuthSpec appAuthSpec) { + public static void configureAuth(AppAuthSpec appAuthSpec, Deployment deployment, + Map env) { if (appAuthSpec == null) { return; } - envVars.put(EnvironmentVariables.APICURIO_REGISTRY_AUTH_ENABLED, + env.put(EnvironmentVariables.APICURIO_REGISTRY_AUTH_ENABLED, createEnvVar(EnvironmentVariables.APICURIO_REGISTRY_AUTH_ENABLED, Optional.ofNullable(appAuthSpec.getEnabled()).orElse(Boolean.FALSE).toString())); - envVars.put(EnvironmentVariables.APICURIO_REGISTRY_APP_CLIENT_ID, createEnvVar( - EnvironmentVariables.APICURIO_REGISTRY_APP_CLIENT_ID, appAuthSpec.getAppClientId())); - - envVars.put(EnvironmentVariables.APICURIO_REGISTRY_UI_CLIENT_ID, createEnvVar( - EnvironmentVariables.APICURIO_REGISTRY_UI_CLIENT_ID, appAuthSpec.getUiClientId())); - - envVars.put(EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL, createEnvVar( - EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL, appAuthSpec.getAuthServerUrl())); - - putIfNotBlank(envVars, EnvironmentVariables.APICURIO_REGISTRY_APP_CLIENT_ID, + putIfNotBlank(env, EnvironmentVariables.APICURIO_REGISTRY_APP_CLIENT_ID, appAuthSpec.getAppClientId()); - putIfNotBlank(envVars, EnvironmentVariables.APICURIO_REGISTRY_UI_CLIENT_ID, - appAuthSpec.getUiClientId()); - putIfNotBlank(envVars, EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL, + putIfNotBlank(env, EnvironmentVariables.APICURIO_REGISTRY_UI_CLIENT_ID, appAuthSpec.getUiClientId()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_UI_AUTH_OIDC_REDIRECT_URI, + appAuthSpec.getRedirectURI()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_UI_AUTH_OIDC_LOGOUT_URL, appAuthSpec.getLogoutURL()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL, appAuthSpec.getAuthServerUrl()); + putIfNotBlank(env, EnvironmentVariables.OIDC_TLS_VERIFICATION, appAuthSpec.getTlsVerification()); + + AuthTLS.configureAuthTLS(appAuthSpec, deployment, env); } /** @@ -55,14 +53,13 @@ public static void configureAuth(Map envVars, AppAuthSpec appAut * @param value The value to be set (ignored if null or blank). */ private static void putIfNotBlank(Map envVars, String name, String value) { - if (Utils.isBlank(value)) { + if (!Utils.isBlank(value)) { envVars.put(name, createEnvVar(name, value)); } } /** - * Creates an environment variable using the given name and value. If the value is null, it will be - * replaced with an empty string. + * 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. diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/AuthTLS.java b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/AuthTLS.java new file mode 100644 index 0000000000..1df4f8660f --- /dev/null +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/AuthTLS.java @@ -0,0 +1,71 @@ +package io.apicurio.registry.operator.feat; + +import io.apicurio.registry.operator.api.v1.spec.auth.AppAuthSpec; +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 java.util.Optional.ofNullable; + +public class AuthTLS { + + /** + * Configure TLS for OIDC authentication + */ + public static boolean configureAuthTLS(AppAuthSpec appAuthSpec, Deployment deployment, + Map env) { + + // spotless:off + var keystore = new SecretKeyRefTool(getAuthTLSSpec(appAuthSpec) + .map(AuthTLSSpec::getKeystoreSecretRef) + .orElse(null), "user.p12"); + + var keystorePassword = new SecretKeyRefTool(getAuthTLSSpec(appAuthSpec) + .map(AuthTLSSpec::getKeystorePasswordSecretRef) + .orElse(null), "user.password"); + + var truststore = new SecretKeyRefTool(getAuthTLSSpec(appAuthSpec) + .map(AuthTLSSpec::getTruststoreSecretRef) + .orElse(null), "ca.p12"); + + var truststorePassword = new SecretKeyRefTool(getAuthTLSSpec(appAuthSpec) + .map(AuthTLSSpec::getTruststorePasswordSecretRef) + .orElse(null), "ca.password"); + // spotless:on + + if (truststore.isValid() && truststorePassword.isValid() && keystore.isValid() + && keystorePassword.isValid()) { + + // ===== Keystore + + addEnvVar(env, OIDC_TLS_KEYSTORE_TYPE, "PKCS12"); + keystore.applySecretVolume(deployment, REGISTRY_APP_CONTAINER_NAME); + addEnvVar(env, OIDC_TLS_KEYSTORE_LOCATION, keystore.getSecretVolumeKeyPath()); + keystorePassword.applySecretEnvVar(env, OIDC_TLS_KEYSTORE_PASSWORD); + + // ===== Truststore + + addEnvVar(env, OIDC_TLS_TRUSTSTORE_TYPE, "PKCS12"); + truststore.applySecretVolume(deployment, REGISTRY_APP_CONTAINER_NAME); + addEnvVar(env, OIDC_TLS_TRUSTSTORE_LOCATION, truststore.getSecretVolumeKeyPath()); + truststorePassword.applySecretEnvVar(env, OIDC_TLS_TRUSTSTORE_PASSWORD); + + return true; + } + return false; + } + + private static Optional getAuthTLSSpec(AppAuthSpec primary) { + // spotless:off + return ofNullable(primary) + .map(AppAuthSpec::getTls); + // spotless:on + } +} 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 103bc3b1d9..cc63330e93 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 @@ -4,10 +4,10 @@ import io.apicurio.registry.operator.OperatorException; import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; import io.apicurio.registry.operator.api.v1.ApicurioRegistry3Spec; -import io.apicurio.registry.operator.api.v1.spec.AppAuthSpec; 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.AppAuthSpec; import io.apicurio.registry.operator.feat.Auth; import io.apicurio.registry.operator.feat.Cors; import io.apicurio.registry.operator.feat.KafkaSql; @@ -84,9 +84,9 @@ protected Deployment desired(ApicurioRegistry3 primary, Context 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 + "=" - + "http://keycloak:8090/realms/registry"); + + "http://simple-keycloak.apps.cluster.example:8090/realms/registry"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_UI_AUTH_OIDC_REDIRECT_URI + "=" + + "http://simple-ui.apps.cluster.example"); + assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) + .contains(EnvironmentVariables.APICURIO_UI_AUTH_OIDC_LOGOUT_URL + "=" + + "http://simple-ui.apps.cluster.example"); } } diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/unit/CorsTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/unit/CorsTest.java index cd6d203cdc..6b6ebde01d 100644 --- a/operator/controller/src/test/java/io/apicurio/registry/operator/unit/CorsTest.java +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/unit/CorsTest.java @@ -19,7 +19,7 @@ public class CorsTest { public void testConfigureAllowedOrigins() throws Exception { doTestAllowedOrigins("k8s/examples/cors/example-default.yaml", "*"); doTestAllowedOrigins("k8s/examples/cors/example-ingress.yaml", - "http://simple-ui.apps.cluster.example", "https://simple-ui.apps.cluster.example"); + "https://simple-ui.apps.cluster.example", "https://simple-ui.apps.cluster.example"); doTestAllowedOrigins("k8s/examples/cors/example-env-vars.yaml", "https://ui.example.org"); doTestAllowedOrigins("k8s/examples/cors/example-env-vars-and-ingress.yaml", "https://ui.example.org"); } diff --git a/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml b/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml index 3a174666b7..cf3aeaf576 100644 --- a/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml +++ b/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml @@ -3175,6 +3175,10 @@ spec: value: "true" - name: KC_HTTP_PORT value: "8090" + - name: PROXY_ADDRESS_FORWARDING + value: "true" + - name: KEYCLOAK_FRONTEND_URL + value: "http://simple-keycloak.apps.cluster.example" ports: - name: http containerPort: 8090 @@ -3192,6 +3196,19 @@ spec: --- apiVersion: v1 kind: Service +metadata: + name: keycloak-headless +spec: + clusterIP: None + ports: + - name: http + port: 8090 + targetPort: 8090 + selector: + app: keycloak +--- +apiVersion: v1 +kind: Service metadata: name: keycloak labels: @@ -3210,9 +3227,6 @@ kind: Ingress metadata: name: keycloak spec: - tls: - - hosts: - - simple-keycloak.apps.cluster.example rules: - host: simple-keycloak.apps.cluster.example http: @@ -3223,4 +3237,4 @@ spec: service: name: keycloak port: - name: http \ No newline at end of file + number: 8090 \ 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 index d23fb1b116..958a6fa721 100644 --- 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 @@ -10,7 +10,10 @@ spec: enabled: true appClientId: registry-api uiClientId: apicurio-registry - authServerUrl: http://keycloak:8090/realms/registry + authServerUrl: http://simple-keycloak.apps.cluster.example:8090/realms/registry + redirectURI: http://simple-ui.apps.cluster.example + logoutURL: http://simple-ui.apps.cluster.example + tlsVerification: none ui: ingress: host: simple-ui.apps.cluster.example diff --git a/operator/controller/src/test/resources/k8s/examples/auth/tls/keycloak.yaml b/operator/controller/src/test/resources/k8s/examples/auth/tls/keycloak.yaml new file mode 100644 index 0000000000..44e47bf010 --- /dev/null +++ b/operator/controller/src/test/resources/k8s/examples/auth/tls/keycloak.yaml @@ -0,0 +1,3244 @@ +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: 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_BOOTSTRAP_ADMIN_USERNAME + value: "admin" + - name: KC_BOOTSTRAP_ADMIN_PASSWORD + value: "admin" + - name: KC_PROXY + value: "edge" + - name: PROXY_ADDRESS_FORWARDING + value: "true" + - name: KEYCLOAK_FRONTEND_URL + value: "https://tls-keycloak.apps.cluster.example" + - name: KC_HEALTH_ENABLED + value: "true" + ports: + - name: app + containerPort: 8080 + - name: health + containerPort: 9000 + readinessProbe: + httpGet: + path: /health/ready + port: 9000 + volumeMounts: + - name: keycloak-volume + mountPath: /opt/keycloak/data/import + volumes: + - name: keycloak-volume + configMap: + name: keycloak-configmap +--- +apiVersion: v1 +kind: Service +metadata: + name: keycloak + labels: + app: keycloak +spec: + type: ClusterIP + ports: + - name: http + port: 8080 + targetPort: 8080 + selector: + app: keycloak +--- +apiVersion: v1 +kind: Secret +metadata: + name: keycloak-tls + namespace: keycloak +type: kubernetes.io/tls +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURYekNDQWtlZ0F3SUJBZ0lVZmQ1OEdNUDljRlMzZzZuNldhNzdsUGtYZUY4d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1B6RXFNQ2dHQTFVRUF3d2hkR3h6TFd0bGVXTnNiMkZyTG1Gd2NITXVZMngxYzNSbGNpNWxlR0Z0Y0d4bApNUkV3RHdZRFZRUUtEQWhMWlhsamJHOWhhekFlRncweU5UQXhNamt4TmpRM016aGFGdzB6TlRBeE1qY3hOalEzCk16aGFNRDh4S2pBb0JnTlZCQU1NSVhSc2N5MXJaWGxqYkc5aGF5NWhjSEJ6TG1Oc2RYTjBaWEl1WlhoaGJYQnMKWlRFUk1BOEdBMVVFQ2d3SVMyVjVZMnh2WVdzd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFSwpBb0lCQVFDeWQ2OEVVMXZpRkhVTlhOSFVzYWpmWmVPdXBxUDE3SjhJaE1DcGY0U3NRWXl4WnFYanhDVTA5N0VlCklyemxjbjFCMzhGRUZQRHdkZ1V5NGt1SGNpZ2pJMFZ5ZVYwNTJRTkYySFpJdThiM3BNVm1zSXRNZWcwcHpaSFgKUjB6c0dRUVBJQS9SaGs0bUpsdjVBdHFhcWkyUmVOQ3BtR3dKZklQVzFUQndLYWZ2VGV6MnZUSXBXY0FmcTJVdQpiVkZhMHBrY2NDRTNZejJNV2VvWnd0dGxCN1RHS2tjNEJObGxrQmxaZ0FLSXhHYk5Bd0JiaVpqejBGZVFIQk90ClcwZUxjUFVEZG1jeDJCQWxzVTFkd21FT1BkLzJUNDFIUlZ5NUlNZ0Fxa2loMHNkcFF5MnpUY2swRDJDMHpFcWUKN1EyMEY2RVRPamI2U0JzN0NXejJHNlk5cmU1RkFnTUJBQUdqVXpCUk1CMEdBMVVkRGdRV0JCUVI0WVpjVC8yMQpmdktrQVptUkxaTXp3anNKU0RBZkJnTlZIU01FR0RBV2dCUVI0WVpjVC8yMWZ2S2tBWm1STFpNendqc0pTREFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFBZ28xTndRbXBtNHQ1UytuM2EKUGhFRTM0dFN0RVd3TW5vNW5zL0hrS2c3VzVtVGl2UEJsOWdUemRDTjlFL3hjb1ZxOEkzVjZxQ2VKN2xmTHBQcgpzdnZ4M2hyTW5WUDUyMUdZV1BKOVFRaDNHeTRDNHBQVjJxeHV2b0ZaaUdKY0diWWcrSnJKTElKWGk1RUdiNk1YCi81bzVRb01GYUtBSGU2WVRjVzA2cEp5NDlFaXdQWkRJM1dCcXlzSTNhbHRWUlNBN3pXdWJHb1JXdjArS0dNcHoKMzNkNWcyT1ovOG5pREhxSFZxNDVucFRyczBXU0pLa1ZoWDBaazFJN0ZTYlJNQzllcXlaRkQzaG45TGF5eDlrbwprcVgyZ0dubDVVV25PWElWdUFXREQ2NE1RTWtyOWZpeXp6ekEvbmJmT2lvVG1PdmxPQmduVnFmVnFMb25kelg2CkY2NlMKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRQ3lkNjhFVTF2aUZIVU4KWE5IVXNhamZaZU91cHFQMTdKOEloTUNwZjRTc1FZeXhacVhqeENVMDk3RWVJcnpsY24xQjM4RkVGUER3ZGdVeQo0a3VIY2lnakkwVnllVjA1MlFORjJIWkl1OGIzcE1WbXNJdE1lZzBwelpIWFIwenNHUVFQSUEvUmhrNG1KbHY1CkF0cWFxaTJSZU5DcG1Hd0pmSVBXMVRCd0thZnZUZXoydlRJcFdjQWZxMlV1YlZGYTBwa2NjQ0UzWXoyTVdlb1oKd3R0bEI3VEdLa2M0Qk5sbGtCbFpnQUtJeEdiTkF3QmJpWmp6MEZlUUhCT3RXMGVMY1BVRGRtY3gyQkFsc1UxZAp3bUVPUGQvMlQ0MUhSVnk1SU1nQXFraWgwc2RwUXkyelRjazBEMkMwekVxZTdRMjBGNkVUT2piNlNCczdDV3oyCkc2WTlyZTVGQWdNQkFBRUNnZ0VBVVRrOVUwZXBBc3p5dFFFd2tvL0UzdCtkYndoeWlPT0hRYlpCaFNML08vS2QKV01QeDdpYUFGSXBDZHdleVZ1N3phUDZ3RkE4LzFRQ3h2d1hWQURFSmFXeU5GOXQ2ZlhCeUYrQzdmTURSZmpYawpqYWI5enZHaGVnd1FPeDA5T2hyc2lRRzdrVTJCMWNVUmlOUXVyOE9SOTZvM3RXZ2tpM0M3QkJTRTloZFBVZzZ0CkNoSzBSZW1GTStQQnZFa1VMbnNtUHNmdnpMU3k0ZVdlajlRUGlNa0JQTnR3VTFjelY4dFZZY2I3UFlKbjN3Uk8KcXl1WjBIU3VFWUl0a3I3cWg0ZW5xR3Zkek4vclZKUUlMc3VLVkhOd0xRbUJYVjMxK1U2TFJRbWJqWW9UU0JqbgppK2V0c0Fta0dFeFFiRUhEb2cza2RML2dQcFRyKzFCaXozNkJpOTFraFFLQmdRRDdsOFprcFJPLy9OUm9CbkhiCkltM2ttbmkxQjZwaUVCczhXamNNejhTNmwzNnBwQVpUMCtGL3FXUTkzZnJYUUpNYWUyOWd5SS9Fby9PQXlzdWkKMFZBTjdWNVVSN01ncjc2TzFJQU5naXVvVC9yRE92bkZQV0thM3BoYnc1UlBBYStsRmxzdy9DNzAwTmtiUjlydgpWY0t4aFluNVhtMVlZMTZSRXZWdFVGRVdnd0tCZ1FDMWwvMld2bytxZkFxZUtUeXBXaHFZUU95Y29ZYXZpVUNKCmlJOG1zaGhsemJhRG9DSm94aE1MTFgzcjJpUlA4anBZQTZyWEZvNEhpU2RKSC9WZFlmMXBQRk4zRkJVVytNZ2YKdnl1U2ZDK3ozV3NrNFpXWEJyUDNaQmxJeFpTRjlzZDRyU2llVUlQZkpsV29lU2JOM3lmUHV6NUU4NmZLbTU1cApxd1lDdTJjTmx3S0JnUUNrVEpOa3VtRFJhWnlVYWxFRUY5SElEaEJ4dUJYT2dGSW84WWt0dHFqMGFXWndCZ3VUCnhyUm1HQXE4VkRBeFRaeERHUVM5SVh5eG41ZXZMY0FhRGJMSHhaRnFYSUJnQWlUaFJXaHlhZVYwdnZZMWRGZ1UKTkdnbVZ1TU1XZ2FLS2NHNGY0Y0IwRTRoMWhsUnRYVUdBdTFuM0pzajNFUndDa1NCWE80bGV1UFpYUUtCZ0VtLwpFdTVBTDIKLS0tLS1FTkQgU0VDUkVULS0tLS0= +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: keycloak-ingress + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "true" +spec: + ingressClassName: nginx + tls: + - hosts: + - tls-keycloak.apps.cluster.example + secretName: keycloak-tls + rules: + - host: tls-keycloak.apps.cluster.example + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: keycloak + port: + number: 8080 \ No newline at end of file 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 ee30450ae6..ee85c15fb7 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.AppAuthSpec; import lombok.*; import lombok.experimental.SuperBuilder; diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/StudioUiAuthSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/StudioUiAuthSpec.java deleted file mode 100644 index 49c76efe03..0000000000 --- a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/StudioUiAuthSpec.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.apicurio.registry.operator.api.v1.spec; - -public class StudioUiAuthSpec { -} diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/UiAuthSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/UiAuthSpec.java deleted file mode 100644 index b4fb9d5513..0000000000 --- a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/UiAuthSpec.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.apicurio.registry.operator.api.v1.spec; - -public class UiAuthSpec { -} diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppAuthSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AppAuthSpec.java similarity index 65% rename from operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppAuthSpec.java rename to operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AppAuthSpec.java index d83a3bc8a4..d12aff7d64 100644 --- a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppAuthSpec.java +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AppAuthSpec.java @@ -1,4 +1,4 @@ -package io.apicurio.registry.operator.api.v1.spec; +package io.apicurio.registry.operator.api.v1.spec.auth; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; @@ -21,7 +21,8 @@ @JsonDeserialize(using = JsonDeserializer.None.class) @JsonInclude(NON_NULL) -@JsonPropertyOrder({ "enabled", "appClientId", "uiClientId", "authServerUrl" }) +@JsonPropertyOrder({ "enabled", "appClientId", "uiClientId", "redirectURI", "authServerUrl", + "tlsVerification" }) @NoArgsConstructor @AllArgsConstructor(access = PRIVATE) @SuperBuilder(toBuilder = true) @@ -38,6 +39,13 @@ public class AppAuthSpec { @JsonSetter(nulls = Nulls.SKIP) private Boolean enabled; + @JsonProperty("tls") + @JsonPropertyDescription(""" + OIDC TLS configuration. + When custom certificates are used, this is the field to be used to configure the keystore and the trustore""") + @JsonSetter(nulls = Nulls.SKIP) + private AuthTLSSpec tls; + @JsonProperty("appClientId") @JsonPropertyDescription(""" Apicurio Registry backend clientId used for OIDC authentication. @@ -52,9 +60,27 @@ public class AppAuthSpec { @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("logoutURL") + @JsonPropertyDescription(""" + Apicurio Registry UI redirect URI used for redirection after logout.""") + @JsonSetter(nulls = Nulls.SKIP) + private String logoutURL; + @JsonProperty("authServerUrl") @JsonPropertyDescription(""" URL of the identity server.""") @JsonSetter(nulls = Nulls.SKIP) private String authServerUrl; + + @JsonProperty("tlsVerification") + @JsonPropertyDescription(""" + Verify the identity server certificate.""") + @JsonSetter(nulls = Nulls.SKIP) + private String tlsVerification; } 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..df980eb7b2 --- /dev/null +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthTLSSpec.java @@ -0,0 +1,69 @@ +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({ "keystoreSecretRef", "keystorePasswordSecretRef", "truststoreSecretRef", + "truststorePasswordSecretRef" }) +@NoArgsConstructor +@AllArgsConstructor(access = PRIVATE) +@SuperBuilder(toBuilder = true) +@Getter +@Setter +@EqualsAndHashCode +@ToString +public class AuthTLSSpec { + + /** + * Reference to the Secret that contains the TLS keystore (in PKCS12 format). Key user.p12 is + * assumed by default. + */ + @JsonProperty("keystoreSecretRef") + @JsonPropertyDescription(""" + Reference to the Secret that contains the TLS keystore (in PKCS12 format). \ + Key `user.p12` is assumed by default.""") + @JsonSetter(nulls = Nulls.SKIP) + private SecretKeyRef keystoreSecretRef; + + /** + * Reference to the Secret that contains the TLS keystore password. Key user.password is + * assumed by default. + */ + @JsonProperty("keystorePasswordSecretRef") + @JsonPropertyDescription(""" + Reference to the Secret that contains the TLS keystore password. + Key `user.password` is assumed by default.""") + @JsonSetter(nulls = Nulls.SKIP) + private SecretKeyRef keystorePasswordSecretRef; + + /** + * 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; +} From 88918f3f301cffabb3844e96c4280ebac059b425 Mon Sep 17 00:00:00 2001 From: Carles Arnal Date: Thu, 30 Jan 2025 16:22:27 +0100 Subject: [PATCH 07/11] Make the backend use the keycloak service as the route for authentication --- .../apicurio/registry/operator/it/ITBase.java | 43 +++---------------- .../registry/operator/it/KeycloakITTest.java | 20 ++++----- .../resources/k8s/examples/auth/keycloak.yaml | 33 +++++++------- ...imple-with_keycloak.apicurioregistry3.yaml | 11 +++-- 4 files changed, 40 insertions(+), 67 deletions(-) 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 6b3e9a9a76..ade6ea64b2 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,8 +2,6 @@ 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.apps.Deployment; @@ -20,7 +18,11 @@ import jakarta.enterprise.util.TypeLiteral; import org.awaitility.Awaitility; import org.eclipse.microprofile.config.ConfigProvider; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -219,41 +221,6 @@ 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( diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java index 4637068b7d..6a2652a14e 100644 --- a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java @@ -46,9 +46,6 @@ void testKeycloakPlain() { .isEqualTo(1); }); - createKeycloakDNSResolution("simple-keycloak.apps.cluster.example", - "keycloak." + namespace + ".svc.cluster.local"); - // Deploy Registry var registry = deserialize("k8s/examples/auth/simple-with_keycloak.apicurioregistry3.yaml", ApicurioRegistry3.class); @@ -60,10 +57,13 @@ void testKeycloakPlain() { Assertions.assertEquals("registry-api", appAuthSpec.getAppClientId()); Assertions.assertEquals("apicurio-registry", appAuthSpec.getUiClientId()); Assertions.assertEquals(true, appAuthSpec.getEnabled()); - Assertions.assertEquals("http://simple-keycloak.apps.cluster.example:8090/realms/registry", + Assertions.assertEquals("https://simple-keycloak.apps.cluster.example/realms/registry", appAuthSpec.getAuthServerUrl()); - Assertions.assertEquals("http://simple-ui.apps.cluster.example", appAuthSpec.getRedirectURI()); - Assertions.assertEquals("http://simple-ui.apps.cluster.example", appAuthSpec.getLogoutURL()); + Assertions.assertEquals("https://simple-ui.apps.cluster.example", appAuthSpec.getRedirectURI()); + Assertions.assertEquals("https://simple-ui.apps.cluster.example", appAuthSpec.getLogoutURL()); + + // We must change the auth url of the backend to use the service. + appAuthSpec.setAuthServerUrl("http://keycloak." + namespace + ".svc.cluster.local/realms/registry"); client.resource(registry).create(); @@ -86,13 +86,13 @@ void testKeycloakPlain() { 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 + "=" - + "http://simple-keycloak.apps.cluster.example:8090/realms/registry"); + .contains(EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL + "=" + "http://keycloak." + + namespace + ".svc.cluster.local/realms/registry"); assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) .contains(EnvironmentVariables.APICURIO_UI_AUTH_OIDC_REDIRECT_URI + "=" - + "http://simple-ui.apps.cluster.example"); + + "https://simple-ui.apps.cluster.example"); assertThat(appEnv).map(ev -> ev.getName() + "=" + ev.getValue()) .contains(EnvironmentVariables.APICURIO_UI_AUTH_OIDC_LOGOUT_URL + "=" - + "http://simple-ui.apps.cluster.example"); + + "https://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 index cf3aeaf576..ccd0a5fd9a 100644 --- a/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml +++ b/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml @@ -3178,7 +3178,7 @@ spec: - name: PROXY_ADDRESS_FORWARDING value: "true" - name: KEYCLOAK_FRONTEND_URL - value: "http://simple-keycloak.apps.cluster.example" + value: "https://simple-keycloak.apps.cluster.example" ports: - name: http containerPort: 8090 @@ -3196,19 +3196,6 @@ spec: --- apiVersion: v1 kind: Service -metadata: - name: keycloak-headless -spec: - clusterIP: None - ports: - - name: http - port: 8090 - targetPort: 8090 - selector: - app: keycloak ---- -apiVersion: v1 -kind: Service metadata: name: keycloak labels: @@ -3216,17 +3203,31 @@ metadata: spec: ports: - name: http - port: 8090 + port: 80 targetPort: 8090 selector: app: keycloak type: LoadBalancer --- +apiVersion: v1 +kind: Secret +metadata: + name: keycloak-tls + namespace: keycloak +type: kubernetes.io/tls +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURYekNDQWtlZ0F3SUJBZ0lVZmQ1OEdNUDljRlMzZzZuNldhNzdsUGtYZUY4d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1B6RXFNQ2dHQTFVRUF3d2hkR3h6TFd0bGVXTnNiMkZyTG1Gd2NITXVZMngxYzNSbGNpNWxlR0Z0Y0d4bApNUkV3RHdZRFZRUUtEQWhMWlhsamJHOWhhekFlRncweU5UQXhNamt4TmpRM016aGFGdzB6TlRBeE1qY3hOalEzCk16aGFNRDh4S2pBb0JnTlZCQU1NSVhSc2N5MXJaWGxqYkc5aGF5NWhjSEJ6TG1Oc2RYTjBaWEl1WlhoaGJYQnMKWlRFUk1BOEdBMVVFQ2d3SVMyVjVZMnh2WVdzd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFSwpBb0lCQVFDeWQ2OEVVMXZpRkhVTlhOSFVzYWpmWmVPdXBxUDE3SjhJaE1DcGY0U3NRWXl4WnFYanhDVTA5N0VlCklyemxjbjFCMzhGRUZQRHdkZ1V5NGt1SGNpZ2pJMFZ5ZVYwNTJRTkYySFpJdThiM3BNVm1zSXRNZWcwcHpaSFgKUjB6c0dRUVBJQS9SaGs0bUpsdjVBdHFhcWkyUmVOQ3BtR3dKZklQVzFUQndLYWZ2VGV6MnZUSXBXY0FmcTJVdQpiVkZhMHBrY2NDRTNZejJNV2VvWnd0dGxCN1RHS2tjNEJObGxrQmxaZ0FLSXhHYk5Bd0JiaVpqejBGZVFIQk90ClcwZUxjUFVEZG1jeDJCQWxzVTFkd21FT1BkLzJUNDFIUlZ5NUlNZ0Fxa2loMHNkcFF5MnpUY2swRDJDMHpFcWUKN1EyMEY2RVRPamI2U0JzN0NXejJHNlk5cmU1RkFnTUJBQUdqVXpCUk1CMEdBMVVkRGdRV0JCUVI0WVpjVC8yMQpmdktrQVptUkxaTXp3anNKU0RBZkJnTlZIU01FR0RBV2dCUVI0WVpjVC8yMWZ2S2tBWm1STFpNendqc0pTREFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFBZ28xTndRbXBtNHQ1UytuM2EKUGhFRTM0dFN0RVd3TW5vNW5zL0hrS2c3VzVtVGl2UEJsOWdUemRDTjlFL3hjb1ZxOEkzVjZxQ2VKN2xmTHBQcgpzdnZ4M2hyTW5WUDUyMUdZV1BKOVFRaDNHeTRDNHBQVjJxeHV2b0ZaaUdKY0diWWcrSnJKTElKWGk1RUdiNk1YCi81bzVRb01GYUtBSGU2WVRjVzA2cEp5NDlFaXdQWkRJM1dCcXlzSTNhbHRWUlNBN3pXdWJHb1JXdjArS0dNcHoKMzNkNWcyT1ovOG5pREhxSFZxNDVucFRyczBXU0pLa1ZoWDBaazFJN0ZTYlJNQzllcXlaRkQzaG45TGF5eDlrbwprcVgyZ0dubDVVV25PWElWdUFXREQ2NE1RTWtyOWZpeXp6ekEvbmJmT2lvVG1PdmxPQmduVnFmVnFMb25kelg2CkY2NlMKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRQ3lkNjhFVTF2aUZIVU4KWE5IVXNhamZaZU91cHFQMTdKOEloTUNwZjRTc1FZeXhacVhqeENVMDk3RWVJcnpsY24xQjM4RkVGUER3ZGdVeQo0a3VIY2lnakkwVnllVjA1MlFORjJIWkl1OGIzcE1WbXNJdE1lZzBwelpIWFIwenNHUVFQSUEvUmhrNG1KbHY1CkF0cWFxaTJSZU5DcG1Hd0pmSVBXMVRCd0thZnZUZXoydlRJcFdjQWZxMlV1YlZGYTBwa2NjQ0UzWXoyTVdlb1oKd3R0bEI3VEdLa2M0Qk5sbGtCbFpnQUtJeEdiTkF3QmJpWmp6MEZlUUhCT3RXMGVMY1BVRGRtY3gyQkFsc1UxZAp3bUVPUGQvMlQ0MUhSVnk1SU1nQXFraWgwc2RwUXkyelRjazBEMkMwekVxZTdRMjBGNkVUT2piNlNCczdDV3oyCkc2WTlyZTVGQWdNQkFBRUNnZ0VBVVRrOVUwZXBBc3p5dFFFd2tvL0UzdCtkYndoeWlPT0hRYlpCaFNML08vS2QKV01QeDdpYUFGSXBDZHdleVZ1N3phUDZ3RkE4LzFRQ3h2d1hWQURFSmFXeU5GOXQ2ZlhCeUYrQzdmTURSZmpYawpqYWI5enZHaGVnd1FPeDA5T2hyc2lRRzdrVTJCMWNVUmlOUXVyOE9SOTZvM3RXZ2tpM0M3QkJTRTloZFBVZzZ0CkNoSzBSZW1GTStQQnZFa1VMbnNtUHNmdnpMU3k0ZVdlajlRUGlNa0JQTnR3VTFjelY4dFZZY2I3UFlKbjN3Uk8KcXl1WjBIU3VFWUl0a3I3cWg0ZW5xR3Zkek4vclZKUUlMc3VLVkhOd0xRbUJYVjMxK1U2TFJRbWJqWW9UU0JqbgppK2V0c0Fta0dFeFFiRUhEb2cza2RML2dQcFRyKzFCaXozNkJpOTFraFFLQmdRRDdsOFprcFJPLy9OUm9CbkhiCkltM2ttbmkxQjZwaUVCczhXamNNejhTNmwzNnBwQVpUMCtGL3FXUTkzZnJYUUpNYWUyOWd5SS9Fby9PQXlzdWkKMFZBTjdWNVVSN01ncjc2TzFJQU5naXVvVC9yRE92bkZQV0thM3BoYnc1UlBBYStsRmxzdy9DNzAwTmtiUjlydgpWY0t4aFluNVhtMVlZMTZSRXZWdFVGRVdnd0tCZ1FDMWwvMld2bytxZkFxZUtUeXBXaHFZUU95Y29ZYXZpVUNKCmlJOG1zaGhsemJhRG9DSm94aE1MTFgzcjJpUlA4anBZQTZyWEZvNEhpU2RKSC9WZFlmMXBQRk4zRkJVVytNZ2YKdnl1U2ZDK3ozV3NrNFpXWEJyUDNaQmxJeFpTRjlzZDRyU2llVUlQZkpsV29lU2JOM3lmUHV6NUU4NmZLbTU1cApxd1lDdTJjTmx3S0JnUUNrVEpOa3VtRFJhWnlVYWxFRUY5SElEaEJ4dUJYT2dGSW84WWt0dHFqMGFXWndCZ3VUCnhyUm1HQXE4VkRBeFRaeERHUVM5SVh5eG41ZXZMY0FhRGJMSHhaRnFYSUJnQWlUaFJXaHlhZVYwdnZZMWRGZ1UKTkdnbVZ1TU1XZ2FLS2NHNGY0Y0IwRTRoMWhsUnRYVUdBdTFuM0pzajNFUndDa1NCWE80bGV1UFpYUUtCZ0VtLwpFdTVBTDIKLS0tLS1FTkQgU0VDUkVULS0tLS0= +--- 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: @@ -3237,4 +3238,4 @@ spec: service: name: keycloak port: - number: 8090 \ No newline at end of file + number: 80 \ 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 index 958a6fa721..8b6bfc55ce 100644 --- 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 @@ -10,10 +10,15 @@ spec: enabled: true appClientId: registry-api uiClientId: apicurio-registry - authServerUrl: http://simple-keycloak.apps.cluster.example:8090/realms/registry - redirectURI: http://simple-ui.apps.cluster.example - logoutURL: http://simple-ui.apps.cluster.example + authServerUrl: https://simple-keycloak.apps.cluster.example/realms/registry + redirectURI: https://simple-ui.apps.cluster.example + logoutURL: https://simple-ui.apps.cluster.example tlsVerification: none ui: ingress: host: simple-ui.apps.cluster.example + env: + - name: REGISTRY_API_URL + value: https://simple-app.apps.cluster.example/apis/registry/v3 + - name: REGISTRY_AUTH_URL + value: https://simple-keycloak.apps.cluster.example/realms/registry From 893dcc8e11e23c3441806824b606a98954afda27 Mon Sep 17 00:00:00 2001 From: Carles Arnal Date: Thu, 30 Jan 2025 19:53:01 +0100 Subject: [PATCH 08/11] Add tls verification configuration --- .../operator/EnvironmentVariables.java | 8 +- .../apicurio/registry/operator/feat/Auth.java | 30 +- .../registry/operator/feat/AuthTLS.java | 29 +- .../registry/operator/utils/Utils.java | 29 + .../apicurio/registry/operator/it/ITBase.java | 43 +- .../registry/operator/it/KeycloakITTest.java | 10 +- .../operator/it/KeycloakTLSITTest.java | 98 + .../registry/operator/unit/CorsTest.java | 2 +- .../resources/k8s/examples/auth/keycloak.yaml | 56 +- ...imple-with_keycloak.apicurioregistry3.yaml | 8 +- .../k8s/examples/auth/tls/keycloak.yaml | 3244 ----------------- ...imple-with_keycloak.apicurioregistry3.yaml | 31 + .../api/v1/spec/auth/AppAuthSpec.java | 18 +- .../api/v1/spec/auth/AuthTLSSpec.java | 13 +- 14 files changed, 278 insertions(+), 3341 deletions(-) create mode 100644 operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakTLSITTest.java delete mode 100644 operator/controller/src/test/resources/k8s/examples/auth/tls/keycloak.yaml create mode 100644 operator/controller/src/test/resources/k8s/examples/auth/tls/simple-with_keycloak.apicurioregistry3.yaml 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 9141e7cf7f..d48779eabb 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 @@ -31,11 +31,9 @@ public class EnvironmentVariables { 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_KEYSTORE_TYPE = "QUARKUS_OIDC_TLS_KEY_STORE_FILE_TYPE"; - public static final String OIDC_TLS_KEYSTORE_LOCATION = "QUARKUS_OIDC_TLS_KEY_STORE_LOCATION"; + public static final String OIDC_TLS_KEYSTORE_LOCATION = "QUARKUS_OIDC_TLS_KEY_STORE_FILE"; public static final String OIDC_TLS_KEYSTORE_PASSWORD = "QUARKUS_OIDC_TLS_KEY_STORE_PASSWORD"; - public static final String OIDC_TLS_TRUSTSTORE_TYPE = "QUARKUS_OIDC_TLS_TRUST_STORE_FILE_TYPE"; - public static final String OIDC_TLS_TRUSTSTORE_LOCATION = "QUARKUS_OIDC_TLS_TRUST_STORE_NAME"; - public static final String OIDC_TLS_TRUSTSTORE_PASSWORD = "QUARKUS_OIDC_TLS_TRUST_STORE_PASWORD"; + 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"; } diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/Auth.java b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/Auth.java index b923108b68..f7f2add59b 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/Auth.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/Auth.java @@ -2,14 +2,15 @@ import io.apicurio.registry.operator.EnvironmentVariables; import io.apicurio.registry.operator.api.v1.spec.auth.AppAuthSpec; -import io.apicurio.registry.operator.utils.Utils; import io.fabric8.kubernetes.api.model.EnvVar; -import io.fabric8.kubernetes.api.model.EnvVarBuilder; 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. */ @@ -40,32 +41,7 @@ public static void configureAuth(AppAuthSpec appAuthSpec, Deployment deployment, putIfNotBlank(env, EnvironmentVariables.APICURIO_UI_AUTH_OIDC_LOGOUT_URL, appAuthSpec.getLogoutURL()); putIfNotBlank(env, EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL, appAuthSpec.getAuthServerUrl()); - putIfNotBlank(env, EnvironmentVariables.OIDC_TLS_VERIFICATION, appAuthSpec.getTlsVerification()); AuthTLS.configureAuthTLS(appAuthSpec, deployment, env); } - - /** - * Adds an environment variable to the map only if the value is not null or blank. - * - * @param envVars The environment variables map. - * @param name The name of the environment variable. - * @param value The value to be set (ignored if null or blank). - */ - private static void putIfNotBlank(Map 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. - */ - private static EnvVar createEnvVar(String name, String value) { - return new EnvVarBuilder().withName(name).withValue(value).build(); - } } diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/AuthTLS.java b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/AuthTLS.java index 1df4f8660f..6daf72097a 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/AuthTLS.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/AuthTLS.java @@ -1,5 +1,6 @@ package io.apicurio.registry.operator.feat; +import io.apicurio.registry.operator.EnvironmentVariables; import io.apicurio.registry.operator.api.v1.spec.auth.AppAuthSpec; import io.apicurio.registry.operator.api.v1.spec.auth.AuthTLSSpec; import io.apicurio.registry.operator.utils.SecretKeyRefTool; @@ -12,6 +13,7 @@ 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 { @@ -19,9 +21,12 @@ public class AuthTLS { /** * Configure TLS for OIDC authentication */ - public static boolean configureAuthTLS(AppAuthSpec appAuthSpec, Deployment deployment, + public static void configureAuthTLS(AppAuthSpec appAuthSpec, Deployment deployment, Map env) { + putIfNotBlank(env, EnvironmentVariables.OIDC_TLS_VERIFICATION, + appAuthSpec.getTls().getTlsVerificationType()); + // spotless:off var keystore = new SecretKeyRefTool(getAuthTLSSpec(appAuthSpec) .map(AuthTLSSpec::getKeystoreSecretRef) @@ -39,27 +44,17 @@ public static boolean configureAuthTLS(AppAuthSpec appAuthSpec, Deployment deplo .map(AuthTLSSpec::getTruststorePasswordSecretRef) .orElse(null), "ca.password"); // spotless:on - - if (truststore.isValid() && truststorePassword.isValid() && keystore.isValid() - && keystorePassword.isValid()) { - - // ===== Keystore - - addEnvVar(env, OIDC_TLS_KEYSTORE_TYPE, "PKCS12"); - keystore.applySecretVolume(deployment, REGISTRY_APP_CONTAINER_NAME); - addEnvVar(env, OIDC_TLS_KEYSTORE_LOCATION, keystore.getSecretVolumeKeyPath()); - keystorePassword.applySecretEnvVar(env, OIDC_TLS_KEYSTORE_PASSWORD); - - // ===== Truststore - - addEnvVar(env, OIDC_TLS_TRUSTSTORE_TYPE, "PKCS12"); + 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); + } - return true; + if (keystore.isValid() && keystorePassword.isValid()) { + keystore.applySecretVolume(deployment, REGISTRY_APP_CONTAINER_NAME); + addEnvVar(env, OIDC_TLS_KEYSTORE_LOCATION, keystore.getSecretVolumeKeyPath()); + keystorePassword.applySecretEnvVar(env, OIDC_TLS_KEYSTORE_PASSWORD); } - return false; } private static Optional getAuthTLSSpec(AppAuthSpec primary) { diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/utils/Utils.java b/operator/controller/src/main/java/io/apicurio/registry/operator/utils/Utils.java index 87a00ca1c2..b0345127fb 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/utils/Utils.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/utils/Utils.java @@ -1,5 +1,10 @@ package io.apicurio.registry.operator.utils; +import io.fabric8.kubernetes.api.model.EnvVar; +import io.fabric8.kubernetes.api.model.EnvVarBuilder; + +import java.util.Map; + public class Utils { private Utils() { @@ -8,4 +13,28 @@ private Utils() { public static boolean isBlank(String value) { return value == null || value.isBlank(); } + + /** + * Adds an environment variable to the map only if the value is not null or blank. + * + * @param envVars The environment variables map. + * @param name The name of the environment variable. + * @param value The value to be set (ignored if null or blank). + */ + public static void putIfNotBlank(Map 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/ITBase.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/ITBase.java index ade6ea64b2..6b3e9a9a76 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.apps.Deployment; @@ -18,11 +20,7 @@ import jakarta.enterprise.util.TypeLiteral; import org.awaitility.Awaitility; import org.eclipse.microprofile.config.ConfigProvider; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -221,6 +219,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( diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java index 6a2652a14e..e5b9769eb9 100644 --- a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java @@ -46,6 +46,9 @@ void testKeycloakPlain() { .isEqualTo(1); }); + createKeycloakDNSResolution("simple-keycloak.apps.cluster.example", + "keycloak." + namespace + ".svc.cluster.local"); + // Deploy Registry var registry = deserialize("k8s/examples/auth/simple-with_keycloak.apicurioregistry3.yaml", ApicurioRegistry3.class); @@ -62,9 +65,6 @@ void testKeycloakPlain() { Assertions.assertEquals("https://simple-ui.apps.cluster.example", appAuthSpec.getRedirectURI()); Assertions.assertEquals("https://simple-ui.apps.cluster.example", appAuthSpec.getLogoutURL()); - // We must change the auth url of the backend to use the service. - appAuthSpec.setAuthServerUrl("http://keycloak." + namespace + ".svc.cluster.local/realms/registry"); - client.resource(registry).create(); // Assertions, checks registry deployments exist @@ -86,8 +86,8 @@ void testKeycloakPlain() { 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 + "=" + "http://keycloak." - + namespace + ".svc.cluster.local/realms/registry"); + .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"); diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakTLSITTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakTLSITTest.java new file mode 100644 index 0000000000..63882e0d28 --- /dev/null +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakTLSITTest.java @@ -0,0 +1,98 @@ +package io.apicurio.registry.operator.it; + +import io.apicurio.registry.operator.EnvironmentVariables; +import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.utils.Serialization; +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 java.util.List; + +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.ResourceFactory.deserialize; +import static io.apicurio.registry.operator.resource.app.AppDeploymentResource.getContainerFromDeployment; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +@QuarkusTest +public class KeycloakTLSITTest extends ITBase { + + private static final Logger log = LoggerFactory.getLogger(KeycloakTLSITTest.class); + + @BeforeAll + public static void init() { + Awaitility.setDefaultTimeout(Duration.ofSeconds(60)); + } + + @Test + void testKeycloakPlain() { + // Preparation, deploy Keycloak + List resources = Serialization + .unmarshal(KeycloakTLSITTest.class.getResourceAsStream("/k8s/examples/auth/keycloak.yaml")); + + createResources(resources, "Keycloak"); + + await().ignoreExceptions().untilAsserted(() -> { + assertThat(client.apps().deployments().withName("keycloak").get().getStatus().getReadyReplicas()) + .isEqualTo(1); + }); + + // Deploy Registry + var registry = deserialize("k8s/examples/auth/tls/simple-with_keycloak.apicurioregistry3.yaml", + ApicurioRegistry3.class); + + registry.getMetadata().setNamespace(namespace); + + var appAuthSpec = registry.getSpec().getApp().getAuth(); + + Assertions.assertEquals("registry-api", appAuthSpec.getAppClientId()); + Assertions.assertEquals("apicurio-registry", appAuthSpec.getUiClientId()); + Assertions.assertEquals(true, appAuthSpec.getEnabled()); + Assertions.assertEquals("https://simple-keycloak.apps.cluster.example/realms/registry", + appAuthSpec.getAuthServerUrl()); + Assertions.assertEquals("https://simple-ui.apps.cluster.example", appAuthSpec.getRedirectURI()); + Assertions.assertEquals("https://simple-ui.apps.cluster.example", appAuthSpec.getLogoutURL()); + + // We must change the auth url of the backend to use the service. + appAuthSpec.setAuthServerUrl("https://keycloak." + namespace + ".svc.cluster.local/realms/registry"); + + 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://keycloak." + + namespace + ".svc.cluster.local/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"); + } +} diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/unit/CorsTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/unit/CorsTest.java index 6b6ebde01d..cd6d203cdc 100644 --- a/operator/controller/src/test/java/io/apicurio/registry/operator/unit/CorsTest.java +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/unit/CorsTest.java @@ -19,7 +19,7 @@ public class CorsTest { public void testConfigureAllowedOrigins() throws Exception { doTestAllowedOrigins("k8s/examples/cors/example-default.yaml", "*"); doTestAllowedOrigins("k8s/examples/cors/example-ingress.yaml", - "https://simple-ui.apps.cluster.example", "https://simple-ui.apps.cluster.example"); + "http://simple-ui.apps.cluster.example", "https://simple-ui.apps.cluster.example"); doTestAllowedOrigins("k8s/examples/cors/example-env-vars.yaml", "https://ui.example.org"); doTestAllowedOrigins("k8s/examples/cors/example-env-vars-and-ingress.yaml", "https://ui.example.org"); } diff --git a/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml b/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml index ccd0a5fd9a..8f47c914b5 100644 --- a/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml +++ b/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml @@ -3144,6 +3144,23 @@ kind: ConfigMap metadata: name: keycloak-configmap --- +apiVersion: v1 +kind: Secret +metadata: + name: keycloak-tls +type: kubernetes.io/tls +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVHekNDQXdPZ0F3SUJBZ0lVRHE1a3N5dGovR20xdGRRUU91bGdkZ3dadXJNd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1l6RUxNQWtHQTFVRUJoTUNWVk14Q3pBSkJnTlZCQWdNQWtOQk1SWXdGQVlEVlFRSERBMVRZVzRnUm5KaApibU5wYzJOdk1SRXdEd1lEVlFRS0RBaExaWGxqYkc5aGF6RWNNQm9HQTFVRUF3d1RLaTV6ZG1NdVkyeDFjM1JsCmNpNXNiMk5oYkRBZUZ3MHlOVEF4TXpBeE9EUTBNRGRhRncweU5qQXhNekF4T0RRME1EZGFNR014Q3pBSkJnTlYKQkFZVEFsVlRNUXN3Q1FZRFZRUUlEQUpEUVRFV01CUUdBMVVFQnd3TlUyRnVJRVp5WVc1amFYTmpiekVSTUE4RwpBMVVFQ2d3SVMyVjVZMnh2WVdzeEhEQWFCZ05WQkFNTUV5b3VjM1pqTG1Oc2RYTjBaWEl1Ykc5allXd3dnZ0VpCk1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRRHRxanoyRzAvQTRnUUZGeXo5VzNmbmNkTGoKdFB1MWR6d2tFZ1BUdmM0SlFSQ2hBV0lCQkEyWTlNZUlCTzk0MFpGMWNJa1dMZjFvUURsRXVYNnVRVjZHdTJ4aQpVWDZ5QnNtK2krS0tZdWg2S2Jyd3V1ZmRBTzVFc0pYelpuRmR2L2lUNzBaWUJPTnNPVzBmVmhLY3dBaW9uSFN4CnI2aW1kb2V0S3EzTmJrZ1daQlBpL29SbGhFR2FwRitLTHpRZlU3U1d2bnNsdU5YQXVaMnZpQW5NVGdNNVhmcWoKR3VqSXZaVEliTUQxS05tR0c4U2ZjQXdmSHNXbXBOajUyOTNiQkNVN2lhUkFBakdBZkMyekdVNTJFL0xVQjJPTApxaDZVcUpzdW5pRjJXNWNJREs3OElEZ2tWQysxUy9haEVwUm1WVGZndk00R09zUmtJQXJsS2hpRCttVm5BZ01CCkFBR2pnY1l3Z2NNd2dhRUdBMVVkRVFTQm1UQ0Jsb0lJYTJWNVkyeHZZV3VDREd0bGVXTnNiMkZyTG5OMlk0SWEKYTJWNVkyeHZZV3N1YzNaakxtTnNkWE4wWlhJdWJHOWpZV3lDRXlvdWMzWmpMbU5zZFhOMFpYSXViRzlqWVd5QwpNeW91WVhCcFkzVnlhVzh0Y21WbmFYTjBjbmt0YjNCbGNtRjBiM0l0ZEdWemRDNXpkbU11WTJ4MWMzUmxjaTVzCmIyTmhiSUlXS2k1aGNIQnpMbU5zZFhOMFpYSXVaWGhoYlhCc1pUQWRCZ05WSFE0RUZnUVVBS0grcWZiYjNHMXIKTHRvaHl6ZUdVRDJ2N2Jrd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFOUnZ0V1RHWGthWS9LVCtWaEpwdzJpcQpHZUNJU2JBU28rNTZNMExabDU0M3JlaHhaRUQ0VzBXajdiTTJueFRtdG9sQ0MybGM3cTJST1lLQzU1Y3YvKzdGCitwMnEzWWZQeHpDV0hRN1F6UlRvK2FWN2FubFlOSUdXaXB3T0lPVHhvZmVzOTVmLzdwNTRzRXJzVTliUE9BTUsKLzZCaG5OUzZ4ZmxwczJMZTJrSXREV1dFUVVVZHZQZCtJN0R4SXFxdkpQQVpUL0NZcHFZU3RDQ3NhNWlYcFphOQpkdTl2dW9LVFVnRHhSYld0Rkw5dE5GNUFGM3hjYlVYenc0bE9Hd0g2WmNxZEdXVVA2NU8xYlg5UnJxYkNabHEvCmpWc21vV3BIUG9nK241WGFVUXRmcExRN2xIODNXeUFTMEsyUjYzME4vWFVRZjk1WTV0SWhRd281cmRPcS9wQT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2d0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktrd2dnU2xBZ0VBQW9JQkFRRHRxanoyRzAvQTRnUUYKRnl6OVczZm5jZExqdFB1MWR6d2tFZ1BUdmM0SlFSQ2hBV0lCQkEyWTlNZUlCTzk0MFpGMWNJa1dMZjFvUURsRQp1WDZ1UVY2R3UyeGlVWDZ5QnNtK2krS0tZdWg2S2Jyd3V1ZmRBTzVFc0pYelpuRmR2L2lUNzBaWUJPTnNPVzBmClZoS2N3QWlvbkhTeHI2aW1kb2V0S3EzTmJrZ1daQlBpL29SbGhFR2FwRitLTHpRZlU3U1d2bnNsdU5YQXVaMnYKaUFuTVRnTTVYZnFqR3VqSXZaVEliTUQxS05tR0c4U2ZjQXdmSHNXbXBOajUyOTNiQkNVN2lhUkFBakdBZkMyegpHVTUyRS9MVUIyT0xxaDZVcUpzdW5pRjJXNWNJREs3OElEZ2tWQysxUy9haEVwUm1WVGZndk00R09zUmtJQXJsCktoaUQrbVZuQWdNQkFBRUNnZ0VBUFBQMFBvb3lvaDFWT3BqL0NOdDBuUDhzUHdvanBCNjRaZWZZNDIxQ2NrMGwKbnFWa0pTNk03aldaRnVGdEtXZFdEdkNWdUFLRGcwRTFidExFRXB6aFNWeTRKMThQUjBsR1pJQXIwYUljVFhoMgpZVmNPTURnVTQ3a0dQQzZCMUxGdlowRnVvUlJmMzFtN0N0WmdYNk5BbFQzTEQrY2xNY3drYjkwS1JpMktmM01jCnJGdTA1UTBvQWEzaEQ4WGQ4a3dMSEJCOVNWREpqTlEzaVlCaTlDNElNQ3EzekNZNjg4a0xEVkFMQlUrQkswTm8KcEN1VlhWMU1jdVFYdURPL2JSdUJoWktBWGFpUWRqNGdRdlhkT1NVL0ozeE41UmpKT1hkRVE0eGNuRGhJRjA1NQpNbjRXZHY5dnVSd0k4akVPeFI1MDl1NUw5eEx4Mlp3MUhYWkZ4S0VhQVFLQmdRRC9rZi81dlhOM0tqYVljSzdLCk9VY0F6T1dQSlhGVlc2VFc0OVhFQkhMbkZZTVgyK2xqZGlDKzhZbE52S3F4V3JVUFRnVDhTMGd2ZEkwK1FwMHcKOUhHZkdxTDRqSEEwUmFla0MvTzFqWlUrMGdMWE9HMTQ5RkZmaEZEZmlHNHFZSHNUOW5HS1dEVFl6a29uVCs5RApteGllN09PUEVTakVrdElEdm91NEc4b09ZUUtCZ1FEdUVJZ1daelkrRnVkV3l1MUI3aTdiMzlqSGlwck5FcC83Ckdrbm5adG5UTG1sVFFvaEtFeEhUYlhHajRMbUUrWDhFcFl6S1BSTEFXZ3k0WVlpa1c3NklEOGYvS2YyNjlwNFkKVWFPVmd2Vi95UkFLcUt2cktxRWJWVDVFbWVhSkRSYkd3QjVsMUkvU3ordTVhVnJjOGdhMW9GVjhTVWh6WVhxawpyRnJUQlVRNHh3S0JnUUNRRTJ5dEMrS1dJVlFaNkJ5Qm9IeW90Tm9OTXR5TGRGaWZWRjNrWFdXeFpHMDgvQnE4Cm1aR29VMTVHVnNBYnI3azI0WkxwOEQ3SGtmczJTRE41cjdTR0wyd0VscGVDd0duYmoybGF4bkNHczdvcVBvTm8KbHNOeEI1VFlEMytib0R5Q2FvSmpyWXBOVnk1eXJ4dUJqMlAzNUMvOUxYMUFKRTBGRTF5TTlBMmF3UUtCZ1FERApIQ2xzc3dweHVMUEJIbTkxS1pDZUE4ZlhRbkc4d1JiODVYN01lQzd1Tll0OUFYcFJ1MjlRcU5JS1RaTyt0L3l5CkFqeHhRN3lxaVNNRHVtd1N5RjVQaFIrVXNRV1N4YXlzb2J4QVBTU1hwbUhQV280TUh0UUxZc0cxMTZ4cGhxQzAKSTZER05IL3JkeHpxeUwzL2R4eW9uczl4SFY2VHNjQ2lWZC9hTXViRzRRS0JnUURNZkNlQTNmN1FvcEhOSkRIOQpvWHpOTlVKZWU0RXN0ZUs4ZWZoYS9SNHpGeDdWcm9FY0FrYTF6bmdXbXRSdmtFNWhldytHNlBEWFhDcFhSaGZpCkQyd0JrR0IvQWw2WlQvOFA1dDFmQXU5NWNtbVdtUk5leHFmRXdnTVRJOE5zVjhpa2d4OEpzZ2xyYzlneUQ1WXcKeGdROGQ5MmZabGxFdisrUWVhaHFkUXkzUkE9PQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t +--- +apiVersion: v1 +kind: Secret +metadata: + name: keycloak-truststore +data: + truststore: MIILQAIBAzCCCvYGCSqGSIb3DQEHAaCCCucEggrjMIIK3zCCBSoGCSqGSIb3DQEHBqCCBRswggUXAgEAMIIFEAYJKoZIhvcNAQcBMF8GCSqGSIb3DQEFDTBSMDEGCSqGSIb3DQEFDDAkBBA6eRD5E7kSv50VBDTbKGiwAgIIADAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQlyGrpNprV7ehpgWtpeG4sICCBKDO4TaLLkXo/JfrMfoSDRO8vBqncWmJ1qEKLHM1rP7fdsoF5nl+iliOWCOi6RT5zT+AgIwvSXgDI3cSdMW7BAexwHahN1CABwDR9XnSNgI2G57jMHQIAiMrMA00oyJpsIjfEnTu1/ymNAUj57dzcectDneCQHubZvZzs7ZpRNpcUPwhGTskEGyRhm5Xcohn59iPE8mmEWZsX2cdXBNPJHiovxTRKLRZwhDoWrNvcOOhVaQ50MdkgrZsvqq5X1IQyKZm+FLxb/o0gQ6lkPRdlUMEmWHLdDQ41JOQbHG3KHLfZW9J6ofwSRoNxhJvYck0dhwvmubkw4Cosd7e/IDZ5TiVRZ4DITYxUEmMuCbtIw826seGJP5rUTMHt40UpgYJnQXZj7os3YyO1D3JVmvAHMFuqG/998R1Ll9fTs09ml8adBDwATBtYtRoFUgq+lnwoZg6w195E/hVmo1LtsMvO7/t0eOqlWecFUGf7lhndSNShlbKsJgYVfMQsYKRQ565OFIvgvpSl9belsQR7A+kpsPAeKM2uZMlCi9YrjUhSHjQSdjb1O2NCTXBXULCc9AgagyOWuzDNEdj2QE5ewxvTX/XRLEu8KkPP1VowGIyh3MnmMQxj2En3mmCE9wULIVFYMizOyI2dyK5KGIeBFCrd+YE7+E5nDY3WrRfsDvX4z+NAsVll7OcieGHrDGRuNhErYe0YooA7FRsy1oXXuXg8OUNCxn/D4ioHt9zS+2ck0KJZkjDgy8aP5/1vA/1MQAwxfy431tHqkONspLR/fEjRAMWs/7SwHQeI9CB2jIZEYqIhFC1x5Sc9kgZ00AeAdnjK0innr2m/skzdu8+uSi8Mf3MUNf4v8S8gyqz57SUGxZ8H9z1pDedmYBv0QGRXK2DUrBGc9azggIo3dN10p70jwSUZvYBX1m4HzEElRFZ67YWOPLNY0gctWD+MLnaTKkvLE4+R6bHsjZ8wZzWrj3A9P8eZ2IjZhJET/3QTMWMekdFhqRBv6lPge8Xaovqk63ReHrpkUxdW+QkfPxCvg0Ny/eO1TqE2p7WpWJxzEr6r9UoaHGRgXnw0Z3646DUKLqKX2PQ5s9HZTLrcMxrv8gx1zAIMR4JVJpCECRiwvKGIvAl4mKhGGkXZIe9VuXTp7E3k2RgKaNhUfjhi57hfpZ9CsmZtvf6DVFGmd++DB/zrkX/ypk5d2ZNgA1Lc9CjCTBlsTLoqOPO63/KUXY9WMsOc6Aalm7XhMCEOwJPflHQqGwGaI/zw/VN54tmw7+y5cqLHJHV5eIHHMRHzSOsQCPYs2gw2HYEZMUFnn8MQwvD6EfmKp6tWi/3YvPf1Lq+LeLc24yMD+OXnv2NOg8sdapjG6snX4mVZMnzr4cmY59PRiRdtgsgIdKNB5Oq/SCcxiItRksrMo7hNBvAE5rN5ZaVR87i295aJibHQ7+TIdUzZUr0vugxjc6Z8K5w+rOg6pLjm8V/84pGcWWNeKamf+V2z3x1WCieheVGlDXJbsRdIMKPjPsl8bk77DBAEy3aNmI9uc40XF7illQdbAk1wPT9IMBcdkDQd5F7WOkXq9J5nAq3dDCCBa0GCSqGSIb3DQEHAaCCBZ4EggWaMIIFljCCBZIGCyqGSIb3DQEMCgECoIIFOTCCBTUwXwYJKoZIhvcNAQUNMFIwMQYJKoZIhvcNAQUMMCQEEF2lvAPOGfpDm7KH+lVKjtQCAggAMAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBDfHF4u/SgMTb1+n23MQaSZBIIE0Oz3+pPmxGxlKj1L/1QXEFbyMt86ScQBEIyXiSbEJS0hWTs/e7lvYWU9poWi8hATHrJcgjdnZSipS8Bue8vqhFqme7rsETDLgnNpUQcjQxn/yD9j4n8a0sDY7bmeoYusb2QB9eXiD4ZImXLb0NnKibpqc2MOwJICC2O7Y2pkIpKvymsuxqameQRp5laoyUo8L5SwgbN9tOo+iRKXoVD5eAuOmUfHKU/inOzxyxmr9xF1E+lUS/vOEMS4j7qFrg3RTh6OTt4O8IwpOEY6jhvhcI0X6YIQALB0tEXNsGzZkHfwTrF4ppZBJStWSwFo6bvGMj7gJWhynul1BnUdcnsfOeHqKoo5a1kO6jc5JSyYtoeWJyFomCYoDd7SMbTYC7FPhSddZw9+IaaSYNYM5ayOqgfV6M6qh/f5K/0tW3MJYT9hSjaVgFBrkBSPo07GpQX7SXphoqyDxc+G4CWzxtVSTB7eod583MY147ha0OqPNS+LHklCMlF3YOOWt7025JXFLk1jsJ/yQYFzPkaeZeK+xCNabUVwJsqLHaB++xGzrzi4mr3IB473u9VBZF+1liIRAmImBJqDSU7qdPJ8xRSk2Y9QNOZfX/gJZu2E0KnVLgMiAE5dpuJ16OCcBM8W2YDhRnKq/H80WDTrXSjuTrFMrMn41wO5DTtvytqikPDMbfQEeSEuXCUKOgeWaemazrKmPe/ZovAGMINSlg0h+U4D0YkiOWJ9dH6d91WGYZpMxzr1LbBjCqSkcOMTJuXcZSOsfckm0MzhfjX1n6Fn+14yvz6Ep15ESK5wcKZbPAcrwU2h2ZODO3HTz1Dc7lKMUy7kBPChVQtqOH0kKBL5kpP6FDvjPo5UxNvMlUjqhF+Uo/g/8IPGr2+F+x12+/VncWWZfn0xkZ4nuTNrNEXsyYCI6s1qBaKHa1tpjoR5ncgBFg3gN5j0LCKI8sJukmEclrs09OGudEFHq9K1y7CeJkGn7fWelPxxlvbNAxJvefZw9RMcTrG/N4j12b111JY/3+13QrGzH8OdDi91iGMvg48+AVBuL9D0XQYe9KIhzOR2tGBBeQvGcHhULzzEqv5aQHH62yfri1El3n4Aut5hP/9sdqkUlPK/MwiPkxwCIOFho+Tl66rYcOCO8U+HAePJP33QV9lFClpzmtfh7Mj8xv2szWfhA/xUK7TFgfrMbjgOZI/l+8+t77VPSB8Ur6fvvaHRyP6yqf83yDY119uLo3idS2XyswhT4zG/Lc4Fx9Cbhnz4VKIoDjTwnIcQH25KEJYvTgl/1IdVGR/w7ZbwfJF0o9CnT+oa89AONbYpObwiB35ImUJAmQ30ockRbfknFV7ZPQGGkj7tIu+DmygJN0qKM3LasedpJwwfwV+fibLfqWstZmQqxPl4KdyjLgaY0vHqROiGllGFZv8foHjObS16npWgD51+hzrZqXSG2XK3f1RAbz9LsS4d5YNU63vmCJbR0LNIFUbyijCgSTImEWBWa0DnuRs3hwWXhLu32g8IMnK4Ai3tut9XCb3L0X2W9k7lKeDntzE6JoRIJ4gXEBbLqM/ZYmXF9Kcd5Xa3sIRQXeE1gYXhde0iwJ1538no+sFPfhNcyYggaRbvp/WHyzq82AHF1nubbx954jeAn6dOhS49MUYwHwYJKoZIhvcNAQkUMRIeEABrAGUAeQBjAGwAbwBhAGswIwYJKoZIhvcNAQkVMRYEFIdlbOhFpWzKLhy2ou13L+PLiRDSMEEwMTANBglghkgBZQMEAgEFAAQgoh3teDOnQzRB3DfUXVI6GItHsnwuhrSgiuPhPSqfkykECBoicn2i1WYzAgIIAA== + password: YXBpY3VyaW8= +--- apiVersion: apps/v1 kind: Deployment metadata: @@ -3165,6 +3182,8 @@ spec: 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 @@ -3173,26 +3192,39 @@ spec: value: "edge" - name: KC_HEALTH_ENABLED value: "true" - - name: KC_HTTP_PORT - value: "8090" + - 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: 8090 + 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 @@ -3202,23 +3234,13 @@ metadata: app: keycloak spec: ports: - - name: http - port: 80 - targetPort: 8090 + - name: https + port: 443 + targetPort: 8443 selector: app: keycloak type: LoadBalancer --- -apiVersion: v1 -kind: Secret -metadata: - name: keycloak-tls - namespace: keycloak -type: kubernetes.io/tls -data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURYekNDQWtlZ0F3SUJBZ0lVZmQ1OEdNUDljRlMzZzZuNldhNzdsUGtYZUY4d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1B6RXFNQ2dHQTFVRUF3d2hkR3h6TFd0bGVXTnNiMkZyTG1Gd2NITXVZMngxYzNSbGNpNWxlR0Z0Y0d4bApNUkV3RHdZRFZRUUtEQWhMWlhsamJHOWhhekFlRncweU5UQXhNamt4TmpRM016aGFGdzB6TlRBeE1qY3hOalEzCk16aGFNRDh4S2pBb0JnTlZCQU1NSVhSc2N5MXJaWGxqYkc5aGF5NWhjSEJ6TG1Oc2RYTjBaWEl1WlhoaGJYQnMKWlRFUk1BOEdBMVVFQ2d3SVMyVjVZMnh2WVdzd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFSwpBb0lCQVFDeWQ2OEVVMXZpRkhVTlhOSFVzYWpmWmVPdXBxUDE3SjhJaE1DcGY0U3NRWXl4WnFYanhDVTA5N0VlCklyemxjbjFCMzhGRUZQRHdkZ1V5NGt1SGNpZ2pJMFZ5ZVYwNTJRTkYySFpJdThiM3BNVm1zSXRNZWcwcHpaSFgKUjB6c0dRUVBJQS9SaGs0bUpsdjVBdHFhcWkyUmVOQ3BtR3dKZklQVzFUQndLYWZ2VGV6MnZUSXBXY0FmcTJVdQpiVkZhMHBrY2NDRTNZejJNV2VvWnd0dGxCN1RHS2tjNEJObGxrQmxaZ0FLSXhHYk5Bd0JiaVpqejBGZVFIQk90ClcwZUxjUFVEZG1jeDJCQWxzVTFkd21FT1BkLzJUNDFIUlZ5NUlNZ0Fxa2loMHNkcFF5MnpUY2swRDJDMHpFcWUKN1EyMEY2RVRPamI2U0JzN0NXejJHNlk5cmU1RkFnTUJBQUdqVXpCUk1CMEdBMVVkRGdRV0JCUVI0WVpjVC8yMQpmdktrQVptUkxaTXp3anNKU0RBZkJnTlZIU01FR0RBV2dCUVI0WVpjVC8yMWZ2S2tBWm1STFpNendqc0pTREFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFBZ28xTndRbXBtNHQ1UytuM2EKUGhFRTM0dFN0RVd3TW5vNW5zL0hrS2c3VzVtVGl2UEJsOWdUemRDTjlFL3hjb1ZxOEkzVjZxQ2VKN2xmTHBQcgpzdnZ4M2hyTW5WUDUyMUdZV1BKOVFRaDNHeTRDNHBQVjJxeHV2b0ZaaUdKY0diWWcrSnJKTElKWGk1RUdiNk1YCi81bzVRb01GYUtBSGU2WVRjVzA2cEp5NDlFaXdQWkRJM1dCcXlzSTNhbHRWUlNBN3pXdWJHb1JXdjArS0dNcHoKMzNkNWcyT1ovOG5pREhxSFZxNDVucFRyczBXU0pLa1ZoWDBaazFJN0ZTYlJNQzllcXlaRkQzaG45TGF5eDlrbwprcVgyZ0dubDVVV25PWElWdUFXREQ2NE1RTWtyOWZpeXp6ekEvbmJmT2lvVG1PdmxPQmduVnFmVnFMb25kelg2CkY2NlMKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRQ3lkNjhFVTF2aUZIVU4KWE5IVXNhamZaZU91cHFQMTdKOEloTUNwZjRTc1FZeXhacVhqeENVMDk3RWVJcnpsY24xQjM4RkVGUER3ZGdVeQo0a3VIY2lnakkwVnllVjA1MlFORjJIWkl1OGIzcE1WbXNJdE1lZzBwelpIWFIwenNHUVFQSUEvUmhrNG1KbHY1CkF0cWFxaTJSZU5DcG1Hd0pmSVBXMVRCd0thZnZUZXoydlRJcFdjQWZxMlV1YlZGYTBwa2NjQ0UzWXoyTVdlb1oKd3R0bEI3VEdLa2M0Qk5sbGtCbFpnQUtJeEdiTkF3QmJpWmp6MEZlUUhCT3RXMGVMY1BVRGRtY3gyQkFsc1UxZAp3bUVPUGQvMlQ0MUhSVnk1SU1nQXFraWgwc2RwUXkyelRjazBEMkMwekVxZTdRMjBGNkVUT2piNlNCczdDV3oyCkc2WTlyZTVGQWdNQkFBRUNnZ0VBVVRrOVUwZXBBc3p5dFFFd2tvL0UzdCtkYndoeWlPT0hRYlpCaFNML08vS2QKV01QeDdpYUFGSXBDZHdleVZ1N3phUDZ3RkE4LzFRQ3h2d1hWQURFSmFXeU5GOXQ2ZlhCeUYrQzdmTURSZmpYawpqYWI5enZHaGVnd1FPeDA5T2hyc2lRRzdrVTJCMWNVUmlOUXVyOE9SOTZvM3RXZ2tpM0M3QkJTRTloZFBVZzZ0CkNoSzBSZW1GTStQQnZFa1VMbnNtUHNmdnpMU3k0ZVdlajlRUGlNa0JQTnR3VTFjelY4dFZZY2I3UFlKbjN3Uk8KcXl1WjBIU3VFWUl0a3I3cWg0ZW5xR3Zkek4vclZKUUlMc3VLVkhOd0xRbUJYVjMxK1U2TFJRbWJqWW9UU0JqbgppK2V0c0Fta0dFeFFiRUhEb2cza2RML2dQcFRyKzFCaXozNkJpOTFraFFLQmdRRDdsOFprcFJPLy9OUm9CbkhiCkltM2ttbmkxQjZwaUVCczhXamNNejhTNmwzNnBwQVpUMCtGL3FXUTkzZnJYUUpNYWUyOWd5SS9Fby9PQXlzdWkKMFZBTjdWNVVSN01ncjc2TzFJQU5naXVvVC9yRE92bkZQV0thM3BoYnc1UlBBYStsRmxzdy9DNzAwTmtiUjlydgpWY0t4aFluNVhtMVlZMTZSRXZWdFVGRVdnd0tCZ1FDMWwvMld2bytxZkFxZUtUeXBXaHFZUU95Y29ZYXZpVUNKCmlJOG1zaGhsemJhRG9DSm94aE1MTFgzcjJpUlA4anBZQTZyWEZvNEhpU2RKSC9WZFlmMXBQRk4zRkJVVytNZ2YKdnl1U2ZDK3ozV3NrNFpXWEJyUDNaQmxJeFpTRjlzZDRyU2llVUlQZkpsV29lU2JOM3lmUHV6NUU4NmZLbTU1cApxd1lDdTJjTmx3S0JnUUNrVEpOa3VtRFJhWnlVYWxFRUY5SElEaEJ4dUJYT2dGSW84WWt0dHFqMGFXWndCZ3VUCnhyUm1HQXE4VkRBeFRaeERHUVM5SVh5eG41ZXZMY0FhRGJMSHhaRnFYSUJnQWlUaFJXaHlhZVYwdnZZMWRGZ1UKTkdnbVZ1TU1XZ2FLS2NHNGY0Y0IwRTRoMWhsUnRYVUdBdTFuM0pzajNFUndDa1NCWE80bGV1UFpYUUtCZ0VtLwpFdTVBTDIKLS0tLS1FTkQgU0VDUkVULS0tLS0= ---- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -3238,4 +3260,4 @@ spec: service: name: keycloak port: - number: 80 \ No newline at end of file + 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 index 8b6bfc55ce..2e49ddeae0 100644 --- 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 @@ -13,12 +13,8 @@ spec: authServerUrl: https://simple-keycloak.apps.cluster.example/realms/registry redirectURI: https://simple-ui.apps.cluster.example logoutURL: https://simple-ui.apps.cluster.example - tlsVerification: none + tls: + tlsVerificationType: none ui: ingress: host: simple-ui.apps.cluster.example - env: - - name: REGISTRY_API_URL - value: https://simple-app.apps.cluster.example/apis/registry/v3 - - name: REGISTRY_AUTH_URL - value: https://simple-keycloak.apps.cluster.example/realms/registry diff --git a/operator/controller/src/test/resources/k8s/examples/auth/tls/keycloak.yaml b/operator/controller/src/test/resources/k8s/examples/auth/tls/keycloak.yaml deleted file mode 100644 index 44e47bf010..0000000000 --- a/operator/controller/src/test/resources/k8s/examples/auth/tls/keycloak.yaml +++ /dev/null @@ -1,3244 +0,0 @@ -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: 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_BOOTSTRAP_ADMIN_USERNAME - value: "admin" - - name: KC_BOOTSTRAP_ADMIN_PASSWORD - value: "admin" - - name: KC_PROXY - value: "edge" - - name: PROXY_ADDRESS_FORWARDING - value: "true" - - name: KEYCLOAK_FRONTEND_URL - value: "https://tls-keycloak.apps.cluster.example" - - name: KC_HEALTH_ENABLED - value: "true" - ports: - - name: app - containerPort: 8080 - - name: health - containerPort: 9000 - readinessProbe: - httpGet: - path: /health/ready - port: 9000 - volumeMounts: - - name: keycloak-volume - mountPath: /opt/keycloak/data/import - volumes: - - name: keycloak-volume - configMap: - name: keycloak-configmap ---- -apiVersion: v1 -kind: Service -metadata: - name: keycloak - labels: - app: keycloak -spec: - type: ClusterIP - ports: - - name: http - port: 8080 - targetPort: 8080 - selector: - app: keycloak ---- -apiVersion: v1 -kind: Secret -metadata: - name: keycloak-tls - namespace: keycloak -type: kubernetes.io/tls -data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURYekNDQWtlZ0F3SUJBZ0lVZmQ1OEdNUDljRlMzZzZuNldhNzdsUGtYZUY4d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1B6RXFNQ2dHQTFVRUF3d2hkR3h6TFd0bGVXTnNiMkZyTG1Gd2NITXVZMngxYzNSbGNpNWxlR0Z0Y0d4bApNUkV3RHdZRFZRUUtEQWhMWlhsamJHOWhhekFlRncweU5UQXhNamt4TmpRM016aGFGdzB6TlRBeE1qY3hOalEzCk16aGFNRDh4S2pBb0JnTlZCQU1NSVhSc2N5MXJaWGxqYkc5aGF5NWhjSEJ6TG1Oc2RYTjBaWEl1WlhoaGJYQnMKWlRFUk1BOEdBMVVFQ2d3SVMyVjVZMnh2WVdzd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFSwpBb0lCQVFDeWQ2OEVVMXZpRkhVTlhOSFVzYWpmWmVPdXBxUDE3SjhJaE1DcGY0U3NRWXl4WnFYanhDVTA5N0VlCklyemxjbjFCMzhGRUZQRHdkZ1V5NGt1SGNpZ2pJMFZ5ZVYwNTJRTkYySFpJdThiM3BNVm1zSXRNZWcwcHpaSFgKUjB6c0dRUVBJQS9SaGs0bUpsdjVBdHFhcWkyUmVOQ3BtR3dKZklQVzFUQndLYWZ2VGV6MnZUSXBXY0FmcTJVdQpiVkZhMHBrY2NDRTNZejJNV2VvWnd0dGxCN1RHS2tjNEJObGxrQmxaZ0FLSXhHYk5Bd0JiaVpqejBGZVFIQk90ClcwZUxjUFVEZG1jeDJCQWxzVTFkd21FT1BkLzJUNDFIUlZ5NUlNZ0Fxa2loMHNkcFF5MnpUY2swRDJDMHpFcWUKN1EyMEY2RVRPamI2U0JzN0NXejJHNlk5cmU1RkFnTUJBQUdqVXpCUk1CMEdBMVVkRGdRV0JCUVI0WVpjVC8yMQpmdktrQVptUkxaTXp3anNKU0RBZkJnTlZIU01FR0RBV2dCUVI0WVpjVC8yMWZ2S2tBWm1STFpNendqc0pTREFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFBZ28xTndRbXBtNHQ1UytuM2EKUGhFRTM0dFN0RVd3TW5vNW5zL0hrS2c3VzVtVGl2UEJsOWdUemRDTjlFL3hjb1ZxOEkzVjZxQ2VKN2xmTHBQcgpzdnZ4M2hyTW5WUDUyMUdZV1BKOVFRaDNHeTRDNHBQVjJxeHV2b0ZaaUdKY0diWWcrSnJKTElKWGk1RUdiNk1YCi81bzVRb01GYUtBSGU2WVRjVzA2cEp5NDlFaXdQWkRJM1dCcXlzSTNhbHRWUlNBN3pXdWJHb1JXdjArS0dNcHoKMzNkNWcyT1ovOG5pREhxSFZxNDVucFRyczBXU0pLa1ZoWDBaazFJN0ZTYlJNQzllcXlaRkQzaG45TGF5eDlrbwprcVgyZ0dubDVVV25PWElWdUFXREQ2NE1RTWtyOWZpeXp6ekEvbmJmT2lvVG1PdmxPQmduVnFmVnFMb25kelg2CkY2NlMKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRQ3lkNjhFVTF2aUZIVU4KWE5IVXNhamZaZU91cHFQMTdKOEloTUNwZjRTc1FZeXhacVhqeENVMDk3RWVJcnpsY24xQjM4RkVGUER3ZGdVeQo0a3VIY2lnakkwVnllVjA1MlFORjJIWkl1OGIzcE1WbXNJdE1lZzBwelpIWFIwenNHUVFQSUEvUmhrNG1KbHY1CkF0cWFxaTJSZU5DcG1Hd0pmSVBXMVRCd0thZnZUZXoydlRJcFdjQWZxMlV1YlZGYTBwa2NjQ0UzWXoyTVdlb1oKd3R0bEI3VEdLa2M0Qk5sbGtCbFpnQUtJeEdiTkF3QmJpWmp6MEZlUUhCT3RXMGVMY1BVRGRtY3gyQkFsc1UxZAp3bUVPUGQvMlQ0MUhSVnk1SU1nQXFraWgwc2RwUXkyelRjazBEMkMwekVxZTdRMjBGNkVUT2piNlNCczdDV3oyCkc2WTlyZTVGQWdNQkFBRUNnZ0VBVVRrOVUwZXBBc3p5dFFFd2tvL0UzdCtkYndoeWlPT0hRYlpCaFNML08vS2QKV01QeDdpYUFGSXBDZHdleVZ1N3phUDZ3RkE4LzFRQ3h2d1hWQURFSmFXeU5GOXQ2ZlhCeUYrQzdmTURSZmpYawpqYWI5enZHaGVnd1FPeDA5T2hyc2lRRzdrVTJCMWNVUmlOUXVyOE9SOTZvM3RXZ2tpM0M3QkJTRTloZFBVZzZ0CkNoSzBSZW1GTStQQnZFa1VMbnNtUHNmdnpMU3k0ZVdlajlRUGlNa0JQTnR3VTFjelY4dFZZY2I3UFlKbjN3Uk8KcXl1WjBIU3VFWUl0a3I3cWg0ZW5xR3Zkek4vclZKUUlMc3VLVkhOd0xRbUJYVjMxK1U2TFJRbWJqWW9UU0JqbgppK2V0c0Fta0dFeFFiRUhEb2cza2RML2dQcFRyKzFCaXozNkJpOTFraFFLQmdRRDdsOFprcFJPLy9OUm9CbkhiCkltM2ttbmkxQjZwaUVCczhXamNNejhTNmwzNnBwQVpUMCtGL3FXUTkzZnJYUUpNYWUyOWd5SS9Fby9PQXlzdWkKMFZBTjdWNVVSN01ncjc2TzFJQU5naXVvVC9yRE92bkZQV0thM3BoYnc1UlBBYStsRmxzdy9DNzAwTmtiUjlydgpWY0t4aFluNVhtMVlZMTZSRXZWdFVGRVdnd0tCZ1FDMWwvMld2bytxZkFxZUtUeXBXaHFZUU95Y29ZYXZpVUNKCmlJOG1zaGhsemJhRG9DSm94aE1MTFgzcjJpUlA4anBZQTZyWEZvNEhpU2RKSC9WZFlmMXBQRk4zRkJVVytNZ2YKdnl1U2ZDK3ozV3NrNFpXWEJyUDNaQmxJeFpTRjlzZDRyU2llVUlQZkpsV29lU2JOM3lmUHV6NUU4NmZLbTU1cApxd1lDdTJjTmx3S0JnUUNrVEpOa3VtRFJhWnlVYWxFRUY5SElEaEJ4dUJYT2dGSW84WWt0dHFqMGFXWndCZ3VUCnhyUm1HQXE4VkRBeFRaeERHUVM5SVh5eG41ZXZMY0FhRGJMSHhaRnFYSUJnQWlUaFJXaHlhZVYwdnZZMWRGZ1UKTkdnbVZ1TU1XZ2FLS2NHNGY0Y0IwRTRoMWhsUnRYVUdBdTFuM0pzajNFUndDa1NCWE80bGV1UFpYUUtCZ0VtLwpFdTVBTDIKLS0tLS1FTkQgU0VDUkVULS0tLS0= ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: keycloak-ingress - annotations: - nginx.ingress.kubernetes.io/ssl-redirect: "true" -spec: - ingressClassName: nginx - tls: - - hosts: - - tls-keycloak.apps.cluster.example - secretName: keycloak-tls - rules: - - host: tls-keycloak.apps.cluster.example - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: keycloak - port: - number: 8080 \ No newline at end of file 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..ca96913d76 --- /dev/null +++ b/operator/controller/src/test/resources/k8s/examples/auth/tls/simple-with_keycloak.apicurioregistry3.yaml @@ -0,0 +1,31 @@ +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 + - name: REGISTRY_AUTH_URL + value: https://simple-keycloak.apps.cluster.example/realms/registry diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AppAuthSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AppAuthSpec.java index d12aff7d64..acea6667d2 100644 --- a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AppAuthSpec.java +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AppAuthSpec.java @@ -21,8 +21,7 @@ @JsonDeserialize(using = JsonDeserializer.None.class) @JsonInclude(NON_NULL) -@JsonPropertyOrder({ "enabled", "appClientId", "uiClientId", "redirectURI", "authServerUrl", - "tlsVerification" }) +@JsonPropertyOrder({ "enabled", "appClientId", "uiClientId", "redirectURI", "authServerUrl", "tls" }) @NoArgsConstructor @AllArgsConstructor(access = PRIVATE) @SuperBuilder(toBuilder = true) @@ -39,13 +38,6 @@ public class AppAuthSpec { @JsonSetter(nulls = Nulls.SKIP) private Boolean enabled; - @JsonProperty("tls") - @JsonPropertyDescription(""" - OIDC TLS configuration. - When custom certificates are used, this is the field to be used to configure the keystore and the trustore""") - @JsonSetter(nulls = Nulls.SKIP) - private AuthTLSSpec tls; - @JsonProperty("appClientId") @JsonPropertyDescription(""" Apicurio Registry backend clientId used for OIDC authentication. @@ -78,9 +70,11 @@ public class AppAuthSpec { @JsonSetter(nulls = Nulls.SKIP) private String authServerUrl; - @JsonProperty("tlsVerification") + @JsonProperty("tls") @JsonPropertyDescription(""" - Verify the identity server certificate.""") + OIDC TLS configuration. + When custom certificates are used, this is the field to be used to configure the keystore and the trustore""") @JsonSetter(nulls = Nulls.SKIP) - private String tlsVerification; + private AuthTLSSpec tls; + } 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 index df980eb7b2..b60155ead7 100644 --- 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 @@ -12,8 +12,8 @@ @JsonDeserialize(using = None.class) @JsonInclude(Include.NON_NULL) -@JsonPropertyOrder({ "keystoreSecretRef", "keystorePasswordSecretRef", "truststoreSecretRef", - "truststorePasswordSecretRef" }) +@JsonPropertyOrder({ "tlsVerificationType", "keystoreSecretRef", "keystorePasswordSecretRef", + "truststoreSecretRef", "truststorePasswordSecretRef" }) @NoArgsConstructor @AllArgsConstructor(access = PRIVATE) @SuperBuilder(toBuilder = true) @@ -23,6 +23,15 @@ @ToString public class AuthTLSSpec { + /** + * Type of TLS verification. + */ + @JsonProperty("tlsVerificationType") + @JsonPropertyDescription(""" + Verify the identity server certificate.""") + @JsonSetter(nulls = Nulls.SKIP) + private String tlsVerificationType; + /** * Reference to the Secret that contains the TLS keystore (in PKCS12 format). Key user.p12 is * assumed by default. From cbf2ce53269c43573bedb9269c5513e4cfda82f3 Mon Sep 17 00:00:00 2001 From: Carles Arnal Date: Fri, 31 Jan 2025 09:02:47 +0100 Subject: [PATCH 09/11] Fix tls testing and make the regular test use tls-secured keycloak with verification disabled --- .../registry/operator/it/KeycloakITTest.java | 6 ++++++ .../registry/operator/it/KeycloakTLSITTest.java | 17 +++++++++++------ .../resources/k8s/examples/auth/keycloak.yaml | 6 +++--- .../simple-with_keycloak.apicurioregistry3.yaml | 2 -- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java index e5b9769eb9..4350c039c1 100644 --- a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java @@ -33,6 +33,10 @@ 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 disabled at the Apicurio Registry level, so even in that case the deployment works. + */ @Test void testKeycloakPlain() { // Preparation, deploy Keycloak @@ -94,5 +98,7 @@ void testKeycloakPlain() { 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"); } } diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakTLSITTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakTLSITTest.java index 63882e0d28..11209cea39 100644 --- a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakTLSITTest.java +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakTLSITTest.java @@ -33,8 +33,12 @@ 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 testKeycloakPlain() { + void testKeycloakTLS() { // Preparation, deploy Keycloak List resources = Serialization .unmarshal(KeycloakTLSITTest.class.getResourceAsStream("/k8s/examples/auth/keycloak.yaml")); @@ -46,6 +50,9 @@ void testKeycloakPlain() { .isEqualTo(1); }); + createKeycloakDNSResolution("simple-keycloak.apps.cluster.example", + "keycloak." + namespace + ".svc.cluster.local"); + // Deploy Registry var registry = deserialize("k8s/examples/auth/tls/simple-with_keycloak.apicurioregistry3.yaml", ApicurioRegistry3.class); @@ -62,9 +69,6 @@ void testKeycloakPlain() { Assertions.assertEquals("https://simple-ui.apps.cluster.example", appAuthSpec.getRedirectURI()); Assertions.assertEquals("https://simple-ui.apps.cluster.example", appAuthSpec.getLogoutURL()); - // We must change the auth url of the backend to use the service. - appAuthSpec.setAuthServerUrl("https://keycloak." + namespace + ".svc.cluster.local/realms/registry"); - client.resource(registry).create(); // Assertions, checks registry deployments exist @@ -86,13 +90,14 @@ void testKeycloakPlain() { 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://keycloak." - + namespace + ".svc.cluster.local/realms/registry"); + .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/resources/k8s/examples/auth/keycloak.yaml b/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml index 8f47c914b5..75a6bd0a50 100644 --- a/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml +++ b/operator/controller/src/test/resources/k8s/examples/auth/keycloak.yaml @@ -3150,15 +3150,15 @@ metadata: name: keycloak-tls type: kubernetes.io/tls data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVHekNDQXdPZ0F3SUJBZ0lVRHE1a3N5dGovR20xdGRRUU91bGdkZ3dadXJNd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1l6RUxNQWtHQTFVRUJoTUNWVk14Q3pBSkJnTlZCQWdNQWtOQk1SWXdGQVlEVlFRSERBMVRZVzRnUm5KaApibU5wYzJOdk1SRXdEd1lEVlFRS0RBaExaWGxqYkc5aGF6RWNNQm9HQTFVRUF3d1RLaTV6ZG1NdVkyeDFjM1JsCmNpNXNiMk5oYkRBZUZ3MHlOVEF4TXpBeE9EUTBNRGRhRncweU5qQXhNekF4T0RRME1EZGFNR014Q3pBSkJnTlYKQkFZVEFsVlRNUXN3Q1FZRFZRUUlEQUpEUVRFV01CUUdBMVVFQnd3TlUyRnVJRVp5WVc1amFYTmpiekVSTUE4RwpBMVVFQ2d3SVMyVjVZMnh2WVdzeEhEQWFCZ05WQkFNTUV5b3VjM1pqTG1Oc2RYTjBaWEl1Ykc5allXd3dnZ0VpCk1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRRHRxanoyRzAvQTRnUUZGeXo5VzNmbmNkTGoKdFB1MWR6d2tFZ1BUdmM0SlFSQ2hBV0lCQkEyWTlNZUlCTzk0MFpGMWNJa1dMZjFvUURsRXVYNnVRVjZHdTJ4aQpVWDZ5QnNtK2krS0tZdWg2S2Jyd3V1ZmRBTzVFc0pYelpuRmR2L2lUNzBaWUJPTnNPVzBmVmhLY3dBaW9uSFN4CnI2aW1kb2V0S3EzTmJrZ1daQlBpL29SbGhFR2FwRitLTHpRZlU3U1d2bnNsdU5YQXVaMnZpQW5NVGdNNVhmcWoKR3VqSXZaVEliTUQxS05tR0c4U2ZjQXdmSHNXbXBOajUyOTNiQkNVN2lhUkFBakdBZkMyekdVNTJFL0xVQjJPTApxaDZVcUpzdW5pRjJXNWNJREs3OElEZ2tWQysxUy9haEVwUm1WVGZndk00R09zUmtJQXJsS2hpRCttVm5BZ01CCkFBR2pnY1l3Z2NNd2dhRUdBMVVkRVFTQm1UQ0Jsb0lJYTJWNVkyeHZZV3VDREd0bGVXTnNiMkZyTG5OMlk0SWEKYTJWNVkyeHZZV3N1YzNaakxtTnNkWE4wWlhJdWJHOWpZV3lDRXlvdWMzWmpMbU5zZFhOMFpYSXViRzlqWVd5QwpNeW91WVhCcFkzVnlhVzh0Y21WbmFYTjBjbmt0YjNCbGNtRjBiM0l0ZEdWemRDNXpkbU11WTJ4MWMzUmxjaTVzCmIyTmhiSUlXS2k1aGNIQnpMbU5zZFhOMFpYSXVaWGhoYlhCc1pUQWRCZ05WSFE0RUZnUVVBS0grcWZiYjNHMXIKTHRvaHl6ZUdVRDJ2N2Jrd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFOUnZ0V1RHWGthWS9LVCtWaEpwdzJpcQpHZUNJU2JBU28rNTZNMExabDU0M3JlaHhaRUQ0VzBXajdiTTJueFRtdG9sQ0MybGM3cTJST1lLQzU1Y3YvKzdGCitwMnEzWWZQeHpDV0hRN1F6UlRvK2FWN2FubFlOSUdXaXB3T0lPVHhvZmVzOTVmLzdwNTRzRXJzVTliUE9BTUsKLzZCaG5OUzZ4ZmxwczJMZTJrSXREV1dFUVVVZHZQZCtJN0R4SXFxdkpQQVpUL0NZcHFZU3RDQ3NhNWlYcFphOQpkdTl2dW9LVFVnRHhSYld0Rkw5dE5GNUFGM3hjYlVYenc0bE9Hd0g2WmNxZEdXVVA2NU8xYlg5UnJxYkNabHEvCmpWc21vV3BIUG9nK241WGFVUXRmcExRN2xIODNXeUFTMEsyUjYzME4vWFVRZjk1WTV0SWhRd281cmRPcS9wQT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2d0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktrd2dnU2xBZ0VBQW9JQkFRRHRxanoyRzAvQTRnUUYKRnl6OVczZm5jZExqdFB1MWR6d2tFZ1BUdmM0SlFSQ2hBV0lCQkEyWTlNZUlCTzk0MFpGMWNJa1dMZjFvUURsRQp1WDZ1UVY2R3UyeGlVWDZ5QnNtK2krS0tZdWg2S2Jyd3V1ZmRBTzVFc0pYelpuRmR2L2lUNzBaWUJPTnNPVzBmClZoS2N3QWlvbkhTeHI2aW1kb2V0S3EzTmJrZ1daQlBpL29SbGhFR2FwRitLTHpRZlU3U1d2bnNsdU5YQXVaMnYKaUFuTVRnTTVYZnFqR3VqSXZaVEliTUQxS05tR0c4U2ZjQXdmSHNXbXBOajUyOTNiQkNVN2lhUkFBakdBZkMyegpHVTUyRS9MVUIyT0xxaDZVcUpzdW5pRjJXNWNJREs3OElEZ2tWQysxUy9haEVwUm1WVGZndk00R09zUmtJQXJsCktoaUQrbVZuQWdNQkFBRUNnZ0VBUFBQMFBvb3lvaDFWT3BqL0NOdDBuUDhzUHdvanBCNjRaZWZZNDIxQ2NrMGwKbnFWa0pTNk03aldaRnVGdEtXZFdEdkNWdUFLRGcwRTFidExFRXB6aFNWeTRKMThQUjBsR1pJQXIwYUljVFhoMgpZVmNPTURnVTQ3a0dQQzZCMUxGdlowRnVvUlJmMzFtN0N0WmdYNk5BbFQzTEQrY2xNY3drYjkwS1JpMktmM01jCnJGdTA1UTBvQWEzaEQ4WGQ4a3dMSEJCOVNWREpqTlEzaVlCaTlDNElNQ3EzekNZNjg4a0xEVkFMQlUrQkswTm8KcEN1VlhWMU1jdVFYdURPL2JSdUJoWktBWGFpUWRqNGdRdlhkT1NVL0ozeE41UmpKT1hkRVE0eGNuRGhJRjA1NQpNbjRXZHY5dnVSd0k4akVPeFI1MDl1NUw5eEx4Mlp3MUhYWkZ4S0VhQVFLQmdRRC9rZi81dlhOM0tqYVljSzdLCk9VY0F6T1dQSlhGVlc2VFc0OVhFQkhMbkZZTVgyK2xqZGlDKzhZbE52S3F4V3JVUFRnVDhTMGd2ZEkwK1FwMHcKOUhHZkdxTDRqSEEwUmFla0MvTzFqWlUrMGdMWE9HMTQ5RkZmaEZEZmlHNHFZSHNUOW5HS1dEVFl6a29uVCs5RApteGllN09PUEVTakVrdElEdm91NEc4b09ZUUtCZ1FEdUVJZ1daelkrRnVkV3l1MUI3aTdiMzlqSGlwck5FcC83Ckdrbm5adG5UTG1sVFFvaEtFeEhUYlhHajRMbUUrWDhFcFl6S1BSTEFXZ3k0WVlpa1c3NklEOGYvS2YyNjlwNFkKVWFPVmd2Vi95UkFLcUt2cktxRWJWVDVFbWVhSkRSYkd3QjVsMUkvU3ordTVhVnJjOGdhMW9GVjhTVWh6WVhxawpyRnJUQlVRNHh3S0JnUUNRRTJ5dEMrS1dJVlFaNkJ5Qm9IeW90Tm9OTXR5TGRGaWZWRjNrWFdXeFpHMDgvQnE4Cm1aR29VMTVHVnNBYnI3azI0WkxwOEQ3SGtmczJTRE41cjdTR0wyd0VscGVDd0duYmoybGF4bkNHczdvcVBvTm8KbHNOeEI1VFlEMytib0R5Q2FvSmpyWXBOVnk1eXJ4dUJqMlAzNUMvOUxYMUFKRTBGRTF5TTlBMmF3UUtCZ1FERApIQ2xzc3dweHVMUEJIbTkxS1pDZUE4ZlhRbkc4d1JiODVYN01lQzd1Tll0OUFYcFJ1MjlRcU5JS1RaTyt0L3l5CkFqeHhRN3lxaVNNRHVtd1N5RjVQaFIrVXNRV1N4YXlzb2J4QVBTU1hwbUhQV280TUh0UUxZc0cxMTZ4cGhxQzAKSTZER05IL3JkeHpxeUwzL2R4eW9uczl4SFY2VHNjQ2lWZC9hTXViRzRRS0JnUURNZkNlQTNmN1FvcEhOSkRIOQpvWHpOTlVKZWU0RXN0ZUs4ZWZoYS9SNHpGeDdWcm9FY0FrYTF6bmdXbXRSdmtFNWhldytHNlBEWFhDcFhSaGZpCkQyd0JrR0IvQWw2WlQvOFA1dDFmQXU5NWNtbVdtUk5leHFmRXdnTVRJOE5zVjhpa2d4OEpzZ2xyYzlneUQ1WXcKeGdROGQ5MmZabGxFdisrUWVhaHFkUXkzUkE9PQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQvakNDQXVhZ0F3SUJBZ0lVVHNkUjVKQ1JMWng4Z05uNS9IYzRMbGU5cEI4d0RRWUpLb1pJaHZjTkFRRUwKQlFBd2RERUxNQWtHQTFVRUJoTUNWVk14Q3pBSkJnTlZCQWdNQWtOQk1SWXdGQVlEVlFRSERBMVRZVzRnUm5KaApibU5wYzJOdk1SRXdEd1lEVlFRS0RBaExaWGxqYkc5aGF6RXRNQ3NHQTFVRUF3d2tjMmx0Y0d4bExXdGxlV05zCmIyRnJMbUZ3Y0hNdVkyeDFjM1JsY2k1bGVHRnRjR3hsTUI0WERUSTFNREV6TVRBM01qSTFNVm9YRFRJMk1ERXoKTVRBM01qSTFNVm93ZERFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVJZd0ZBWURWUVFIREExVApZVzRnUm5KaGJtTnBjMk52TVJFd0R3WURWUVFLREFoTFpYbGpiRzloYXpFdE1Dc0dBMVVFQXd3a2MybHRjR3hsCkxXdGxlV05zYjJGckxtRndjSE11WTJ4MWMzUmxjaTVsZUdGdGNHeGxNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUYKQUFPQ0FROEFNSUlCQ2dLQ0FRRUFxMUtvdXk3UlRtZmlnWDhwZmdJdERvcUFadEJ4UW1YbDh5cUxzZk5JYWJjeQptRldEcHRBZVpRRXJHTU9qRG96U1BiZlBhY1RCUnIvQ0J0T2hOcEd4b2pNTjdGOWdIMGFaRFFmTm14RDVsQkpiClJJS0Q2cGN2U2VTNWZrUCtHS3RwMHd0ZmpURzI3a0E0MStLMHVZdVZPQUhERHdlRU9qUUlpdVVYNDB5Wmlsa3gKeGs5VGF0ejBCZ3B0VVc2V0xZMU1ZWWZqdTB3SGRmTWYxb0diREJ1WDhsQ0dGK3ZIV3B5RUFSamZZQnJ4S0dZYQpVVys5SXN1aTJZbk9YSUR5Q0RabmdQMWN0bXQramM3SlpDUFpkQWtQUzJJOHcvV0xMbVFldHJ5dm5TZGQrUnVuCnZJUGhhRXZhV1YwMTh0dk9BMjRqOHNSVG0xVDdOUGp2RlFvREphY2I5UUlEQVFBQm80R0hNSUdFTUdNR0ExVWQKRVFSY01GcUNKSE5wYlhCc1pTMXJaWGxqYkc5aGF5NWhjSEJ6TG1Oc2RYTjBaWEl1WlhoaGJYQnNaWUlJYTJWNQpZMnh2WVd1Q0RHdGxlV05zYjJGckxuTjJZNElhYTJWNVkyeHZZV3N1YzNaakxtTnNkWE4wWlhJdWJHOWpZV3d3CkhRWURWUjBPQkJZRUZLbng1SGM2RGZVcUlhWGMyZnZxNkZiVkc2V1BNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUIKQVFBTUVjWUFnTDdUMUlRZEcxMnBudndEUDFyY05lSUdkUGdabFZsbVh2dk01N01HSFZQMDZEMlNrYnlBaVFsUQpyNk9oemU0b1MrOVZjcmtxQXFvbzJIMy9mUFQvZEZMeXdxclhXNDdicy83YUV0RDdiR25md0NrZ1MybWhuNFpLCjl2cFdScnJLUTRaQUc4WUc0ckpGYnNhTU5BaE1CL2pvVWpqTGg2MFJvUW5EZFdteWhBYmwwN0FSekNRd3V2OE0KRHZwenJNaS9VOG5CRmx2VVBSZUlwc2VqajlIQXl5ZlJCdjJkSndTWkdMeEZPYjJmbFNJeFNDOGFhYUo4TlFtRApUWmVaSW54dFV2SHdFbm5IMHU1cEIzd0UrdThOa2xHU3U3WG8zY3RLKzlxNENiNTF2R2Fwd2Q2ZGh0am5tbG14CmdnODJTdGFMMW45azAxUmh1OWtlWEEzSgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ3JVcWk3THRGT1orS0IKZnlsK0FpME9pb0JtMEhGQ1plWHpLb3V4ODBocHR6S1lWWU9tMEI1bEFTc1l3Nk1Pak5JOXQ4OXB4TUZHdjhJRwowNkUya2JHaU13M3NYMkFmUnBrTkI4MmJFUG1VRWx0RWdvUHFseTlKNUxsK1EvNFlxMm5UQzErTk1iYnVRRGpYCjRyUzVpNVU0QWNNUEI0UTZOQWlLNVJmalRKbUtXVEhHVDFOcTNQUUdDbTFSYnBZdGpVeGhoK083VEFkMTh4L1cKZ1pzTUc1ZnlVSVlYNjhkYW5JUUJHTjlnR3ZFb1pocFJiNzBpeTZMWmljNWNnUElJTm1lQS9WeTJhMzZOenNsawpJOWwwQ1E5TFlqekQ5WXN1WkI2MnZLK2RKMTM1RzZlOGcrRm9TOXBaWFRYeTI4NERiaVB5eEZPYlZQczArTzhWCkNnTWxweHYxQWdNQkFBRUNnZ0VBQVdiL1A3RTVKZWJpYnRjMzFIYUtnaXNiNGFpOVRZaWdaektsS1ZGSm4wNEYKQmExc3p6M1VzSExKWWRoTi9VblN6SDd3L3o4RlFTbFNYam4rOFoyNlZCM0RDeGN1UzY2Sk9ZZnU2SldsZkdXMAp4akNVbUg1c2ZIWmlqeUozM3hKNFd2Vmg5a2pDY3o1RXpZejl4UzdxenhjY1hkWGN4S2hmSkR3azhROG01UTczCnhMRkNLWHl6aEV2VS9iWEkxYnBJaUhVUjI1OXU4R3FWU2RBLzlsVEhxQ1J2OUh0R0w4bWZaNjZXbmNtdnVJYTgKQWg4RThmbFIxMFRvdGFyeDE2MFhoZVZ2SWZHMHpJWHNqRU9ydWZpU09VZzZNTkI3Ukt6UGxyemxwemRNSXhndwpFbXcwR0t1MlZpRFY0YmhGeWpJVmF2Und3TnpNbFF0Q2hja0k3NlBKRVFLQmdRRFpIcmlsQ25WUHVCTVgwOWd4Cm8vdy9Wb0RubEd2VjhGcVdnTmxEaEQ2bUdQdEVSd3JWc0lGc0xCMzBOUGZKQ2JxL1k0NlFXRkFEWE1lUVV6MFcKVnV4OHZtYkhIYW53QzNJUmdzTEVJZWlBYVNyalNvd0dJM25ScWdXRDIyUXNxUElWSE1nMTA1VDFEVlFoUFRVNQo1eG52NEpxVHpwY2ZNYllKaWw4TzVleXVMUUtCZ1FES0FINHpyeXRNd0FZRXl0bUUwR1FxaXp6bWtqSCtNYndvCitMeHNCc2s2NW8xUDNNaTFRTFFNVk9ESUp1aU5paVkxWldWUnB3QUttOVBCVWF3dWkyL3hnTVEzR0dCM2lReVUKN09rYjhzQjVWcU9RbllPekFXclQ1WjVMbEE2bU9pNTlkaGo4VGhRK3A3dmhBTThxYUdFaDUzbytaaVI4NlJOYQpmclc1NnJFSjZRS0JnQU50eThhM2VwRE92R1owN3dZaHE5c29ONUx3c2g5SDAzWnJCSU5mZEVuVlBTcWY0Smp0ClY2M29xSFJ4M2JQc0ZtbnRYWlFFQVFkOGtKNUQ0a1FYRFZjTy9ycUlTbVhER0lNVVNKVW1YL0NmT0pFc1hUZS8KbWEzby9IdFBuSzVqNEtiYXRJSzlHNjRmRHNRVG5tS1N0c01tRGVybHFWZFJ4cWt4NlR4bi95dmxBb0dBSTRPRwpySXVGYjA3clAvaHgyWm5Sb25Fc1dkVW1HT3loaWdxQkJmVURkK21FdXArMFlibG9iaXFXQWxwazFFMlBmaCsvCmVtV0Q2bG1QRnhQRzA4QU1CRWVCTjdaL2IwU1hBNi9MSS9DL3loVmpXRTk1RWdXVHhJRkFIVVdBZGRVUXkxbngKOXBtN2tYQm41WjBBenhIZTBka2ljc24rYkQrRitZa0JaaGhPMjlFQ2dZRUFwTlhxRVZ5NUdsa0pOWjBKZS9YVwp6ZHpMdkl0eEZObWxKMkpuaGtWdmMxRXJ3RlovQ3l0SGdwMnQ1aUZQTXFoYThhSnRsdzVkVDJBZno4bWtHWnc5CmRmMmozaTljWU00cDdXbm9SMkZ0TERsTjdEY1lrK0tDSHYvMTlycjdOVXhOZ0NJc3Q5SHhod0FGeEt4djBmSEoKRUVJdjRiNkQwU25JOTFRQnk0T1JIVEk9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= --- apiVersion: v1 kind: Secret metadata: name: keycloak-truststore data: - truststore: MIILQAIBAzCCCvYGCSqGSIb3DQEHAaCCCucEggrjMIIK3zCCBSoGCSqGSIb3DQEHBqCCBRswggUXAgEAMIIFEAYJKoZIhvcNAQcBMF8GCSqGSIb3DQEFDTBSMDEGCSqGSIb3DQEFDDAkBBA6eRD5E7kSv50VBDTbKGiwAgIIADAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQlyGrpNprV7ehpgWtpeG4sICCBKDO4TaLLkXo/JfrMfoSDRO8vBqncWmJ1qEKLHM1rP7fdsoF5nl+iliOWCOi6RT5zT+AgIwvSXgDI3cSdMW7BAexwHahN1CABwDR9XnSNgI2G57jMHQIAiMrMA00oyJpsIjfEnTu1/ymNAUj57dzcectDneCQHubZvZzs7ZpRNpcUPwhGTskEGyRhm5Xcohn59iPE8mmEWZsX2cdXBNPJHiovxTRKLRZwhDoWrNvcOOhVaQ50MdkgrZsvqq5X1IQyKZm+FLxb/o0gQ6lkPRdlUMEmWHLdDQ41JOQbHG3KHLfZW9J6ofwSRoNxhJvYck0dhwvmubkw4Cosd7e/IDZ5TiVRZ4DITYxUEmMuCbtIw826seGJP5rUTMHt40UpgYJnQXZj7os3YyO1D3JVmvAHMFuqG/998R1Ll9fTs09ml8adBDwATBtYtRoFUgq+lnwoZg6w195E/hVmo1LtsMvO7/t0eOqlWecFUGf7lhndSNShlbKsJgYVfMQsYKRQ565OFIvgvpSl9belsQR7A+kpsPAeKM2uZMlCi9YrjUhSHjQSdjb1O2NCTXBXULCc9AgagyOWuzDNEdj2QE5ewxvTX/XRLEu8KkPP1VowGIyh3MnmMQxj2En3mmCE9wULIVFYMizOyI2dyK5KGIeBFCrd+YE7+E5nDY3WrRfsDvX4z+NAsVll7OcieGHrDGRuNhErYe0YooA7FRsy1oXXuXg8OUNCxn/D4ioHt9zS+2ck0KJZkjDgy8aP5/1vA/1MQAwxfy431tHqkONspLR/fEjRAMWs/7SwHQeI9CB2jIZEYqIhFC1x5Sc9kgZ00AeAdnjK0innr2m/skzdu8+uSi8Mf3MUNf4v8S8gyqz57SUGxZ8H9z1pDedmYBv0QGRXK2DUrBGc9azggIo3dN10p70jwSUZvYBX1m4HzEElRFZ67YWOPLNY0gctWD+MLnaTKkvLE4+R6bHsjZ8wZzWrj3A9P8eZ2IjZhJET/3QTMWMekdFhqRBv6lPge8Xaovqk63ReHrpkUxdW+QkfPxCvg0Ny/eO1TqE2p7WpWJxzEr6r9UoaHGRgXnw0Z3646DUKLqKX2PQ5s9HZTLrcMxrv8gx1zAIMR4JVJpCECRiwvKGIvAl4mKhGGkXZIe9VuXTp7E3k2RgKaNhUfjhi57hfpZ9CsmZtvf6DVFGmd++DB/zrkX/ypk5d2ZNgA1Lc9CjCTBlsTLoqOPO63/KUXY9WMsOc6Aalm7XhMCEOwJPflHQqGwGaI/zw/VN54tmw7+y5cqLHJHV5eIHHMRHzSOsQCPYs2gw2HYEZMUFnn8MQwvD6EfmKp6tWi/3YvPf1Lq+LeLc24yMD+OXnv2NOg8sdapjG6snX4mVZMnzr4cmY59PRiRdtgsgIdKNB5Oq/SCcxiItRksrMo7hNBvAE5rN5ZaVR87i295aJibHQ7+TIdUzZUr0vugxjc6Z8K5w+rOg6pLjm8V/84pGcWWNeKamf+V2z3x1WCieheVGlDXJbsRdIMKPjPsl8bk77DBAEy3aNmI9uc40XF7illQdbAk1wPT9IMBcdkDQd5F7WOkXq9J5nAq3dDCCBa0GCSqGSIb3DQEHAaCCBZ4EggWaMIIFljCCBZIGCyqGSIb3DQEMCgECoIIFOTCCBTUwXwYJKoZIhvcNAQUNMFIwMQYJKoZIhvcNAQUMMCQEEF2lvAPOGfpDm7KH+lVKjtQCAggAMAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBDfHF4u/SgMTb1+n23MQaSZBIIE0Oz3+pPmxGxlKj1L/1QXEFbyMt86ScQBEIyXiSbEJS0hWTs/e7lvYWU9poWi8hATHrJcgjdnZSipS8Bue8vqhFqme7rsETDLgnNpUQcjQxn/yD9j4n8a0sDY7bmeoYusb2QB9eXiD4ZImXLb0NnKibpqc2MOwJICC2O7Y2pkIpKvymsuxqameQRp5laoyUo8L5SwgbN9tOo+iRKXoVD5eAuOmUfHKU/inOzxyxmr9xF1E+lUS/vOEMS4j7qFrg3RTh6OTt4O8IwpOEY6jhvhcI0X6YIQALB0tEXNsGzZkHfwTrF4ppZBJStWSwFo6bvGMj7gJWhynul1BnUdcnsfOeHqKoo5a1kO6jc5JSyYtoeWJyFomCYoDd7SMbTYC7FPhSddZw9+IaaSYNYM5ayOqgfV6M6qh/f5K/0tW3MJYT9hSjaVgFBrkBSPo07GpQX7SXphoqyDxc+G4CWzxtVSTB7eod583MY147ha0OqPNS+LHklCMlF3YOOWt7025JXFLk1jsJ/yQYFzPkaeZeK+xCNabUVwJsqLHaB++xGzrzi4mr3IB473u9VBZF+1liIRAmImBJqDSU7qdPJ8xRSk2Y9QNOZfX/gJZu2E0KnVLgMiAE5dpuJ16OCcBM8W2YDhRnKq/H80WDTrXSjuTrFMrMn41wO5DTtvytqikPDMbfQEeSEuXCUKOgeWaemazrKmPe/ZovAGMINSlg0h+U4D0YkiOWJ9dH6d91WGYZpMxzr1LbBjCqSkcOMTJuXcZSOsfckm0MzhfjX1n6Fn+14yvz6Ep15ESK5wcKZbPAcrwU2h2ZODO3HTz1Dc7lKMUy7kBPChVQtqOH0kKBL5kpP6FDvjPo5UxNvMlUjqhF+Uo/g/8IPGr2+F+x12+/VncWWZfn0xkZ4nuTNrNEXsyYCI6s1qBaKHa1tpjoR5ncgBFg3gN5j0LCKI8sJukmEclrs09OGudEFHq9K1y7CeJkGn7fWelPxxlvbNAxJvefZw9RMcTrG/N4j12b111JY/3+13QrGzH8OdDi91iGMvg48+AVBuL9D0XQYe9KIhzOR2tGBBeQvGcHhULzzEqv5aQHH62yfri1El3n4Aut5hP/9sdqkUlPK/MwiPkxwCIOFho+Tl66rYcOCO8U+HAePJP33QV9lFClpzmtfh7Mj8xv2szWfhA/xUK7TFgfrMbjgOZI/l+8+t77VPSB8Ur6fvvaHRyP6yqf83yDY119uLo3idS2XyswhT4zG/Lc4Fx9Cbhnz4VKIoDjTwnIcQH25KEJYvTgl/1IdVGR/w7ZbwfJF0o9CnT+oa89AONbYpObwiB35ImUJAmQ30ockRbfknFV7ZPQGGkj7tIu+DmygJN0qKM3LasedpJwwfwV+fibLfqWstZmQqxPl4KdyjLgaY0vHqROiGllGFZv8foHjObS16npWgD51+hzrZqXSG2XK3f1RAbz9LsS4d5YNU63vmCJbR0LNIFUbyijCgSTImEWBWa0DnuRs3hwWXhLu32g8IMnK4Ai3tut9XCb3L0X2W9k7lKeDntzE6JoRIJ4gXEBbLqM/ZYmXF9Kcd5Xa3sIRQXeE1gYXhde0iwJ1538no+sFPfhNcyYggaRbvp/WHyzq82AHF1nubbx954jeAn6dOhS49MUYwHwYJKoZIhvcNAQkUMRIeEABrAGUAeQBjAGwAbwBhAGswIwYJKoZIhvcNAQkVMRYEFIdlbOhFpWzKLhy2ou13L+PLiRDSMEEwMTANBglghkgBZQMEAgEFAAQgoh3teDOnQzRB3DfUXVI6GItHsnwuhrSgiuPhPSqfkykECBoicn2i1WYzAgIIAA== + 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 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 index ca96913d76..fc5a4b0d9c 100644 --- 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 @@ -27,5 +27,3 @@ spec: env: - name: REGISTRY_API_URL value: https://simple-app.apps.cluster.example/apis/registry/v3 - - name: REGISTRY_AUTH_URL - value: https://simple-keycloak.apps.cluster.example/realms/registry From 39b1e2d78226906ebba9ddc17b9ddc0038a1c3f3 Mon Sep 17 00:00:00 2001 From: Carles Arnal Date: Fri, 31 Jan 2025 10:25:55 +0100 Subject: [PATCH 10/11] Add remaning auth values and authz configuration --- .../operator/EnvironmentVariables.java | 22 ++++- .../apicurio/registry/operator/feat/Auth.java | 47 ---------- .../operator/feat/security/AdminOverride.java | 44 ++++++++++ .../registry/operator/feat/security/Auth.java | 56 ++++++++++++ .../operator/feat/{ => security}/AuthTLS.java | 31 ++----- .../operator/feat/security/Authz.java | 49 +++++++++++ .../resource/app/AppDeploymentResource.java | 6 +- .../registry/operator/it/KeycloakITTest.java | 5 +- .../operator/it/KeycloakTLSITTest.java | 8 +- .../operator/api/v1/spec/AppSpec.java | 4 +- .../api/v1/spec/auth/AdminOverrideSpec.java | 69 +++++++++++++++ .../auth/{AppAuthSpec.java => AuthSpec.java} | 30 +++++-- .../api/v1/spec/auth/AuthTLSSpec.java | 25 +----- .../operator/api/v1/spec/auth/AuthzSpec.java | 87 +++++++++++++++++++ .../api/v1/spec/auth/BasicAuthSpec.java | 45 ++++++++++ 15 files changed, 414 insertions(+), 114 deletions(-) delete mode 100644 operator/controller/src/main/java/io/apicurio/registry/operator/feat/Auth.java create mode 100644 operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/AdminOverride.java create mode 100644 operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/Auth.java rename operator/controller/src/main/java/io/apicurio/registry/operator/feat/{ => security}/AuthTLS.java (57%) create mode 100644 operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/Authz.java create mode 100644 operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AdminOverrideSpec.java rename operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/{AppAuthSpec.java => AuthSpec.java} (77%) create mode 100644 operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthzSpec.java create mode 100644 operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/BasicAuthSpec.java 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 d48779eabb..aa0c4f5f9d 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 @@ -25,15 +25,29 @@ public class EnvironmentVariables { 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_KEYSTORE_LOCATION = "QUARKUS_OIDC_TLS_KEY_STORE_FILE"; - public static final String OIDC_TLS_KEYSTORE_PASSWORD = "QUARKUS_OIDC_TLS_KEY_STORE_PASSWORD"; 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_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/Auth.java b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/Auth.java deleted file mode 100644 index f7f2add59b..0000000000 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/Auth.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.apicurio.registry.operator.feat; - -import io.apicurio.registry.operator.EnvironmentVariables; -import io.apicurio.registry.operator.api.v1.spec.auth.AppAuthSpec; -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 appAuthSpec The authentication specification containing required auth settings. If null, no - * changes will be made to envVars. - */ - public static void configureAuth(AppAuthSpec appAuthSpec, Deployment deployment, - Map env) { - if (appAuthSpec == null) { - return; - } - - env.put(EnvironmentVariables.APICURIO_REGISTRY_AUTH_ENABLED, - createEnvVar(EnvironmentVariables.APICURIO_REGISTRY_AUTH_ENABLED, - Optional.ofNullable(appAuthSpec.getEnabled()).orElse(Boolean.FALSE).toString())); - - putIfNotBlank(env, EnvironmentVariables.APICURIO_REGISTRY_APP_CLIENT_ID, - appAuthSpec.getAppClientId()); - putIfNotBlank(env, EnvironmentVariables.APICURIO_REGISTRY_UI_CLIENT_ID, appAuthSpec.getUiClientId()); - putIfNotBlank(env, EnvironmentVariables.APICURIO_UI_AUTH_OIDC_REDIRECT_URI, - appAuthSpec.getRedirectURI()); - putIfNotBlank(env, EnvironmentVariables.APICURIO_UI_AUTH_OIDC_LOGOUT_URL, appAuthSpec.getLogoutURL()); - putIfNotBlank(env, EnvironmentVariables.APICURIO_REGISTRY_AUTH_SERVER_URL, - appAuthSpec.getAuthServerUrl()); - - AuthTLS.configureAuthTLS(appAuthSpec, deployment, env); - } -} 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..aaf7463d6e --- /dev/null +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/AdminOverride.java @@ -0,0 +1,44 @@ +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 (Boolean.parseBoolean(adminOverrideSpec.getEnabled())) { + env.put(EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_ENABLED, + createEnvVar(EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_ENABLED, + adminOverrideSpec.getEnabled())); + + 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..ccdd02c669 --- /dev/null +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/Auth.java @@ -0,0 +1,56 @@ +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()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_ANONYMOUS_READ_ACCESS_ENABLED, + authSpec.getAnonymousReads().toString()); + + if (authSpec.getBasicAuth() != null && Boolean.parseBoolean(authSpec.getBasicAuth().getEnabled())) { + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTHN_BASIC_CLIENT_CREDENTIALS_ENABLED, + authSpec.getBasicAuth().getEnabled()); + 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/AuthTLS.java b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/AuthTLS.java similarity index 57% rename from operator/controller/src/main/java/io/apicurio/registry/operator/feat/AuthTLS.java rename to operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/AuthTLS.java index 6daf72097a..40f1911501 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/AuthTLS.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/AuthTLS.java @@ -1,7 +1,7 @@ -package io.apicurio.registry.operator.feat; +package io.apicurio.registry.operator.feat.security; import io.apicurio.registry.operator.EnvironmentVariables; -import io.apicurio.registry.operator.api.v1.spec.auth.AppAuthSpec; +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; @@ -21,26 +21,17 @@ public class AuthTLS { /** * Configure TLS for OIDC authentication */ - public static void configureAuthTLS(AppAuthSpec appAuthSpec, Deployment deployment, - Map env) { + public static void configureAuthTLS(AuthSpec authSpec, Deployment deployment, Map env) { putIfNotBlank(env, EnvironmentVariables.OIDC_TLS_VERIFICATION, - appAuthSpec.getTls().getTlsVerificationType()); + authSpec.getTls().getTlsVerificationType()); // spotless:off - var keystore = new SecretKeyRefTool(getAuthTLSSpec(appAuthSpec) - .map(AuthTLSSpec::getKeystoreSecretRef) - .orElse(null), "user.p12"); - - var keystorePassword = new SecretKeyRefTool(getAuthTLSSpec(appAuthSpec) - .map(AuthTLSSpec::getKeystorePasswordSecretRef) - .orElse(null), "user.password"); - - var truststore = new SecretKeyRefTool(getAuthTLSSpec(appAuthSpec) + var truststore = new SecretKeyRefTool(getAuthTLSSpec(authSpec) .map(AuthTLSSpec::getTruststoreSecretRef) .orElse(null), "ca.p12"); - var truststorePassword = new SecretKeyRefTool(getAuthTLSSpec(appAuthSpec) + var truststorePassword = new SecretKeyRefTool(getAuthTLSSpec(authSpec) .map(AuthTLSSpec::getTruststorePasswordSecretRef) .orElse(null), "ca.password"); // spotless:on @@ -49,18 +40,12 @@ public static void configureAuthTLS(AppAuthSpec appAuthSpec, Deployment deployme addEnvVar(env, OIDC_TLS_TRUSTSTORE_LOCATION, truststore.getSecretVolumeKeyPath()); truststorePassword.applySecretEnvVar(env, OIDC_TLS_TRUSTSTORE_PASSWORD); } - - if (keystore.isValid() && keystorePassword.isValid()) { - keystore.applySecretVolume(deployment, REGISTRY_APP_CONTAINER_NAME); - addEnvVar(env, OIDC_TLS_KEYSTORE_LOCATION, keystore.getSecretVolumeKeyPath()); - keystorePassword.applySecretEnvVar(env, OIDC_TLS_KEYSTORE_PASSWORD); - } } - private static Optional getAuthTLSSpec(AppAuthSpec primary) { + private static Optional getAuthTLSSpec(AuthSpec primary) { // spotless:off return ofNullable(primary) - .map(AppAuthSpec::getTls); + .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..dbbaba4eb1 --- /dev/null +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/Authz.java @@ -0,0 +1,49 @@ +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 (Boolean.parseBoolean(authzSpec.getEnabled())) { + env.put(EnvironmentVariables.APICURIO_AUTH_ROLE_BASED_AUTHORIZATION, createEnvVar( + EnvironmentVariables.APICURIO_AUTH_ROLE_BASED_AUTHORIZATION, authzSpec.getEnabled())); + + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION, + authzSpec.getOwnerOnly()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION_LIMIT_GROUP_ACCESS, + authzSpec.getGroupAccess()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_AUTHENTICATED_READ_ACCESS_ENABLED, + authzSpec.getReadAccess()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_ROLE_SOURCE, authzSpec.getRoleSource()); + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_ROLES_ADMIN, authzSpec.getDeveloperRole()); + 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 cc63330e93..97b875cb5e 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,11 +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.AppAuthSpec; -import io.apicurio.registry.operator.feat.Auth; +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; @@ -79,7 +79,7 @@ protected Deployment desired(ApicurioRegistry3 primary, Context 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"); + .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"); 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 ee85c15fb7..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,7 +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.AppAuthSpec; +import io.apicurio.registry.operator.api.v1.spec.auth.AuthSpec; import lombok.*; import lombok.experimental.SuperBuilder; @@ -51,7 +51,7 @@ Configure features of the Apicurio Registry backend (app). Configure authentication and authorization of Apicurio Registry. """) @JsonSetter(nulls = SKIP) - private AppAuthSpec auth; + private AuthSpec auth; /** * DEPRECATED: Use the `app.storage.type` and `app.storage.sql` fields instead. The operator will attempt 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..cefc85b83f --- /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 String 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/AppAuthSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthSpec.java similarity index 77% rename from operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AppAuthSpec.java rename to operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthSpec.java index acea6667d2..5e16f6b07e 100644 --- a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AppAuthSpec.java +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthSpec.java @@ -21,7 +21,8 @@ @JsonDeserialize(using = JsonDeserializer.None.class) @JsonInclude(NON_NULL) -@JsonPropertyOrder({ "enabled", "appClientId", "uiClientId", "redirectURI", "authServerUrl", "tls" }) +@JsonPropertyOrder({ "enabled", "appClientId", "uiClientId", "redirectURI", "authServerUrl", "logoutURL", + "anonymousReads", "basicAuth", "tls", "authz" }) @NoArgsConstructor @AllArgsConstructor(access = PRIVATE) @SuperBuilder(toBuilder = true) @@ -29,7 +30,7 @@ @Setter @EqualsAndHashCode @ToString -public class AppAuthSpec { +public class AuthSpec { @JsonProperty("enabled") @JsonPropertyDescription(""" @@ -58,23 +59,40 @@ public class AppAuthSpec { @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("authServerUrl") + @JsonProperty("anonymousReads") @JsonPropertyDescription(""" - URL of the identity server.""") + 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 String authServerUrl; + 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 keystore and the trustore""") + 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 index b60155ead7..4a21d864c1 100644 --- 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 @@ -12,8 +12,7 @@ @JsonDeserialize(using = None.class) @JsonInclude(Include.NON_NULL) -@JsonPropertyOrder({ "tlsVerificationType", "keystoreSecretRef", "keystorePasswordSecretRef", - "truststoreSecretRef", "truststorePasswordSecretRef" }) +@JsonPropertyOrder({ "tlsVerificationType", "truststoreSecretRef", "truststorePasswordSecretRef" }) @NoArgsConstructor @AllArgsConstructor(access = PRIVATE) @SuperBuilder(toBuilder = true) @@ -32,28 +31,6 @@ public class AuthTLSSpec { @JsonSetter(nulls = Nulls.SKIP) private String tlsVerificationType; - /** - * Reference to the Secret that contains the TLS keystore (in PKCS12 format). Key user.p12 is - * assumed by default. - */ - @JsonProperty("keystoreSecretRef") - @JsonPropertyDescription(""" - Reference to the Secret that contains the TLS keystore (in PKCS12 format). \ - Key `user.p12` is assumed by default.""") - @JsonSetter(nulls = Nulls.SKIP) - private SecretKeyRef keystoreSecretRef; - - /** - * Reference to the Secret that contains the TLS keystore password. Key user.password is - * assumed by default. - */ - @JsonProperty("keystorePasswordSecretRef") - @JsonPropertyDescription(""" - Reference to the Secret that contains the TLS keystore password. - Key `user.password` is assumed by default.""") - @JsonSetter(nulls = Nulls.SKIP) - private SecretKeyRef keystorePasswordSecretRef; - /** * Name of a Secret that contains the TLS truststore (in PKCS12 format). Key ca.p12 is * assumed by default. 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..a423153c69 --- /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 String 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 String 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 String 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 String readAccess; + + @JsonProperty("roleSource") + @JsonPropertyDescription(""" + When set to token, user roles are taken from the authentication token.""") + @JsonSetter(nulls = Nulls.SKIP) + private String roleSource; + + @JsonProperty("roleSource") + @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..083324154c --- /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 String enabled; + + @JsonProperty("cacheExpiration") + @JsonPropertyDescription(""" + Client credentials token expiration time.""") + @JsonSetter(nulls = Nulls.SKIP) + private String cacheExpiration; +} From 35264bbad64dc0b494c88f920a2cd48987a6804c Mon Sep 17 00:00:00 2001 From: Carles Arnal Date: Fri, 31 Jan 2025 11:25:42 +0100 Subject: [PATCH 11/11] Add tests for authorization --- .../operator/EnvironmentVariables.java | 1 + .../operator/feat/security/AdminOverride.java | 7 +- .../registry/operator/feat/security/Auth.java | 11 +- .../operator/feat/security/Authz.java | 33 +++-- .../{KeycloakITTest.java => AuthITTest.java} | 67 +++------ ...cloakTLSITTest.java => AuthTLSITTest.java} | 50 ++----- .../registry/operator/it/AuthzITTest.java | 131 ++++++++++++++++++ .../registry/operator/it/BaseAuthTest.java | 44 ++++++ ...authz-with_keycloak.apicurioregistry3.yaml | 42 ++++++ ...imple-with_keycloak.apicurioregistry3.yaml | 4 + operator/install/install.yaml | 129 +++++++++++++++++ .../api/v1/spec/auth/AdminOverrideSpec.java | 2 +- .../operator/api/v1/spec/auth/AuthzSpec.java | 10 +- .../api/v1/spec/auth/BasicAuthSpec.java | 2 +- 14 files changed, 427 insertions(+), 106 deletions(-) rename operator/controller/src/test/java/io/apicurio/registry/operator/it/{KeycloakITTest.java => AuthITTest.java} (63%) rename operator/controller/src/test/java/io/apicurio/registry/operator/it/{KeycloakTLSITTest.java => AuthTLSITTest.java} (68%) create mode 100644 operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthzITTest.java create mode 100644 operator/controller/src/test/java/io/apicurio/registry/operator/it/BaseAuthTest.java create mode 100644 operator/controller/src/test/resources/k8s/examples/auth/authz-with_keycloak.apicurioregistry3.yaml 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 aa0c4f5f9d..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 @@ -46,6 +46,7 @@ public class EnvironmentVariables { 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"; 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 index aaf7463d6e..35eb782d8a 100644 --- 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 @@ -26,10 +26,13 @@ public static void configureAdminOverride(AdminOverrideSpec adminOverrideSpec, M return; } - if (Boolean.parseBoolean(adminOverrideSpec.getEnabled())) { + if (adminOverrideSpec.getEnabled() != null && adminOverrideSpec.getEnabled()) { env.put(EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_ENABLED, createEnvVar(EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_ENABLED, - adminOverrideSpec.getEnabled())); + adminOverrideSpec.getEnabled().toString())); + + putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_ROLE, + adminOverrideSpec.getRole()); putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_ADMIN_OVERRIDE_FROM, adminOverrideSpec.getFrom()); 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 index ccdd02c669..c57a993a2d 100644 --- 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 @@ -40,12 +40,15 @@ public static void configureAuth(AuthSpec authSpec, Deployment deployment, Map env) return; } - if (Boolean.parseBoolean(authzSpec.getEnabled())) { - env.put(EnvironmentVariables.APICURIO_AUTH_ROLE_BASED_AUTHORIZATION, createEnvVar( - EnvironmentVariables.APICURIO_AUTH_ROLE_BASED_AUTHORIZATION, authzSpec.getEnabled())); - - putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION, - authzSpec.getOwnerOnly()); - putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION_LIMIT_GROUP_ACCESS, - authzSpec.getGroupAccess()); - putIfNotBlank(env, EnvironmentVariables.APICURIO_AUTH_AUTHENTICATED_READ_ACCESS_ENABLED, - authzSpec.getReadAccess()); + 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.getDeveloperRole()); + 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, diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthITTest.java similarity index 63% rename from operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java rename to operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthITTest.java index e8412bcb63..a36b24a683 100644 --- a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakITTest.java +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthITTest.java @@ -2,36 +2,19 @@ import io.apicurio.registry.operator.EnvironmentVariables; import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.client.utils.Serialization; +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 java.util.List; 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.ResourceFactory.deserialize; import static io.apicurio.registry.operator.resource.app.AppDeploymentResource.getContainerFromDeployment; import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; @QuarkusTest -public class KeycloakITTest extends ITBase { - - private static final Logger log = LoggerFactory.getLogger(KeycloakITTest.class); - - @BeforeAll - public static void init() { - Awaitility.setDefaultTimeout(Duration.ofSeconds(60)); - } +public class AuthITTest extends BaseAuthTest { /** * In this test, Keycloak is deployed using a self-signed certificate with the hostname set to the ingress @@ -39,36 +22,21 @@ public static void init() { * works. */ @Test - void testKeycloakPlain() { + void testAuthTlsNoVerification() { // Preparation, deploy Keycloak - List resources = Serialization - .unmarshal(KeycloakITTest.class.getResourceAsStream("/k8s/examples/auth/keycloak.yaml")); - - 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"); + ApicurioRegistry3 registry = prepareInfra("/k8s/examples/auth/keycloak.yaml", + "k8s/examples/auth/simple-with_keycloak.apicurioregistry3.yaml"); + AuthSpec authSpec = registry.getSpec().getApp().getAuth(); - // Deploy Registry - var registry = deserialize("k8s/examples/auth/simple-with_keycloak.apicurioregistry3.yaml", - ApicurioRegistry3.class); - - registry.getMetadata().setNamespace(namespace); - - var appAuthSpec = registry.getSpec().getApp().getAuth(); - - Assertions.assertEquals("registry-api", appAuthSpec.getAppClientId()); - Assertions.assertEquals("apicurio-registry", appAuthSpec.getUiClientId()); - Assertions.assertEquals(true, appAuthSpec.getEnabled()); + 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", - appAuthSpec.getAuthServerUrl()); - Assertions.assertEquals("https://simple-ui.apps.cluster.example", appAuthSpec.getRedirectURI()); - Assertions.assertEquals("https://simple-ui.apps.cluster.example", appAuthSpec.getLogoutURL()); + 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(); @@ -101,5 +69,12 @@ void testKeycloakPlain() { + "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/KeycloakTLSITTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthTLSITTest.java similarity index 68% rename from operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakTLSITTest.java rename to operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthTLSITTest.java index d46d18f419..078e622a08 100644 --- a/operator/controller/src/test/java/io/apicurio/registry/operator/it/KeycloakTLSITTest.java +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/AuthTLSITTest.java @@ -2,8 +2,7 @@ import io.apicurio.registry.operator.EnvironmentVariables; import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.client.utils.Serialization; +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; @@ -13,20 +12,17 @@ import org.slf4j.LoggerFactory; import java.time.Duration; -import java.util.List; 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.ResourceFactory.deserialize; import static io.apicurio.registry.operator.resource.app.AppDeploymentResource.getContainerFromDeployment; import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; @QuarkusTest -public class KeycloakTLSITTest extends ITBase { +public class AuthTLSITTest extends BaseAuthTest { - private static final Logger log = LoggerFactory.getLogger(KeycloakTLSITTest.class); + private static final Logger log = LoggerFactory.getLogger(AuthTLSITTest.class); @BeforeAll public static void init() { @@ -39,36 +35,18 @@ public static void init() { * Quarkus application using the custom resource. */ @Test - void testKeycloakTLS() { - // Preparation, deploy Keycloak - List resources = Serialization - .unmarshal(KeycloakTLSITTest.class.getResourceAsStream("/k8s/examples/auth/keycloak.yaml")); - - 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("k8s/examples/auth/tls/simple-with_keycloak.apicurioregistry3.yaml", - ApicurioRegistry3.class); - - registry.getMetadata().setNamespace(namespace); - - var appAuthSpec = registry.getSpec().getApp().getAuth(); - - Assertions.assertEquals("registry-api", appAuthSpec.getAppClientId()); - Assertions.assertEquals("apicurio-registry", appAuthSpec.getUiClientId()); - Assertions.assertEquals(true, appAuthSpec.getEnabled()); + 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", - appAuthSpec.getAuthServerUrl()); - Assertions.assertEquals("https://simple-ui.apps.cluster.example", appAuthSpec.getRedirectURI()); - Assertions.assertEquals("https://simple-ui.apps.cluster.example", appAuthSpec.getLogoutURL()); + 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(); 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/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/simple-with_keycloak.apicurioregistry3.yaml b/operator/controller/src/test/resources/k8s/examples/auth/simple-with_keycloak.apicurioregistry3.yaml index 2e49ddeae0..81a2d74418 100644 --- 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 @@ -13,6 +13,10 @@ spec: 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: diff --git a/operator/install/install.yaml b/operator/install/install.yaml index a63fcd7ed1..b4261e716f 100644 --- a/operator/install/install.yaml +++ b/operator/install/install.yaml @@ -31,6 +31,11 @@ spec: 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. @@ -39,11 +44,135 @@ spec: 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. 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 index cefc85b83f..d68f3bca61 100644 --- 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 @@ -35,7 +35,7 @@ public class AdminOverrideSpec { @JsonPropertyDescription(""" Auth admin override enabled.""") @JsonSetter(nulls = Nulls.SKIP) - private String enabled; + private Boolean enabled; @JsonProperty("from") @JsonPropertyDescription(""" 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 index a423153c69..805921b1ed 100644 --- 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 @@ -35,25 +35,25 @@ public class AuthzSpec { @JsonPropertyDescription(""" Enabled role-based authorization.""") @JsonSetter(nulls = Nulls.SKIP) - private String enabled; + 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 String ownerOnly; + 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 String groupAccess; + 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 String readAccess; + private Boolean readAccess; @JsonProperty("roleSource") @JsonPropertyDescription(""" @@ -61,7 +61,7 @@ public class AuthzSpec { @JsonSetter(nulls = Nulls.SKIP) private String roleSource; - @JsonProperty("roleSource") + @JsonProperty("adminRole") @JsonPropertyDescription(""" The name of the role that indicates a user is an admin.""") @JsonSetter(nulls = Nulls.SKIP) 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 index 083324154c..aa781df86f 100644 --- 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 @@ -35,7 +35,7 @@ public class BasicAuthSpec { @JsonPropertyDescription(""" Enabled client credentials.""") @JsonSetter(nulls = Nulls.SKIP) - private String enabled; + private Boolean enabled; @JsonProperty("cacheExpiration") @JsonPropertyDescription("""