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);
}