diff --git a/README.md b/README.md
index 75b0841..852f870 100644
--- a/README.md
+++ b/README.md
@@ -96,7 +96,7 @@ the created users will be tenantless and asked to create a new tenant.
To address this issue, this extension introduces the concept of `tenant-specific IDPs` and an additional authenticator that facilitates the creation of required memberships.
To configure an IDP as tenant-specific, tenants' IDs should be added to the `multi-tenancy.tenants` configuration attribute of the IDP as a **comma-separated list**.
-This can be achieved using the standard [Keycloak REST API](https://www.keycloak.org/docs-api/22.0.5/rest-api/index.html#_identity_providers).
+This can be achieved using the standard [Keycloak REST API](https://www.keycloak.org/docs-api/23.0.1/rest-api/index.html#_identity_providers).
> **_Note_**
> - _With tenant-specific IDP configuration, the IDP limits access to only the tenants listed in the configuration.
diff --git a/pom.xml b/pom.xml
index bfa9bbd..1d4e640 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,7 +17,7 @@
- 22.0.5
+ 23.0.1
diff --git a/src/main/java/dev/sultanov/keycloak/multitenancy/resource/AbstractAdminResource.java b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/AbstractAdminResource.java
index 5df7f92..b057e1f 100644
--- a/src/main/java/dev/sultanov/keycloak/multitenancy/resource/AbstractAdminResource.java
+++ b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/AbstractAdminResource.java
@@ -4,14 +4,11 @@
import jakarta.persistence.EntityManager;
import jakarta.ws.rs.NotAuthorizedException;
import jakarta.ws.rs.NotFoundException;
-import jakarta.ws.rs.core.Context;
-import jakarta.ws.rs.core.HttpHeaders;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.keycloak.Config;
-import org.keycloak.common.ClientConnection;
import org.keycloak.connections.jpa.JpaConnectionProvider;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
@@ -29,13 +26,6 @@
public abstract class AbstractAdminResource {
- @Context
- protected ClientConnection clientConnection;
-
- @Context
- private HttpHeaders headers;
-
- @Context
protected KeycloakSession session;
protected final RealmModel realm;
@@ -47,18 +37,20 @@ public abstract class AbstractAdminResource {
protected TenantProvider tenantProvider;
- public AbstractAdminResource(RealmModel realm) {
- this.realm = realm;
+ public AbstractAdminResource(KeycloakSession session) {
+ this.session = session;
+ this.realm = session.getContext().getRealm();
+ setup();
}
- public void setup() {
+ private void setup() {
setupAuth();
setupEvents();
setupProvider();
}
private void setupAuth() {
- String tokenString = AppAuthManager.extractAuthorizationHeaderToken(headers);
+ var tokenString = AppAuthManager.extractAuthorizationHeaderToken(session.getContext().getRequestHeaders());
if (tokenString == null) {
throw new NotAuthorizedException("Bearer");
@@ -73,9 +65,9 @@ private void setupAuth() {
throw new NotAuthorizedException("Bearer token format error");
}
- String realmName = token.getIssuer().substring(token.getIssuer().lastIndexOf('/') + 1);
- RealmManager realmManager = new RealmManager(session);
- RealmModel realm = realmManager.getRealmByName(realmName);
+ var realmName = token.getIssuer().substring(token.getIssuer().lastIndexOf('/') + 1);
+ var realmManager = new RealmManager(session);
+ var realm = realmManager.getRealmByName(realmName);
if (realm == null) {
throw new NotAuthorizedException("Unknown realm in token");
@@ -84,8 +76,8 @@ private void setupAuth() {
var bearerTokenAuthenticator = new BearerTokenAuthenticator(session);
bearerTokenAuthenticator.setRealm(realm);
bearerTokenAuthenticator.setUriInfo(session.getContext().getUri());
- bearerTokenAuthenticator.setConnection(clientConnection);
- bearerTokenAuthenticator.setHeaders(headers);
+ bearerTokenAuthenticator.setConnection(session.getContext().getConnection());
+ bearerTokenAuthenticator.setHeaders(session.getContext().getRequestHeaders());
AuthenticationManager.AuthResult authResult = bearerTokenAuthenticator.authenticate();
if (authResult == null) {
throw new NotAuthorizedException("Bearer");
diff --git a/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantInvitationsResource.java b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantInvitationsResource.java
index 0f64b40..ffa7147 100644
--- a/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantInvitationsResource.java
+++ b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantInvitationsResource.java
@@ -35,7 +35,7 @@
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.keycloak.events.admin.OperationType;
import org.keycloak.models.Constants;
-import org.keycloak.models.RealmModel;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
@@ -43,8 +43,8 @@ public class TenantInvitationsResource extends AbstractAdminResource {
private final TenantModel tenant;
- public TenantMembershipsResource(RealmModel realm, TenantModel tenant) {
- super(realm);
+ public TenantMembershipsResource(KeycloakSession session, TenantModel tenant) {
+ super(session);
this.tenant = tenant;
}
diff --git a/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantResource.java b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantResource.java
index 80a6a9b..8f22111 100644
--- a/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantResource.java
+++ b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantResource.java
@@ -8,16 +8,15 @@
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.openapi.annotations.Operation;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.events.admin.OperationType;
-import org.keycloak.models.RealmModel;
+import org.keycloak.models.KeycloakSession;
public class TenantResource extends AbstractAdminResource {
private final TenantModel tenant;
- public TenantResource(RealmModel realm, TenantModel tenant) {
- super(realm);
+ public TenantResource(KeycloakSession session, TenantModel tenant) {
+ super(session);
this.tenant = tenant;
}
@@ -40,17 +39,11 @@ public void deleteTenant() {
@Path("invitations")
public TenantInvitationsResource invitations() {
- TenantInvitationsResource resource = new TenantInvitationsResource(realm, tenant);
- ResteasyProviderFactory.getInstance().injectProperties(resource);
- resource.setup();
- return resource;
+ return new TenantInvitationsResource(session, tenant);
}
@Path("memberships")
public TenantMembershipsResource memberships() {
- TenantMembershipsResource resource = new TenantMembershipsResource(realm, tenant);
- ResteasyProviderFactory.getInstance().injectProperties(resource);
- resource.setup();
- return resource;
+ return new TenantMembershipsResource(session, tenant);
}
}
diff --git a/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantsResource.java b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantsResource.java
index a8c1e9e..8226360 100644
--- a/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantsResource.java
+++ b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantsResource.java
@@ -22,15 +22,14 @@
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.events.admin.OperationType;
import org.keycloak.models.Constants;
-import org.keycloak.models.RealmModel;
+import org.keycloak.models.KeycloakSession;
public class TenantsResource extends AbstractAdminResource {
- public TenantsResource(RealmModel realm) {
- super(realm);
+ public TenantsResource(KeycloakSession session) {
+ super(session);
}
@POST
@@ -77,10 +76,7 @@ public TenantResource getTenantResource(@PathParam("tenantId") String tenantId)
if (!auth.isTenantAdmin(model)) {
throw new NotAuthorizedException(String.format("Insufficient permission to access %s", tenantId));
} else {
- TenantResource resource = new TenantResource(realm, model);
- ResteasyProviderFactory.getInstance().injectProperties(resource);
- resource.setup();
- return resource;
+ return new TenantResource(session, model);
}
}
}
diff --git a/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantsResourceProvider.java b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantsResourceProvider.java
index 10c71a5..84cdcb7 100644
--- a/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantsResourceProvider.java
+++ b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantsResourceProvider.java
@@ -1,8 +1,6 @@
package dev.sultanov.keycloak.multitenancy.resource;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.RealmModel;
import org.keycloak.services.resource.RealmResourceProvider;
public class TenantsResourceProvider implements RealmResourceProvider {
@@ -15,11 +13,7 @@ public TenantsResourceProvider(KeycloakSession session) {
@Override
public Object getResource() {
- RealmModel realm = session.getContext().getRealm();
- TenantsResource resource = new TenantsResource(realm);
- ResteasyProviderFactory.getInstance().injectProperties(resource);
- resource.setup();
- return resource;
+ return new TenantsResource(session);
}
@Override
diff --git a/src/test/java/dev/sultanov/keycloak/multitenancy/support/BaseIntegrationTest.java b/src/test/java/dev/sultanov/keycloak/multitenancy/support/BaseIntegrationTest.java
index fc4f439..4f51ee8 100644
--- a/src/test/java/dev/sultanov/keycloak/multitenancy/support/BaseIntegrationTest.java
+++ b/src/test/java/dev/sultanov/keycloak/multitenancy/support/BaseIntegrationTest.java
@@ -18,7 +18,7 @@ public class BaseIntegrationTest {
private static final Integer MAILHOG_HTTP_PORT = 8025;
private static final Network network = Network.newNetwork();
- private static final KeycloakContainer keycloak = new KeycloakContainer("quay.io/keycloak/keycloak:22.0.5")
+ private static final KeycloakContainer keycloak = new KeycloakContainer("quay.io/keycloak/keycloak:23.0.1")
.withRealmImportFiles("/realm-export.json", "/idp-realm-export.json")
.withProviderClassesFrom("target/classes")
.withNetwork(network)
diff --git a/src/test/java/dev/sultanov/keycloak/multitenancy/support/browser/SignInPage.java b/src/test/java/dev/sultanov/keycloak/multitenancy/support/browser/SignInPage.java
index ffabbdf..bc3dcd3 100644
--- a/src/test/java/dev/sultanov/keycloak/multitenancy/support/browser/SignInPage.java
+++ b/src/test/java/dev/sultanov/keycloak/multitenancy/support/browser/SignInPage.java
@@ -3,6 +3,7 @@
import static org.assertj.core.api.Assertions.assertThat;
import com.microsoft.playwright.Page;
+import com.microsoft.playwright.Page.GetByLabelOptions;
import com.microsoft.playwright.options.AriaRole;
public class SignInPage extends AbstractPage {
@@ -24,7 +25,7 @@ public SelectLoginMethodPage tryAnotherWay() {
public SignInPage fillCredentials(String email, String password) {
page.getByLabel("Email").fill(email);
- page.getByLabel("Password").fill(password);
+ page.getByLabel("Password", new GetByLabelOptions().setExact(true)).fill(password);
return this;
}
diff --git a/src/test/resources/idp-realm-export.json b/src/test/resources/idp-realm-export.json
index 6a7202d..4af5c1d 100644
--- a/src/test/resources/idp-realm-export.json
+++ b/src/test/resources/idp-realm-export.json
@@ -1794,7 +1794,6 @@
"cibaInterval" : "5",
"realmReusableOtpCode" : "false"
},
- "keycloakVersion" : "22.0.5",
"userManagedAccessAllowed" : false,
"clientProfiles" : {
"profiles" : [ ]