[dna-commits] DNA SVN: r1378 - in trunk: dna-jcr/src/main/java/org/jboss/dna/jcr and 4 other directories.

dna-commits at lists.jboss.org dna-commits at lists.jboss.org
Tue Dec 1 22:10:42 EST 2009


Author: bcarothers
Date: 2009-12-01 22:10:41 -0500 (Tue, 01 Dec 2009)
New Revision: 1378

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java
   trunk/web/dna-web-jcr-rest/src/main/java/org/jboss/dna/web/jcr/rest/JcrResources.java
   trunk/web/dna-web-jcr-rest/src/main/java/org/jboss/dna/web/jcr/rest/spi/DnaJcrRepositoryProvider.java
Log:
DNA-565 JCR Implementation Should Support Anonymous Access More Easily

Committed patch that adds a JcrRepository.Option for ANONYMOUS_USER_ROLES. This defaults to null (indicating that an anonymous user will not be granted any roles and will not be able to log in). It can be set to a comma-delimited list of role names that will be provided to an anonymous user. A test case has been added at the JCR layer.

The patch also changes the REST server around a bit to accommodate this new access. If the requesting user is not authenticated, the REST code will now attempt to log in anonymously. I've tested this locally and it did work for me, but I couldn't figure out a good way to write test cases for it and still test the behavior when it's not enabled (short of creating a whole new subproject which doesn't seem worth it).

I added this line to the configRepository.xml in the dna-web-jcr-rest project to test the anonymous access:
                <anonymousUserRoles jcr:primaryType="dna:option" value="connect,readonly"/>

This gave all unauthenticated users the ability to connect to the REST server and read-only privileges for all workspaces in all repositories.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java	2009-12-01 16:01:17 UTC (rev 1377)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java	2009-12-02 03:10:41 UTC (rev 1378)
@@ -26,7 +26,6 @@
 import java.security.AccessControlContext;
 import java.security.AccessController;
 import java.util.UUID;
-import javax.security.auth.login.LoginException;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.component.ClassLoaderFactory;
 import org.jboss.dna.common.component.StandardClassLoaderFactory;
@@ -300,10 +299,8 @@
      * @return the execution context that is identical with this execution context, but with a different security context; never
      *         null
      * @throws IllegalArgumentException if the <code>name</code> is null
-     * @throws LoginException if there <code>name</code> is invalid (or there is no login context named "other"), or if the
-     *         default callback handler JAAS property was not set or could not be loaded
      */
