Author: dj
Date: Thu Aug 18 10:04:48 2016
New Revision: 1756752

URL: http://svn.apache.org/viewvc?rev=1756752&view=rev
Log:
OAK-4679 : Backport OAK-4119, OAK-4101, OAK-4087 and OAK-4344
- backported OAK-4101

Added:
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContext.java
      - copied unchanged from r1744292, 
jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContext.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalIdentityConstants.java
      - copied unchanged from r1744292, 
jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalIdentityConstants.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/
      - copied from r1744292, 
jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/AbstractExternalAuthTest.java
      - copied, changed from r1740114, 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/AbstractExternalAuthTest.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleDynamicMembershipTest.java
      - copied unchanged from r1744292, 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleDynamicMembershipTest.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestSecurityProvider.java
      - copied, changed from r1744292, 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestSecurityProvider.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncConfigTest.java
      - copied unchanged from r1740114, 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncConfigTest.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncResultImplTest.java
      - copied unchanged from r1740114, 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncResultImplTest.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncedIdentityTest.java
      - copied unchanged from r1740114, 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncedIdentityTest.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/RepExternalIdTest.java
      - copied unchanged from r1740114, 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/RepExternalIdTest.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/CustomCredentialsSupportTest.java
      - copied, changed from r1735141, 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/CustomCredentialsSupportTest.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandlerTest.java
      - copied, changed from r1739712, 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandlerTest.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContextTest.java
      - copied, changed from r1744292, 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DynamicSyncContextTest.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/
      - copied from r1744292, 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/
    
jackrabbit/oak/branches/1.4/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/
      - copied from r1735141, 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/
    
jackrabbit/oak/branches/1.4/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/
      - copied from r1735141, 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/
    
jackrabbit/oak/branches/1.4/oak-core/src/test/java/org/apache/jackrabbit/oak/util/TreeUtilTest.java
      - copied unchanged from r1742077, 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/util/TreeUtilTest.java
    
jackrabbit/oak/branches/1.4/oak-doc/src/site/markdown/security/authentication/defaultusersync.md
      - copied unchanged from r1740250, 
jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/defaultusersync.md
Removed:
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/DefaultSyncHandlerTest.java
Modified:
    jackrabbit/oak/branches/1.4/   (props changed)
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncConfig.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContext.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/package-info.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncConfigImpl.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandler.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTest.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTestBase.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContextTest.java
    
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/principal/ExternalIdentityImporterTest.java
    
jackrabbit/oak/branches/1.4/oak-auth-ldap/src/test/java/org/apache/jackrabbit/oak/security/authentication/ldap/LdapLoginTestBase.java
    
jackrabbit/oak/branches/1.4/oak-core/src/main/java/org/apache/jackrabbit/oak/util/TreeUtil.java
    
jackrabbit/oak/branches/1.4/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java
    
jackrabbit/oak/branches/1.4/oak-doc/src/site/markdown/security/authentication.md
    
jackrabbit/oak/branches/1.4/oak-doc/src/site/markdown/security/authentication/externalloginmodule.md
    
jackrabbit/oak/branches/1.4/oak-doc/src/site/markdown/security/authentication/usersync.md
    
jackrabbit/oak/branches/1.4/oak-doc/src/site/markdown/security/principal/principalprovider.md

Propchange: jackrabbit/oak/branches/1.4/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Thu Aug 18 10:04:48 2016
@@ -1,3 +1,3 @@
 /jackrabbit/oak/branches/1.0:1665962
-/jackrabbit/oak/trunk:1733615,1733875,1733913,1733929,1734230,1734254,1734279,1734941,1735052,1735267,1735405,1735484,1735549,1735564,1735588,1735622,1735638,1735919,1735983,1736176,1737309-1737310,1737334,1737349,1737998,1738004,1738136,1738138,1738207,1738252,1738775,1738795,1738833,1738950,1738957,1738963,1739894,1740116,1740625-1740626,1740971,1741032,1741339,1741343,1742363,1742520,1742888,1742916,1743097,1743172,1743343,1744265,1744959,1745038,1745197,1745368,1746086,1746117,1746342,1746345,1746696,1746981,1747198,1747200,1747341-1747342,1747492,1747512,1748505,1748553,1748722,1748870,1749275,1749350,1749464,1749475,1749645,1749662,1749815,1749872,1749875,1749899,1750052,1750076-1750077,1750287,1750457,1750462,1750465,1750495,1750626,1750809,1750886,1751410,1751445-1751446,1751478,1751755,1751871,1752198,1752202,1752273-1752274,1752438,1752447,1752508,1752616,1752659,1752672,1753262,1753331-1753332,1753355,1753444,1754117,1754239,1755157,1756520,1756580
+/jackrabbit/oak/trunk:1733615,1733875,1733913,1733929,1734230,1734254,1734279,1734941,1735052,1735081,1735141,1735267,1735405,1735484,1735549,1735564,1735588,1735622,1735638,1735919,1735983,1736176,1737309-1737310,1737334,1737349,1737998,1738004,1738136,1738138,1738207,1738252,1738775,1738795,1738833,1738950,1738957,1738963,1739712,1739760,1739867,1739894,1739959-1739960,1740114,1740116,1740250,1740333,1740360,1740625-1740626,1740774,1740837,1740971,1741016,1741032,1741339,1741343,1742077,1742117,1742363,1742520,1742888,1742916,1743097,1743172,1743343,1744265,1744292,1744959,1745038,1745197,1745368,1746086,1746117,1746342,1746345,1746696,1746981,1747198,1747200,1747341-1747342,1747492,1747512,1748505,1748553,1748722,1748870,1749275,1749350,1749464,1749475,1749645,1749662,1749815,1749872,1749875,1749899,1750052,1750076-1750077,1750287,1750457,1750462,1750465,1750495,1750626,1750809,1750886,1751410,1751445-1751446,1751478,1751755,1751871,1752198,1752202,1752273-1752274,1752438,1752447
 
,1752508,1752616,1752659,1752672,1753262,1753331-1753332,1753355,1753444,1754117,1754239,1755157,1756520,1756580
 /jackrabbit/trunk:1345480

Modified: 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncConfig.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncConfig.java?rev=1756752&r1=1756751&r2=1756752&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncConfig.java
 (original)
+++ 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncConfig.java
 Thu Aug 18 10:04:48 2016
