Skip to content

Commit

Permalink
feat: initial commit of rhbk-24
Browse files Browse the repository at this point in the history
  • Loading branch information
NithinKuruba committed May 8, 2024
1 parent 8b6e0fc commit 0f81254
Show file tree
Hide file tree
Showing 83 changed files with 138 additions and 49 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/publish-image-rhbk-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ jobs:
with:
context: docker/keycloak
push: true
tags: ${{ env.GITHUB_REGISTRY }}/${{env.IMAGE_NAME}}:dev-rhbk-22
file: docker/keycloak/Dockerfile-22
tags: ${{ env.GITHUB_REGISTRY }}/${{env.IMAGE_NAME}}:dev-rhbk-24
file: docker/keycloak/Dockerfile-24
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new

Expand Down
24 changes: 12 additions & 12 deletions docker/kc-cron-job/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1132,9 +1132,9 @@ camelcase@^6.2.0:
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==

camelize@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b"
integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=
version "1.0.1"
resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3"
integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==

caniuse-lite@^1.0.30001541:
version "1.0.30001550"
Expand Down Expand Up @@ -1301,9 +1301,9 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.2:
ms "2.1.2"

decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
version "0.2.2"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9"
integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==

dedent@^1.0.0:
version "1.5.1"
Expand Down Expand Up @@ -1816,7 +1816,7 @@ fill-range@^7.0.1:
filter-obj@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b"
integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs=
integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==

find-up@^4.0.0, find-up@^4.1.0:
version "4.1.0"
Expand Down Expand Up @@ -1849,9 +1849,9 @@ flatted@^3.2.9:
integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==

follow-redirects@^1.14.0:
version "1.14.4"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379"
integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==
version "1.15.6"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==

follow-redirects@^1.15.0:
version "1.15.2"
Expand Down Expand Up @@ -3662,7 +3662,7 @@ stack-utils@^2.0.3:
strict-uri-encode@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==

string-length@^4.0.1:
version "4.0.2"
Expand Down Expand Up @@ -3954,7 +3954,7 @@ url-join@^4.0.0:
url-template@^2.0.8:
version "2.0.8"
resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21"
integrity sha1-/FZaPMy/93MMd19WQflVV5FDnyE=
integrity sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==

util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
Expand Down
6 changes: 3 additions & 3 deletions docker/keycloak/Dockerfile-22
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ COPY --from=builder /opt/keycloak/ /opt/keycloak/
# copy the theme directory to `/opt/keycloak/themes/` for now, but we can consider to archive to be deployed later.
COPY ./extensions-22/themes/src/main/resources/theme /opt/keycloak/themes

COPY ./configuration/22.0/cache-ispn-custom.xml /opt/keycloak/conf
COPY ./configuration/22/cache-ispn-custom.xml /opt/keycloak/conf

COPY ./configuration/22.0/keycloak.conf /opt/keycloak/conf
COPY ./configuration/22/keycloak.conf /opt/keycloak/conf

COPY ./configuration/22.0/quarkus.properties /opt/keycloak/conf
COPY ./configuration/22/quarkus.properties /opt/keycloak/conf

# change these values to point to a running postgres instance
ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
FROM maven:3.8.5-openjdk-17-slim AS extensions-builder

COPY ./extensions-24.0.2 /tmp/
COPY ./extensions-24 /tmp/
WORKDIR /tmp/
RUN mvn -B clean package --file pom.xml

FROM quay.io/keycloak/keycloak:24.0.2 as builder
FROM registry.redhat.io/rhbk/keycloak-rhel9:24 as builder

# Enable health and metrics support
ENV KC_HEALTH_ENABLED=true
Expand All @@ -16,21 +16,21 @@ ENV KC_DB=postgres
COPY --from=extensions-builder /tmp/services/target/bcgov-services-1.0.0.jar /opt/keycloak/providers/

WORKDIR /opt/keycloak
# for demonstration purposes only, please make sure to use proper certificates in production instead
RUN keytool -genkeypair -storepass password -storetype PKCS12 -keyalg RSA -keysize 2048 -dname "CN=server" -alias server -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore conf/server.keystore

RUN /opt/keycloak/bin/kc.sh build

FROM quay.io/keycloak/keycloak:24.0.2
FROM registry.redhat.io/rhbk/keycloak-rhel9:24

COPY --from=builder /opt/keycloak/ /opt/keycloak/

