Author: bcarothers
Date: 2009-05-11 14:39:47 -0400 (Mon, 11 May 2009)
New Revision: 901
Added:
trunk/dna-jcr/src/test/resources/security/
trunk/dna-jcr/src/test/resources/security/jaas.conf.xml
trunk/dna-jcr/src/test/resources/security/tck_roles.properties
trunk/dna-jcr/src/test/resources/security/tck_users.properties
Modified:
trunk/dna-jcr/pom.xml
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
Log:
Added DNA-372_idtrust_auth_auth.patch that replaces the previous patch. This adds all of
the functionality of the previous patch plus authorization (i.e.,
JcrSession.checkPermission now works). Tests were modified accordingly.
The patch uses JBoss IDTrust to declaratively add a (configurable) login manager from
InMemoryRepositoryStub. The InMemoryRepositoryStub uses
/src/test/resources/security/jaas.config.xml as the configuration file of choice. The test
implementation of this file specifies a JBoss UsersRolesLoginModule, that loads its users
from /src/test/resources/security/tck_users.properties. Each property name in this file
represents a valid user and each property value represents their passwordl The
UsersRolesLoginModule also specifies a file of user roles -
/src/test/resources/security/tck_roles.properties. Each property name is a user name
(which must match a user from tck_users.properties) and each property value is a
comma-delimited list of assigned roles for the user. All of this code (and the IDTrust
dependencies) are in test scope.
JcrSession loads the roles for the JAAS subject in its contstructor and stores them in a
set ("entitlements"). It expects roles named "readonly",
"readwrite", "readonly.<workspaceName>", or
"readwrite.<workspaceName>". The former two formats imply a global
permission that is assigned to the user for all workspaces. JcrSession.checkPermission
determines if the permission to be checked is a read permission or if it requires
readwrite access and checks to see if the JAAS subject has a role with the naming pattern
above assigned. This introduces a dependency on JBoss SX. Per-path permissions are not
supported at this time. Finer-grained access ("add_node" vs.
"set_property" vs. "remove") are not supported at this time. Better
authorization implementations are always welcome.
Modified: trunk/dna-jcr/pom.xml
===================================================================
--- trunk/dna-jcr/pom.xml 2009-05-11 17:25:19 UTC (rev 900)
+++ trunk/dna-jcr/pom.xml 2009-05-11 18:39:47 UTC (rev 901)
@@ -107,5 +107,35 @@
<artifactId>google-collect</artifactId>
<version>snapshot-20080530</version>
</dependency>
+
+ <dependency>
+ <groupId>org.jboss.security</groupId>
+ <artifactId>jboss-idtrust</artifactId>
+ <version>2.0.2.CR1</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.security</groupId>
+ <artifactId>jboss-security-spi-bare</artifactId>
+ <version>2.0.2.SP6</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.security</groupId>
+ <artifactId>jbosssx-bare</artifactId>
+ <version>2.0.2.SP6</version>
+ <exclusions>
+ <exclusion>
+ <groupId>apache-xalan</groupId>
+ <artifactId>xalan</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>apache-xalan</groupId>
+ <artifactId>serializer</artifactId>
+ </exclusion>
+ </exclusions>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
\ No newline at end of file
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java 2009-05-11 17:25:19 UTC
(rev 900)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java 2009-05-11 18:39:47 UTC
(rev 901)
@@ -28,7 +28,10 @@
import java.io.OutputStream;
import java.security.AccessControlException;
import java.security.Principal;
+import java.security.acl.Group;
import java.util.Calendar;
+import java.util.Enumeration;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -84,6 +87,9 @@
private static final String[] NO_ATTRIBUTES_NAMES = new String[] {};
+ private static final String READ_PERMISSION = "readonly";
+ private static final String WRITE_PERMISSION = "readwrite";
+
/**
* The repository that created this session.
*/
@@ -115,6 +121,11 @@
*/
private final Graph graph;
+ /**
+ * The set of assigned entitlements for the logged-in subject
+ */
+ private Set<String> entitlements;
+
private final SessionCache cache;
/**
@@ -153,12 +164,29 @@
this.graph);
this.isLive = true;
+ Subject subject = this.executionContext.getSubject();
+ this.entitlements = new HashSet<String>();
+
+ if (subject != null) {
+ for (Principal principal : subject.getPrincipals()) {
+ if (principal instanceof Group) {
+ Group group = (Group)principal;
+ Enumeration<? extends Principal> roles = group.members();
+
+ while (roles.hasMoreElements()) {
+ Principal role = roles.nextElement();
+ entitlements.add(role.getName());
+ }
+ }
+ }
+ }
assert this.repository != null;
assert this.sessionAttributes != null;
assert this.workspace != null;
assert this.executionContext != null;
assert this.sessionRegistry != null;
assert this.graph != null;
+ assert this.entitlements != null;
}
// Added to facilitate mock testing of items without necessarily requiring an entire
repository structure to be built
@@ -274,6 +302,18 @@
}
/**
+ * Returns the entitlements (permissions) available to the {@link
ExecutionContext#getSubject() subject} for this session.
+ * <p>
+ * Entitlements are exposed through this method to allow for easier mock testing.
+ * </p>
+ *
+ * @return the entitlements (permissions) available to the {@link
ExecutionContext#getSubject() subject} for this session.
+ */
+ Set<String> entitlements() {
+ return this.entitlements;
+ }
+
+ /**
* {@inheritDoc}
*
* @throws IllegalArgumentException if either <code>path</code> or
<code>actions</code> is empty or <code>null</code>.
@@ -283,9 +323,22 @@
String actions ) {
CheckArg.isNotEmpty(path, "path");
CheckArg.isNotEmpty(actions, "actions");
- if (!"read".equals(actions)) {
- throw new AccessControlException(JcrI18n.permissionDenied.text(path,
actions));
+
+ Set<String> entitlements = entitlements();
+ if ("read".equals(actions)) {
+ // readonly access is sufficient
+ if (entitlements.contains(READ_PERMISSION) ||
entitlements.contains(READ_PERMISSION + "." + this.workspace.getName())) {
+ return;
+ }
}
+
+ // need readwrite access
+ if (entitlements.contains(WRITE_PERMISSION) ||
entitlements.contains(WRITE_PERMISSION + "." + this.workspace.getName())) {
+ return;
+ }
+
+ throw new AccessControlException(JcrI18n.permissionDenied.text(path, actions));
+
}
/**
@@ -602,11 +655,8 @@
*
* @see javax.jcr.Session#impersonate(javax.jcr.Credentials)
*/
- @SuppressWarnings( "unused" )
public Session impersonate( Credentials credentials ) throws RepositoryException {
- // this is not right:
- // return repository.login(credentials);
- throw new UnsupportedOperationException();
+ return repository.login(credentials, this.workspace.getName());
}
/**
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java 2009-05-11
17:25:19 UTC (rev 900)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java 2009-05-11
18:39:47 UTC (rev 901)
@@ -25,12 +25,9 @@
import java.io.File;
import java.net.URI;
-import java.security.AccessControlContext;
-import java.security.AccessController;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
-import javax.jcr.Credentials;
import org.apache.jackrabbit.test.RepositoryStub;
import org.jboss.dna.common.collection.Problem;
import org.jboss.dna.graph.ExecutionContext;
@@ -42,71 +39,56 @@
import org.jboss.dna.graph.io.GraphImporter;
import org.jboss.dna.graph.property.Path;
import org.jboss.dna.jcr.JcrRepository.Options;
+import org.jboss.security.config.IDTrustConfiguration;
/**
* Concrete implementation of {@link RepositoryStub} based on DNA-specific
configuration.
*/
public class InMemoryRepositoryStub extends RepositoryStub {
private JcrRepository repository;
- protected InMemoryRepositorySource source;
- protected AccessControlContext accessControlContext = AccessController.getContext();
- private Credentials superUserCredentials = new Credentials() {
- private static final long serialVersionUID = 1L;
+ static {
- @SuppressWarnings( "unused" )
- public AccessControlContext getAccessControlContext() {
- return accessControlContext;
- }
- };
+ // Initialize IDTrust
+ String configFile = "security/jaas.conf.xml";
+ IDTrustConfiguration idtrustConfig = new IDTrustConfiguration();
- private Credentials readWriteCredentials = new Credentials() {
- private static final long serialVersionUID = 1L;
-
- @SuppressWarnings( "unused" )
- public AccessControlContext getAccessControlContext() {
- return accessControlContext;
+ try {
+ idtrustConfig.config(configFile);
+ } catch (Exception ex) {
+ throw new IllegalStateException(ex);
}
- };
+ }
- private Credentials readOnlyCredentials = new Credentials() {
- private static final long serialVersionUID = 1L;
-
- @SuppressWarnings( "unused" )
- public AccessControlContext getAccessControlContext() {
- return accessControlContext;
- }
- };
-
- protected ExecutionContext executionContext = new ExecutionContext();
-
- protected RepositoryConnectionFactory connectionFactory = new
RepositoryConnectionFactory() {
- public RepositoryConnection createConnection( String sourceName ) {
- return source.getConnection();
- }
- };
-
public InMemoryRepositoryStub( Properties env ) {
super(env);
// Create the in-memory (DNA) repository
- source = new InMemoryRepositorySource();
+ final InMemoryRepositorySource source = new InMemoryRepositorySource();
// Various calls will fail if you do not set a non-null name for the source
source.setName("TestRepositorySource");
- // Make sure the path to the namespaces exists ...
- Graph graph = Graph.create(source.getName(), connectionFactory,
executionContext);
-
graph.create("/jcr:system").and().create("/jcr:system/dna:namespaces");
+ ExecutionContext executionContext = new ExecutionContext();
+ executionContext.getNamespaceRegistry().register(TestLexicon.Namespace.PREFIX,
TestLexicon.Namespace.URI);
+ RepositoryConnectionFactory connectionFactory = new RepositoryConnectionFactory()
{
+ public RepositoryConnection createConnection( String sourceName ) {
+ return source.getConnection();
+ }
+ };
+
// Wrap a connection to the in-memory (DNA) repository in a (JCR) repository
Map<Options, String> options =
Collections.singletonMap(Options.PROJECT_NODE_TYPES, "false");
- repository = new JcrRepository(executionContext.create(accessControlContext),
connectionFactory, source.getName(), null,
- options);
+ repository = new JcrRepository(executionContext, connectionFactory,
source.getName(), null, options);
RepositoryNodeTypeManager nodeTypes = repository.getRepositoryTypeManager();
// Set up some sample nodes in the graph to match the expected test
configuration
+ Graph graph = Graph.create(source.getName(), connectionFactory,
executionContext);
+ GraphImporter importer = new GraphImporter(graph);
+ Path destinationPath =
executionContext.getValueFactories().getPathFactory().createRootPath();
+
try {
CndNodeTypeSource nodeTypeSource = new
CndNodeTypeSource("/tck_test_types.cnd");
@@ -119,14 +101,8 @@
nodeTypes.registerNodeTypes(nodeTypeSource);
-
executionContext.getNamespaceRegistry().register(TestLexicon.Namespace.PREFIX,
TestLexicon.Namespace.URI);
-
- Path destinationPath =
executionContext.getValueFactories().getPathFactory().create("/");
- GraphImporter importer = new GraphImporter(graph);
-
URI xmlContent = new
File("src/test/resources/repositoryForTckTests.xml").toURI();
- Graph.Batch batch = importer.importXml(xmlContent,
Location.create(destinationPath));
- batch.execute();
+ importer.importXml(xmlContent, Location.create(destinationPath)).execute();
} catch (Exception ex) {
// The TCK tries to quash this exception. Print it out to be more obvious.
@@ -138,36 +114,6 @@
/**
* {@inheritDoc}
*
- * @see org.apache.jackrabbit.test.RepositoryStub#getSuperuserCredentials()
- */
- @Override
- public Credentials getSuperuserCredentials() {
- return superUserCredentials;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.apache.jackrabbit.test.RepositoryStub#getReadOnlyCredentials()
- */
- @Override
- public Credentials getReadOnlyCredentials() {
- return readOnlyCredentials;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.apache.jackrabbit.test.RepositoryStub#getReadOnlyCredentials()
- */
- @Override
- public Credentials getReadWriteCredentials() {
- return readWriteCredentials;
- }
-
- /**
- * {@inheritDoc}
- *
* @see org.apache.jackrabbit.test.RepositoryStub#getRepository()
*/
@Override
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java 2009-05-11 17:25:19
UTC (rev 900)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java 2009-05-11 18:39:47
UTC (rev 901)
@@ -38,7 +38,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.security.AccessControlException;
import java.security.Principal;
import java.util.Calendar;
import java.util.Collections;
@@ -192,11 +191,6 @@
session.addLockToken(null);
}
- @Test
- public void shouldAllowCheckReadPermission() throws Exception {
- session.checkPermission("/", "read");
- }
-
@Test( expected = IllegalArgumentException.class )
public void shouldNotAllowCheckPermissionWithNoPath() throws Exception {
session.checkPermission(null, "read");
@@ -217,11 +211,6 @@
session.checkPermission("/", "");
}
- @Test( expected = AccessControlException.class )
- public void shouldNotAllowCheckNonReadPermission() throws Exception {
- session.checkPermission("/", "any");
- }
-
@Test
public void shouldReturnNullValueForNullAttributeName() throws Exception {
assertThat(session.getAttribute(null), nullValue());
@@ -365,11 +354,6 @@
assertThat(session.hasPendingChanges(), is(false));
}
- @Test( expected = UnsupportedOperationException.class )
- public void shouldAllowImpersonation() throws Exception {
- assertThat(session.impersonate(null), notNullValue());
- }
-
@Test
public void shouldProvideItemExists() throws Exception {
assertThat(session.itemExists("/a/b"), is(true));
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java 2009-05-11 17:25:19 UTC
(rev 900)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java 2009-05-11 18:39:47 UTC
(rev 901)
@@ -27,7 +27,9 @@
import junit.framework.TestSuite;
import org.apache.jackrabbit.test.JCRTestSuite;
import org.apache.jackrabbit.test.api.AddNodeTest;
+import org.apache.jackrabbit.test.api.CheckPermissionTest;
import org.apache.jackrabbit.test.api.DocumentViewImportTest;
+import org.apache.jackrabbit.test.api.ImpersonateTest;
import org.apache.jackrabbit.test.api.NamespaceRegistryTest;
import org.apache.jackrabbit.test.api.NodeAddMixinTest;
import org.apache.jackrabbit.test.api.NodeCanAddMixinTest;
@@ -226,8 +228,8 @@
addTestSuite(WorkspaceMoveVersionableTest.class);
addTestSuite(RepositoryLoginTest.class);
- // addTestSuite(ImpersonateTest.class);
- // addTestSuite(CheckPermissionTest.class);
+ addTestSuite(ImpersonateTest.class);
+ addTestSuite(CheckPermissionTest.class);
addTestSuite(DocumentViewImportTest.class);
addTestSuite(SerializationTest.class);
Added: trunk/dna-jcr/src/test/resources/security/jaas.conf.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/security/jaas.conf.xml (rev
0)
+++ trunk/dna-jcr/src/test/resources/security/jaas.conf.xml 2009-05-11 18:39:47 UTC (rev
901)
@@ -0,0 +1,24 @@
+<?xml version='1.0'?>
+
+<policy
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="urn:jboss:security-config:5.0"
xmlns="urn:jboss:security-config:5.0"
+ xmlns:jbxb="urn:jboss:security-config:5.0">
+ <application-policy name="dna-jcr">
+ <authentication>
+ <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
flag="required">
+ <module-option
name="usersProperties">security/tck_users.properties</module-option>
+ <module-option
name="rolesProperties">security/tck_roles.properties</module-option>
+ <module-option name="name">1.1</module-option>
+ <module-option name="succeed">true</module-option>
+ <module-option name="throwEx">false</module-option>
+ </login-module>
+ </authentication>
+<!--
+ <authorization>
+ <policy-module
code="org.jboss.security.idtrust.impl.plugins.authorization.IDTrustAuthorizationModule">
+ <module-option name="roles">validuser</module-option>
+ </policy-module>
+ </authorization>
+-->
+ </application-policy>
+</policy>
Property changes on: trunk/dna-jcr/src/test/resources/security/jaas.conf.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-jcr/src/test/resources/security/tck_roles.properties
===================================================================
--- trunk/dna-jcr/src/test/resources/security/tck_roles.properties
(rev 0)
+++ trunk/dna-jcr/src/test/resources/security/tck_roles.properties 2009-05-11 18:39:47 UTC
(rev 901)
@@ -0,0 +1,4 @@
+#<userName>=[readonly[.<workspaceName>] |
readwrite[.<workspaceName>]][, [readonly[.<workspaceName>] |
readwrite[.<workspaceName>]]]*
+superuser=readwrite
+readwrite=readwrite
+readonly=readonly
Added: trunk/dna-jcr/src/test/resources/security/tck_users.properties
===================================================================
--- trunk/dna-jcr/src/test/resources/security/tck_users.properties
(rev 0)
+++ trunk/dna-jcr/src/test/resources/security/tck_users.properties 2009-05-11 18:39:47 UTC
(rev 901)
@@ -0,0 +1,4 @@
+# <username>=<password>
+superuser=superuser
+readwrite=readwrite
+readonly=readonly