You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by an...@apache.org on 2021/03/09 15:53:40 UTC

svn commit: r1887377 - in /jackrabbit/oak/trunk/oak-auth-external/src: main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ main/java/org/a...

Author: angela
Date: Tue Mar  9 15:53:40 2021
New Revision: 1887377

URL: http://svn.apache.org/viewvc?rev=1887377&view=rev
Log:
OAK-9368 : Monitoring for external authentication 

Added:
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitor.java   (with props)
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitorImpl.java   (with props)
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitorImplTest.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfiguration.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfigurationTest.java

Modified: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java?rev=1887377&r1=1887376&r2=1887377&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java Tue Mar  9 15:53:40 2021
@@ -16,6 +16,7 @@
  */
 package org.apache.jackrabbit.oak.spi.security.authentication.external.impl;
 
+import com.google.common.base.Stopwatch;
 import com.google.common.collect.Iterables;
 import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.oak.api.AuthInfo;
@@ -42,6 +43,7 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncManager;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncResult;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncedIdentity;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.monitor.ExternalIdentityMonitor;
 import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
 import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils;
 import org.jetbrains.annotations.NotNull;
@@ -59,9 +61,12 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
 /**
  * {@code ExternalLoginModule} implements a {@code LoginModule} that uses an
  * {@link ExternalIdentityProvider} for authentication.
@@ -116,6 +121,8 @@ public class ExternalLoginModule extends
     private Set<? extends Principal> principals;
     private AuthInfo authInfo;
 
+    private ExternalIdentityMonitor monitor;
+
     /**
      * Default constructor for the OSGIi LoginModuleFactory case and the default non-OSGi JAAS case.
      */
@@ -186,6 +193,12 @@ public class ExternalLoginModule extends
         } else {
             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;
+        }
     }
 
     @Override
@@ -268,6 +281,7 @@ public class ExternalLoginModule extends
         } catch (SyncException e) {
             log.error("SyncHandler {} throws sync exception for '{}'", syncHandler.getName(), logId, e);
             onError();
+            monitor.syncFailed(e);
             throw createLoginException(e, "Error while syncing user.");
         }
     }