# copy the theme directory to `/opt/keycloak/themes/` for now, but we can consider to archive to be deployed later.
COPY ./extensions-24.0.2/themes/src/main/resources/theme /opt/keycloak/themes
COPY ./extensions-24/themes/src/main/resources/theme /opt/keycloak/themes

# COPY ./configuration/cache-ispn-custom.xml /opt/keycloak/conf
COPY ./configuration/24/cache-ispn-custom.xml /opt/keycloak/conf

COPY ./configuration/keycloak.conf /opt/keycloak/conf
COPY ./configuration/24/keycloak.conf /opt/keycloak/conf

COPY ./configuration/quarkus.properties /opt/keycloak/conf
COPY ./configuration/24/quarkus.properties /opt/keycloak/conf

# change these values to point to a running postgres instance
ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
37 changes: 37 additions & 0 deletions docker/keycloak/configuration/24/keycloak.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
health-enabled=true
metrics-enabled=true

# database
db-pool-min-size=5
db-pool-max-size=20

# theme
spi-theme-static-max-age=2592000
spi-theme-cache-themes=true
spi-theme-cache-templates=true

# logging
log=console,file
log-console-color=false
log-file=/var/log/eap/${HOSTNAME}.log

# root-logger-level:INFO
log-level=info,com.arjuna:warn,io.jaegertracing.Configuration:warn,org.jboss.as.config:debug,org.keycloak.events:debug,sun.rmi:warn
log-console-output=json
log-file-output=json

# SPIs
spi-login-protocol-openid-connect-legacy-logout-redirect-uri=true

# cache
cache=ispn
# DNS_PING is particularly useful in environments like Kubernetes and Red Hat OpenShift where UDP multicast, a different cluster discovery method, might not be available. This is because DNS is a standard service that's always available, making DNS_PING a reliable way for Infinispan nodes to discover each other.
# The below option requires passing -Djgroups.dns.query=sso-keycloak-ping.<NAMESPACE>.svc.cluster.local to start command
cache-stack=kubernetes
#cache-config-file=cache-ispn-custom.xml

# tls
# https-key-store-file=server.keystore
# https-key-store-password=password

