Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/actions/conditional/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ outputs:
admin-v2:
description: Should Admin v2 tests execute
value: ${{ steps.changes.outputs.admin-v2 }}
ci-jackson3:
description: Should Jackson 3 admin client tests execute
value: ${{ steps.changes.outputs.ci-jackson3 }}

runs:
using: composite
Expand Down
9 changes: 8 additions & 1 deletion .github/actions/conditional/conditions
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,11 @@ js/libs/ui-shared/ ci ci-webauthn
*.ts codeql-typescript
*.tsx codeql-typescript

rest/admin-v2/ admin-v2
rest/admin-v2/ admin-v2 ci-jackson3
integration/admin-client-jackson3/ ci-jackson3
integration/admin-client-core/ ci-jackson3
integration/admin-client/ ci-jackson3
core/src/main/java/org/keycloak/json/ ci-jackson3
core/src/main/java/org/keycloak/representations/ ci-jackson3
core/src/main/resources/META-INF/services/ ci-jackson3
tests/base/ ci-jackson3
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ jobs:
ci-azure: ${{ steps.azure-tests.outputs.run-azure-tests }}
ci-additional-dbs: ${{ steps.additional-dbs-tests.outputs.run-additional-dbs-tests }}
ci-admin-v2: ${{ steps.conditional.outputs.admin-v2 }}
ci-jackson3: ${{ steps.conditional.outputs.ci-jackson3 }}
permissions:
contents: read
pull-requests: read
Expand Down Expand Up @@ -1293,6 +1294,27 @@ jobs:
- name: Run tests
run: ./mvnw verify -pl rest/admin-v2/tests/pom.xml

jackson3-admin-client-tests:
name: Jackson 3 Admin Client
if: needs.conditional.outputs.ci-jackson3 == 'true'
runs-on: ubuntu-latest
needs:
- build
- conditional
timeout-minutes: 30
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

- id: integration-test-setup
name: Integration test setup
uses: ./.github/actions/integration-test-setup

- name: Run Admin v2 tests with Jackson 3
run: ./mvnw verify -pl rest/admin-v2/tests/pom.xml -Pjackson3

- name: Run Base smoke tests with Jackson 3
run: ./mvnw verify -pl tests/base -Pjackson3

mixed-cluster-compatibility-tests:
name: Cluster Compatibility Tests
if: needs.version-compatibility.outputs.matrix != 'skip'
Expand Down Expand Up @@ -1395,6 +1417,7 @@ jobs:
- mixed-cluster-compatibility-tests
- authzen-integration-tests
- admin-v2-tests
- jackson3-admin-client-tests
- testsuite-deprecation-check
- stateless-tests
- stateless-cluster-integration-tests
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/java/org/keycloak/jose/jwe/JWE.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import org.keycloak.jose.jwe.JWEHeader.JWEHeaderBuilder;
import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider;
import org.keycloak.jose.jwe.enc.JWEEncryptionProvider;
import org.keycloak.util.JsonSerialization;
import org.keycloak.json.KeycloakJsonMapperFactory;