@@ -364,6 +378,7 @@ public class ExternalLoginModule extends
     private void syncUser(@NotNull ExternalUser user, @Nullable UserManager userMgr) throws SyncException {
         Root root = getRootOrThrow();
         UserManager userManager = getUsermanagerOrThrow(userMgr);
+        Stopwatch watch = Stopwatch.createStarted();
         int numAttempt = 0;
         while (numAttempt++ < MAX_SYNC_ATTEMPTS) {
             SyncContext context = syncHandler.createContext(idp, userManager, new ValueFactoryImpl(root, NamePathMapper.DEFAULT));
@@ -376,6 +391,7 @@ public class ExternalLoginModule extends
                     timer.mark("commit");
                 }
                 debug("syncUser({}) {}, status: {}", user.getId(), timer.getString(), syncResult.getStatus().toString());
+                monitor.doneSyncExternalIdentity(watch.elapsed(NANOSECONDS), syncResult, numAttempt-1);
                 return;
             } catch (CommitFailedException e) {
                 log.warn("User synchronization failed during commit: {}. (attempt {}/{})", e, numAttempt, MAX_SYNC_ATTEMPTS);
@@ -396,13 +412,15 @@ public class ExternalLoginModule extends
         UserManager userManager = getUsermanagerOrThrow(userMgr);
         SyncContext context = syncHandler.createContext(idp, userManager, new ValueFactoryImpl(root, NamePathMapper.DEFAULT));
         try {
+            Stopwatch watch = Stopwatch.createStarted();
             DebugTimer timer = new DebugTimer();
             context = syncHandler.createContext(idp, userManager, new ValueFactoryImpl(root, NamePathMapper.DEFAULT));
-            context.sync(id);
+            SyncResult syncResult = context.sync(id);
             timer.mark("sync");
             root.commit();
             timer.mark("commit");
             debug("validateUser({}) {}", id, timer.getString());
+            monitor.doneSyncId(watch.elapsed(NANOSECONDS), syncResult);
         } catch (CommitFailedException e) {
             throw new SyncException("User synchronization failed during commit.", e);
         } finally {

Added: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitor.java?rev=1887377&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitor.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitor.java Tue Mar  9 15:53:40 2021
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.spi.security.authentication.external.impl.monitor;
+
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentity;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncException;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncResult;
+import org.apache.jackrabbit.oak.stats.Monitor;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.Map;
+
+public interface ExternalIdentityMonitor extends Monitor<ExternalIdentityMonitor> {
+
+    ExternalIdentityMonitor NOOP = new ExternalIdentityMonitor() {};
+
+    /**
+     * Mark the successful completion of {@link org.apache.jackrabbit.oak.spi.security.authentication.external.SyncContext#sync(ExternalIdentity)}.
+     *
+     * @param timeTakenNanos Time in nanoseconds spend to complete {@link org.apache.jackrabbit.oak.spi.security.authentication.external.SyncContext#sync(ExternalIdentity)}
+     * @param result The result of the sync operation.
+     * @param retryCount The number of retries needed to complete the sync.
+     */
+    default void doneSyncExternalIdentity(long timeTakenNanos, @NotNull SyncResult result, int retryCount) {};
+
+    /**
+     * Mark the successful completion of {@link org.apache.jackrabbit.oak.spi.security.authentication.external.SyncContext#sync(String)}.
+     *
+     * @param timeTakenNanos Time in nanoseconds spend to complete {@link org.apache.jackrabbit.oak.spi.security.authentication.external.SyncContext#sync(String)}
+     * @param result The result of the sync operation.
+     */
+    default void doneSyncId(long timeTakenNanos, @NotNull SyncResult result) {};
+
+    /**
+     * Mark the failure of a sync operation that resulted in the given {@link SyncException}.
+     *
+     * @param syncException The sync exception.
+     */
+    default void syncFailed(@NotNull SyncException syncException) {};
+
+    @Override
+    @NotNull
+    default Class<ExternalIdentityMonitor> getMonitorClass() {
+        return ExternalIdentityMonitor.class;
+    }
+
+    @Override
+    @NotNull
+    default Map<Object, Object> getMonitorProperties() {
+        return Collections.emptyMap();
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitorImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitorImpl.java?rev=1887377&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitorImpl.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitorImpl.java Tue Mar  9 15:53:40 2021
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.spi.security.authentication.external.impl.monitor;
+
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncException;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncResult;
+import org.apache.jackrabbit.oak.stats.MeterStats;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.apache.jackrabbit.oak.stats.StatsOptions;
+import org.apache.jackrabbit.oak.stats.TimerStats;
+import org.jetbrains.annotations.NotNull;
+
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+public class ExternalIdentityMonitorImpl implements ExternalIdentityMonitor {
+
+    private final TimerStats syncTimer;
+    private final MeterStats syncRetries;
+    private final TimerStats syncIdTimer;
+    private final MeterStats syncFailed;
+
+    public ExternalIdentityMonitorImpl(@NotNull StatisticsProvider statisticsProvider) {
+        syncTimer = statisticsProvider.getTimer("security.authentication.external.sync_external_identity.timer", StatsOptions.METRICS_ONLY);
+        syncRetries = statisticsProvider.getMeter("security.authentication.external.sync_external_identity.retries", StatsOptions.DEFAULT);
+        syncIdTimer = statisticsProvider.getTimer("security.authentication.external.sync_id.timer", StatsOptions.METRICS_ONLY);
+        syncFailed = statisticsProvider.getMeter("security.authentication.external.sync.failed", StatsOptions.DEFAULT);
+    }
+
+    @Override
+    public void doneSyncExternalIdentity(long timeTakenNanos, @NotNull SyncResult result, int retryCount) {
+        // note: currently the sync-result is ignored. further improvements might choose to exclude certain
+        // results or refine the stats gather by result type.
+        syncTimer.update(timeTakenNanos, NANOSECONDS);
+        if (retryCount > 0){
+            syncRetries.mark(retryCount);
+        }
+    }
+
+    @Override
+    public void doneSyncId(long timeTakenNanos, @NotNull SyncResult result) {
+        // note: currently the sync-result is ignored. further improvements might choose to exclude certain
+        // results or refine the stats gather by result type.
+        syncIdTimer.update(timeTakenNanos, NANOSECONDS);
+    }
+
+    @Override
+    public void syncFailed(@NotNull SyncException syncException) {
+        syncFailed.mark();
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitorImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfiguration.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfiguration.java?rev=1887377&r1=1887376&r2=1887377&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfiguration.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfiguration.java Tue Mar  9 15:53:40 2021
@@ -42,12 +42,16 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.apache.jackrabbit.oak.spi.security.SecurityConfiguration;
 import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalIdentityConstants;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.monitor.ExternalIdentityMonitor;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.monitor.ExternalIdentityMonitorImpl;
 import org.apache.jackrabbit.oak.spi.security.principal.EmptyPrincipalProvider;
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration;
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalManagerImpl;
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
 import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
 import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter;
+import org.apache.jackrabbit.oak.stats.Monitor;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
 import org.jetbrains.annotations.NotNull;
 import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
@@ -85,6 +89,8 @@ public class ExternalPrincipalConfigurat
     private SyncConfigTracker syncConfigTracker;
     private SyncHandlerMappingTracker syncHandlerMappingTracker;
 
+    private ExternalIdentityMonitor monitor = ExternalIdentityMonitor.NOOP;
+
     @SuppressWarnings("UnusedDeclaration")
     public ExternalPrincipalConfiguration() {
         super();
@@ -137,6 +143,12 @@ public class ExternalPrincipalConfigurat
         return Collections.singletonList(new ExternalIdentityImporter());
     }
 
+    @Override
+    public @NotNull Iterable<Monitor<?>> getMonitors(@NotNull StatisticsProvider statisticsProvider) {
+        monitor = new ExternalIdentityMonitorImpl(statisticsProvider);
+        return Collections.singleton(monitor);
+    }
+
     @NotNull
     @Override
     public List<ThreeWayConflictHandler> getConflictHandlers() {

Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java?rev=1887377&r1=1887376&r2=1887377&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleTest.java Tue Mar  9 15:53:40 2021
@@ -19,6 +19,7 @@ package org.apache.jackrabbit.oak.spi.se
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
+import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.oak.AbstractSecurityTest;
 import org.apache.jackrabbit.oak.api.AuthInfo;
@@ -39,7 +40,9 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityProviderManager;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncException;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncManager;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncResult;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.TestIdentityProvider;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.monitor.ExternalIdentityMonitor;
 import org.apache.jackrabbit.oak.spi.security.principal.EmptyPrincipalProvider;
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration;
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
@@ -49,6 +52,7 @@ import org.apache.jackrabbit.oak.spi.whi
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 
 import javax.jcr.Credentials;
@@ -89,8 +93,8 @@ import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.withSettings;
 
@@ -102,13 +106,23 @@ public class ExternalLoginModuleTest ext
     private final ExternalIdentityProviderManager extIPMgr = mock(ExternalIdentityProviderManager.class);
     private final SyncManager syncManager = mock(SyncManager.class);
     private final LoginModuleMonitor monitor = mock(LoginModuleMonitor.class);
+    private final ExternalIdentityMonitor externalIdentityMonitor = mock(ExternalIdentityMonitor.class);
+
+    @Before
+    public void before() throws Exception {
+        super.before();
+        wb.register(ExternalIdentityMonitor.class, externalIdentityMonitor, Collections.emptyMap());
+    }
 
     @After
-    public void after() {
-        clearInvocations(monitor, syncManager, extIPMgr, wb);
+    public void after() throws Exception {
+        try {
+            clearInvocations(externalIdentityMonitor, monitor, syncManager, extIPMgr);
+        } finally {
+            super.after();
+        }
     }
 
-    @NotNull
     private CallbackHandler createCallbackHandler(@Nullable Whiteboard wb, @Nullable ContentRepository contentRepository, @Nullable SecurityProvider securityProvider, @Nullable Credentials creds) {
         return callbacks -> {
             for (Callback cb : callbacks) {
@@ -120,13 +134,19 @@ public class ExternalLoginModuleTest ext
                     ((RepositoryCallback) cb).setLoginModuleMonitor(monitor);
                 } else if (cb instanceof CredentialsCallback) {
                     ((CredentialsCallback) cb).setCredentials(creds);
-                }else {
+                } else {
                     throw new UnsupportedCallbackException(cb);
                 }
             }
         };
     }
 
+    private void verifySyncException(@NotNull Exception e) {
+        assertTrue(e.getCause() instanceof SyncException);
+        verify(externalIdentityMonitor).syncFailed((SyncException) e.getCause());
+        verifyNoMoreInteractions(externalIdentityMonitor);
+    }
+
     @Test
     public void testInitializeMissingWhiteboard() throws LoginException {
         loginModule.setIdpManager(extIPMgr);
@@ -383,7 +403,7 @@ public class ExternalLoginModuleTest ext
         try {
             loginModule.login();
         } catch (LoginException e) {
-            assertTrue(e.getCause() instanceof SyncException);
+            verifySyncException(e);
             throw e;
         } finally {
             verify(monitor, times(2)).loginError();
@@ -411,7 +431,7 @@ public class ExternalLoginModuleTest ext
         try {
             loginModule.login();
         } catch (LoginException e) {
-            assertTrue(e.getCause() instanceof SyncException);
+            verifySyncException(e);
             throw e;
         } finally {
             verify(monitor, times(2)).loginError();
@@ -444,7 +464,7 @@ public class ExternalLoginModuleTest ext
         try {
             loginModule.login();
         } catch (LoginException e) {
-            assertTrue(e.getCause() instanceof SyncException);
+            verifySyncException(e);
             throw e;
         } finally {
             verify(monitor).loginError();
@@ -477,6 +497,7 @@ public class ExternalLoginModuleTest ext
         assertFalse(loginModule.commit());
         assertFalse(loginModule.logout());
 
+        verify(externalIdentityMonitor).doneSyncId(anyLong(), any(SyncResult.class));
         verifyNoInteractions(monitor);
     }
 
@@ -508,7 +529,7 @@ public class ExternalLoginModuleTest ext
         try {
             loginModule.login();
         } catch (LoginException e) {
-            assertTrue(e.getCause() instanceof SyncException);
+            verifySyncException(e);
             throw e;
         } finally {
             verify(monitor).loginError();
@@ -535,4 +556,30 @@ public class ExternalLoginModuleTest ext
             verifyNoMoreInteractions(monitor);
         }
     }
+
+    @Test
+    public void testSyncUser() 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("testUser", new char[0]);
+        CallbackHandler cbh = createCallbackHandler(wb, getContentRepository(), getSecurityProvider(), crds);
+
+        loginModule.initialize(new Subject(), cbh, new HashMap<>(), ImmutableMap.of(PARAM_IDP_NAME, DEFAULT_IDP_NAME, PARAM_SYNC_HANDLER_NAME, "syncHandler"));
+        assertTrue(loginModule.login());
+        root.refresh();
+        Authorizable a = getUserManager(root).getAuthorizable("testUser");
+        assertNotNull(a);
+        assertTrue(a.hasProperty(REP_EXTERNAL_ID));
+
+        assertTrue(loginModule.commit());
+        assertTrue(loginModule.logout());
+
+        verify(externalIdentityMonitor).doneSyncExternalIdentity(anyLong(), any(SyncResult.class), anyInt());
+        verify(monitor).principalsCollected(anyLong(), anyInt());
+        verifyNoMoreInteractions(externalIdentityMonitor, monitor);
+    }
 }
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitorImplTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitorImplTest.java?rev=1887377&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitorImplTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitorImplTest.java Tue Mar  9 15:53:40 2021
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.oak.spi.security.authentication.external.impl.monitor;
+
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncException;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncResult;
+import org.apache.jackrabbit.oak.stats.CounterStats;
+import org.apache.jackrabbit.oak.stats.MeterStats;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.apache.jackrabbit.oak.stats.StatsOptions;
+import org.apache.jackrabbit.oak.stats.TimerStats;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class ExternalIdentityMonitorImplTest {
+
+    private final MeterStats meter = mock(MeterStats.class);
+    private final TimerStats timer = mock(TimerStats.class);
+    private final CounterStats counter = mock(CounterStats.class);
+
+    private StatisticsProvider statisticsProvider;
+    private ExternalIdentityMonitorImpl monitor;
+
+    @Before
+    public void before() {
+        statisticsProvider = mockStatisticsProvider();
+        monitor = new ExternalIdentityMonitorImpl(statisticsProvider);
+    }
+
+    @After
+    public void after() {
+        clearInvocations(meter, timer, statisticsProvider);
+    }
+
+    private StatisticsProvider mockStatisticsProvider() {
+        StatisticsProvider statisticsProvider = mock(StatisticsProvider.class);
+        when(statisticsProvider.getMeter(anyString(), any(StatsOptions.class))).thenReturn(meter);
+        when(statisticsProvider.getTimer(anyString(), any(StatsOptions.class))).thenReturn(timer);
+        when(statisticsProvider.getCounterStats(anyString(), any(StatsOptions.class))).thenReturn(counter);
+        return statisticsProvider;
+    }
+
+    @Test
+    public void testConstructor() {
+        verify(statisticsProvider, times(2)).getMeter(anyString(), any(StatsOptions.class));
+        verify(statisticsProvider, times(2)).getTimer(anyString(), any(StatsOptions.class));
+        verifyNoMoreInteractions(statisticsProvider);
+        verifyNoInteractions(meter);
+        verifyNoInteractions(timer);
+    }
+
+    @Test
+    public void testDoneSyncExternalIdentity() {
+        monitor.doneSyncExternalIdentity(20, mock(SyncResult.class), 34);
+        verify(timer).update(20, NANOSECONDS);
+        verify(meter).mark(34);
+        verifyNoMoreInteractions(meter, timer);
+    }
+
+    @Test
+    public void testDoneSyncId() {
+        monitor.doneSyncId(5,  mock(SyncResult.class));
+        verify(timer).update(5, NANOSECONDS);
+        verifyNoMoreInteractions(timer);
+        verifyNoInteractions(meter);
+    }
+
+    @Test
+    public void testSyncFailed() {
+        monitor.syncFailed(mock(SyncException.class));
+        verify(meter).mark();
+        verifyNoMoreInteractions(meter);
+        verifyNoInteractions(timer);
+    }
+
+    @Test
+    public void testGetMonitorClass() {
+        assertSame(ExternalIdentityMonitor.class, monitor.getMonitorClass());
+    }
+
+    @Test
+    public void testGetMonitorProperties() {
+        assertTrue(monitor.getMonitorProperties().isEmpty());
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/monitor/ExternalIdentityMonitorImplTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfigurationTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfigurationTest.java?rev=1887377&r1=1887376&r2=1887377&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfigurationTest.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalPrincipalConfigurationTest.java Tue Mar  9 15:53:40 2021
@@ -25,6 +25,7 @@ import java.util.Map;
 import javax.jcr.ValueFactory;
 
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
 import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.oak.api.ContentSession;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
@@ -45,9 +46,12 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalIdentityConstants;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalLoginModuleFactory;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.SyncHandlerMapping;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.impl.monitor.ExternalIdentityMonitorImpl;
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration;
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
 import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter;
+import org.apache.jackrabbit.oak.stats.Monitor;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
 import org.apache.sling.testing.mock.osgi.MockOsgi;
 import org.jetbrains.annotations.NotNull;
 import org.junit.Ignore;
@@ -189,6 +193,13 @@ public class ExternalPrincipalConfigurat
     }
 
     @Test
+    public void testGetMonitors() {
+        Iterable<Monitor<?>> monitors = externalPrincipalConfiguration.getMonitors(StatisticsProvider.NOOP);
+        assertEquals(1, Iterables.size(monitors));
+        assertTrue(monitors.iterator().next() instanceof ExternalIdentityMonitorImpl);
+    }
+
+    @Test
     public void testDeactivateWithNullTrackers() {
         ExternalPrincipalConfiguration epc = new ExternalPrincipalConfiguration(getSecurityProvider());
         MockOsgi.deactivate(epc, context.bundleContext(), Collections.emptyMap());