diff --git a/docker-compose.yml b/docker-compose.yml index f14bf82..892ddd2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ services: image: quay.io/keycloak/keycloak:21.0 command: [ 'start-dev', '--debug' ] volumes: - - ./target/keycloak-event-listener-sns-1.0.2-SNAPSHOT.jar:/opt/keycloak/providers/keycloak-event-listener-sns.jar + - ./target/keycloak-event-listener-sns-1.1.1-SNAPSHOT.jar:/opt/keycloak/providers/keycloak-event-listener-sns.jar environment: - KEYCLOAK_ADMIN=admin - KEYCLOAK_ADMIN_PASSWORD=admin diff --git a/pom.xml b/pom.xml index 77a8f54..cc62561 100644 --- a/pom.xml +++ b/pom.xml @@ -65,12 +65,13 @@ 3.5.0 3.4.1 - 20.0.5 + 21.1.2 1.12.578 5.9.2 3.24.2 3.5.3.Final 5.1.1 + 1.1 @@ -250,7 +251,12 @@ ${mockito.version} test - + + javax.transaction + jta + ${jta.version} + test + diff --git a/src/main/java/fr/redfroggy/keycloak/SnsEventListenerProvider.java b/src/main/java/fr/redfroggy/keycloak/SnsEventListenerProvider.java index 691a7dc..d5aad66 100644 --- a/src/main/java/fr/redfroggy/keycloak/SnsEventListenerProvider.java +++ b/src/main/java/fr/redfroggy/keycloak/SnsEventListenerProvider.java @@ -4,28 +4,24 @@ import org.keycloak.events.EventListenerProvider; import org.keycloak.events.EventListenerTransaction; import org.keycloak.events.admin.AdminEvent; -import org.keycloak.models.KeycloakTransactionManager; -import org.keycloak.models.RealmProvider; -import org.keycloak.models.UserModel; -import org.keycloak.models.UserProvider; +import org.keycloak.models.*; + +import static org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction; public class SnsEventListenerProvider implements EventListenerProvider { private final SnsEventPublisher snsEventPublisher; + private final KeycloakSessionFactory sessionFactory; private final EventListenerTransaction transaction = new EventListenerTransaction(this::sendAdminEvent, this::sendEvent); - private final UserProvider userProvider; - - private final RealmProvider realmProvider; - - public SnsEventListenerProvider(SnsEventPublisher snsEventPublisher, KeycloakTransactionManager transactionManager, - UserProvider userProvider, RealmProvider realmProvider) { + public SnsEventListenerProvider(SnsEventPublisher snsEventPublisher, + KeycloakSessionFactory sessionFactory, + KeycloakTransactionManager transactionManager) { this.snsEventPublisher = snsEventPublisher; + this.sessionFactory = sessionFactory; transactionManager.enlistAfterCompletion(transaction); - this.userProvider = userProvider; - this.realmProvider = realmProvider; } @Override @@ -44,19 +40,23 @@ public void close() { } private void sendEvent(Event event) { - snsEventPublisher.sendEvent(new SnsEvent(event, getUsername(event.getRealmId(), event.getUserId()))); + runJobInTransaction(sessionFactory, session -> + snsEventPublisher.sendEvent(new SnsEvent(event, getUsername(session.realms(), session.users(), event.getRealmId(), event.getUserId())))); } private void sendAdminEvent(AdminEvent adminEvent, boolean includeRepresentation) { - String adminUserId = null; - if (adminEvent.getAuthDetails() != null) { - adminUserId = adminEvent.getAuthDetails().getUserId(); - } - snsEventPublisher.sendAdminEvent(new SnsAdminEvent(adminEvent, getUsername(adminEvent.getRealmId(), adminUserId))); - + runJobInTransaction(sessionFactory, session -> { + String adminUserId = null; + if (adminEvent.getAuthDetails() != null) { + adminUserId = adminEvent.getAuthDetails().getUserId(); + } + snsEventPublisher.sendAdminEvent( + new SnsAdminEvent(adminEvent, getUsername(session.realms(), session.users(), adminEvent.getRealmId(), adminUserId))); + } + ); } - private String getUsername(String realmId, String userId) { + private String getUsername(RealmProvider realmProvider, UserProvider userProvider, String realmId, String userId) { UserModel user; if (userId != null) { user = userProvider.getUserById(realmProvider.getRealm(realmId), userId); diff --git a/src/main/java/fr/redfroggy/keycloak/SnsEventListenerProviderFactory.java b/src/main/java/fr/redfroggy/keycloak/SnsEventListenerProviderFactory.java index 4d5b8e2..eaee8e6 100644 --- a/src/main/java/fr/redfroggy/keycloak/SnsEventListenerProviderFactory.java +++ b/src/main/java/fr/redfroggy/keycloak/SnsEventListenerProviderFactory.java @@ -28,7 +28,9 @@ public void close() { public EventListenerProvider create(KeycloakSession session) { AmazonSNSAsync snsClient = AmazonSNSAsyncClientBuilder.standard().build(); ObjectMapper mapper = new ObjectMapper(); - return new SnsEventListenerProvider(new SnsEventPublisher(snsClient, snsEventListenerConfiguration, mapper), session.getTransactionManager(), session.users(), session.realms()); + return new SnsEventListenerProvider(new SnsEventPublisher(snsClient, snsEventListenerConfiguration, mapper), + session.getKeycloakSessionFactory(), + session.getTransactionManager()); } @Override diff --git a/src/test/java/fr/redfroggy/keycloak/SnsEventListenerProviderTest.java b/src/test/java/fr/redfroggy/keycloak/SnsEventListenerProviderTest.java index 8191493..4a58ebf 100644 --- a/src/test/java/fr/redfroggy/keycloak/SnsEventListenerProviderTest.java +++ b/src/test/java/fr/redfroggy/keycloak/SnsEventListenerProviderTest.java @@ -9,11 +9,7 @@ import org.keycloak.events.EventListenerTransaction; import org.keycloak.events.admin.AdminEvent; import org.keycloak.events.admin.AuthDetails; -import org.keycloak.models.KeycloakTransactionManager; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RealmProvider; -import org.keycloak.models.UserModel; -import org.keycloak.models.UserProvider; +import org.keycloak.models.*; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; @@ -47,6 +43,12 @@ class SnsEventListenerProviderTest { @Mock private KeycloakTransactionManager transactionManagerMock; + @Mock + private KeycloakSessionFactory sessionFactory; + + @Mock + private KeycloakSession session; + @Mock private SnsEventPublisher snsEventPublisherMock; @@ -76,6 +78,11 @@ void shouldAddEventToTransaction(){ when(realmProviderMock.getRealm("realmId")).thenReturn(realmMock); when(userProviderMock.getUserById(realmMock, "userId")).thenReturn(userMock); when(userMock.getUsername()).thenReturn("username"); + when(sessionFactory.create()).thenReturn(session); + when(session.getTransactionManager()).thenReturn(transactionManagerMock); + when(session.realms()).thenReturn(realmProviderMock); + when(session.users()).thenReturn(userProviderMock); + snsEventListenerProvider.onEvent(eventMock); verify(transactionManagerMock).enlistAfterCompletion(transactionCaptor.capture()); EventListenerTransaction transaction = transactionCaptor.getValue(); @@ -91,6 +98,9 @@ void shouldAddEventToTransaction(){ void shouldAddEventToTransactionWithUsernameToNullBecauseUserIdNull(){ when(eventMock.getUserId()).thenReturn(null); when(eventMock.getRealmId()).thenReturn("realmId"); + when(sessionFactory.create()).thenReturn(session); + when(session.getTransactionManager()).thenReturn(transactionManagerMock); + snsEventListenerProvider.onEvent(eventMock); verify(transactionManagerMock).enlistAfterCompletion(transactionCaptor.capture()); EventListenerTransaction transaction = transactionCaptor.getValue(); @@ -109,6 +119,11 @@ void shouldAddEventToTransactionWithUsernameToNullBecauseUserNull(){ when(realmProviderMock.getRealm("realmId")).thenReturn(realmMock); when(userProviderMock.getUserById(realmMock, "userId")).thenReturn(null); snsEventListenerProvider.onEvent(eventMock); + when(sessionFactory.create()).thenReturn(session); + when(session.getTransactionManager()).thenReturn(transactionManagerMock); + when(session.realms()).thenReturn(realmProviderMock); + when(session.users()).thenReturn(userProviderMock); + verify(transactionManagerMock).enlistAfterCompletion(transactionCaptor.capture()); EventListenerTransaction transaction = transactionCaptor.getValue(); transaction.begin(); @@ -127,6 +142,11 @@ void shouldAddAdminEventToTransaction(){ when(realmProviderMock.getRealm("realmId")).thenReturn(realmMock); when(userProviderMock.getUserById(realmMock, "userId")).thenReturn(userMock); when(userMock.getUsername()).thenReturn("username"); + when(sessionFactory.create()).thenReturn(session); + when(session.getTransactionManager()).thenReturn(transactionManagerMock); + when(session.realms()).thenReturn(realmProviderMock); + when(session.users()).thenReturn(userProviderMock); + snsEventListenerProvider.onEvent(adminEventMock, true); verify(transactionManagerMock).enlistAfterCompletion(transactionCaptor.capture()); EventListenerTransaction transaction = transactionCaptor.getValue(); @@ -141,6 +161,9 @@ void shouldAddAdminEventToTransaction(){ @Test void shouldAddAdminEventToTransactionWithUsernameToNullBecauseAuthDetailsNull(){ when(adminEventMock.getAuthDetails()).thenReturn(null); + when(sessionFactory.create()).thenReturn(session); + when(session.getTransactionManager()).thenReturn(transactionManagerMock); + snsEventListenerProvider.onEvent(adminEventMock, true); verify(transactionManagerMock).enlistAfterCompletion(transactionCaptor.capture()); EventListenerTransaction transaction = transactionCaptor.getValue();