/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
Expand Down Expand Up @@ -62,7 +62,7 @@ public JOSEHeader getHeader() {
if (header == null && base64Header != null) {
try {
byte[] decodedHeader = Base64Url.decode(base64Header);
header = JsonSerialization.readValue(decodedHeader, JWEHeader.class);
header = KeycloakJsonMapperFactory.mapper().readValue(decodedHeader, JWEHeader.class);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
Expand All @@ -72,7 +72,7 @@ public JOSEHeader getHeader() {

public String getBase64Header() throws IOException {
if (base64Header == null && header != null) {
byte[] contentBytes = JsonSerialization.writeValueAsBytes(header);
byte[] contentBytes = KeycloakJsonMapperFactory.mapper().writeValueAsBytes(header);
base64Header = Base64Url.encode(contentBytes);
}
return base64Header;
Expand Down
11 changes: 2 additions & 9 deletions core/src/main/java/org/keycloak/jose/jwe/JWEHeader.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@

import org.keycloak.jose.JOSEHeader;
import org.keycloak.jose.jwk.ECPublicJWK;
import org.keycloak.json.KeycloakJsonMapperFactory;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
Expand Down Expand Up @@ -141,15 +140,9 @@ public String getAgreementPartyVInfo() {
return agreementPartyVInfo;
}

private static final ObjectMapper mapper = new ObjectMapper();

static {
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}

public String toString() {
try {
return mapper.writeValueAsString(this);
return KeycloakJsonMapperFactory.mapper().writeValueAsString(this);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
Expand Down
21 changes: 10 additions & 11 deletions core/src/main/java/org/keycloak/jose/jwk/JWKParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.util.Base64Url;
import org.keycloak.crypto.KeyType;
import org.keycloak.util.JsonSerialization;

import com.fasterxml.jackson.databind.JsonNode;
import org.keycloak.json.KeycloakJsonMapperFactory;
import org.keycloak.json.RawJsonValue;

/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
Expand All @@ -56,7 +55,7 @@ public static JWKParser create(JWK jwk) {

public JWKParser parse(String jwk) {
try {
this.jwk = JsonSerialization.mapper.readValue(jwk, JWK.class);
this.jwk = KeycloakJsonMapperFactory.mapper().readValue(jwk, JWK.class);
return this;
} catch (Exception e) {
throw new RuntimeException(e);
Expand All @@ -75,21 +74,21 @@ public PublicKey toPublicKey() {

// subtypes may store properties differently while representing the same JWK, serializing it to nodes
// makes sure there is no difference when creating the keys
JsonNode normalizedJwkNode = JsonSerialization.writeValueAsNode(jwk);
RawJsonValue normalizedJwk = KeycloakJsonMapperFactory.mapper().convertValue(jwk, RawJsonValue.class);
if (KeyType.RSA.equals(keyType)) {
return createRSAPublicKey(normalizedJwkNode);
return createRSAPublicKey(normalizedJwk);
} else if (KeyType.EC.equals(keyType)) {
return createECPublicKey(normalizedJwkNode);
return createECPublicKey(normalizedJwk);
} else if (KeyType.OKP.equals(keyType)) {
return JWKBuilder.EdEC_UTILS.createOKPPublicKey(jwk);
} else if (KeyType.AKP.equals(keyType)) {
return createAPKPublicKey(normalizedJwkNode);
return createAPKPublicKey(normalizedJwk);
} else {
throw new RuntimeException("Unsupported keyType " + keyType);
}
}

private static PublicKey createECPublicKey(JsonNode jwk) {
private static PublicKey createECPublicKey(RawJsonValue jwk) {


/* Try retrieving the necessary fields */
Expand Down Expand Up @@ -133,7 +132,7 @@ private static PublicKey createECPublicKey(JsonNode jwk) {
}
}

private static PublicKey createRSAPublicKey(JsonNode jwk) {
private static PublicKey createRSAPublicKey(RawJsonValue jwk) {
BigInteger modulus = new BigInteger(1, Base64Url.decode(jwk.path(RSAPublicJWK.MODULUS).asText(null)));
BigInteger publicExponent = new BigInteger(1, Base64Url.decode(jwk.path(RSAPublicJWK.PUBLIC_EXPONENT).asText(null)));

Expand All @@ -145,7 +144,7 @@ private static PublicKey createRSAPublicKey(JsonNode jwk) {
}
}

private static PublicKey createAPKPublicKey(JsonNode jwk) {
private static PublicKey createAPKPublicKey(RawJsonValue jwk) {
String algorithm = jwk.path(JWK.ALGORITHM).asText();
String publicKey = jwk.path(AKPPublicJWK.PUB).asText();
return AKPUtils.fromEncodedPub(publicKey, algorithm);
Expand Down
9 changes: 4 additions & 5 deletions core/src/main/java/org/keycloak/jose/jws/JWSBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@
import org.keycloak.jose.jwk.JWK;
import org.keycloak.jose.jws.crypto.HMACProvider;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.util.JsonSerialization;
import org.keycloak.json.KeycloakJsonMapperFactory;

import com.fasterxml.jackson.core.JsonProcessingException;

/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
Expand Down Expand Up @@ -103,7 +102,7 @@ public EncodingBuilder content(byte[] bytes) {

public EncodingBuilder jsonContent(Object object) {
try {
this.contentBytes = JsonSerialization.writeValueAsBytes(object);
this.contentBytes = KeycloakJsonMapperFactory.mapper().writeValueAsBytes(object);
} catch (IOException e) {
throw new RuntimeException(e);
}
Expand Down Expand Up @@ -137,8 +136,8 @@ protected String encodeHeader(String sigAlgName) {
}
if (jwk != null) {
try {
builder.append(",\"jwk\" : ").append(JsonSerialization.mapper.writeValueAsString(jwk));
} catch (JsonProcessingException e) {
builder.append(",\"jwk\" : ").append(KeycloakJsonMapperFactory.mapper().writeValueAsString(jwk));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/java/org/keycloak/jose/jws/JWSHeader.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

import org.keycloak.jose.JOSEHeader;
import org.keycloak.jose.jwk.JWK;
import org.keycloak.util.JsonSerialization;
import org.keycloak.json.KeycloakJsonMapperFactory;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
Expand Down Expand Up @@ -166,7 +166,7 @@ public void setOtherClaims(String name, Object value) {

public String toString() {
try {
return JsonSerialization.writeValueAsString(this);
return KeycloakJsonMapperFactory.mapper().writeValueAsString(this);
} catch (IOException e) {
throw new RuntimeException(e);
}
Expand Down
16 changes: 3 additions & 13 deletions core/src/main/java/org/keycloak/jose/jws/JWSInput.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@

import org.keycloak.common.util.Base64Url;
import org.keycloak.jose.JOSE;
import org.keycloak.util.JsonSerialization;

import com.fasterxml.jackson.core.type.TypeReference;
import org.keycloak.json.KeycloakJsonMapperFactory;

/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
Expand Down Expand Up @@ -56,7 +54,7 @@ public JWSInput(String wire) throws JWSInputException {

}
byte[] headerBytes = Base64Url.decode(encodedHeader);
header = JsonSerialization.readValue(headerBytes, JWSHeader.class);
header = KeycloakJsonMapperFactory.mapper().readValue(headerBytes, JWSHeader.class);
} catch (Throwable t) {
throw new JWSInputException(t);
}
Expand Down Expand Up @@ -95,15 +93,7 @@ public byte[] getSignature() {

public <T> T readJsonContent(Class<T> type) throws JWSInputException {
try {
return JsonSerialization.readValue(content, type);
} catch (IOException e) {
throw new JWSInputException(e);
}
}

public <T> T readJsonContent(TypeReference<T> type) throws JWSInputException {
try {
return JsonSerialization.readValue(content, type);
return KeycloakJsonMapperFactory.mapper().readValue(content, type);
} catch (IOException e) {
throw new JWSInputException(e);
}
Expand Down
47 changes: 47 additions & 0 deletions core/src/main/java/org/keycloak/json/Jackson2JsonMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.keycloak.json;

import java.io.IOException;
import java.io.InputStream;

import org.keycloak.util.JsonSerialization;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
* Jackson 2 implementation of {@link KeycloakJsonMapper} backed by the
* {@link ObjectMapper} from {@link JsonSerialization}.
*/
public final class Jackson2JsonMapper implements KeycloakJsonMapper {

private final ObjectMapper delegate = JsonSerialization.mapper;

@Override
public <T> T convertValue(Object fromValue, Class<T> toValueType) {
return delegate.convertValue(fromValue, toValueType);
}

@Override
public <T> T readValue(byte[] src, Class<T> valueType) throws IOException {
return delegate.readValue(src, valueType);
}

@Override
public <T> T readValue(String src, Class<T> valueType) throws IOException {
return delegate.readValue(src, valueType);
}

@Override
public <T> T readValue(InputStream src, Class<T> valueType) throws IOException {
return delegate.readValue(src, valueType);
}

@Override
public byte[] writeValueAsBytes(Object value) throws IOException {
return delegate.writeValueAsBytes(value);
}

@Override
public String writeValueAsString(Object value) throws IOException {
return delegate.writeValueAsString(value);
}
}
Loading