@@ -118,7 +118,9 @@ public class DefaultSyncConfig {
         }
 
         /**
-         * Sets the auto membership
+         * Sets the auto membership. Note that the passed group names will be 
trimmed
+         * and empty string values will be ignored (along with {@code null} 
values).
+         *
          * @param autoMembership the membership
          * @return {@code this}
          * @see #getAutoMembership()
@@ -127,7 +129,7 @@ public class DefaultSyncConfig {
         public Authorizable setAutoMembership(@Nonnull String ... 
autoMembership) {
             this.autoMembership = new HashSet<String>();
             for (String groupName: autoMembership) {
-                if (!groupName.trim().isEmpty()) {
+                if (groupName != null && !groupName.trim().isEmpty()) {
                     this.autoMembership.add(groupName.trim());
                 }
             }
@@ -202,6 +204,8 @@ public class DefaultSyncConfig {
 
         private long membershipNestingDepth;
 
+        private boolean dynamicMembership;
+
         /**
          * Returns the duration in milliseconds until the group membership of 
a user is expired. If the
          * membership information is expired it is re-synced according to the 
maximum nesting depth.
@@ -249,6 +253,40 @@ public class DefaultSyncConfig {
             return this;
         }
 
+        /**
+         * Returns {@code true} if a dynamic group membership is enabled.
+         *
+         * Turning this option on may alter the behavior of other configuration
+         * options dealing with synchronization of group accounts and group 
membership.
+         * In particular it's an implementation detail if external groups may
+         * no longer be synchronized into the repository.
+         *
+         * @return {@code true} if dynamic group membership for external
+         * user identities is turn on; {@code false} otherwise.
+         */
+        @Nonnull
+        public boolean getDynamicMembership() {
+            return dynamicMembership;
+        }
+
+        /**
+         * Enable or disable the dynamic group membership. If turned external
+         * identities and their group membership will be synchronized such 
that the
+         * membership information is generated dynamically. External groups may
+         * or may not be synchronized into the repository if this option is 
turned
+         * on.
+         *
+         * @param dynamicMembership Boolean flag to enable or disable a 
dedicated
+         *                      dynamic group management.
+         * @return {@code this}
+         * @see #getDynamicMembership() for details.
+         */
+        @Nonnull
+        public User setDynamicMembership(boolean dynamicMembership) {
+            this.dynamicMembership = dynamicMembership;
+            return this;
+        }
+
     }
 
     /**

Modified: 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContext.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContext.java?rev=1756752&r1=1756751&r2=1756752&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContext.java
 (original)
+++ 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/DefaultSyncContext.java
 Thu Aug 18 10:04:48 2016
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.oak.spi.security.authentication.external.basic;
 
 import java.io.ByteArrayInputStream;
+import java.io.InputStream;
 import java.math.BigDecimal;
 import java.security.Principal;
 import java.util.ArrayList;
@@ -221,6 +222,12 @@ public class DefaultSyncContext implemen
     @Nonnull
     @Override
     public SyncResult sync(@Nonnull ExternalIdentity identity) throws 
SyncException {
+        ExternalIdentityRef ref = identity.getExternalId();
+        if (!isSameIDP(ref)) {
+            // create result in accordance with sync(String) where status is 
FOREIGN
+            boolean isGroup = (identity instanceof ExternalGroup);
+            return new DefaultSyncResultImpl(new 
DefaultSyncedIdentity(identity.getId(), ref, isGroup, -1), 
SyncResult.Status.FOREIGN);
+        }
         try {
             DebugTimer timer = new DebugTimer();
             DefaultSyncResultImpl ret;
@@ -249,7 +256,7 @@ public class DefaultSyncContext implemen
                 throw new IllegalArgumentException("identity must be user or 
group but was: " + identity);
             }
             if (log.isDebugEnabled()) {
-                log.debug("sync({}) -> {} {}", 
identity.getExternalId().getString(), identity.getId(), timer.getString());
+                log.debug("sync({}) -> {} {}", ref.getString(), 
identity.getId(), timer.getString());
             }
             if (created) {
                 ret.setStatus(SyncResult.Status.ADD);
@@ -275,47 +282,25 @@ public class DefaultSyncContext implemen
                 return new DefaultSyncResultImpl(new DefaultSyncedIdentity(id, 
null, false, -1), SyncResult.Status.NO_SUCH_AUTHORIZABLE);
             }
             // check if we need to deal with this authorizable
-            ExternalIdentityRef ref = DefaultSyncContext.getIdentityRef(auth);
-            if (ref == null || !idp.getName().equals(ref.getProviderName())) {
-                return new DefaultSyncResultImpl(new DefaultSyncedIdentity(id, 
null, false, -1), SyncResult.Status.FOREIGN);
+            ExternalIdentityRef ref = getIdentityRef(auth);
+            if (ref == null || !isSameIDP(ref)) {
+                return new DefaultSyncResultImpl(new DefaultSyncedIdentity(id, 
ref, auth.isGroup(), -1), SyncResult.Status.FOREIGN);
             }
 
             if (auth.isGroup()) {
-                Group group = (Group) auth;
                 ExternalGroup external = idp.getGroup(id);
                 timer.mark("retrieve");
                 if (external == null) {
-                    DefaultSyncedIdentity syncId = 
DefaultSyncContext.createSyncedIdentity(auth);
-                    if (group.getDeclaredMembers().hasNext()) {
-                        log.info("won't remove local group with members: {}", 
id);
-                        ret = new DefaultSyncResultImpl(syncId, 
SyncResult.Status.NOP);
-                    } else if (!keepMissing) {
-                        auth.remove();
-                        log.debug("removing authorizable '{}' that no longer 
exists on IDP {}", id, idp.getName());
-                        timer.mark("remove");
-                        ret = new DefaultSyncResultImpl(syncId, 
SyncResult.Status.DELETE);
-                    } else {
-                        ret = new DefaultSyncResultImpl(syncId, 
SyncResult.Status.MISSING);
-                        log.info("external identity missing for {}, but purge 
== false.", id);
-                    }
+                    ret = handleMissingIdentity(id, auth, timer);
                 } else {
-                    ret = syncGroup(external, group);
+                    ret = syncGroup(external, (Group) auth);
                     timer.mark("sync");
                 }
             } else {
                 ExternalUser external = idp.getUser(id);
                 timer.mark("retrieve");
                 if (external == null) {
-                    DefaultSyncedIdentity syncId = 
DefaultSyncContext.createSyncedIdentity(auth);
-                    if (!keepMissing) {
-                        auth.remove();
-                        log.debug("removing authorizable '{}' that no longer 
exists on IDP {}", id, idp.getName());
-                        timer.mark("remove");
-                        ret = new DefaultSyncResultImpl(syncId, 
SyncResult.Status.DELETE);
-                    } else {
-                        ret = new DefaultSyncResultImpl(syncId, 
SyncResult.Status.MISSING);
-                        log.info("external identity missing for {}, but purge 
== false.", id);
-                    }
+                    ret = handleMissingIdentity(id, auth, timer);
                 } else {
                     ret = syncUser(external, (User) auth);
                     timer.mark("sync");
@@ -332,6 +317,26 @@ public class DefaultSyncContext implemen
         }
     }
 
+    private DefaultSyncResultImpl handleMissingIdentity(@Nonnull String id,
+                                                        @Nonnull Authorizable 
authorizable,
+                                                        @Nonnull DebugTimer 
timer) throws RepositoryException {
+        DefaultSyncedIdentity syncId = createSyncedIdentity(authorizable);
+        SyncResult.Status status;
+        if (authorizable.isGroup() && ((Group) 
authorizable).getDeclaredMembers().hasNext()) {
+            log.info("won't remove local group with members: {}", id);
+            status = SyncResult.Status.NOP;
+        } else if (!keepMissing) {
+            authorizable.remove();
+            log.debug("removing authorizable '{}' that no longer exists on IDP 
{}", id, idp.getName());
+            timer.mark("remove");
+            status = SyncResult.Status.DELETE;
+        } else {
+            status = SyncResult.Status.MISSING;
+            log.info("external identity missing for {}, but purge == false.", 
id);
+        }
+        return new DefaultSyncResultImpl(syncId, status);
+    }
+
     /**
      * Retrieves the repository authorizable that corresponds to the given 
external identity
      * @param external the external identity
@@ -375,7 +380,7 @@ public class DefaultSyncContext implemen
                 principal,
                 PathUtils.concatRelativePaths(config.user().getPathPrefix(), 
externalUser.getIntermediatePath())
         );
-        user.setProperty(REP_EXTERNAL_ID, 
valueFactory.createValue(externalUser.getExternalId().getString()));
+        setExternalId(user, externalUser);
         return user;
     }
 
@@ -395,53 +400,73 @@ public class DefaultSyncContext implemen
                 principal,
                 PathUtils.concatRelativePaths(config.group().getPathPrefix(), 
externalGroup.getIntermediatePath())
         );
-        group.setProperty(REP_EXTERNAL_ID, 
valueFactory.createValue(externalGroup.getExternalId().getString()));
+        setExternalId(group, externalGroup);
         return group;
     }
 
+    /**
+     * Sets the {@link #REP_EXTERNAL_ID} as obtained from {@code 
externalIdentity}
+     * to the specified {@code authorizable} (user or group). The property is
+     * a single value of type {@link javax.jcr.PropertyType#STRING STRING}.
+     *
+     * @param authorizable The user or group that needs to get the {@link 
#REP_EXTERNAL_ID} property set.
+     * @param externalIdentity The {@link ExternalIdentity} from which to 
retrieve the value of the property.
+     * @throws RepositoryException If setting the property using {@link 
Authorizable#setProperty(String, Value)} fails.
+     */
+    private void setExternalId(@Nonnull Authorizable authorizable, @Nonnull 
ExternalIdentity externalIdentity) throws RepositoryException {
+        log.debug("Fallback: setting rep:externalId without adding the 
corresponding mixin type");
+        authorizable.setProperty(REP_EXTERNAL_ID, 
valueFactory.createValue(externalIdentity.getExternalId().getString()));
+    }
+
     @Nonnull
     protected DefaultSyncResultImpl syncUser(@Nonnull ExternalUser external, 
@Nonnull User user) throws RepositoryException {
+        SyncResult.Status status;
         // first check if user is expired
-        if (!forceUserSync && !isExpired(user, 
config.user().getExpirationTime(), "Properties")) {
-            DefaultSyncedIdentity syncId = 
DefaultSyncContext.createSyncedIdentity(user);
-            return new DefaultSyncResultImpl(syncId, SyncResult.Status.NOP);
-        }
-
-        // synchronize the properties
-        syncProperties(external, user, config.user().getPropertyMapping());
-
-        // synchronize auto-group membership
-        applyMembership(user, config.user().getAutoMembership());
-
-        if (isExpired(user, config.user().getMembershipExpirationTime(), 
"Membership")) {
-            // synchronize external memberships
-            syncMembership(external, user, 
config.user().getMembershipNestingDepth());
+        if (!forceUserSync && !isExpired(user)) {
+            status = SyncResult.Status.NOP;
+        } else {
+            syncExternalIdentity(external, user, config.user());
+            if (isExpired(user, config.user().getMembershipExpirationTime(), 
"Membership")) {
+                // synchronize external memberships
+                syncMembership(external, user, 
config.user().getMembershipNestingDepth());
+            }
+            // finally "touch" the sync property
+            user.setProperty(REP_LAST_SYNCED, nowValue);
+            status = SyncResult.Status.UPDATE;
         }
-
-        // finally "touch" the sync property
-        user.setProperty(REP_LAST_SYNCED, nowValue);
-        DefaultSyncedIdentity syncId = 
DefaultSyncContext.createSyncedIdentity(user);
-        return new DefaultSyncResultImpl(syncId, SyncResult.Status.UPDATE);
+        return new DefaultSyncResultImpl(createSyncedIdentity(user), status);
     }
 
     @Nonnull
     protected DefaultSyncResultImpl syncGroup(@Nonnull ExternalGroup external, 
@Nonnull Group group) throws RepositoryException {
-        // first check if user is expired
-        if (!forceGroupSync && !isExpired(group, 
config.group().getExpirationTime(), "Properties")) {
-            DefaultSyncedIdentity syncId = 
DefaultSyncContext.createSyncedIdentity(group);
-            return new DefaultSyncResultImpl(syncId, SyncResult.Status.NOP);
+        SyncResult.Status status;
+        // first check if group is expired
+        if (!forceGroupSync && !isExpired(group)) {
+            status = SyncResult.Status.NOP;
+        } else {
+            syncExternalIdentity(external, group, config.group());
+            // finally "touch" the sync property
+            group.setProperty(REP_LAST_SYNCED, nowValue);
+            status = SyncResult.Status.UPDATE;
         }
+        return new DefaultSyncResultImpl(createSyncedIdentity(group), status);
+    }
 
-        // synchronize the properties
-        syncProperties(external, group, config.group().getPropertyMapping());
-
-        // synchronize auto-group membership
-        applyMembership(group, config.group().getAutoMembership());
-
-        // finally "touch" the sync property
-        group.setProperty(REP_LAST_SYNCED, nowValue);
-        DefaultSyncedIdentity syncId = 
DefaultSyncContext.createSyncedIdentity(group);
-        return new DefaultSyncResultImpl(syncId, SyncResult.Status.UPDATE);
+    /**
+     * Synchronize content common to both external users and external groups:
+     * - properties
+     * - auto-group membership
+     *
+     * @param external The external identity
+     * @param authorizable The corresponding repository user/group
+     * @param config The sync configuration
+     * @throws RepositoryException If an error occurs.
+     */
+    private void syncExternalIdentity(@Nonnull ExternalIdentity external,
+                                      @Nonnull Authorizable authorizable,
+                                      @Nonnull DefaultSyncConfig.Authorizable 
config) throws RepositoryException {
+        syncProperties(external, authorizable, config.getPropertyMapping());
+        applyMembership(authorizable, config.getAutoMembership());
     }
 
     /**
@@ -533,7 +558,7 @@ public class DefaultSyncContext implemen
         }
         timer.mark("adding");
         // remove us from the lost membership groups
-        for (Group grp: declaredExternalGroups.values()) {
+        for (Group grp : declaredExternalGroups.values()) {
             grp.removeMember(auth);
             log.debug("- removing member '{}' for group '{}'", auth.getID(), 
grp.getID());
         }
@@ -550,7 +575,7 @@ public class DefaultSyncContext implemen
      * @param groups set of groups.
      */
     protected void applyMembership(@Nonnull Authorizable member, @Nonnull 
Set<String> groups) throws RepositoryException {
-        for (String groupName: groups) {
+        for (String groupName : groups) {
             Authorizable group = userManager.getAuthorizable(groupName);
             if (group == null) {
                 log.warn("Unable to apply auto-membership to {}. No such 
group: {}", member.getID(), groupName);
@@ -574,7 +599,7 @@ public class DefaultSyncContext implemen
     protected void syncProperties(@Nonnull ExternalIdentity ext, @Nonnull 
Authorizable auth, @Nonnull Map<String, String> mapping)
             throws RepositoryException {
         Map<String, ?> properties = ext.getProperties();
-        for (Map.Entry<String, String> entry: mapping.entrySet()) {
+        for (Map.Entry<String, String> entry : mapping.entrySet()) {
             String relPath = entry.getKey();
             String name = entry.getValue();
             Object obj = properties.get(name);
@@ -601,6 +626,17 @@ public class DefaultSyncContext implemen
 
     /**
      * Checks if the given authorizable needs syncing based on the {@link 
#REP_LAST_SYNCED} property.
+     *
+     * @param authorizable the authorizable to check
+     * @return {@code true} if the authorizable needs sync
+     */
+    private boolean isExpired(@Nonnull Authorizable authorizable) throws 
RepositoryException {
+        long expTime = (authorizable.isGroup()) ? 
config.group().getExpirationTime() : config.user().getExpirationTime();
+        return isExpired(authorizable, expTime, "Properties");
+    }
+
+    /**
+     * Checks if the given authorizable needs syncing based on the {@link 
#REP_LAST_SYNCED} property.
      * @param auth the authorizable to check
      * @param expirationTime the expiration time to compare to.
      * @param type debug message type
@@ -655,6 +691,10 @@ public class DefaultSyncContext implemen
         } else if (v instanceof byte[]) {
             Binary bin = valueFactory.createBinary(new 
ByteArrayInputStream((byte[])v));
             return valueFactory.createValue(bin);
+        } else if (v instanceof Binary) {
+            return valueFactory.createValue((Binary) v);
+        } else if (v instanceof InputStream) {
+            return valueFactory.createValue((InputStream) v);
         } else if (v instanceof char[]) {
             return valueFactory.createValue(new String((char[]) v));
         } else {
@@ -690,7 +730,19 @@ public class DefaultSyncContext implemen
      * @return {@code true} if same IDP.
      */
     protected boolean isSameIDP(@Nullable Authorizable auth) throws 
RepositoryException {
-        ExternalIdentityRef ref = DefaultSyncContext.getIdentityRef(auth);
+        ExternalIdentityRef ref = getIdentityRef(auth);
         return ref != null && idp.getName().equals(ref.getProviderName());
     }
+
+    /**
+     * Tests if the given {@link ExternalIdentityRef} refers to the same IDP
+     * as associated with this context instance.
+     *
+     * @param ref The {@link ExternalIdentityRef} to be tested.
+     * @return {@code true} if {@link ExternalIdentityRef#getProviderName()} 
refers
+     * to the IDP associated with this context instance.
+     */
+    protected boolean isSameIDP(@Nonnull ExternalIdentityRef ref) {
+        return idp.getName().equals(ref.getProviderName());
+    }
 }
\ No newline at end of file

Modified: 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/package-info.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/package-info.java?rev=1756752&r1=1756751&r2=1756752&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/package-info.java
 (original)
+++ 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/basic/package-info.java
 Thu Aug 18 10:04:48 2016
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.0.1")
+@Version("1.1.0")
 @Export
 package org.apache.jackrabbit.oak.spi.security.authentication.external.basic;
 

Modified: 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncConfigImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncConfigImpl.java?rev=1756752&r1=1756751&r2=1756752&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncConfigImpl.java
 (original)
+++ 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncConfigImpl.java
 Thu Aug 18 10:04:48 2016
@@ -56,12 +56,12 @@ public class DefaultSyncConfigImpl exten
     public static final String PARAM_NAME = "handler.name";
 
     /**
-     * @see DefaultSyncConfigImpl.User#getExpirationTime()
+     * @see DefaultSyncConfig.User#getExpirationTime()
      */
     public static final String PARAM_USER_EXPIRATION_TIME_DEFAULT = "1h";
 
     /**
-     * @see DefaultSyncConfigImpl.User#getExpirationTime()
+     * @see DefaultSyncConfig.User#getExpirationTime()
      */
     @Property(
             label = "User Expiration Time",
@@ -71,12 +71,12 @@ public class DefaultSyncConfigImpl exten
     public static final String PARAM_USER_EXPIRATION_TIME = 
"user.expirationTime";
 
     /**
-     * @see DefaultSyncConfigImpl.User#getAutoMembership()
+     * @see DefaultSyncConfig.User#getAutoMembership()
      */
     public static final String[] PARAM_USER_AUTO_MEMBERSHIP_DEFAULT = {};
 
     /**
-     * @see DefaultSyncConfigImpl.User#getAutoMembership()
+     * @see DefaultSyncConfig.User#getAutoMembership()
      */
     @Property(
             label = "User auto membership",
@@ -87,12 +87,12 @@ public class DefaultSyncConfigImpl exten
     public static final String PARAM_USER_AUTO_MEMBERSHIP = 
"user.autoMembership";
 
     /**
-     * @see DefaultSyncConfigImpl.User#getPropertyMapping()
+     * @see DefaultSyncConfig.User#getPropertyMapping()
      */
     public static final String[] PARAM_USER_PROPERTY_MAPPING_DEFAULT = 
{"rep:fullname=cn"};
 
     /**
-     * @see DefaultSyncConfigImpl.User#getPropertyMapping()
+     * @see DefaultSyncConfig.User#getPropertyMapping()
      */
     @Property(
             label = "User property mapping",
@@ -104,12 +104,12 @@ public class DefaultSyncConfigImpl exten
     public static final String PARAM_USER_PROPERTY_MAPPING = 
"user.propertyMapping";
 
     /**
-     * @see DefaultSyncConfigImpl.User#getPathPrefix()
+     * @see DefaultSyncConfig.User#getPathPrefix()
      */
     public static final String PARAM_USER_PATH_PREFIX_DEFAULT = "";
 
     /**
-     * @see DefaultSyncConfigImpl.User#getPathPrefix()
+     * @see DefaultSyncConfig.User#getPathPrefix()
      */
     @Property(
             label = "User Path Prefix",
@@ -119,12 +119,12 @@ public class DefaultSyncConfigImpl exten
     public static final String PARAM_USER_PATH_PREFIX = "user.pathPrefix";
 
     /**
-     * @see DefaultSyncConfigImpl.User#getMembershipExpirationTime()
+     * @see DefaultSyncConfig.User#getMembershipExpirationTime()
      */
     public static final String PARAM_USER_MEMBERSHIP_EXPIRATION_TIME_DEFAULT = 
"1h";
 
     /**
-     * @see DefaultSyncConfigImpl.User#getMembershipExpirationTime()
+     * @see DefaultSyncConfig.User#getMembershipExpirationTime()
      */
     @Property(
             label = "User Membership Expiration",
@@ -134,12 +134,12 @@ public class DefaultSyncConfigImpl exten
     public static final String PARAM_USER_MEMBERSHIP_EXPIRATION_TIME = 
"user.membershipExpTime";
 
     /**
-     * @see User#getMembershipNestingDepth()
+     * @see DefaultSyncConfig.User#getMembershipNestingDepth()
      */
     public static final int PARAM_USER_MEMBERSHIP_NESTING_DEPTH_DEFAULT = 0;
 
     /**
-     * @see User#getMembershipNestingDepth()
+     * @see DefaultSyncConfig.User#getMembershipNestingDepth()
      */
     @Property(
             label = "User membership nesting depth",
@@ -152,12 +152,38 @@ public class DefaultSyncConfigImpl exten
     public static final String PARAM_USER_MEMBERSHIP_NESTING_DEPTH = 
"user.membershipNestingDepth";
 
     /**
-     * @see DefaultSyncConfigImpl.Group#getExpirationTime()
+     * @see DefaultSyncConfig.User#getDynamicMembership()
+     */
+    public static final boolean PARAM_USER_DYNAMIC_MEMBERSHIP_DEFAULT = false;
+
+    /**
+     * Configuration option to enable dynamic group membership. If enabled the
+     * implementation will no longer synchronized group accounts into the 
repository
+     * but instead will enable a dedicated principal management: This results 
in
+     * external users having their complete principal set as defined external 
IDP
+     * synchronized to the repository asserting proper population of the
+     * {@link javax.security.auth.Subject} upon login. Please note that the 
external
+     * groups are reflected through the built-in principal management and thus 
can
+     * be retrieved for authorization purposes. However, the information is no
+     * longer reflected through the Jackrabbit user management API.
+     *
+     * @see DefaultSyncConfig.User#getDynamicMembership()
+     */
+    @Property(
+            label = "User Dynamic Membership",
+            description = "If enabled membership of external identities (user) 
is no longer fully reflected " +
+                    "within the repositories user management.",
+            boolValue = PARAM_USER_DYNAMIC_MEMBERSHIP_DEFAULT
+    )
+    public static final String PARAM_USER_DYNAMIC_MEMBERSHIP = 
"user.dynamicMembership";
+
+    /**
+     * @see DefaultSyncConfig.Group#getExpirationTime()
      */
     public static final String PARAM_GROUP_EXPIRATION_TIME_DEFAULT = "1d";
 
     /**
-     * @see DefaultSyncConfigImpl.Group#getExpirationTime()
+     * @see DefaultSyncConfig.Group#getExpirationTime()
      */
     @Property(
             label = "Group Expiration Time",
@@ -167,12 +193,12 @@ public class DefaultSyncConfigImpl exten
     public static final String PARAM_GROUP_EXPIRATION_TIME = 
"group.expirationTime";
 
     /**
-     * @see DefaultSyncConfigImpl.Group#getAutoMembership()
+     * @see DefaultSyncConfig.Group#getAutoMembership()
      */
     public static final String[] PARAM_GROUP_AUTO_MEMBERSHIP_DEFAULT = {};
 
     /**
-     * @see DefaultSyncConfigImpl.Group#getAutoMembership()
+     * @see DefaultSyncConfig.Group#getAutoMembership()
      */
     @Property(
             label = "Group auto membership",
@@ -183,12 +209,12 @@ public class DefaultSyncConfigImpl exten
     public static final String PARAM_GROUP_AUTO_MEMBERSHIP = 
"group.autoMembership";
 
     /**
-     * @see DefaultSyncConfigImpl.Group#getPropertyMapping()
+     * @see DefaultSyncConfig.Group#getPropertyMapping()
      */
     public static final String[] PARAM_GROUP_PROPERTY_MAPPING_DEFAULT = {};
 
     /**
-     * @see DefaultSyncConfigImpl.Group#getPropertyMapping()
+     * @see DefaultSyncConfig.Group#getPropertyMapping()
      */
     @Property(
             label = "Group property mapping",
@@ -199,12 +225,12 @@ public class DefaultSyncConfigImpl exten
     public static final String PARAM_GROUP_PROPERTY_MAPPING = 
"group.propertyMapping";
 
     /**
-     * @see DefaultSyncConfigImpl.Group#getPathPrefix()
+     * @see DefaultSyncConfig.Group#getPathPrefix()
      */
     public static final String PARAM_GROUP_PATH_PREFIX_DEFAULT = "";
 
     /**
-     * @see DefaultSyncConfigImpl.Group#getPathPrefix()
+     * @see DefaultSyncConfig.Group#getPathPrefix()
      */
     @Property(
             label = "Group Path Prefix",
@@ -229,6 +255,7 @@ public class DefaultSyncConfigImpl exten
         cfg.user()
                 .setMembershipExpirationTime(getMilliSeconds(params, 
PARAM_USER_MEMBERSHIP_EXPIRATION_TIME, 
PARAM_USER_MEMBERSHIP_EXPIRATION_TIME_DEFAULT, ONE_HOUR))
                 
.setMembershipNestingDepth(params.getConfigValue(PARAM_USER_MEMBERSHIP_NESTING_DEPTH,
 PARAM_USER_MEMBERSHIP_NESTING_DEPTH_DEFAULT))
+                
.setDynamicMembership(params.getConfigValue(PARAM_USER_DYNAMIC_MEMBERSHIP, 
PARAM_USER_DYNAMIC_MEMBERSHIP_DEFAULT))
                 .setExpirationTime(getMilliSeconds(params, 
PARAM_USER_EXPIRATION_TIME, PARAM_USER_EXPIRATION_TIME_DEFAULT, ONE_HOUR))
                 .setPathPrefix(params.getConfigValue(PARAM_USER_PATH_PREFIX, 
PARAM_USER_PATH_PREFIX_DEFAULT))
                 
.setAutoMembership(params.getConfigValue(PARAM_USER_AUTO_MEMBERSHIP, 
PARAM_USER_AUTO_MEMBERSHIP_DEFAULT))

Modified: 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandler.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandler.java?rev=1756752&r1=1756751&r2=1756752&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandler.java
 (original)
+++ 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandler.java
 Thu Aug 18 10:04:48 2016
@@ -103,7 +103,11 @@ public class DefaultSyncHandler implemen
     @Override
     public SyncContext createContext(@Nonnull ExternalIdentityProvider idp, 
@Nonnull UserManager userManager,
                                      @Nonnull ValueFactory valueFactory) 
throws SyncException {
-        return new DefaultSyncContext(config, idp, userManager, valueFactory);
+        if (config.user().getDynamicMembership()) {
+            return new DynamicSyncContext(config, idp, userManager, 
valueFactory);
+        } else {
+            return new DefaultSyncContext(config, idp, userManager, 
valueFactory);
+        }
     }
 
     /**

Modified: 
jackrabbit/oak/branches/1.4/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/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java?rev=1756752&r1=1756751&r2=1756752&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java
 (original)
+++ 
jackrabbit/oak/branches/1.4/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java
 Thu Aug 18 10:04:48 2016
@@ -17,14 +17,13 @@
 package org.apache.jackrabbit.oak.spi.security.authentication.external.impl;
 
 import java.security.Principal;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 
+import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 import javax.jcr.Credentials;
-import javax.jcr.SimpleCredentials;
 import javax.security.auth.Subject;
 import javax.security.auth.callback.CallbackHandler;
 import javax.security.auth.login.LoginException;
@@ -41,11 +40,13 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.apache.jackrabbit.oak.spi.security.authentication.AuthInfoImpl;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.ImpersonationCredentials;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.PreAuthenticatedLogin;
+import 
org.apache.jackrabbit.oak.spi.security.authentication.credentials.SimpleCredentialsSupport;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityException;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityProvider;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityProviderManager;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityRef;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser;
+import 
org.apache.jackrabbit.oak.spi.security.authentication.credentials.CredentialsSupport;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.external.SyncContext;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.external.SyncException;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.external.SyncHandler;
@@ -57,7 +58,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * ExternalLoginModule implements a LoginModule that uses and external 
identity provider for login.
+ * {@code ExternalLoginModule} implements a {@code LoginModule} that uses an
+ * {@link ExternalIdentityProvider} for authentication.
  */
 public class ExternalLoginModule extends AbstractLoginModule {
 
@@ -79,6 +81,8 @@ public class ExternalLoginModule extends
 
     private SyncManager syncManager;
 
+    private CredentialsSupport credentialsSupport = 
SimpleCredentialsSupport.getInstance();
+
     /**
      * internal configuration when invoked from a factory rather than jaas
      */
@@ -121,8 +125,8 @@ public class ExternalLoginModule extends
 
     //--------------------------------------------------------< LoginModule 
>---
     @Override
-    public void initialize(Subject subject, CallbackHandler callbackHandler, 
Map<String, ?> ss, Map<String, ?> opts) {
-        super.initialize(subject, callbackHandler, ss, opts);
+    public void initialize(Subject subject, CallbackHandler callbackHandler, 
Map<String, ?> sharedState, Map<String, ?> opts) {
+        super.initialize(subject, callbackHandler, sharedState, opts);
 
         // merge options with osgi options if needed
         if (osgiConfig != null) {
@@ -168,6 +172,12 @@ public class ExternalLoginModule extends
                 }
             }
         }
+
+        if (idp instanceof CredentialsSupport) {
+            credentialsSupport = (CredentialsSupport) idp;
+        } else {
+            log.debug("No 'SupportedCredentials' configured. Using default 
implementation supporting 'SimpleCredentials'.");
+        }
     }
 
     @Override
@@ -178,18 +188,16 @@ public class ExternalLoginModule extends
         credentials = getCredentials();
 
         // check if we have a pre authenticated login from a previous login 
module
-        final String userId;
         final PreAuthenticatedLogin preAuthLogin = getSharedPreAuthLogin();
-        if (preAuthLogin != null) {
-            userId = preAuthLogin.getUserId();
-        } else {
-            userId = credentials instanceof SimpleCredentials ? 
((SimpleCredentials) credentials).getUserID() : null;
-        }
+        final String userId = getUserId(preAuthLogin, credentials);
+
         if (userId == null && credentials == null) {
-            log.debug("No credentials found for external login module. 
ignoring.");
+            log.debug("No credentials|userId found for external login module. 
ignoring.");
             return false;
         }
 
+        // remember identification for log-output
+        Object logId = (userId != null) ? userId : credentials;
         try {
             SyncedIdentity sId = null;
             UserManager userMgr = getUserManager();
@@ -237,11 +245,7 @@ public class ExternalLoginModule extends
                 return true;
             } else {
                 if (log.isDebugEnabled()) {
-                    if (userId != null) {
-                        log.debug("IDP {} returned null for simple creds of 
{}", idp.getName(), userId);
-                    } else {
-                        log.debug("IDP {} returned null for {}", 
idp.getName(), credentials);
-                    }
+                    log.debug("IDP {} returned null for {}", idp.getName(), 
logId);
                 }
 
                 if (sId != null) {
@@ -252,16 +256,13 @@ public class ExternalLoginModule extends
                 return false;
             }
         } catch (ExternalIdentityException e) {
-            log.error("Error while authenticating '{}' with {}",
-                    userId == null ? credentials : userId, idp.getName(), e);
+            log.error("Error while authenticating '{}' with {}", logId, 
idp.getName(), e);
             return false;
         } catch (LoginException e) {
-            log.debug("IDP {} throws login exception for '{}': {}",
-                    idp.getName(), userId == null ? credentials : userId, 
e.getMessage());
+            log.debug("IDP {} throws login exception for '{}': {}", 
idp.getName(), logId, e.getMessage());
             throw e;
         } catch (Exception e) {
-            log.debug("SyncHandler {} throws sync exception for '{}'",
-                    syncHandler.getName(), userId == null ? credentials : 
userId, e);
+            log.debug("SyncHandler {} throws sync exception for '{}'", 
syncHandler.getName(), logId, e);
             LoginException le = new LoginException("Error while syncing 
user.");
             le.initCause(e);
             throw le;
@@ -299,6 +300,19 @@ public class ExternalLoginModule extends
         return true;
     }
 
+    //------------------------------------------------------------< private 
>---
+
+    @CheckForNull
+    private String getUserId(@CheckForNull PreAuthenticatedLogin preAuthLogin, 
@CheckForNull Credentials credentials) {
+        if (preAuthLogin != null) {
+            return preAuthLogin.getUserId();
+        } else if (credentials != null){
+            return credentialsSupport.getUserId(credentials);
+        } else {
+            return null;
+        }
+    }
+
     /**
      * Initiates synchronization of the external user.
      * @param user the external user
@@ -385,14 +399,11 @@ public class ExternalLoginModule extends
         Map<String, Object> attributes = new HashMap<String, Object>();
         Object shared = sharedState.get(SHARED_KEY_ATTRIBUTES);
         if (shared instanceof Map) {
-            for (Object key : ((Map) shared).keySet()) {
-                attributes.put(key.toString(), ((Map) shared).get(key));
-            }
-        } else if (creds instanceof SimpleCredentials) {
-            SimpleCredentials sc = (SimpleCredentials) creds;
-            for (String attrName : sc.getAttributeNames()) {
-                attributes.put(attrName, sc.getAttribute(attrName));
+            for (Map.Entry entry : ((Map<?,?>) shared).entrySet()) {
+                attributes.put(entry.getKey().toString(), entry.getValue());
             }
+        } else if (creds != null) {
+            attributes.putAll(credentialsSupport.getAttributes(creds));
         }
         return new AuthInfoImpl(userId, attributes, principals);
     }
@@ -407,22 +418,22 @@ public class ExternalLoginModule extends
     }
 
     /**
-     * @return An immutable set containing only the {@link SimpleCredentials} 
class.
+     * @return the set of credentials classes as exposed by the configured
+     * {@link CredentialsSupport} implementation.
      */
     @Nonnull
     @Override
     protected Set<Class> getSupportedCredentials() {
-        // TODO: delegate getSupportedCredentials to IDP (OAK-4000)
-        Class scClass = SimpleCredentials.class;
-        return Collections.singleton(scClass);
+        return credentialsSupport.getCredentialClasses();
     }
 
+    //----------------------------------------------< public setters (JAAS) 
>---
 
-    public void setSyncManager(SyncManager syncManager) {
+    public void setSyncManager(@Nonnull SyncManager syncManager) {
         this.syncManager = syncManager;
     }
 
-    public void setIdpManager(ExternalIdentityProviderManager idpManager) {
+    public void setIdpManager(@Nonnull ExternalIdentityProviderManager 
idpManager) {
         this.idpManager = idpManager;
     }
 }
\ No newline at end of file

Copied: 
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/AbstractExternalAuthTest.java
 (from r1740114, 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/AbstractExternalAuthTest.java)
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/AbstractExternalAuthTest.java?p2=jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/AbstractExternalAuthTest.java&p1=jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/AbstractExternalAuthTest.java&r1=1740114&r2=1756752&rev=1756752&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/AbstractExternalAuthTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/AbstractExternalAuthTest.java
 Thu Aug 18 10:04:48 2016
@@ -16,13 +16,17 @@
  */
 package org.apache.jackrabbit.oak.spi.security.authentication.external;
 
+import java.security.PrivilegedExceptionAction;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
+import javax.jcr.NoSuchWorkspaceException;
 import javax.jcr.RepositoryException;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginException;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicates;
@@ -31,7 +35,12 @@ import com.google.common.collect.Sets;
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.authentication.SystemSubject;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncConfig;
+import 
org.apache.jackrabbit.oak.spi.security.authentication.external.impl.principal.ExternalPrincipalConfiguration;
 import org.junit.After;
 import org.junit.Before;
 
@@ -47,9 +56,13 @@ public abstract class AbstractExternalAu
     protected ExternalIdentityProvider idp;
 
     protected DefaultSyncConfig syncConfig;
+    protected ExternalPrincipalConfiguration externalPrincipalConfiguration = 
new ExternalPrincipalConfiguration();
 
     private Set<String> ids;
 
+    private ContentSession systemSession;
+    private Root systemRoot;
+
     @Before
     public void before() throws Exception {
         super.before();
@@ -67,6 +80,10 @@ public abstract class AbstractExternalAu
             destroyIDP();
             idp = null;
 
+            if (systemSession != null) {
+                systemSession.close();
+            }
+
             // discard any pending changes
             root.refresh();
 
@@ -106,6 +123,13 @@ public abstract class AbstractExternalAu
         }), Predicates.notNull());
     }
 
+    @Override
+    protected SecurityProvider getSecurityProvider() {
+        if (securityProvider == null) {
+            securityProvider = new 
TestSecurityProvider(getSecurityConfigParameters(), 
externalPrincipalConfiguration);
+        }
+        return securityProvider;
+    }
 
     protected ExternalIdentityProvider createIDP() {
         return new TestIdentityProvider();
@@ -127,4 +151,17 @@ public abstract class AbstractExternalAu
         syncConfig.user().setMembershipNestingDepth(1);
         return syncConfig;
     }
+
+    protected Root getSystemRoot() throws Exception {
+        if (systemRoot == null) {
+            systemSession = Subject.doAs(SystemSubject.INSTANCE, new 
PrivilegedExceptionAction<ContentSession>() {
+                @Override
+                public ContentSession run() throws LoginException, 
NoSuchWorkspaceException {
+                    return getContentRepository().login(null, null);
+                }
+            });
+            systemRoot = systemSession.getLatestRoot();
+        }
+        return systemRoot;
+    }
 }
\ No newline at end of file

Modified: 
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTest.java?rev=1756752&r1=1756751&r2=1756752&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTest.java
 Thu Aug 18 10:04:48 2016
@@ -24,8 +24,7 @@ import javax.security.auth.login.LoginEx
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.oak.api.ContentSession;
-import org.apache.jackrabbit.oak.namepath.NamePathMapper;
-import org.apache.jackrabbit.oak.plugins.value.ValueFactoryImpl;
+import 
org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncContext;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -43,12 +42,6 @@ public class ExternalLoginModuleTest ext
 
     protected final HashMap<String, Object> options = new HashMap<String, 
Object>();
 
-    private final String userId = "testUser";
-
-    private final static String TEST_CONSTANT_PROPERTY_NAME = 
"profile/constantProperty";
-
-    private final static String TEST_CONSTANT_PROPERTY_VALUE = 
"constant-value";
-
     @Before
     public void before() throws Exception {
         super.before();
@@ -59,15 +52,6 @@ public class ExternalLoginModuleTest ext
         super.after();
     }
 
-    protected ExternalIdentityProvider createIDP() {
-        return new TestIdentityProvider();
-    }
-
-    @Override
-    protected void destroyIDP(ExternalIdentityProvider idp) {
-    // ignore
-    }
-
     @Test
     public void testLoginFailed() throws Exception {
         UserManager userManager = getUserManager(root);
@@ -78,7 +62,7 @@ public class ExternalLoginModuleTest ext
         } catch (LoginException e) {
             // success
         } finally {
-            assertNull(userManager.getAuthorizable(userId));
+            assertNull(userManager.getAuthorizable(USER_ID));
         }
     }
 
@@ -87,15 +71,15 @@ public class ExternalLoginModuleTest ext
         UserManager userManager = getUserManager(root);
         ContentSession cs = null;
         try {
-            assertNull(userManager.getAuthorizable(userId));
+            assertNull(userManager.getAuthorizable(USER_ID));
 
-            cs = login(new SimpleCredentials(userId, new char[0]));
+            cs = login(new SimpleCredentials(USER_ID, new char[0]));
 
             root.refresh();
 
-            Authorizable a = userManager.getAuthorizable(userId);
+            Authorizable a = userManager.getAuthorizable(USER_ID);
             assertNotNull(a);
-            ExternalUser user = idp.getUser(userId);
+            ExternalUser user = idp.getUser(USER_ID);
             for (String prop : user.getProperties().keySet()) {
                 assertTrue(a.hasProperty(prop));
             }
@@ -113,15 +97,15 @@ public class ExternalLoginModuleTest ext
         UserManager userManager = getUserManager(root);
         ContentSession cs = null;
         try {
-            assertNull(userManager.getAuthorizable(userId));
+            assertNull(userManager.getAuthorizable(USER_ID));
 
-            cs = login(new SimpleCredentials(userId.toUpperCase(), new 
char[0]));
+            cs = login(new SimpleCredentials(USER_ID.toUpperCase(), new 
char[0]));
 
             root.refresh();
 
-            Authorizable a = userManager.getAuthorizable(userId);
+            Authorizable a = userManager.getAuthorizable(USER_ID);
             assertNotNull(a);
-            ExternalUser user = idp.getUser(userId);
+            ExternalUser user = idp.getUser(USER_ID);
             for (String prop : user.getProperties().keySet()) {
                 assertTrue(a.hasProperty(prop));
             }
@@ -139,7 +123,7 @@ public class ExternalLoginModuleTest ext
         UserManager userManager = getUserManager(root);
         ContentSession cs = null;
         try {
-            cs = login(new SimpleCredentials(userId, new char[0]));
+            cs = login(new SimpleCredentials(USER_ID, new char[0]));
 
             root.refresh();
             for (String id : new String[]{"a", "b", "c"}) {
@@ -162,7 +146,7 @@ public class ExternalLoginModuleTest ext
         UserManager userManager = getUserManager(root);
         ContentSession cs = null;
         try {
-            cs = login(new SimpleCredentials(userId, new char[0]));
+            cs = login(new SimpleCredentials(USER_ID, new char[0]));
 
             root.refresh();
             for (String id : new String[]{"a", "b", "c", "aa", "aaa"}) {
@@ -180,18 +164,18 @@ public class ExternalLoginModuleTest ext
     public void testSyncUpdate() throws Exception {
         // create user upfront in order to test update mode
         UserManager userManager = getUserManager(root);
-        ExternalUser externalUser = idp.getUser(userId);
+        ExternalUser externalUser = idp.getUser(USER_ID);
         Authorizable user = userManager.createUser(externalUser.getId(), null);
-        user.setProperty("rep:externalId", new ValueFactoryImpl(root, 
NamePathMapper.DEFAULT).createValue(externalUser.getExternalId().getString()));
+        user.setProperty(DefaultSyncContext.REP_EXTERNAL_ID, 
getValueFactory().createValue(externalUser.getExternalId().getString()));
         root.commit();
 
         ContentSession cs = null;
         try {
-            cs = login(new SimpleCredentials(userId, new char[0]));
+            cs = login(new SimpleCredentials(USER_ID, new char[0]));
 
             root.refresh();
 
-            Authorizable a = userManager.getAuthorizable(userId);
+            Authorizable a = userManager.getAuthorizable(USER_ID);
             assertNotNull(a);
             for (String prop : externalUser.getProperties().keySet()) {
                 assertTrue(a.hasProperty(prop));

Modified: 
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTestBase.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTestBase.java?rev=1756752&r1=1756751&r2=1756752&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTestBase.java
 (original)
+++ 
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTestBase.java
 Thu Aug 18 10:04:48 2016
@@ -19,17 +19,9 @@ package org.apache.jackrabbit.oak.spi.se
 
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
 import javax.security.auth.login.AppConfigurationEntry;
 import javax.security.auth.login.Configuration;
 
-import org.apache.jackrabbit.api.security.user.Authorizable;
-import org.apache.jackrabbit.api.security.user.UserManager;
-import org.apache.jackrabbit.oak.AbstractSecurityTest;
 import org.apache.jackrabbit.oak.Oak;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.external.basic.DefaultSyncConfig;
 import 
org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler;
@@ -43,81 +35,43 @@ import org.junit.After;
 import org.junit.Before;
 
 /**
- * ExternalLoginModuleTest...
+ * Abstract base test for external-authentication including proper OSGi service
+ * registrations required for repository login respecting the {@link 
ExternalLoginModule}.
  */
-public abstract class ExternalLoginModuleTestBase extends AbstractSecurityTest 
{
-
-    private Set<String> ids = new HashSet<String>();
+public abstract class ExternalLoginModuleTestBase extends 
AbstractExternalAuthTest {
 
     private Registration testIdpReg;
-
     private Registration syncHandlerReg;
 
     protected final HashMap<String, Object> options = new HashMap<String, 
Object>();
 
     protected Whiteboard whiteboard;
 
-    protected ExternalIdentityProvider idp;
-
     protected SyncManager syncManager;
 
     protected ExternalIdentityProviderManager idpManager;
 
-    protected DefaultSyncConfig syncConfig;
-
     @Before
     public void before() throws Exception {
         super.before();
-        UserManager userManager = getUserManager(root);
-        Iterator<Authorizable> iter = 
userManager.findAuthorizables("jcr:primaryType", null);
-        while (iter.hasNext()) {
-            ids.add(iter.next().getID());
-        }
-        idp = createIDP();
 
         testIdpReg = whiteboard.register(ExternalIdentityProvider.class, idp, 
Collections.<String, Object>emptyMap());
 
         options.put(ExternalLoginModule.PARAM_SYNC_HANDLER_NAME, "default");
         options.put(ExternalLoginModule.PARAM_IDP_NAME, idp.getName());
 
-        // set default sync config
-        syncConfig = new DefaultSyncConfig();
-        Map<String, String> mapping = new HashMap<String, String>();
-        mapping.put("name", "name");
-        mapping.put("email", "email");
-        mapping.put("profile/name", "profile/name");
-        mapping.put("profile/age", "profile/age");
-        mapping.put("profile/constantProperty", "\"constant-value\"");
-        syncConfig.user().setPropertyMapping(mapping);
-        syncConfig.user().setMembershipNestingDepth(1);
         setSyncConfig(syncConfig);
     }
 
     @After
     public void after() throws Exception {
-        if (testIdpReg != null) {
-            testIdpReg.unregister();
-            testIdpReg = null;
-        }
-        destroyIDP(idp);
-        idp = null;
-        setSyncConfig(null);
-
         try {
-            UserManager userManager = getUserManager(root);
-            Iterator<Authorizable> iter = 
userManager.findAuthorizables("jcr:primaryType", null);
-            while (iter.hasNext()) {
-                ids.remove(iter.next().getID());
-            }
-            for (String id : ids) {
-                Authorizable a = userManager.getAuthorizable(id);
-                if (a != null) {
-                    a.remove();
-                }
+            if (testIdpReg != null) {
+                testIdpReg.unregister();
+                testIdpReg = null;
             }
-            root.commit();
+            setSyncConfig(null);
         } finally {
-            root.refresh();
             super.after();
         }
     }
@@ -136,12 +90,8 @@ public abstract class ExternalLoginModul
         return oak;
     }
 
-    protected abstract ExternalIdentityProvider createIDP();
-
-    protected abstract void destroyIDP(ExternalIdentityProvider idp);
-
     protected SynchronizationMBean createMBean() {
-        // todo: how to retrieve JCR repository here? maybe we should base the 
sync mbean on oak directly.
+        // todo: how to retrieve JCR repository here? maybe we should base the 
sync mbean on oak directly (=> OAK-4218).
         // JackrabbitRepository repository =  null;
         // return new SyncMBeanImpl(repository, syncManager, "default", 
idpManager, idp.getName());
 

Modified: 
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java?rev=1756752&r1=1756751&r2=1756752&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java
 (original)
+++ 
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java
 Thu Aug 18 10:04:48 2016
@@ -27,26 +27,58 @@ import javax.jcr.Credentials;
 import javax.jcr.SimpleCredentials;
 import javax.security.auth.login.LoginException;
 
+import com.google.common.collect.ImmutableList;
+
 public class TestIdentityProvider implements ExternalIdentityProvider {
 
+    public static final String ID_TEST_USER = "testUser";
+    public static final String ID_SECOND_USER = "secondUser";
+    public static final String ID_WILDCARD_USER = "wildcardUser";
+
+    public static final String ID_EXCEPTION = "throw!";
+
+    public static final String DEFAULT_IDP_NAME = "test";
+
     private final Map<String, ExternalGroup> externalGroups = new 
HashMap<String, ExternalGroup>();
     private final Map<String, ExternalUser> externalUsers = new 
HashMap<String, ExternalUser>();
 
+    private final String idpName;
 
     public TestIdentityProvider() {
-        addGroup(new TestGroup("aa"));
-        addGroup(new TestGroup("aaa"));
-        addGroup(new TestGroup("a").withGroups("aa", "aaa"));
-        addGroup(new TestGroup("b").withGroups("a"));
-        addGroup(new TestGroup("c"));
+        this(DEFAULT_IDP_NAME);
+    }
 
-        addUser(new TestUser("testUser")
+    public TestIdentityProvider(@Nonnull String idpName) {
+        this.idpName = idpName;
+
+        addGroup(new TestGroup("aa", getName()));
+        addGroup(new TestGroup("aaa", getName()));
+        addGroup(new TestGroup("a", getName()).withGroups("aa", "aaa"));
+        addGroup(new TestGroup("b", getName()).withGroups("a"));
+        addGroup(new TestGroup("c", getName()));
+        addGroup(new TestGroup("secondGroup", getName()));
+        addGroup(new TestGroup("_gr_u_", getName()));
+        addGroup(new TestGroup("g%r%", getName()));
+
+        addUser(new TestUser(ID_TEST_USER, getName())
                 .withProperty("name", "Test User")
                 .withProperty("profile/name", "Public Name")
                 .withProperty("profile/age", 72)
                 .withProperty("email", "[email protected]")
                 .withGroups("a", "b", "c")
         );
+
+        addUser(new TestUser(ID_SECOND_USER, getName())
+                .withProperty("profile/name", "Second User")
+                .withProperty("age", 24)
+                .withProperty("col", ImmutableList.of("v1", "v2", "v3"))
+                .withProperty("boolArr", new Boolean[]{true, false})
+                .withProperty("charArr", new char[]{'t', 'o', 'b'})
+                .withProperty("byteArr", new byte[0])
+                .withGroups("secondGroup"));
+
+        addUser(new TestUser(ID_WILDCARD_USER, getName())
+                .withGroups("_gr_u_", "g%r%"));
     }
 
     private void addUser(TestIdentity user) {
@@ -74,6 +106,9 @@ public class TestIdentityProvider implem
 
     @Override
     public ExternalUser getUser(@Nonnull String userId) throws 
ExternalIdentityException {
+        if (ID_EXCEPTION.equals(userId)) {
+            throw new ExternalIdentityException(ID_EXCEPTION);
+        }
         return externalUsers.get(userId.toLowerCase());
     }
 
@@ -94,6 +129,9 @@ public class TestIdentityProvider implem
 
     @Override
     public ExternalGroup getGroup(@Nonnull String name) throws 
ExternalIdentityException {
+        if (ID_EXCEPTION.equals(name)) {
+            throw new ExternalIdentityException(ID_EXCEPTION);
+        }
         return externalGroups.get(name.toLowerCase());
     }
 
@@ -109,17 +147,33 @@ public class TestIdentityProvider implem
         return externalGroups.values().iterator();
     }
 
-    private static class TestIdentity implements ExternalIdentity {
+    public static class TestIdentity implements ExternalIdentity {
 
         private final String userId;
+        private final String principalName;
         private final ExternalIdentityRef id;
 
         private final Set<ExternalIdentityRef> groups = new 
HashSet<ExternalIdentityRef>();
         private final Map<String, Object> props = new HashMap<String, 
Object>();
 
-        private TestIdentity(String userId) {
+        public TestIdentity() {
+            this("externalId", "principalName", DEFAULT_IDP_NAME);
+        }
+
+        public TestIdentity(@Nonnull String userId) {
+            this(userId, userId, DEFAULT_IDP_NAME);
+        }
+
+        public TestIdentity(@Nonnull String userId, @Nonnull String 
principalName, @Nonnull String idpName) {
             this.userId = userId;
-            id = new ExternalIdentityRef(userId, "test");
+            this.principalName = principalName;
+            id = new ExternalIdentityRef(userId, idpName);
+        }
+
+        public TestIdentity(@Nonnull ExternalIdentity base) {
+            userId = base.getId();
+            principalName = base.getPrincipalName();
+            id = base.getExternalId();
         }
 
         @Nonnull
@@ -131,7 +185,7 @@ public class TestIdentityProvider implem
         @Nonnull
         @Override
         public String getPrincipalName() {
-            return userId;
+            return principalName;
         }
 
         @Nonnull
@@ -164,16 +218,16 @@ public class TestIdentityProvider implem
 
         protected TestIdentity withGroups(String ... grps) {
             for (String grp: grps) {
-                groups.add(new ExternalIdentityRef(grp, "test"));
+                groups.add(new ExternalIdentityRef(grp, id.getProviderName()));
             }
             return this;
         }
     }
 
-    private static class TestUser extends TestIdentity implements ExternalUser 
{
+    public static class TestUser extends TestIdentity implements ExternalUser {
 
-        private TestUser(String userId) {
-            super(userId);
+        public TestUser(String userId, @Nonnull String idpName) {
+            super(userId, userId, idpName);
         }
 
         public String getPassword() {
@@ -182,10 +236,10 @@ public class TestIdentityProvider implem
 
     }
 
-    private static class TestGroup extends TestIdentity implements 
ExternalGroup {
+    public static class TestGroup extends TestIdentity implements 
ExternalGroup {
 
-        private TestGroup(String userId) {
-            super(userId);
+        public TestGroup(@Nonnull String userId, @Nonnull String idpName) {
+            super(userId, userId, idpName);
         }
 
         @Nonnull
@@ -194,4 +248,36 @@ public class TestIdentityProvider implem
             return null;
         }
     }
+
+    public static final class ForeignExternalUser extends 
TestIdentityProvider.TestIdentity implements ExternalUser {
+
+        public ForeignExternalUser() {
+            super();
+        }
+
+        @Nonnull
+        @Override
+        public ExternalIdentityRef getExternalId() {
+            return new ExternalIdentityRef(getId(), "AnotherExternalIDP");
+        }
+    }
+
+    public static final class ForeignExternalGroup extends 
TestIdentityProvider.TestIdentity implements ExternalGroup {
+
+        public ForeignExternalGroup() {
+            super();
+        }
+
+        @Nonnull
+        @Override
+        public ExternalIdentityRef getExternalId() {
+            return new ExternalIdentityRef(getId(), "AnotherExternalIDP");
+        }
+
+        @Nonnull
+        @Override
+        public Iterable<ExternalIdentityRef> getDeclaredMembers() {
+            return ImmutableList.of();
+        }
+    }
 }
\ No newline at end of file

Copied: 
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestSecurityProvider.java
 (from r1744292, 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestSecurityProvider.java)
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestSecurityProvider.java?p2=jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestSecurityProvider.java&p1=jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestSecurityProvider.java&r1=1744292&r2=1756752&rev=1756752&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestSecurityProvider.java
 (original)
+++ 
jackrabbit/oak/branches/1.4/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestSecurityProvider.java
 Thu Aug 18 10:04:48 2016
@@ -27,7 +27,8 @@ import org.apache.jackrabbit.oak.spi.sec
 import static com.google.common.base.Preconditions.checkNotNull;
 
 public class TestSecurityProvider extends SecurityProviderImpl {
-    public TestSecurityProvider(@Nonnull ConfigurationParameters 
configuration) {
+
+    public TestSecurityProvider(@Nonnull ConfigurationParameters 
configuration, @Nonnull ExternalPrincipalConfiguration 
externalPrincipalConfiguration) {
         super(configuration);
 
         PrincipalConfiguration principalConfiguration = 
getConfiguration(PrincipalConfiguration.class);
@@ -35,7 +36,7 @@ public class TestSecurityProvider extend
             throw new IllegalStateException();
         } else {
             PrincipalConfiguration defConfig = 
checkNotNull(((CompositePrincipalConfiguration) 
principalConfiguration).getDefaultConfig());
-            bindPrincipalConfiguration((new 
ExternalPrincipalConfiguration(this)));
+            bindPrincipalConfiguration(externalPrincipalConfiguration);
             bindPrincipalConfiguration(defConfig);
         }
     }


Reply via email to