SLING-6973: Adjust AbstractSlingRepository2 to reflect SLING-6963 - patch provided by Angela Schreiber.

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1801483 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index 8342801..0c2e3c4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -111,7 +111,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.serviceusermapper</artifactId>
-            <version>1.0.0</version>
+            <version>1.3.3-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
         
diff --git a/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository2.java b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository2.java
index 6ae66d7..848e915 100644
--- a/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository2.java
+++ b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository2.java
@@ -18,6 +18,12 @@
  */
 package org.apache.sling.jcr.base;
 
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 import javax.jcr.Credentials;
 import javax.jcr.GuestCredentials;
 import javax.jcr.LoginException;
@@ -27,6 +33,7 @@
 import javax.jcr.Session;
 import javax.jcr.SimpleCredentials;
 import javax.jcr.Value;
+import javax.security.auth.Subject;
 
 import org.apache.sling.jcr.api.SlingRepository;
 import org.apache.sling.serviceusermapping.ServiceUserMapper;
@@ -90,7 +97,7 @@
         if(usingBundle == null) {
             throw new IllegalArgumentException("usingBundle is null");
         }
-     }
+    }
 
     /**
      * @return The {@link AbstractSlingRepositoryManager} controlling this
@@ -138,6 +145,35 @@
      */
     protected abstract Session createAdministrativeSession(final String workspace) throws RepositoryException;
 
+
+    /**
+     * Creates a new service-session. This method is used by {@link #loginService(String, String)}
+     * and {@link #impersonateFromService(String, Credentials, String)} to avoid
+     * code duplication.
+     *
+     * @param usingBundle The bundle using a service session.
+     * @param subServiceName The optional name of the subservice.
+     * @param workspaceName The JCR workspace name or {@code null}.
+     * @return A new service session or {@code null} if neither a user-name nor a
+     *         principal names mapping has been defined.
+     * @throws RepositoryException If an error occurs while creating the service session.
+     */
+    private Session createServiceSession(Bundle usingBundle, String subServiceName, String workspaceName) throws RepositoryException {
+        final ServiceUserMapper serviceUserMapper = this.getSlingRepositoryManager().getServiceUserMapper();
+        if (serviceUserMapper != null) {
+            final Iterable<String> principalNames = serviceUserMapper.getServicePrincipalNames(usingBundle, subServiceName);
+            if (principalNames != null) {
+                return createServiceSession(principalNames, workspaceName);
+            }
+
+            final String userName = serviceUserMapper.getServiceUserID(usingBundle, subServiceName);
+            if (userName != null) {
+                return createServiceSession(userName, workspaceName);
+            }
+        }
+        return null;
+    }
+
     /**
      * Creates a service-session for the service's {@code serviceUserName} by
      * impersonating the user from an administrative session.
@@ -170,6 +206,44 @@
         }
     }
 
+    /**
+     * Creates a service-session for the service's {@code servicePrincipalNames}
+     * using a pre-authenticated {@link Subject}.
+     * <p>
+     * Implementations of this class may overwrite this method to meet additional
+     * needs wrt the the nature of the principals, the {@code Subject} or additional
+     * attributes passed to the repository login.
+     *
+     * @param servicePrincipalNames The names of the service principals to create the session for
+     * @param workspace The workspace to access or {@code null} to access the {@link #getDefaultWorkspace() default workspace}
+     * @return A new service session
+     * @throws RepositoryException If a general error occurs while creating the session.
+     */
+    protected Session createServiceSession(final Iterable<String> servicePrincipalNames, final String workspaceName) throws RepositoryException {
+        Set<Principal> principals = new HashSet<>();
+        for (final String pName : servicePrincipalNames) {
+            if (pName != null && !pName.isEmpty()) {
+                principals.add(new Principal() {
+                    @Override
+                    public String getName() {
+                        return pName;
+                    }
+                });
+            }
+        };
+        Subject subject = new Subject(true, principals, Collections.emptySet(), Collections.emptySet());
+        try {
+            return Subject.doAsPrivileged(subject, new PrivilegedExceptionAction<Session>() {
+                @Override
+                public Session run() throws Exception {
+                    return AbstractSlingRepository2.this.getRepository().login(null, workspaceName);
+                }
+            }, null);
+        } catch (PrivilegedActionException e) {
+            throw new RepositoryException("failed to retrieve service session.", e);
+        }
+    }
+
     // login implementations (may be overwritten)
 
     /**
@@ -250,7 +324,7 @@
      */
     @Override
     public Session login(Credentials credentials, String workspace)
