From: Mathieu Baudier Date: Sun, 20 Mar 2011 18:09:50 +0000 (+0000) Subject: Improve Jackrabbit security X-Git-Tag: argeo-commons-2.1.30~1339 X-Git-Url: http://git.argeo.org/?a=commitdiff_plain;h=03db65bd74ce09b696a4c5af15a58df988e5368d;p=lgpl%2Fargeo-commons.git Improve Jackrabbit security git-svn-id: https://svn.argeo.org/commons/trunk@4325 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- diff --git a/demo/argeo-node-ui.product b/demo/argeo-node-ui.product index 820f999c7..8082cfce1 100644 --- a/demo/argeo-node-ui.product +++ b/demo/argeo-node-ui.product @@ -8,7 +8,7 @@ -console -clean - -Dlog4j.configuration="file:${system_property:user.home}/dev/src/commons/demo/log4j.properties" + -Dlog4j.configuration="file:${system_property:user.home}/dev/src/commons/demo/log4j.properties" -Dorg.argeo.security.ui.initialPerspective=org.argeo.jcr.ui.explorer.perspective -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts @@ -92,15 +92,16 @@ - + + - + @@ -154,9 +155,8 @@ - + - diff --git a/demo/log4j.properties b/demo/log4j.properties index 8a89a4aa3..1e5895edf 100644 --- a/demo/log4j.properties +++ b/demo/log4j.properties @@ -2,7 +2,9 @@ log4j.rootLogger=WARN, console ## Levels log4j.logger.org.argeo=DEBUG -log4j.logger.org.springframework.security=DEBUG +log4j.logger.org.argeo.jackrabbit.remote.ExtendedDispatcherServlet=WARN + +log4j.logger.org.springframework.security=WARN log4j.logger.org.apache.catalina=INFO log4j.logger.org.apache.coyote=INFO diff --git a/eclipse/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/commands/InitUserHome.java b/eclipse/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/commands/InitUserHome.java deleted file mode 100644 index 6bc5dff9f..000000000 --- a/eclipse/runtime/org.argeo.eclipse.ui.jcr/src/main/java/org/argeo/eclipse/ui/jcr/commands/InitUserHome.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.argeo.eclipse.ui.jcr.commands; - -import javax.jcr.Node; -import javax.jcr.Session; - -import org.argeo.eclipse.ui.dialogs.Error; -import org.argeo.jcr.ArgeoNames; -import org.argeo.jcr.ArgeoTypes; -import org.eclipse.core.commands.AbstractHandler; -import org.eclipse.core.commands.ExecutionEvent; -import org.eclipse.core.commands.ExecutionException; - -/** Init the user home directory within the node */ -public class InitUserHome extends AbstractHandler { - private Session session; - - private String defaultHome = "home"; - - public Object execute(ExecutionEvent event) throws ExecutionException { - String userID = ""; - try { - userID = session.getUserID(); - Node rootNode = session.getRootNode(); - Node homeNode; - if (!rootNode.hasNode(defaultHome)) { - homeNode = rootNode.addNode(defaultHome, ArgeoTypes.ARGEO_HOME); - } else { - homeNode = rootNode.getNode(defaultHome); - } - - if (!homeNode.hasNode(userID)) { - Node userHome = homeNode.addNode(userID); - userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME); - userHome.setProperty(ArgeoNames.ARGEO_USER_ID, userID); - } - session.save(); - } catch (Exception e) { - Error.show("Cannot initialize home for user '" + userID + "'", e); - } - return null; - } - - public void setSession(Session session) { - this.session = session; - } - -} diff --git a/gis/runtime/org.argeo.gis.geotools/src/main/java/org/argeo/geotools/jcr/GeoJcrIndex.java b/gis/runtime/org.argeo.gis.geotools/src/main/java/org/argeo/geotools/jcr/GeoJcrIndex.java index a1175abc7..75c1b81ad 100644 --- a/gis/runtime/org.argeo.gis.geotools/src/main/java/org/argeo/geotools/jcr/GeoJcrIndex.java +++ b/gis/runtime/org.argeo.gis.geotools/src/main/java/org/argeo/geotools/jcr/GeoJcrIndex.java @@ -7,6 +7,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.Executor; import javax.jcr.Node; import javax.jcr.Property; @@ -53,7 +54,7 @@ public class GeoJcrIndex implements EventListener, GisNames, GisTypes { private DataStore dataStore; private Session session; - private SystemExecutionService systemExecutionService; + private Executor systemExecutionService; /** The key is the workspace */ private Map> geoJcrIndexes = Collections @@ -63,7 +64,7 @@ public class GeoJcrIndex implements EventListener, GisNames, GisTypes { private FilterFactory2 ff = new FilterFactoryImpl(); public void init() { - systemExecutionService.executeAsSystem(new Runnable() { + systemExecutionService.execute(new Runnable() { public void run() { initGeoJcrIndex(); } @@ -102,7 +103,7 @@ public class GeoJcrIndex implements EventListener, GisNames, GisTypes { final Set toRemove = new HashSet(); // execute with system authentication so that JCR can be read - systemExecutionService.executeAsSystem(new Runnable() { + systemExecutionService.execute(new Runnable() { public void run() { while (events.hasNext()) { Event event = events.nextEvent(); @@ -292,7 +293,7 @@ public class GeoJcrIndex implements EventListener, GisNames, GisTypes { } public void setSystemExecutionService( - SystemExecutionService systemExecutionService) { + Executor systemExecutionService) { this.systemExecutionService = systemExecutionService; } diff --git a/security/modules/org.argeo.security.dao.jackrabbit/META-INF/MANIFEST.MF b/security/modules/org.argeo.security.dao.jackrabbit/META-INF/MANIFEST.MF index 437eb0ec0..51a6ca231 100644 --- a/security/modules/org.argeo.security.dao.jackrabbit/META-INF/MANIFEST.MF +++ b/security/modules/org.argeo.security.dao.jackrabbit/META-INF/MANIFEST.MF @@ -1,4 +1,8 @@ -Bundle-SymbolicName: org.argeo.security.dao.ldap +Bundle-SymbolicName: org.argeo.security.dao.jackrabbit Bundle-Version: 0.2.3.SNAPSHOT Import-Package: javax.jcr;version="[2.0.0,3.0.0)", + org.argeo.security, + org.argeo.security.jackrabbit.providers, + org.argeo.security.jcr, + org.springframework.security.providers;specification-version="2.0.6.RELEASE" Bundle-Name: Security DAO Jackrabbit diff --git a/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/jcrsecuritydao-osgi.xml b/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/jcrsecuritydao-osgi.xml index 6e511014b..0c2706d03 100644 --- a/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/jcrsecuritydao-osgi.xml +++ b/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/jcrsecuritydao-osgi.xml @@ -10,15 +10,15 @@ http://www.springframework.org/schema/util/spring-util-2.5.xsd"> - - + \ No newline at end of file diff --git a/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/jcrsecuritydao.xml b/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/jcrsecuritydao.xml index ca84bb90c..43f8520a7 100644 --- a/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/jcrsecuritydao.xml +++ b/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/jcrsecuritydao.xml @@ -3,11 +3,8 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> - - - - + \ No newline at end of file diff --git a/security/modules/org.argeo.security.dao.ldap/META-INF/MANIFEST.MF b/security/modules/org.argeo.security.dao.ldap/META-INF/MANIFEST.MF index d377808d8..24e756417 100644 --- a/security/modules/org.argeo.security.dao.ldap/META-INF/MANIFEST.MF +++ b/security/modules/org.argeo.security.dao.ldap/META-INF/MANIFEST.MF @@ -19,4 +19,4 @@ Import-Package: com.sun.jndi.ldap;resolution:=optional, org.springframework.security.providers.ldap.authenticator, org.springframework.security.userdetails, org.springframework.security.userdetails.ldap -Bundle-Name: Security Manager LDAP +Bundle-Name: Security DAO LDAP diff --git a/security/modules/org.argeo.security.dao.ldap/META-INF/spring/jcr.xml b/security/modules/org.argeo.security.dao.ldap/META-INF/spring/jcr.xml deleted file mode 100644 index 2a0a08c98..000000000 --- a/security/modules/org.argeo.security.dao.ldap/META-INF/spring/jcr.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap-jcr.xml b/security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap-jcr.xml new file mode 100644 index 000000000..794ac0195 --- /dev/null +++ b/security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap-jcr.xml @@ -0,0 +1,53 @@ + + + + + + + + + ${argeo.ldap.userClass} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap-osgi.xml b/security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap-osgi.xml index b09aabfe4..67ad89817 100644 --- a/security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap-osgi.xml +++ b/security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap-osgi.xml @@ -17,9 +17,10 @@ - + - @@ -40,13 +40,13 @@ + - @@ -60,7 +60,6 @@ - diff --git a/security/modules/org.argeo.security.dao.ldap/ldap.properties b/security/modules/org.argeo.security.dao.ldap/ldap.properties index 66eb37b43..bd6c91592 100644 --- a/security/modules/org.argeo.security.dao.ldap/ldap.properties +++ b/security/modules/org.argeo.security.dao.ldap/ldap.properties @@ -4,12 +4,17 @@ argeo.security.rolePrefix=ROLE_ argeo.ldap.rootdn=dc=demo,dc=argeo,dc=org argeo.ldap.protocol=ldap argeo.ldap.host=localhost +# default are for Apache Directory Server argeo.ldap.port=10389 argeo.ldap.manager.userdn=uid=admin,ou=system argeo.ldap.manager.password=secret +# USER +argeo.ldap.userClass=inetOrgPerson argeo.ldap.userBase=ou=People argeo.ldap.usernameAttribute=uid +argeo.ldap.passwordAttribute=userPassword +# ROLES argeo.ldap.groupClass=groupOfNames argeo.ldap.groupBase=ou=Roles argeo.ldap.groupRoleAttribute=cn diff --git a/security/modules/org.argeo.security.services/META-INF/spring/osgi.xml b/security/modules/org.argeo.security.services/META-INF/spring/osgi.xml index f83086a2b..6c21dbd26 100644 --- a/security/modules/org.argeo.security.services/META-INF/spring/osgi.xml +++ b/security/modules/org.argeo.security.services/META-INF/spring/osgi.xml @@ -7,6 +7,9 @@ http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> + diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/CurrentUserDao.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/CurrentUserDao.java index 6e5b4ec3c..736b0bafd 100644 --- a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/CurrentUserDao.java +++ b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/CurrentUserDao.java @@ -22,6 +22,7 @@ package org.argeo.security; public interface CurrentUserDao { public void updateCurrentUserPassword(String oldPassword, String newPassword); + @Deprecated public String getDefaultRole(); } diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SystemExecutionService.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SystemExecutionService.java index f5ef357c0..eba5d49d9 100644 --- a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SystemExecutionService.java +++ b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SystemExecutionService.java @@ -1,18 +1,22 @@ package org.argeo.security; +import java.util.concurrent.Executor; + import org.springframework.core.task.TaskExecutor; /** * Allows to execute code authenticated as a system user (that is not a real - * person) + * person). The {@link Executor} interface interface is not used directly in + * order to allow future extension of this interface and to simplify its + * publication (e.g. as an OSGi service) and interception. Support for Spring's + * {@link TaskExecutor} will be dropped when upgrading to Spring 3, since it is + * only to ensure compatibility with versions of Java before 1.5. */ -public interface SystemExecutionService { +public interface SystemExecutionService extends Executor, TaskExecutor { /** * Executes this Runnable within a system authenticated context. * Implementations should make sure that this method is properly secured via * Java permissions since it could access to everything without credentials. */ - public void executeAsSystem(Runnable runnable); - - public TaskExecutor createSystemAuthenticatedTaskExecutor(); + public void execute(Runnable runnable); } diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/KeyBasedSystemExecutionService.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/KeyBasedSystemExecutionService.java index 01d760f12..08ef6428a 100644 --- a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/KeyBasedSystemExecutionService.java +++ b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/KeyBasedSystemExecutionService.java @@ -8,11 +8,12 @@ import org.springframework.security.AuthenticationManager; import org.springframework.security.context.SecurityContext; import org.springframework.security.context.SecurityContextHolder; -public class KeyBasedSystemExecutionService implements SystemExecutionService { +public class KeyBasedSystemExecutionService implements SystemExecutionService, + TaskExecutor { private AuthenticationManager authenticationManager; private String systemAuthenticationKey; - public void executeAsSystem(Runnable runnable) { + public void execute(Runnable runnable) { wrapWithSystemAuthentication(runnable).run(); } diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/CurrentUserDaoJcr.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/CurrentUserDaoJcr.java new file mode 100644 index 000000000..0c8cc33b1 --- /dev/null +++ b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/CurrentUserDaoJcr.java @@ -0,0 +1,21 @@ +package org.argeo.security.jcr; + +import org.argeo.security.CurrentUserDao; + +public class CurrentUserDaoJcr implements CurrentUserDao { + private String defaultRole= "ROLE_USER"; + + public void updateCurrentUserPassword(String oldPassword, String newPassword) { + throw new UnsupportedOperationException( + "Updating passwords is not supported"); + } + + public String getDefaultRole() { + return defaultRole; + } + + public void setDefaultRole(String defaultRole) { + this.defaultRole = defaultRole; + } + +} diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrAuthenticationProvider.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrAuthenticationProvider.java index bfa51f5b1..b7680ad1b 100644 --- a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrAuthenticationProvider.java +++ b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrAuthenticationProvider.java @@ -12,34 +12,20 @@ import javax.jcr.SimpleCredentials; import org.argeo.ArgeoException; import org.argeo.jcr.ArgeoJcrConstants; -import org.argeo.jcr.ArgeoNames; -import org.argeo.jcr.ArgeoTypes; +import org.argeo.jcr.JcrUtils; import org.argeo.security.SiteAuthenticationToken; import org.springframework.security.Authentication; import org.springframework.security.AuthenticationException; import org.springframework.security.GrantedAuthority; import org.springframework.security.GrantedAuthorityImpl; import org.springframework.security.providers.AuthenticationProvider; +import org.springframework.security.userdetails.UserDetails; /** Connects to a JCR repository and delegate authentication to it. */ public class JcrAuthenticationProvider implements AuthenticationProvider { - private RepositoryFactory repositoryFactory; - private final String defaultHome; - private final String userRole; - - public JcrAuthenticationProvider() { - this("ROLE_USER", "home"); - } + public final static String ROLE_REMOTE_JCR_AUTHENTICATED = "ROLE_REMOTE_JCR_AUTHENTICATED"; - public JcrAuthenticationProvider(String userRole) { - this(userRole, "home"); - } - - public JcrAuthenticationProvider(String defaultHome, String userRole) { - super(); - this.defaultHome = defaultHome; - this.userRole = userRole; - } + private RepositoryFactory repositoryFactory; public Authentication authenticate(Authentication authentication) throws AuthenticationException { @@ -67,53 +53,50 @@ public class JcrAuthenticationProvider implements AuthenticationProvider { session = repository.login(sp); else session = repository.login(sp, workspace); - Node userHome = getUserHome(session); + Node userHome = JcrUtils.getUserHome(session); + if (userHome == null) + throw new ArgeoException("No home found for user " + + session.getUserID()); GrantedAuthority[] authorities = {}; - return new JcrAuthenticationToken(siteAuth.getPrincipal(), - siteAuth.getCredentials(), authorities, url, userHome); + JcrAuthenticationToken authen = new JcrAuthenticationToken( + siteAuth.getPrincipal(), siteAuth.getCredentials(), + authorities, url, userHome); + authen.setDetails(getUserDetails(userHome, authen)); + return authen; } catch (RepositoryException e) { throw new ArgeoException( "Unexpected exception when authenticating to " + url, e); } } + /** + * By default, assigns only the role {@value #ROLE_REMOTE_JCR_AUTHENTICATED} + * . Should typically be overridden in order to assign more relevant roles. + */ protected GrantedAuthority[] getGrantedAuthorities(Session session) { - return new GrantedAuthority[] { new GrantedAuthorityImpl(userRole) }; - } - - @SuppressWarnings("rawtypes") - public boolean supports(Class authentication) { - return SiteAuthenticationToken.class.isAssignableFrom(authentication); + return new GrantedAuthority[] { new GrantedAuthorityImpl( + ROLE_REMOTE_JCR_AUTHENTICATED) }; } - protected Node getUserHome(Session session) { - String userID = ""; + /** Builds user details based on the authentication and the user home. */ + protected UserDetails getUserDetails(Node userHome, + JcrAuthenticationToken authen) { try { - userID = session.getUserID(); - Node rootNode = session.getRootNode(); - Node homeNode; - if (!rootNode.hasNode(defaultHome)) { - homeNode = rootNode.addNode(defaultHome, ArgeoTypes.ARGEO_HOME); - } else { - homeNode = rootNode.getNode(defaultHome); - } - - Node userHome; - if (!homeNode.hasNode(userID)) { - userHome = homeNode.addNode(userID); - userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME); - userHome.setProperty(ArgeoNames.ARGEO_USER_ID, userID); - } else { - userHome = homeNode.getNode(userID); - } - session.save(); - return userHome; + // TODO: loads enabled, locked, etc. from the home node. + return new JcrUserDetails(userHome.getPath(), authen.getPrincipal() + .toString(), authen.getCredentials().toString(), true, + true, true, true, authen.getAuthorities()); } catch (Exception e) { - throw new ArgeoException("Cannot initialize home for user '" - + userID + "'", e); + throw new ArgeoException("Cannot get user details for " + userHome, + e); } } + @SuppressWarnings("rawtypes") + public boolean supports(Class authentication) { + return SiteAuthenticationToken.class.isAssignableFrom(authentication); + } + public void register(RepositoryFactory repositoryFactory, Map parameters) { this.repositoryFactory = repositoryFactory; @@ -123,13 +106,4 @@ public class JcrAuthenticationProvider implements AuthenticationProvider { Map parameters) { this.repositoryFactory = null; } - - public String getDefaultHome() { - return defaultHome; - } - - public String getUserRole() { - return userRole; - } - } diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrAuthenticationToken.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrAuthenticationToken.java index 865508317..27b7ee85b 100644 --- a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrAuthenticationToken.java +++ b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrAuthenticationToken.java @@ -2,33 +2,63 @@ package org.argeo.security.jcr; import javax.jcr.Node; import javax.jcr.RepositoryException; +import javax.jcr.Session; import org.argeo.ArgeoException; import org.argeo.security.SiteAuthenticationToken; import org.springframework.security.GrantedAuthority; +/** An authenticated authentication based on a JCR session. */ public class JcrAuthenticationToken extends SiteAuthenticationToken { private static final long serialVersionUID = -2736830165315486169L; - private final transient Node userHome; + + private final transient Session session; + private final String userHomePath; public JcrAuthenticationToken(Object principal, Object credentials, GrantedAuthority[] authorities, String url, Node userHome) { super(principal, credentials, authorities, url, extractWorkspace(userHome)); - this.userHome = userHome; + try { + this.session = userHome.getSession(); + this.userHomePath = userHome.getPath(); + } catch (RepositoryException e) { + throw new ArgeoException("Cannot extract path from " + userHome, e); + } } private static String extractWorkspace(Node userHome) { try { return userHome.getSession().getWorkspace().getName(); } catch (RepositoryException e) { - throw new ArgeoException("Cannot extract workspace of " + userHome, - e); + throw new ArgeoException("Cannot extract workspace from " + + userHome, e); } } - public Node getUserHome() { - return userHome; + /** The path to the authenticated user home node. */ + public String getUserHomePath() { + return userHomePath; + } + + /** The session used to create this authentication. */ + public Session getSession() { + return session; + } + + @Override + public boolean isAuthenticated() { + if (session == null || !session.isLive()) + setAuthenticated(false); + return super.isAuthenticated(); + } + + @Override + public void setAuthenticated(boolean isAuthenticated) + throws IllegalArgumentException { + super.setAuthenticated(isAuthenticated); + if (!isAuthenticated && session != null) + session.logout(); } } diff --git a/security/runtime/org.argeo.security.jackrabbit/pom.xml b/security/runtime/org.argeo.security.jackrabbit/pom.xml index b676aef80..2c74d3ccd 100644 --- a/security/runtime/org.argeo.security.jackrabbit/pom.xml +++ b/security/runtime/org.argeo.security.jackrabbit/pom.xml @@ -31,6 +31,7 @@ org.argeo.dep.osgi.jackrabbit org.argeo.security.jackrabbit.* + *,org.springframework.core diff --git a/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoAccessManager.java b/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoAccessManager.java index 4403def10..b29606318 100644 --- a/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoAccessManager.java +++ b/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoAccessManager.java @@ -1,8 +1,16 @@ package org.argeo.security.jackrabbit; +import javax.jcr.RepositoryException; + import org.apache.jackrabbit.core.security.DefaultAccessManager; /** Intermediary class in order to have a consistent naming in config files. */ public class ArgeoAccessManager extends DefaultAccessManager { + @Override + public boolean canAccess(String workspaceName) throws RepositoryException { + // TODO Auto-generated method stub + return super.canAccess(workspaceName); + } + } diff --git a/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoSecurityManager.java b/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoSecurityManager.java index a3a6d42d6..e5b83e316 100644 --- a/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoSecurityManager.java +++ b/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoSecurityManager.java @@ -1,11 +1,13 @@ package org.argeo.security.jackrabbit; +import java.security.Principal; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.jcr.RepositoryException; +import javax.jcr.Session; import javax.security.auth.Subject; import org.apache.commons.logging.Log; @@ -14,7 +16,11 @@ import org.apache.jackrabbit.api.security.user.Group; import org.apache.jackrabbit.api.security.user.User; import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.jackrabbit.core.DefaultSecurityManager; +import org.apache.jackrabbit.core.RepositoryImpl; +import org.apache.jackrabbit.core.security.AnonymousPrincipal; +import org.apache.jackrabbit.core.security.SecurityConstants; import org.apache.jackrabbit.core.security.SystemPrincipal; +import org.apache.jackrabbit.core.security.authorization.WorkspaceAccessManager; import org.argeo.ArgeoException; import org.springframework.security.Authentication; import org.springframework.security.GrantedAuthority; @@ -71,10 +77,54 @@ public class ArgeoSecurityManager extends DefaultSecurityManager { group.removeMember(user); } - if (log.isDebugEnabled()) - log.debug("Spring and Jackrabbit Security synchronized for user " + if (log.isTraceEnabled()) + log.trace("Spring and Jackrabbit Security synchronized for user " + userId + " in " + (System.currentTimeMillis() - begin) + " ms"); return userId; } + + @Override + protected WorkspaceAccessManager createDefaultWorkspaceAccessManager() { + WorkspaceAccessManager wam = super + .createDefaultWorkspaceAccessManager(); + return new ArgeoWorkspaceAccessManagerImpl(wam); + } + + private class ArgeoWorkspaceAccessManagerImpl implements SecurityConstants, + WorkspaceAccessManager { + private final WorkspaceAccessManager wam; + private String defaultWorkspace; + + public ArgeoWorkspaceAccessManagerImpl(WorkspaceAccessManager wam) { + super(); + this.wam = wam; + } + + public void init(Session systemSession) throws RepositoryException { + wam.init(systemSession); + defaultWorkspace = ((RepositoryImpl) getRepository()).getConfig() + .getDefaultWorkspaceName(); + } + + public void close() throws RepositoryException { + } + + public boolean grants(Set principals, String workspaceName) + throws RepositoryException { + // anonymous has access to the default workspace (required for + // remoting which does a default login when initializing the + // repository) + Boolean anonymous = false; + for (Principal principal : principals) + if (principal instanceof AnonymousPrincipal) + anonymous = true; + + if (anonymous && workspaceName.equals(defaultWorkspace)) + return true; + else + return wam.grants(principals, workspaceName); + } + } + } diff --git a/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrUserDetailsContextMapper.java b/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrUserDetailsContextMapper.java index 86c788a44..2d9fb1c1a 100644 --- a/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrUserDetailsContextMapper.java +++ b/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrUserDetailsContextMapper.java @@ -3,6 +3,7 @@ package org.argeo.security.ldap.jcr; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.Executor; import javax.jcr.Node; import javax.jcr.Repository; @@ -15,8 +16,8 @@ import org.apache.commons.logging.LogFactory; import org.argeo.ArgeoException; import org.argeo.jcr.ArgeoJcrConstants; import org.argeo.jcr.ArgeoNames; +import org.argeo.jcr.ArgeoTypes; import org.argeo.jcr.JcrUtils; -import org.argeo.security.SystemExecutionService; import org.argeo.security.jcr.JcrUserDetails; import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DirContextOperations; @@ -29,64 +30,100 @@ import org.springframework.security.userdetails.ldap.UserDetailsContextMapper; * checks of which values should be mandatory should be performed at a higher * level. */ -public class JcrUserDetailsContextMapper implements UserDetailsContextMapper { +public class JcrUserDetailsContextMapper implements UserDetailsContextMapper, + ArgeoNames { private final static Log log = LogFactory .getLog(JcrUserDetailsContextMapper.class); + private String usernameAttribute; + private String passwordAttribute; + private String homeBasePath; + private String[] userClasses; + private Map propertyToAttributes = new HashMap(); - private SystemExecutionService systemExecutionService; - private String homeBasePath = "/home"; + private Executor systemExecutor; private RepositoryFactory repositoryFactory; public UserDetails mapUserFromContext(final DirContextOperations ctx, final String username, GrantedAuthority[] authorities) { if (repositoryFactory == null) throw new ArgeoException("No JCR repository factory registered"); - final String userHomePath = usernameToHomePath(username); - systemExecutionService.executeAsSystem(new Runnable() { + final StringBuffer userHomePathT = new StringBuffer(""); + systemExecutor.execute(new Runnable() { public void run() { - Session session = null; - try { - Repository nodeRepo = JcrUtils.getRepositoryByAlias( - repositoryFactory, ArgeoJcrConstants.ALIAS_NODE); - session = nodeRepo.login(); - Node userProfile = JcrUtils.mkdirs(session, userHomePath - + '/' + ArgeoNames.ARGEO_USER_PROFILE); - for (String jcrProperty : propertyToAttributes.keySet()) - ldapToJcr(userProfile, jcrProperty, ctx); - session.save(); - if (log.isDebugEnabled()) - log.debug("Mapped " + ctx.getDn() + " to " - + userProfile); - } catch (RepositoryException e) { - throw new ArgeoException("Cannot synchronize JCR and LDAP", - e); - } finally { - session.logout(); - } + String userHomepath = mapLdapToJcr(username, ctx); + userHomePathT.append(userHomepath); } }); // password - byte[] arr = (byte[]) ctx.getAttributeSortedStringSet("userPassword") - .first(); - JcrUserDetails userDetails = new JcrUserDetails(userHomePath, username, - new String(arr), true, true, true, true, authorities); + byte[] arr = (byte[]) ctx + .getAttributeSortedStringSet(passwordAttribute).first(); + JcrUserDetails userDetails = new JcrUserDetails( + userHomePathT.toString(), username, new String(arr), true, + true, true, true, authorities); + // erase password Arrays.fill(arr, (byte) 0); return userDetails; } + /** @return path to the user home node */ + protected String mapLdapToJcr(String username, DirContextOperations ctx) { + Session session = null; + try { + Repository nodeRepo = JcrUtils.getRepositoryByAlias( + repositoryFactory, ArgeoJcrConstants.ALIAS_NODE); + session = nodeRepo.login(); + Node userHome = JcrUtils.getUserHome(session, username); + if (userHome == null) + userHome = createUserHome(session, username); + String userHomePath = userHome.getPath(); + Node userProfile = userHome.hasNode(ARGEO_USER_PROFILE) ? userHome + .getNode(ARGEO_USER_PROFILE) : userHome + .addNode(ARGEO_USER_PROFILE); + for (String jcrProperty : propertyToAttributes.keySet()) + ldapToJcr(userProfile, jcrProperty, ctx); + session.save(); + if (log.isDebugEnabled()) + log.debug("Mapped " + ctx.getDn() + " to " + userProfile); + return userHomePath; + } catch (RepositoryException e) { + JcrUtils.discardQuietly(session); + throw new ArgeoException("Cannot synchronize JCR and LDAP", e); + } finally { + session.logout(); + } + } + + protected Node createUserHome(Session session, String username) { + try { + Node userHome = JcrUtils.mkdirs(session, + usernameToHomePath(username)); + userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME); + userHome.setProperty(ARGEO_USER_ID, username); + return userHome; + } catch (RepositoryException e) { + throw new ArgeoException("Cannot create home node for user " + + username, e); + } + } + + protected String usernameToHomePath(String username) { + return homeBasePath + '/' + JcrUtils.firstCharsToPath(username, 2) + + '/' + username; + } + public void mapUserToContext(UserDetails user, final DirContextAdapter ctx) { if (!(user instanceof JcrUserDetails)) throw new ArgeoException("Unsupported user details: " + user.getClass()); - ctx.setAttributeValues("objectClass", new String[] { "inetOrgPerson" }); - ctx.setAttributeValue("uid", user.getUsername()); - ctx.setAttributeValue("userPassword", user.getPassword()); + ctx.setAttributeValues("objectClass", userClasses); + ctx.setAttributeValue(usernameAttribute, user.getUsername()); + ctx.setAttributeValue(passwordAttribute, user.getPassword()); final JcrUserDetails jcrUserDetails = (JcrUserDetails) user; - systemExecutionService.executeAsSystem(new Runnable() { + systemExecutor.execute(new Runnable() { public void run() { Session session = null; try { @@ -94,9 +131,7 @@ public class JcrUserDetailsContextMapper implements UserDetailsContextMapper { repositoryFactory, ArgeoJcrConstants.ALIAS_NODE); session = nodeRepo.login(); Node userProfile = session.getNode(jcrUserDetails - .getHomePath() - + '/' - + ArgeoNames.ARGEO_USER_PROFILE); + .getHomePath() + '/' + ARGEO_USER_PROFILE); for (String jcrProperty : propertyToAttributes.keySet()) jcrToLdap(userProfile, jcrProperty, ctx); if (log.isDebugEnabled()) @@ -112,11 +147,6 @@ public class JcrUserDetailsContextMapper implements UserDetailsContextMapper { }); } - protected String usernameToHomePath(String username) { - return homeBasePath + '/' + JcrUtils.firstCharsToPath(username, 2) - + '/' + username; - } - protected void ldapToJcr(Node userProfile, String jcrProperty, DirContextOperations ctx) { try { @@ -163,9 +193,8 @@ public class JcrUserDetailsContextMapper implements UserDetailsContextMapper { this.propertyToAttributes = propertyToAttributes; } - public void setSystemExecutionService( - SystemExecutionService systemExecutionService) { - this.systemExecutionService = systemExecutionService; + public void setSystemExecutor(Executor systemExecutor) { + this.systemExecutor = systemExecutor; } public void setHomeBasePath(String homeBasePath) { @@ -181,4 +210,17 @@ public class JcrUserDetailsContextMapper implements UserDetailsContextMapper { Map parameters) { this.repositoryFactory = null; } + + public void setUsernameAttribute(String usernameAttribute) { + this.usernameAttribute = usernameAttribute; + } + + public void setPasswordAttribute(String passwordAttribute) { + this.passwordAttribute = passwordAttribute; + } + + public void setUserClasses(String[] userClasses) { + this.userClasses = userClasses; + } + } diff --git a/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/applicationContext.xml b/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/applicationContext.xml index 42db5686a..faf8e94b8 100644 --- a/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/applicationContext.xml +++ b/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/applicationContext.xml @@ -13,6 +13,12 @@ + + + + + + diff --git a/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security.xml b/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security.xml index caba1aa9f..2627b7583 100644 --- a/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security.xml +++ b/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security.xml @@ -14,7 +14,7 @@ - + diff --git a/server/modules/org.argeo.node.repo.jackrabbit/META-INF/MANIFEST.MF b/server/modules/org.argeo.node.repo.jackrabbit/META-INF/MANIFEST.MF index 1131a3861..c7a1e006c 100644 --- a/server/modules/org.argeo.node.repo.jackrabbit/META-INF/MANIFEST.MF +++ b/server/modules/org.argeo.node.repo.jackrabbit/META-INF/MANIFEST.MF @@ -10,9 +10,7 @@ Import-Package: com.mysql.jdbc;version="[5.0.0,6.0.0)";resolution:=optional, org.apache.xalan.processor, org.argeo.jackrabbit, org.argeo.jcr, - org.argeo.security.jackrabbit.providers, - org.argeo.security.jcr, + org.argeo.security, org.h2;version="[1.0.0,2.0.0)";resolution:=optional, org.postgresql;version="[8.0.0,9.0.0)";resolution:=optional, - org.springframework.beans.factory.config, - org.springframework.security.providers;version="2.0.6.RELEASE" + org.springframework.beans.factory.config diff --git a/server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo-osgi.xml b/server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo-osgi.xml index 0cced08e3..2b8d18a54 100644 --- a/server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo-osgi.xml +++ b/server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo-osgi.xml @@ -10,16 +10,12 @@ http://www.springframework.org/schema/util/spring-util-2.5.xsd"> - - - + @@ -45,8 +41,4 @@ - - - \ No newline at end of file diff --git a/server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo.xml b/server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo.xml index ec8574efe..f228d9da7 100644 --- a/server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo.xml +++ b/server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo.xml @@ -26,6 +26,7 @@ classpath:/org/argeo/jcr/argeo.cnd + @@ -33,10 +34,6 @@ - - - - + \ No newline at end of file diff --git a/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/commands.xml b/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/commands.xml index c1c1f19b9..0a9a4bb61 100644 --- a/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/commands.xml +++ b/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/commands.xml @@ -22,9 +22,4 @@ - - - - diff --git a/server/plugins/org.argeo.jcr.ui.explorer/plugin.xml b/server/plugins/org.argeo.jcr.ui.explorer/plugin.xml index c1a1d6b15..2a8d80a89 100644 --- a/server/plugins/org.argeo.jcr.ui.explorer/plugin.xml +++ b/server/plugins/org.argeo.jcr.ui.explorer/plugin.xml @@ -41,11 +41,6 @@ id="org.argeo.jcr.ui.explorer.addFileFolder" name="Add file folder..."> - - cnds = new ArrayList(); private List cndFiles = new ArrayList(); /** Namespaces to register: key is prefix, value namespace */ @@ -83,59 +84,101 @@ public class JackrabbitContainer implements InitializingBean, DisposableBean, private Boolean autocreateWorkspaces = false; - public void afterPropertiesSet() throws Exception { - // Load cnds as resources - for (String resUrl : cndFiles) { - Resource res = resourceLoader.getResource(resUrl); - byte[] arr = IOUtils.toByteArray(res.getInputStream()); - cnds.add(arr); - } + private Executor systemExecutor; + public void afterPropertiesSet() throws Exception { + // remote repository if (uri != null && !uri.trim().equals("")) { Map params = new HashMap(); - params.put(JcrUtils.REPOSITORY_URI, uri); + params.put(org.apache.jackrabbit.commons.JcrUtils.REPOSITORY_URI, + uri); repository = new Jcr2davRepositoryFactory().getRepository(params); if (repository == null) throw new ArgeoException("Remote Davex repository " + uri + " not found"); log.info("Initialized Jackrabbit repository " + repository + " from uri " + uri); - } else { - if (inMemory && homeDirectory.exists()) { - FileUtils.deleteDirectory(homeDirectory); - log.warn("Deleted Jackrabbit home directory " + homeDirectory); - } + // do not perform further initialization since we assume that the + // remote repository has been properly configured + return; + } - RepositoryConfig config; - InputStream in = configuration.getInputStream(); - InputStream propsIn = null; - try { - Properties vars = new Properties(); - if (variables != null) { - propsIn = variables.getInputStream(); - vars.load(propsIn); - } - // override with system properties - vars.putAll(System.getProperties()); - vars.put( - RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE, - homeDirectory.getCanonicalPath()); - config = RepositoryConfig.create(new InputSource(in), vars); - } catch (Exception e) { - throw new RuntimeException("Cannot read configuration", e); - } finally { - IOUtils.closeQuietly(in); - IOUtils.closeQuietly(propsIn); + // local repository + if (inMemory && homeDirectory.exists()) { + FileUtils.deleteDirectory(homeDirectory); + log.warn("Deleted Jackrabbit home directory " + homeDirectory); + } + + RepositoryConfig config; + InputStream in = configuration.getInputStream(); + InputStream propsIn = null; + try { + Properties vars = new Properties(); + if (variables != null) { + propsIn = variables.getInputStream(); + vars.load(propsIn); } + // override with system properties + vars.putAll(System.getProperties()); + vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE, + homeDirectory.getCanonicalPath()); + config = RepositoryConfig.create(new InputSource(in), vars); + } catch (Exception e) { + throw new RuntimeException("Cannot read configuration", e); + } finally { + IOUtils.closeQuietly(in); + IOUtils.closeQuietly(propsIn); + } - if (inMemory) - repository = new TransientRepository(config); - else - repository = RepositoryImpl.create(config); + if (inMemory) + repository = new TransientRepository(config); + else + repository = RepositoryImpl.create(config); - log.info("Initialized Jackrabbit repository " + repository + " in " - + homeDirectory + " with config " + configuration); + importNodeTypeDefinitions(repository); + + log.info("Initialized Jackrabbit repository " + repository + " in " + + homeDirectory + " with config " + configuration); + } + + /** + * Import declared node type definitions, trying to update them if they have + * changed. In case of failures an error will be logged but no exception + * will be thrown. + */ + protected void importNodeTypeDefinitions(final Repository repository) { + if (systemExecutor == null) { + log.warn("No system executor found"); + return; } + + systemExecutor.execute(new Runnable() { + public void run() { + Reader reader = null; + Session session = null; + try { + session = repository.login(); + // Load cnds as resources + for (String resUrl : cndFiles) { + Resource res = resourceLoader.getResource(resUrl); + byte[] arr = IOUtils.toByteArray(res.getInputStream()); + reader = new InputStreamReader( + new ByteArrayInputStream(arr)); + CndImporter.registerNodeTypes(reader, session, true); + } + session.save(); + } catch (Exception e) { + log.error( + "Cannot import node type definitions " + cndFiles, + e); + JcrUtils.discardQuietly(session); + } finally { + IOUtils.closeQuietly(reader); + JcrUtils.logoutQuietly(session); + } + } + }); + } public void destroy() throws Exception { @@ -220,10 +263,6 @@ public class JackrabbitContainer implements InitializingBean, DisposableBean, try { NamespaceHelper namespaceHelper = new NamespaceHelper(session); namespaceHelper.registerNamespaces(namespaces); - - for (byte[] arr : cnds) - CndImporter.registerNodeTypes(new InputStreamReader( - new ByteArrayInputStream(arr)), session, true); } catch (Exception e) { throw new ArgeoException("Cannot process new session", e); } @@ -292,4 +331,8 @@ public class JackrabbitContainer implements InitializingBean, DisposableBean, this.uri = uri; } + public void setSystemExecutor(Executor systemExecutor) { + this.systemExecutor = systemExecutor; + } + } diff --git a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/CachingSessionProvider.java b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/CachingSessionProvider.java deleted file mode 100644 index 422623cac..000000000 --- a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/CachingSessionProvider.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.argeo.jackrabbit.remote; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.jcr.Credentials; -import javax.jcr.LoginException; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.jackrabbit.server.SessionProvider; - -public class CachingSessionProvider implements SessionProvider { - private static final String JCR_SESSIONS_ATTRIBUTE = "jcrSessions"; - - private final static Log log = LogFactory - .getLog(CachingSessionProvider.class); - - private Credentials credentials = null; - - public CachingSessionProvider() { - } - - public CachingSessionProvider(Credentials credentials) { - this.credentials = credentials; - } - - @SuppressWarnings("unchecked") - public Session getSession(HttpServletRequest request, Repository rep, - String workspace) throws LoginException, ServletException, - RepositoryException { - HttpSession httpSession = request.getSession(); - - if (httpSession.getAttribute(JCR_SESSIONS_ATTRIBUTE) == null) { - httpSession - .setAttribute(JCR_SESSIONS_ATTRIBUTE, Collections - .synchronizedMap(new HashMap())); - } - Map sessions = (Map) httpSession - .getAttribute(JCR_SESSIONS_ATTRIBUTE); - if (!sessions.containsKey(workspace)) { - Session session = rep.login(credentials, workspace); - sessions.put(workspace, session); - return session; - } else { - Session session = sessions.get(workspace); - if (!session.isLive()) { - sessions.remove(workspace); - session = rep.login(credentials, workspace); - sessions.put(workspace, session); - } - return session; - } - } - - public void releaseSession(Session session) { - if (log.isDebugEnabled()) - log.debug("Releasing JCR session " + session); - // session.logout(); - // FIXME: find a way to log out when the HTTP session is expired - } - -} diff --git a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/ExtendedDispatcherServlet.java b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/ExtendedDispatcherServlet.java index 39dbf7f67..ec1d96ea3 100644 --- a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/ExtendedDispatcherServlet.java +++ b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/ExtendedDispatcherServlet.java @@ -34,6 +34,7 @@ public class ExtendedDispatcherServlet extends DispatcherServlet { protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { + // see http://forum.springsource.org/showthread.php?t=53472 try { if (log.isTraceEnabled()) diff --git a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/JcrRemotingHandlerMapping.java b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/JcrRemotingHandlerMapping.java index 41c1a7537..100c91ad1 100644 --- a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/JcrRemotingHandlerMapping.java +++ b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/JcrRemotingHandlerMapping.java @@ -7,6 +7,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; public class JcrRemotingHandlerMapping extends AbstractJackrabbitHandlerMapping { + protected HttpServlet createServlet(Repository repository, String pathPrefix) throws ServletException { JcrRemotingServlet servlet = new JcrRemotingServlet(repository, diff --git a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/SimpleSessionProvider.java b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/SimpleSessionProvider.java index eab7451db..294f3289a 100644 --- a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/SimpleSessionProvider.java +++ b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/SimpleSessionProvider.java @@ -5,7 +5,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; -import javax.jcr.Credentials; import javax.jcr.LoginException; import javax.jcr.Repository; import javax.jcr.RepositoryException; @@ -27,22 +26,19 @@ public class SimpleSessionProvider implements SessionProvider, Serializable { private transient Map sessions; - private Credentials credentials = null; - public Session getSession(HttpServletRequest request, Repository rep, String workspace) throws LoginException, ServletException, RepositoryException { - - // since sessions is transient it can be restored from the session + // since sessions is transient it can't be restored from the session if (sessions == null) sessions = Collections .synchronizedMap(new HashMap()); if (!sessions.containsKey(workspace)) { try { - Session session = rep.login(credentials, workspace); - if (log.isDebugEnabled()) - log.debug("User " + session.getUserID() + " logged into " + Session session = rep.login(null, workspace); + if (log.isTraceEnabled()) + log.trace("User " + session.getUserID() + " logged into " + request.getServletPath()); sessions.put(workspace, session); return session; @@ -53,7 +49,7 @@ public class SimpleSessionProvider implements SessionProvider, Serializable { Session session = sessions.get(workspace); if (!session.isLive()) { sessions.remove(workspace); - session = rep.login(credentials, workspace); + session = rep.login(null, workspace); sessions.put(workspace, session); } return session; diff --git a/server/runtime/org.argeo.server.jcr.mvc/src/main/java/org/argeo/jcr/mvc/MultipleRepositoryHandlerMapping.java b/server/runtime/org.argeo.server.jcr.mvc/src/main/java/org/argeo/jcr/mvc/MultipleRepositoryHandlerMapping.java index 10bb8b570..16ecdd89e 100644 --- a/server/runtime/org.argeo.server.jcr.mvc/src/main/java/org/argeo/jcr/mvc/MultipleRepositoryHandlerMapping.java +++ b/server/runtime/org.argeo.server.jcr.mvc/src/main/java/org/argeo/jcr/mvc/MultipleRepositoryHandlerMapping.java @@ -58,7 +58,8 @@ public abstract class MultipleRepositoryHandlerMapping implements } HttpServlet remotingServlet = (HttpServlet) applicationContext .getBean(beanName); - return new HandlerExecutionChain(remotingServlet); + HandlerExecutionChain hec = new HandlerExecutionChain(remotingServlet); + return hec; } /** The repository name is the first part of the path info */ @@ -120,5 +121,4 @@ public abstract class MultipleRepositoryHandlerMapping implements return initParameters.keys(); } } - } diff --git a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoTypes.java b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoTypes.java index c8eaa6691..dec1a2875 100644 --- a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoTypes.java +++ b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoTypes.java @@ -3,6 +3,5 @@ package org.argeo.jcr; /** JCR types in the http://www.argeo.org/argeo namespace */ public interface ArgeoTypes { public final static String ARGEO_LINK = "argeo:link"; - public final static String ARGEO_HOME = "argeo:home"; public final static String ARGEO_USER_HOME = "argeo:userHome"; } diff --git a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java index ad6ca0edd..72b94fbcd 100644 --- a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java +++ b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java @@ -44,6 +44,11 @@ import javax.jcr.Value; import javax.jcr.nodetype.NodeType; import javax.jcr.query.Query; import javax.jcr.query.QueryResult; +import javax.jcr.query.qom.Constraint; +import javax.jcr.query.qom.DynamicOperand; +import javax.jcr.query.qom.QueryObjectModelFactory; +import javax.jcr.query.qom.Selector; +import javax.jcr.query.qom.StaticOperand; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -53,6 +58,10 @@ import org.argeo.ArgeoException; public class JcrUtils implements ArgeoJcrConstants { private final static Log log = LogFactory.getLog(JcrUtils.class); + /** Prevents instantiation */ + private JcrUtils() { + } + /** * Queries one single node. * @@ -613,4 +622,63 @@ public class JcrUtils implements ArgeoJcrConstants { + uri, e); } } + + /** + * Discards the current changes in a session by calling + * {@link Session#refresh(boolean)} with false, only logging + * potential errors when doing so. To be used typically in a catch block. + */ + public static void discardQuietly(Session session) { + try { + if (session != null) + session.refresh(false); + } catch (RepositoryException e) { + log.warn("Cannot quietly discard session " + session + ": " + + e.getMessage()); + } + } + + /** Logs out the session, not throwing any exception, even if it is null. */ + public static void logoutQuietly(Session session) { + if (session != null) + session.logout(); + } + + /** Returns the home node of the session user or null if none was found. */ + public static Node getUserHome(Session session) { + String userID = session.getUserID(); + return getUserHome(session, userID); + } + + /** + * Returns the home node of the session user or null if none was found. + * + * @param session + * the session to use in order to perform the search, this can be + * a session with a different user ID than the one searched, + * typically when a system or admin session is used. + * @param userID + * the id of the user + */ + public static Node getUserHome(Session session, String userID) { + try { + QueryObjectModelFactory qomf = session.getWorkspace() + .getQueryManager().getQOMFactory(); + + // query the user home for this user id + Selector userHomeSel = qomf.selector(ArgeoTypes.ARGEO_USER_HOME, + "userHome"); + DynamicOperand userIdDop = qomf.propertyValue("userHome", + ArgeoNames.ARGEO_USER_ID); + StaticOperand userIdSop = qomf.literal(session.getValueFactory() + .createValue(userID)); + Constraint constraint = qomf.comparison(userIdDop, + QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop); + Query query = qomf.createQuery(userHomeSel, constraint, null, null); + Node userHome = JcrUtils.querySingleNode(query); + return userHome; + } catch (RepositoryException e) { + throw new ArgeoException("Cannot find home for user " + userID, e); + } + } }