From 98230aa3725b597bee4f4aaf4d120aa7ca745bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9da=20Housni=20Alaoui?= Date: Wed, 10 Jan 2024 11:51:41 +0100 Subject: [PATCH] Add federated identity ProviderEvent(s) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Réda Housni Alaoui --- .../keycloak/storage/UserStorageManager.java | 59 +++++++- .../models/FederatedIdentityModel.java | 16 +++ .../model/FederatedIdentityModelTest.java | 131 ++++++++++++++++++ .../testsuite/model/parameters/LegacyJpa.java | 9 ++ 4 files changed, 213 insertions(+), 2 deletions(-) create mode 100644 testsuite/model/src/test/java/org/keycloak/testsuite/model/FederatedIdentityModelTest.java diff --git a/model/legacy-private/src/main/java/org/keycloak/storage/UserStorageManager.java b/model/legacy-private/src/main/java/org/keycloak/storage/UserStorageManager.java index fca704fd0a..4d4af91690 100755 --- a/model/legacy-private/src/main/java/org/keycloak/storage/UserStorageManager.java +++ b/model/legacy-private/src/main/java/org/keycloak/storage/UserStorageManager.java @@ -575,6 +575,28 @@ public class UserStorageManager extends AbstractStorageManagerMarek Posolda */ @@ -85,4 +87,18 @@ public class FederatedIdentityModel { result = 31 * result + (userName != null ? userName.hashCode() : 0); return result; } + + public interface FederatedIdentityCreatedEvent extends ProviderEvent { + KeycloakSession getKeycloakSession(); + RealmModel getRealm(); + UserModel getUser(); + FederatedIdentityModel getFederatedIdentity(); + } + + public interface FederatedIdentityRemovedEvent extends ProviderEvent { + KeycloakSession getKeycloakSession(); + RealmModel getRealm(); + UserModel getUser(); + FederatedIdentityModel getFederatedIdentity(); + } } diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/FederatedIdentityModelTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/FederatedIdentityModelTest.java new file mode 100644 index 0000000000..81ed0a3639 --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/FederatedIdentityModelTest.java @@ -0,0 +1,131 @@ +/* + * Copyright 2024 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; + +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.keycloak.broker.provider.IdentityProvider; +import org.keycloak.broker.provider.IdentityProviderFactory; +import org.keycloak.models.Constants; +import org.keycloak.models.FederatedIdentityModel; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.provider.ProviderEventListener; +import org.keycloak.testsuite.broker.oidc.TestKeycloakOidcIdentityProviderFactory; + +/** + * @author Réda Housni Alaoui + */ +@RequireProvider(value = IdentityProvider.class, only = TestKeycloakOidcIdentityProviderFactory.ID) +public class FederatedIdentityModelTest extends KeycloakModelTest { + + private static final String IDENTITY_PROVIDER_ALIAS = "idp-test"; + private static final String USERNAME = "jdoe"; + private String realmId; + private String userId; + + @Override + public void createEnvironment(KeycloakSession s) { + RealmModel realm = createRealm(s, "realm"); + realm.setDefaultRole(s.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName())); + + this.realmId = realm.getId(); + + IdentityProviderFactory identityProviderFactory = (IdentityProviderFactory) s.getKeycloakSessionFactory() + .getProviderFactory(IdentityProvider.class, TestKeycloakOidcIdentityProviderFactory.ID); + + IdentityProviderModel identityProviderModel = identityProviderFactory.createConfig(); + identityProviderModel.setAlias(IDENTITY_PROVIDER_ALIAS); + realm.addIdentityProvider(identityProviderModel); + + userId = s.users().addUser(realm, USERNAME).getId(); + } + + @Override + public void cleanEnvironment(KeycloakSession s) { + s.realms().removeRealm(realmId); + } + + @Test + public void addFederatedIdentity() { + + List recordedEvents = new ArrayList<>(); + ProviderEventListener providerEventListener = event -> { + if (event instanceof FederatedIdentityModel.FederatedIdentityCreatedEvent) { + recordedEvents.add((FederatedIdentityModel.FederatedIdentityCreatedEvent) event); + } + }; + getFactory().register(providerEventListener); + try { + withRealm(realmId, (session, realm) -> { + FederatedIdentityModel federatedIdentity = new FederatedIdentityModel(IDENTITY_PROVIDER_ALIAS, userId, USERNAME); + UserModel user = session.users().getUserById(realm, userId); + session.users().addFederatedIdentity(realm, user, federatedIdentity); + + assertThat(recordedEvents, hasSize(1)); + FederatedIdentityModel.FederatedIdentityCreatedEvent event = recordedEvents.get(0); + assertThat(event.getKeycloakSession(), equalTo(session)); + assertThat(event.getRealm(), equalTo(realm)); + assertThat(event.getUser(), equalTo(user)); + assertThat(event.getFederatedIdentity(), equalTo(federatedIdentity)); + + return null; + }); + } finally { + getFactory().unregister(providerEventListener); + } + } + + @Test + public void removeFederatedIdentity() { + List recordedEvents = new ArrayList<>(); + ProviderEventListener providerEventListener = event -> { + if (event instanceof FederatedIdentityModel.FederatedIdentityRemovedEvent) { + recordedEvents.add((FederatedIdentityModel.FederatedIdentityRemovedEvent) event); + } + }; + getFactory().register(providerEventListener); + try { + withRealm(realmId, (session, realm) -> { + FederatedIdentityModel federatedIdentity = new FederatedIdentityModel(IDENTITY_PROVIDER_ALIAS, userId, USERNAME); + UserModel user = session.users().getUserById(realm, userId); + session.users().addFederatedIdentity(realm, user, federatedIdentity); + + session.users().removeFederatedIdentity(realm, user, IDENTITY_PROVIDER_ALIAS); + + assertThat(recordedEvents, hasSize(1)); + FederatedIdentityModel.FederatedIdentityRemovedEvent event = recordedEvents.get(0); + assertThat(event.getKeycloakSession(), equalTo(session)); + assertThat(event.getRealm(), equalTo(realm)); + assertThat(event.getUser(), equalTo(user)); + assertThat(event.getFederatedIdentity(), equalTo(federatedIdentity)); + + return null; + }); + } finally { + getFactory().unregister(providerEventListener); + } + } + +} diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/LegacyJpa.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/LegacyJpa.java index 16434553ae..15ac04aeb4 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/LegacyJpa.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/LegacyJpa.java @@ -17,6 +17,9 @@ package org.keycloak.testsuite.model.parameters; import org.keycloak.authorization.jpa.store.JPAAuthorizationStoreFactory; +import org.keycloak.broker.provider.IdentityProvider; +import org.keycloak.broker.provider.IdentityProviderFactory; +import org.keycloak.broker.provider.IdentityProviderSpi; import org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory; import org.keycloak.connections.jpa.JpaConnectionSpi; import org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory; @@ -68,6 +71,9 @@ public class LegacyJpa extends KeycloakModelParameters { .add(DBLockSpi.class) + //required for FederatedIdentityModel + .add(IdentityProviderSpi.class) + .build(); static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder() @@ -92,6 +98,9 @@ public class LegacyJpa extends KeycloakModelParameters { .add(MigrationProviderFactory.class) .add(LoginProtocolFactory.class) + //required for FederatedIdentityModel + .add(IdentityProviderFactory.class) + .build(); public LegacyJpa() {