proxy-headers=forwarded|xforwarded
10 changes: 10 additions & 0 deletions docker/keycloak/configuration/24/quarkus.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
quarkus.log.console.json.exception-output-type=formatted
quarkus.log.console.json.key-overrides=timestamp=@timestamp
quarkus.log.console.json.additional-field."@version".value=1
quarkus.log.file.json.exception-output-type=formatted
quarkus.log.file.json.key-overrides=timestamp=@timestamp
quarkus.log.file.json.additional-field."@version".value=1
quarkus.log.file.rotation.file-suffix=.yyyy-MM-dd
# Optional: Disable rotation by size (adjust value as needed)
quarkus.log.handler.file.rotation.max-file-size="10000M"
quarkus.log.handler.file.rotation.max-backup-index="100"
3 changes: 3 additions & 0 deletions docker/keycloak/extensions-24/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"java.configuration.updateBuildConfiguration": "interactive"
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<keycloak.version>22.0.0</keycloak.version>
<keycloak.version>24.0.3</keycloak.version>
</properties>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import jakarta.ws.rs.core.MultivaluedHashMap;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator;
import org.keycloak.forms.login.LoginFormsProvider;
Expand All @@ -28,8 +29,7 @@ public void action(AuthenticationFlowContext context) {
@Override
public void authenticate(AuthenticationFlowContext context) {
List<IdentityProviderModel> realmIdps = context.getRealm().getIdentityProvidersStream().toList();
Map<String, ClientScopeModel> scopes =
context.getAuthenticationSession().getClient().getClientScopes(true);
Map<String, ClientScopeModel> scopes = context.getAuthenticationSession().getClient().getClientScopes(true);

Map<String, Map<String, String>> idpContext = new HashMap<>();

Expand All @@ -50,7 +50,7 @@ public void authenticate(AuthenticationFlowContext context) {
}
}

MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>();
MultivaluedMap<String, String> formData = new MultivaluedHashMap<>();

ObjectMapper objectMapper = new ObjectMapper();
try {
Expand All @@ -75,7 +75,8 @@ protected Response challenge(
AuthenticationFlowContext context, MultivaluedMap<String, String> formData) {
LoginFormsProvider forms = context.form();

if (formData.size() > 0) forms.setFormData(formData);
if (formData.size() > 0)
forms.setFormData(formData);

return forms.createLoginUsernamePassword();
}
Expand All @@ -92,5 +93,6 @@ public void setRequiredActions(KeycloakSession session, RealmModel realm, UserMo
}

@Override
public void close() { /* This is ok */ }
public void close() {
/* This is ok */ }
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,30 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Base64;

/** @author <a href="mailto:[email protected]">Junmin Ahn</a> */
public class IDPUserinfoMapper extends AbstractOIDCProtocolMapper
implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {

private static final Logger logger = Logger.getLogger(IDPUserinfoMapper.class);

private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
private static final String BEARER = "Bearer";

private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();

public static final String CLAIM_VALUE = "claim.value";

public static final String USER_ATTRIBUTE = "userAttribute";

public static final String DECODE_USERINFO_RESPONSE = "decodeUserInfoResponse";

static {
configProperties.add(new ProviderConfigProperty(DECODE_USERINFO_RESPONSE, "Decode UserInfo Response",
"Decode response returned from IDP userinfo endpoint", ProviderConfigProperty.BOOLEAN_TYPE, false));
configProperties.add(new ProviderConfigProperty(USER_ATTRIBUTE, "User Attribute",
"User Attribute returned from IDP userinfo endpoint", ProviderConfigProperty.STRING_TYPE, null));

OIDCAttributeMapperHelper.addTokenClaimNameConfig(configProperties);
OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, IDPUserinfoMapper.class);
}
Expand Down Expand Up @@ -74,6 +86,17 @@ private static JsonNode parseJson(String json) {
}
}

private static String decodeUserInfoResponse(String token) {
try {
String[] tokenParts = token.split("\\.");
Base64.Decoder decoder = Base64.getUrlDecoder();
String payload = new String(decoder.decode(tokenParts[1]));
return payload;
} catch (Exception e) {
return null;
}
}

@Override
protected void setClaim(
IDToken token,
Expand All @@ -91,22 +114,34 @@ protected void setClaim(
String userInfoUrl = identityProviderModel.getConfig().get("userInfoUrl");

if (userInfoUrl != null) {
FederatedIdentityModel identity =
keycloakSession.users().getFederatedIdentity(realm, userSession.getUser(), idp);
FederatedIdentityModel identity = keycloakSession.users().getFederatedIdentity(realm, userSession.getUser(),
idp);
String brokerToken = identity.getToken();
AccessTokenResponse brokerAccessToken = parseTokenString(brokerToken);
Client httpClient = ClientBuilder.newClient();
String userinfoString =
httpClient
.target(userInfoUrl)
.request()
.header("Authorization", "Bearer " + brokerAccessToken.getToken())
.get(String.class);

JsonNode jsonNode = parseJson(userinfoString);
Map<String, Object> otherClaims = token.getOtherClaims();
otherClaims.put(
mappingModel.getConfig().get(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME), jsonNode);
String userinfoString = httpClient
.target(userInfoUrl)
.request()
.header("Authorization", "Bearer " + brokerAccessToken.getToken())
.get(String.class);
boolean decode = Boolean.parseBoolean(mappingModel.getConfig().get(DECODE_USERINFO_RESPONSE));
if (decode) {
userinfoString = decodeUserInfoResponse(userinfoString);
}
try {
JsonNode jsonNode = parseJson(userinfoString);
if (jsonNode == null) {
logger.error("null response returned from [" + idp + "] userinfo URL");
}
Map<String, Object> otherClaims = token.getOtherClaims();
otherClaims.put(
mappingModel.getConfig().get(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME),
jsonNode.get(mappingModel.getConfig().get(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME)));
} catch (NullPointerException e) {
logger.errorf("'%s' returned invalid response", idp);
} catch (Exception e) {
logger.errorf("unable to fetch attributes from userinfo endpoint '%s'", userInfoUrl);
}
} else {
logger.error("Identity Provider [" + idp + "] does not have userinfo URL.");
}
Expand All @@ -123,8 +158,10 @@ public static ProtocolMapperModel create(
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
Map<String, String> config = new HashMap<>();
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, tokenClaimName);
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
if (accessToken)
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken)
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
mapper.setConfig(config);
return mapper;
}
Expand Down

0 comments on commit 0f81254

Please sign in to comment.