Skip to content

Commit

Permalink
APPPOCTOOL-16 Upgrade keycloak to v25.0.1 (#111)
Browse files Browse the repository at this point in the history
  • Loading branch information
pfilippov-epam authored Jun 26, 2024
1 parent ebb4d89 commit 6ab7789
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 21 deletions.
2 changes: 1 addition & 1 deletion folio-backend-testing/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<jsonassert.version>1.5.1</jsonassert.version>
<jjwt.version>0.12.5</jjwt.version>
<apache-commons-lang3.version>3.14.0</apache-commons-lang3.version>
<testcontainers-keycloak.version>3.3.1</testcontainers-keycloak.version>
<testcontainers-keycloak.version>3.4.0</testcontainers-keycloak.version>
</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.List;
import javax.net.ssl.SSLContext;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.ssl.SSLInitializationException;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.junit.jupiter.api.extension.AfterAllCallback;
Expand All @@ -27,7 +28,7 @@
@Log4j2
public class KeycloakContainerExtension implements BeforeAllCallback, AfterAllCallback {

private static final String KEYCLOAK_IMAGE = "quay.io/keycloak/keycloak:23.0.7";
private static final String KEYCLOAK_IMAGE = "quay.io/keycloak/keycloak:25.0.1";
private static final String REALM_JSON = "json/keycloak/master-realm.json";
private static final String IMPORTED_CLIENT_ID = "folio-backend-admin-client";
private static final String IMPORTED_CLIENT_SECRET = "supersecret";
Expand Down Expand Up @@ -99,6 +100,8 @@ private static Keycloak keycloakAdminClient() {
private static KeycloakContainer keycloakContainer() {
return new KeycloakContainer(KEYCLOAK_IMAGE)
.withFeaturesEnabled("scripts", "token-exchange", "admin-fine-grained-authz")
.withAdminUsername("keycloak-test-admin")
.withAdminPassword(RandomStringUtils.random(20, true, true))
.withProviderLibsFrom(List.of(readToFile("keycloak/folio-scripts.jar", "folio-scripts", ".jar")))
.useTlsKeystore(SSL_KEYSTORE_PATH, SSL_KEYSTORE_PASSWORD);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,61 +7,88 @@
import static org.springframework.test.context.util.TestContextResourceUtils.convertToClasspathResourcePaths;
import static org.springframework.test.context.util.TestContextResourceUtils.convertToResourceList;

import jakarta.ws.rs.NotFoundException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.io.IOUtils;
import org.folio.test.extensions.KeycloakRealms;
import org.folio.test.extensions.KeycloakRealmsGroup;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.representations.idm.RealmRepresentation;
import org.springframework.core.io.Resource;
import org.springframework.lang.NonNull;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.util.Assert;

@Log4j2
public class KeycloakExecutionListener implements TestExecutionListener {

@Override
public void beforeTestMethod(@NonNull TestContext ctx) {
var adminClient = getKeycloakAdminClient();
getAnnotationsFor(ctx.getTestClass()).forEach(realmPath -> importKeycloakRealm(realmPath, adminClient, ctx));
getAnnotationsFor(ctx.getTestMethod()).forEach(realmPath -> importKeycloakRealm(realmPath, adminClient, ctx));
var annotatedRealms = getAnnotatedRealms(ctx);
var createdRealms = new ArrayList<String>();
for (var realmRepresentation : annotatedRealms) {
log.info("Importing test realm: {}", realmRepresentation.getRealm());
createdRealms.add(realmRepresentation.getRealm());
adminClient.realms().create(realmRepresentation);
}

ctx.setAttribute(getImportedRealmsAttributeName(), createdRealms);
}

@Override
public void afterTestMethod(@NonNull TestContext ctx) {
var keycloak = getKeycloakAdminClient();
var realms = keycloak.realms().findAll();
realms.stream()
.map(RealmRepresentation::getRealm)
.filter(realm -> !realm.equals("master"))
.forEach(realm -> {
log.info("Removing test realm: {}", realm);
keycloak.realms().realm(realm).remove();
});
var importedRawRealmNames = ctx.getAttribute(getImportedRealmsAttributeName());

//noinspection rawtypes
if (importedRawRealmNames instanceof List importedRealmNamesList) {
for (var rawRealmName : importedRealmNamesList) {
if (rawRealmName instanceof String realmName) {
try {
keycloak.realm(realmName).remove();
} catch (NotFoundException e) {
// nothing to do, cause realm is not found
}
}
}
}
}

private static Set<KeycloakRealms> getAnnotationsFor(AnnotatedElement element) {
return getMergedRepeatableAnnotations(element, KeycloakRealms.class, KeycloakRealmsGroup.class);
}

private static void importKeycloakRealm(KeycloakRealms keycloakRealms, Keycloak keycloak, TestContext ctx) {
var realms = keycloakRealms.realms();
Assert.notEmpty(realms, "Empty realms are not allowed");
private static List<RealmRepresentation> getAnnotatedRealms(TestContext testContext) {
var realmsMap = new ArrayList<RealmRepresentation>();
for (var keycloakRealms : getAnnotationsFor(testContext.getTestClass())) {
realmsMap.addAll(getRealmRepresentations(keycloakRealms, testContext));
}

for (var keycloakRealms : getAnnotationsFor(testContext.getTestMethod())) {
realmsMap.addAll(getRealmRepresentations(keycloakRealms, testContext));
}

return realmsMap;
}

var classpathResourcePaths = convertToClasspathResourcePaths(ctx.getTestClass(), realms);
private static List<RealmRepresentation> getRealmRepresentations(KeycloakRealms keycloakRealms, TestContext ctx) {
var realmPaths = keycloakRealms.realms();
var classpathResourcePaths = convertToClasspathResourcePaths(ctx.getTestClass(), realmPaths);
var resources = convertToResourceList(ctx.getApplicationContext(), classpathResourcePaths);
var realms = new ArrayList<RealmRepresentation>();
for (var resource : resources) {
var realmJson = readAsString(resource);
var realmRepresentation = parse(realmJson, RealmRepresentation.class);
log.info("Importing test realm: {}", realmRepresentation.getRealm());
keycloak.realms().create(realmRepresentation);
realms.add(realmRepresentation);
}

return realms;
}

private static String readAsString(Resource resource) {
Expand All @@ -71,4 +98,8 @@ private static String readAsString(Resource resource) {
throw new UncheckedIOException("Failed to read resource as string: " + resource.getFilename(), e);
}
}

private String getImportedRealmsAttributeName() {
return this.getClass() + "#kc-imported-realms";
}
}
2 changes: 1 addition & 1 deletion folio-security/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<properties>
<spring-cloud-bom.version>2023.0.2</spring-cloud-bom.version>
<keycloak-admin-client.version>23.0.7</keycloak-admin-client.version>
<keycloak-admin-client.version>25.0.1</keycloak-admin-client.version>
<jwks-rsa.version>0.22.1</jwks-rsa.version>

<sonar.exclusions>
Expand Down

0 comments on commit 6ab7789

Please sign in to comment.