-
Notifications
You must be signed in to change notification settings - Fork 428
Lazy Initialisation of ExternalIdentityMonitor #2928
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<? extends Principal> principals; | ||
| private AuthInfo authInfo; | ||
|
|
||
| private ExternalIdentityMonitor monitor; | ||
| private ExternalIdentityMonitor monitor; // do not access directly, use monitorSupplier | ||
| private Supplier<ExternalIdentityMonitor> 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<Str | |
| log.debug("No 'SupportedCredentials' configured. Using default implementation supporting 'SimpleCredentials'."); | ||
| } | ||
|
|
||
| monitor = WhiteboardUtils.getService(whiteboard, ExternalIdentityMonitor.class); | ||
| if (monitor == null) { | ||
| log.debug("No ExternalIdentityMonitor registered."); | ||
| monitor = ExternalIdentityMonitor.NOOP; | ||
| } | ||
| monitorSupplier = () -> { | ||
| 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); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } catch (CommitFailedException e) { | ||
| throw new SyncException("User synchronization failed during commit.", e); | ||
| } finally { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -803,4 +803,59 @@ public void testGetExternalIdentityFails() throws Exception { | |
| } | ||
| } | ||
|
|
||
| } | ||
| @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 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This largely duplicates |
||
| 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 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Login succeeds and |
||
| 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); | ||
| } | ||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oak-auth-external enforces 100% branch coverage (
mvn verify -pl oak-auth-external -Pcoveragepasses on trunk but fails on this PR at 0.9). The supplier's cachedmonitor != nullbranch is never exercised because each call site invokesget()only once—please add a test that hits the supplier twice in one login.