-    public ExecutionContext with( SecurityContext securityContext ) throws LoginException {
+    public ExecutionContext with( SecurityContext securityContext ) {
         return new ExecutionContext(this, securityContext);
     }
 

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-12-01 16:01:17 UTC (rev 1377)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-12-02 03:10:41 UTC (rev 1378)
@@ -29,6 +29,7 @@
 import java.security.AccessControlException;
 import java.security.AccessController;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumMap;
 import java.util.HashMap;
@@ -46,7 +47,6 @@
 import javax.security.auth.Subject;
 import javax.security.auth.login.Configuration;
 import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
 import net.jcip.annotations.GuardedBy;
 import net.jcip.annotations.Immutable;
 import net.jcip.annotations.ThreadSafe;
@@ -57,6 +57,7 @@
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
 import org.jboss.dna.graph.JaasSecurityContext;
+import org.jboss.dna.graph.SecurityContext;
 import org.jboss.dna.graph.Subgraph;
 import org.jboss.dna.graph.connector.RepositoryConnection;
 import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
@@ -137,9 +138,15 @@
         /**
          * The depth of the subgraphs that should be loaded the connectors. The default value is 1.
          */
-        READ_DEPTH;
+        READ_DEPTH,
 
         /**
+         * A comma-delimited list of default roles provided for anonymous access. A null or empty value for this option means that
+         * anonymous access is disabled.
+         */
+        ANONYMOUS_USER_ROLES;
+
+        /**
          * Determine the option given the option name. This does more than {@link Option#valueOf(String)}, since this method first
          * tries to match the supplied string to the option's {@link Option#name() name}, then the uppercase version of the
          * supplied string to the option's name, and finally if the supplied string is a camel-case version of the name (e.g.,
@@ -186,6 +193,11 @@
          * The default value for the {@link Option#READ_DEPTH} option is {@value} .
          */
         public static final String READ_DEPTH = "1";
+
+        /**
+         * The default value for the {@link Option#READ_DEPTH} option is {@value} .
+         */
+        public static final String ANONYMOUS_USER_ROLES = null;
     }
 
     /**
@@ -199,6 +211,7 @@
         defaults.put(Option.PROJECT_NODE_TYPES, DefaultOption.PROJECT_NODE_TYPES);
         defaults.put(Option.JAAS_LOGIN_CONFIG_NAME, DefaultOption.JAAS_LOGIN_CONFIG_NAME);
         defaults.put(Option.READ_DEPTH, DefaultOption.READ_DEPTH);
+        defaults.put(Option.ANONYMOUS_USER_ROLES, DefaultOption.ANONYMOUS_USER_ROLES);
         DEFAULT_OPTIONS = Collections.<Option, String>unmodifiableMap(defaults);
     }
 
@@ -217,6 +230,7 @@
     private final FederatedRepositorySource federatedSource;
     private final Observer observer;
     private final NamespaceRegistry persistentRegistry;
+    private final SecurityContext anonymousUserContext;
 
     /**
      * Creates a JCR repository that uses the supplied {@link RepositoryConnectionFactory repository connection factory} to
@@ -411,6 +425,34 @@
 
         this.lockManagers = new ConcurrentHashMap<String, WorkspaceLockManager>();
         this.locksPath = pathFactory.create(pathFactory.createRootPath(), JcrLexicon.SYSTEM, DnaLexicon.LOCKS);
+
+        /*
+         * Set up the anonymous role, if appropriate
+         */
+        SecurityContext anonymousUserContext = null;
+        String rawAnonRoles = options != null ? options.get(Option.ANONYMOUS_USER_ROLES) : null;
+        if (rawAnonRoles != null) {
+            String[] anonRoles = rawAnonRoles.split("\\s*,\\s*");
+            final List<String> roles = Arrays.asList(anonRoles);
+            if (anonRoles.length > 0) {
+                anonymousUserContext = new SecurityContext() {
+
+                    public String getUserName() {
+                        return null;
+                    }
+
+                    public boolean hasRole( String roleName ) {
+                        return roles.contains(roleName);
+                    }
+
+                    public void logout() {
+                    }
+
+                };
+            }
+        }
+
+        this.anonymousUserContext = anonymousUserContext;
     }
 
     protected void initializeSystemContent( Graph systemGraph ) {
@@ -563,16 +605,16 @@
         Map<String, Object> sessionAttributes = new HashMap<String, Object>();
         ExecutionContext execContext = null;
         if (credentials == null) {
-            try {
-                Subject subject = Subject.getSubject(AccessController.getContext());
-                if (subject == null) {
-                    throw new javax.jcr.LoginException(JcrI18n.mustBeInPrivilegedAction.text());
-                }
+            Subject subject = Subject.getSubject(AccessController.getContext());
+            if (subject != null) {
                 execContext = executionContext.with(new JaasSecurityContext(subject));
-            } catch (LoginException le) {
-                // This really can't happen if you're creating the JAAS security context with an existing subject
-                throw new IllegalStateException(le);
             }
+            // Well. There's no JAAS subject. Try using an anonymous user (if that's enabled).
+            else if (anonymousUserContext != null) {
+                execContext = executionContext.with(this.anonymousUserContext);
+            } else {
+                throw new javax.jcr.LoginException(JcrI18n.mustBeInPrivilegedAction.text());
+            }
         } else {
             try {
                 if (credentials instanceof SimpleCredentials) {

Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-12-01 16:01:17 UTC (rev 1377)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-12-02 03:10:41 UTC (rev 1378)
@@ -23,7 +23,7 @@
 #
 cannotConvertValue = Cannot convert {0} value to {1}
 credentialsMustProvideJaasMethod = The Credentials class "{0}" must implement "public LoginContext getLoginContext();", be an instance of "javax.jcr.SimpleCredentials", or be an instance of "org.jboss.dna.jcr.SecurityContextCredentials"
-mustBeInPrivilegedAction=login() can only be called successfully from within a java.security.PrivilegedAction
+mustBeInPrivilegedAction=login() can only be called successfully from within a java.security.PrivilegedAction or when the ANONYMOUS_USER_ROLES repository option is set
 credentialsMustReturnLoginContext = The "getLoginContext()" method in Credentials class "{0}" must not return a null
 defaultWorkspaceName=
 inputStreamConsumed = This value was already consumed as an input stream

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java	2009-12-01 16:01:17 UTC (rev 1377)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java	2009-12-02 03:10:41 UTC (rev 1378)
@@ -249,6 +249,7 @@
         options.put(Option.JAAS_LOGIN_CONFIG_NAME, "test");
         options.put(Option.PROJECT_NODE_TYPES, DefaultOption.PROJECT_NODE_TYPES);
         options.put(Option.READ_DEPTH, DefaultOption.READ_DEPTH);
+        options.put(Option.ANONYMOUS_USER_ROLES, DefaultOption.ANONYMOUS_USER_ROLES);
         assertThat(repository.getOptions(), is(options));
     }
 

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java	2009-12-01 16:01:17 UTC (rev 1377)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java	2009-12-02 03:10:41 UTC (rev 1378)
@@ -26,6 +26,7 @@
 import static org.hamcrest.collection.IsArrayContaining.hasItemInArray;
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
 import static org.junit.Assert.assertThat;
 import java.security.AccessControlContext;
 import java.security.AccessController;
@@ -226,6 +227,19 @@
     }
 
     @Test
+    public void shouldAllowLoginWithNoCredentialsIfAnonAccessEnabled() throws Exception {
+        Map<JcrRepository.Option, String> options = new HashMap<JcrRepository.Option, String>();
+        options.put(JcrRepository.Option.ANONYMOUS_USER_ROLES, JcrSession.DNA_READ_PERMISSION);
+        JcrRepository repository = new JcrRepository(context, connectionFactory, sourceName, descriptors, options);
+
+        session = (JcrSession)repository.login();
+
+        assertThat(session, is(notNullValue()));
+        assertThat(session.getUserID(), is(nullValue()));
+
+    }
+
+    @Test
     public void shouldAllowLoginWithProperCredentials() throws Exception {
         repository.login(credentials);
         repository.login(new SecurityContextCredentials(

Modified: trunk/web/dna-web-jcr-rest/src/main/java/org/jboss/dna/web/jcr/rest/JcrResources.java
===================================================================
--- trunk/web/dna-web-jcr-rest/src/main/java/org/jboss/dna/web/jcr/rest/JcrResources.java	2009-12-01 16:01:17 UTC (rev 1377)
+++ trunk/web/dna-web-jcr-rest/src/main/java/org/jboss/dna/web/jcr/rest/JcrResources.java	2009-12-02 03:10:41 UTC (rev 1378)
@@ -185,13 +185,7 @@
                                 String rawRepositoryName,
                                 String rawWorkspaceName ) throws RepositoryException {
         assert request != null;
-        assert request.getUserPrincipal() != null : "Request must be authorized";
 
-        // Sanity check
-        if (request.getUserPrincipal() == null) {
-            throw new UnauthorizedException("Client is not authorized");
-        }
-
         return RepositoryFactory.getSession(request, repositoryNameFor(rawRepositoryName), workspaceNameFor(rawWorkspaceName));
     }
 

Modified: trunk/web/dna-web-jcr-rest/src/main/java/org/jboss/dna/web/jcr/rest/spi/DnaJcrRepositoryProvider.java
===================================================================
--- trunk/web/dna-web-jcr-rest/src/main/java/org/jboss/dna/web/jcr/rest/spi/DnaJcrRepositoryProvider.java	2009-12-01 16:01:17 UTC (rev 1377)
+++ trunk/web/dna-web-jcr-rest/src/main/java/org/jboss/dna/web/jcr/rest/spi/DnaJcrRepositoryProvider.java	2009-12-02 03:10:41 UTC (rev 1378)
@@ -38,7 +38,6 @@
 import org.jboss.dna.jcr.SecurityContextCredentials;
 import org.jboss.dna.web.jcr.rest.ServletSecurityContext;
 import org.jboss.resteasy.spi.NotFoundException;
-import org.jboss.resteasy.spi.UnauthorizedException;
 import org.xml.sax.SAXException;
 
 /**
@@ -102,13 +101,7 @@
                                String repositoryName,
                                String workspaceName ) throws RepositoryException {
         assert request != null;
-        assert request.getUserPrincipal() != null : "Request must be authorized";
 
-        // Sanity check in case assertions are disabled
-        if (request.getUserPrincipal() == null) {
-            throw new UnauthorizedException("Client is not authorized");
-        }
-
         Repository repository;
 
         try {
@@ -118,6 +111,11 @@
             throw new NotFoundException(re.getMessage(), re);
         }
 
+        // If there's no authenticated user, try an anonymous login
+        if (request.getUserPrincipal() == null) {
+            return repository.login(workspaceName);
+        }
+
         return repository.login(new SecurityContextCredentials(new ServletSecurityContext(request)), workspaceName);
 
     }



More information about the dna-commits mailing list