-    throws LoginException, NoSuchWorkspaceException, RepositoryException {
+            throws LoginException, NoSuchWorkspaceException, RepositoryException {
 
         if (credentials == null) {
             credentials = new GuestCredentials();
@@ -303,15 +377,13 @@
      */
     @Override
     public final Session loginService(final String subServiceName, final String workspace)
-    throws LoginException, RepositoryException {
-        final ServiceUserMapper serviceUserMapper = this.getSlingRepositoryManager().getServiceUserMapper();
-        final String userName = (serviceUserMapper != null) ? serviceUserMapper.getServiceUserID(this.usingBundle,
-            subServiceName) : null;
-        if (userName == null) {
-            throw new LoginException("Cannot derive user name for bundle " + usingBundle + " and sub service "
-                + subServiceName);
+            throws LoginException, RepositoryException {
+        Session s = createServiceSession(usingBundle, subServiceName, workspace);
+        if (s != null) {
+            return s;
+        } else {
+            throw new LoginException("Can neither derive user name nor principal names for bundle " + usingBundle + " and sub service " + subServiceName);
         }
-        return createServiceSession(userName, workspace);
     }
 
     /**
@@ -337,15 +409,12 @@
     @Override
     public Session impersonateFromService(final String subServiceName, final Credentials credentials, final String workspaceName)
             throws LoginException, RepositoryException {
-        final ServiceUserMapper serviceUserMapper = this.getSlingRepositoryManager().getServiceUserMapper();
-        final String userName = (serviceUserMapper != null) ? serviceUserMapper.getServiceUserID(this.usingBundle,
-                subServiceName) : null;
-        if (userName == null) {
-            throw new LoginException("Cannot derive user name for bundle " + usingBundle + " and sub service " + subServiceName);
-        }
         Session serviceSession = null;
         try {
-            serviceSession = createServiceSession(userName, workspaceName);
+            serviceSession = createServiceSession(usingBundle, subServiceName, workspaceName);
+            if (serviceSession == null) {
+                throw new LoginException("Cannot create service session for bundle " + usingBundle + " and sub service " + subServiceName);
+            }
             return serviceSession.impersonate(credentials);
         } finally {
             if (serviceSession != null) {
diff --git a/src/main/java/org/apache/sling/jcr/base/package-info.java b/src/main/java/org/apache/sling/jcr/base/package-info.java
index 8e8d78e..6a3a94e 100644
--- a/src/main/java/org/apache/sling/jcr/base/package-info.java
+++ b/src/main/java/org/apache/sling/jcr/base/package-info.java
@@ -25,7 +25,7 @@
  * {@link org.apache.sling.jcr.base.AbstractSlingRepository2} being the
  * basis for the repository service instance handed to using bundles.
  */
-@org.osgi.annotation.versioning.Version("3.2.0")
+@org.osgi.annotation.versioning.Version("3.3.0")
 package org.apache.sling.jcr.base;
 
 
diff --git a/src/test/java/org/apache/sling/jcr/base/MockSlingRepository2.java b/src/test/java/org/apache/sling/jcr/base/MockSlingRepository2.java
index 8695f86..5cc2a6d 100644
--- a/src/test/java/org/apache/sling/jcr/base/MockSlingRepository2.java
+++ b/src/test/java/org/apache/sling/jcr/base/MockSlingRepository2.java
@@ -35,4 +35,9 @@
         // Assuming we run on a test repo with no access control
         return getRepository().login();
     }
+
+    @Override
+    protected Session createServiceSession(Iterable<String> principalNames, String workspace) throws RepositoryException {
+        return getRepository().login();
+    }
 }
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/jcr/base/internal/WhitelistWiringTest.java b/src/test/java/org/apache/sling/jcr/base/internal/WhitelistWiringTest.java
index dda31d8..ef14d62 100644
--- a/src/test/java/org/apache/sling/jcr/base/internal/WhitelistWiringTest.java
+++ b/src/test/java/org/apache/sling/jcr/base/internal/WhitelistWiringTest.java
@@ -89,6 +89,11 @@
             protected Session createAdministrativeSession(String workspace) throws RepositoryException {
                 return Mockito.mock(Session.class);
             }
+
+            @Override
+            protected Session createServiceSession(Iterable<String> principalNames, String workspace) {
+                return Mockito.mock(Session.class);
+            }
         };
     }