diff --git a/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java b/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java index 447cb45ec2c..56fdbf77541 100644 --- a/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java +++ b/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java @@ -63,6 +63,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -123,7 +124,8 @@ public class ExternalLoginModule extends AbstractLoginModule { private Set principals; private AuthInfo authInfo; - private ExternalIdentityMonitor monitor; + private ExternalIdentityMonitor monitor; // do not access directly, use monitorSupplier + private Supplier monitorSupplier; /** * Default constructor for the OSGIi LoginModuleFactory case and the default non-OSGi JAAS case. @@ -165,11 +167,16 @@ public void initialize(Subject subject, CallbackHandler callbackHandler, Map { + if (monitor == null) { + monitor = WhiteboardUtils.getService(whiteboard, ExternalIdentityMonitor.class); + if (monitor == null) { + log.debug("No ExternalIdentityMonitor registered."); + monitor = ExternalIdentityMonitor.NOOP; + } + } + return monitor; + }; } private void initializeIdpManager(@NotNull String idpName, @NotNull Whiteboard whiteboard) { @@ -294,7 +301,7 @@ public boolean login() throws LoginException { } catch (SyncException e) { log.error("SyncHandler {} throws sync exception for '{}'", syncHandler.getName(), logId, e); onError(); - monitor.syncFailed(e); + monitorSupplier.get().syncFailed(e); throw createLoginException(e, "Error while syncing user."); } } @@ -423,7 +430,7 @@ private void syncUser(@NotNull ExternalUser user, @Nullable UserManager userMgr) timer.mark("commit"); } log.debug("syncUser({}) {}, status: {}", user.getId(), timer, syncResult.getStatus()); - monitor.doneSyncExternalIdentity(watch.elapsed(NANOSECONDS), syncResult, numAttempt-1); + monitorSupplier.get().doneSyncExternalIdentity(watch.elapsed(NANOSECONDS), syncResult, numAttempt-1); success = true; } catch (CommitFailedException e) { log.warn("User synchronization failed during commit: {}. (attempt {}/{})", e, numAttempt, MAX_SYNC_ATTEMPTS); @@ -454,7 +461,7 @@ private void validateUser(@NotNull String id, @NotNull UserManager userMgr) thro root.commit(); timer.mark("commit"); log.debug("validateUser({}) {}", id, timer); - monitor.doneSyncId(watch.elapsed(NANOSECONDS), syncResult); + monitorSupplier.get().doneSyncId(watch.elapsed(NANOSECONDS), syncResult); } catch (CommitFailedException e) { throw new SyncException("User synchronization failed during commit.", e); } finally { diff --git a/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java b/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java index b6f3f81a177..111c666501a 100644 --- a/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java +++ b/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java @@ -803,4 +803,59 @@ public void testGetExternalIdentityFails() throws Exception { } } -} \ No newline at end of file + @Test + public void testMonitorNotResolvedWhenLoginFailsBeforeSync() throws Exception { + ExternalIdentityProvider idp = mock(ExternalIdentityProvider.class, withSettings().extraInterfaces(CredentialsSupport.class)); + when(idp.getName()).thenReturn(DEFAULT_IDP_NAME); + when(((CredentialsSupport) idp).getUserId(any(Credentials.class))).thenReturn(null); + when(((CredentialsSupport) idp).getCredentialClasses()).thenReturn(Set.of(SimpleCredentials.class)); + when(extIPMgr.getProvider(DEFAULT_IDP_NAME)).thenReturn(idp); + when(syncManager.getSyncHandler("syncHandler")).thenReturn(new DefaultSyncHandler(new DefaultSyncConfigImpl().setName("syncHandler"))); + + wb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap()); + wb.register(SyncManager.class, syncManager, Collections.emptyMap()); + + CallbackHandler cbh = createCallbackHandler(wb, getContentRepository(), getSecurityProvider(), new SimpleCredentials("testUser", new char[0])); + loginModule.initialize(new Subject(), cbh, new HashMap<>(), Map.of(PARAM_IDP_NAME, DEFAULT_IDP_NAME, PARAM_SYNC_HANDLER_NAME, "syncHandler")); + assertFalse(loginModule.login()); + + verify(wb, never()).track(ExternalIdentityMonitor.class); + verifyNoInteractions(externalIdentityMonitor); + } + + @Test + public void testMonitorResolvedExactlyOnceOnSuccessfulSync() throws Exception { + when(extIPMgr.getProvider(DEFAULT_IDP_NAME)).thenReturn(new TestIdentityProvider()); + when(syncManager.getSyncHandler("syncHandler")).thenReturn(new DefaultSyncHandler(new DefaultSyncConfigImpl().setName("syncHandler"))); + + wb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap()); + wb.register(SyncManager.class, syncManager, Collections.emptyMap()); + + Credentials crds = new SimpleCredentials(ID_TEST_USER, new char[0]); + CallbackHandler cbh = createCallbackHandler(wb, getContentRepository(), getSecurityProvider(), crds); + loginModule.initialize(new Subject(), cbh, new HashMap<>(), Map.of(PARAM_IDP_NAME, DEFAULT_IDP_NAME, PARAM_SYNC_HANDLER_NAME, "syncHandler")); + assertTrue(loginModule.login()); + + verify(wb, times(1)).track(ExternalIdentityMonitor.class); + } + + @Test + public void testNoopMonitorUsedWhenMonitorNotRegistered() throws Exception { + Whiteboard freshWb = spy(new DefaultWhiteboard()); + when(extIPMgr.getProvider(DEFAULT_IDP_NAME)).thenReturn(new TestIdentityProvider()); + when(syncManager.getSyncHandler("syncHandler")).thenReturn(new DefaultSyncHandler(new DefaultSyncConfigImpl().setName("syncHandler"))); + + freshWb.register(ExternalIdentityProviderManager.class, extIPMgr, Collections.emptyMap()); + freshWb.register(SyncManager.class, syncManager, Collections.emptyMap()); + + Credentials crds = new SimpleCredentials(ID_TEST_USER, new char[0]); + CallbackHandler cbh = createCallbackHandler(freshWb, getContentRepository(), getSecurityProvider(), crds); + + ExternalLoginModule lm = new ExternalLoginModule(); + lm.initialize(new Subject(), cbh, new HashMap<>(), Map.of(PARAM_IDP_NAME, DEFAULT_IDP_NAME, PARAM_SYNC_HANDLER_NAME, "syncHandler")); + assertTrue(lm.login()); + + verify(freshWb, times(1)).track(ExternalIdentityMonitor.class); + } + +}