<property name="repository" ref="nodeRepository" />
<property name="bundleContext" ref="bundleContext" />
</bean>
+ <bean class="org.argeo.jackrabbit.JackrabbitAuthorizations"
+ init-method="run">
+ <property name="principalPrivileges">
+ <map>
+ <entry key="jcr:all" value="ROLE_ADMIN" />
+ </map>
+ </property>
+ <property name="repository" ref="argeoDataModel" />
+ </bean>
<bean id="jcrLdapSynchronizer" class="org.argeo.security.ldap.jcr.JcrLdapSynchronizer"
init-method="init" destroy-method="destroy" depends-on="argeoDataModel">
<property name="repository" ref="nodeRepository" />
<property name="bundleContext" ref="bundleContext" />
</bean>
+ <bean class="org.argeo.jackrabbit.JackrabbitAuthorizations"
+ init-method="run">
+ <property name="principalPrivileges">
+ <map>
+ <entry key="jcr:all" value="ROLE_ADMIN" />
+ </map>
+ </property>
+ <property name="repository" ref="argeoDataModel" />
+ </bean>
<bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
<property name="providers">
<property name="repository" ref="nodeRepository" />
</bean>
+
</beans>
\ No newline at end of file
try {
// Node userProfile = SecurityJcrUtils.createUserProfile(session,
// username);
- Node userProfile = jcrSecurityModel.sync(session, username);
+ Node userProfile = jcrSecurityModel.sync(session, username, null);
session.getWorkspace().getVersionManager()
.checkout(userProfile.getPath());
mainUserInfo.mapToProfileNode(userProfile);
*/
package org.argeo.security;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
import org.springframework.security.Authentication;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.context.SecurityContext;
}
return null;
}
+
+ /**
+ * Converts an array of Spring Security {@link GrantedAuthority} to a
+ * read-only list of strings, for portability and integration
+ */
+ public static List<String> authoritiesToStringList(
+ GrantedAuthority[] authorities) {
+ List<String> lst = new ArrayList<String>();
+ for (GrantedAuthority ga : authorities)
+ lst.add(ga.getAuthority());
+ return Collections.unmodifiableList(lst);
+ }
}
package org.argeo.security.jcr;
+import java.util.List;
+
import javax.jcr.Node;
-import javax.jcr.RepositoryException;
import javax.jcr.Session;
-import javax.jcr.security.Privilege;
-import javax.jcr.version.VersionManager;
-
-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.jcr.UserJcrUtils;
/**
* Manages data expected by the Argeo security model, such as user home and
* profile.
*/
-public class JcrSecurityModel {
- // ArgeoNames not implemented as interface in order to ease derivation by
- // Jackrabbit bundles
-
- /** The home base path. */
- private String homeBasePath = "/home";
-
+public interface JcrSecurityModel {
/**
* To be called before user details are loaded. Make sure than any logged in
* user has a home directory with full access and a profile with information
* about him (read access)
*
- * @return the user profile (whose parent is the user home)
+ * @return the user profile (whose parent is the user home), never null
*/
- public Node sync(Session session, String username) {
- // TODO check user name validity (e.g. should not start by ROLE_)
-
- try {
- Node userHome = UserJcrUtils.getUserHome(session, username);
- if (userHome == null) {
- String homePath = generateUserPath(homeBasePath, username);
- userHome = JcrUtils.mkdirs(session, homePath);
- // userHome = JcrUtils.mkfolders(session, homePath);
- userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME);
- userHome.setProperty(ArgeoNames.ARGEO_USER_ID, username);
- session.save();
-
- JcrUtils.clearAccessControList(session, homePath, username);
- JcrUtils.addPrivilege(session, homePath, username,
- Privilege.JCR_ALL);
- } else {
- // for backward compatibility with pre 1.0 security model
- if (userHome.hasNode(ArgeoNames.ARGEO_PROFILE)) {
- userHome.getNode(ArgeoNames.ARGEO_PROFILE).remove();
- userHome.getSession().save();
- }
- }
-
- Node userProfile = UserJcrUtils.getUserProfile(session, username);
- if (userProfile == null) {
- String personPath = generateUserPath(
- ArgeoJcrConstants.PEOPLE_BASE_PATH, username);
- Node personBase = JcrUtils.mkdirs(session, personPath);
- userProfile = personBase.addNode(ArgeoNames.ARGEO_PROFILE);
- userProfile.addMixin(ArgeoTypes.ARGEO_USER_PROFILE);
- userProfile.setProperty(ArgeoNames.ARGEO_USER_ID, username);
- userProfile.setProperty(ArgeoNames.ARGEO_ENABLED, true);
- userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED,
- true);
- userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED,
- true);
- userProfile.setProperty(
- ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED, true);
- session.save();
-
- JcrUtils.clearAccessControList(session, userProfile.getPath(),
- username);
- JcrUtils.addPrivilege(session, userProfile.getPath(), username,
- Privilege.JCR_READ);
-
- VersionManager versionManager = session.getWorkspace()
- .getVersionManager();
- if (versionManager.isCheckedOut(userProfile.getPath()))
- versionManager.checkin(userProfile.getPath());
- }
- return userProfile;
- } catch (RepositoryException e) {
- JcrUtils.discardQuietly(session);
- throw new ArgeoException("Cannot sync node security model for "
- + username, e);
- }
- }
-
- /** Generate path for a new user home */
- protected String generateUserPath(String base, String username) {
- int atIndex = username.indexOf('@');
- if (atIndex > 0) {
- String domain = username.substring(0, atIndex);
- String name = username.substring(atIndex + 1);
- return base + '/' + JcrUtils.firstCharsToPath(domain, 2) + '/'
- + domain + '/' + JcrUtils.firstCharsToPath(name, 2) + '/'
- + name;
- } else if (atIndex == 0 || atIndex == (username.length() - 1)) {
- throw new ArgeoException("Unsupported username " + username);
- } else {
- return base + '/' + JcrUtils.firstCharsToPath(username, 2) + '/'
- + username;
- }
- }
-
- public void setHomeBasePath(String homeBasePath) {
- this.homeBasePath = homeBasePath;
- }
-
+ public Node sync(Session session, String username, List<String> roles);
}
import org.argeo.ArgeoException;
import org.argeo.jcr.JcrUtils;
import org.argeo.security.OsAuthenticationToken;
+import org.argeo.security.SecurityUtils;
import org.argeo.security.core.OsAuthenticationProvider;
import org.springframework.security.Authentication;
import org.springframework.security.AuthenticationException;
import org.springframework.security.BadCredentialsException;
+import org.springframework.security.GrantedAuthority;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.userdetails.UserDetails;
/** Relies on OS to authenticate and additionally setup JCR */
public class OsJcrAuthenticationProvider extends OsAuthenticationProvider {
private Repository repository;
- // private String securityWorkspace = "security";
- // private Session securitySession;
private Session nodeSession;
private UserDetails userDetails;
- private JcrSecurityModel jcrSecurityModel = new JcrSecurityModel();
+ private JcrSecurityModel jcrSecurityModel = new SimpleJcrSecurityModel();
+
+ private final static String JVM_OSUSER = System.getProperty("user.name");
public void init() {
try {
- // securitySession = repository.login();
nodeSession = repository.login();
} catch (RepositoryException e) {
throw new ArgeoException("Cannot initialize", e);
}
public void destroy() {
- // JcrUtils.logoutQuietly(securitySession);
JcrUtils.logoutQuietly(nodeSession);
}
// consider using the keyring for username / password authentication
// or certificate
UsernamePasswordAuthenticationToken upat = (UsernamePasswordAuthenticationToken) authentication;
- if (!upat.getPrincipal().toString()
- .equals(System.getProperty("user.name")))
+ if (!upat.getPrincipal().toString().equals(JVM_OSUSER))
throw new BadCredentialsException("Wrong credentials");
UsernamePasswordAuthenticationToken authen = new UsernamePasswordAuthenticationToken(
authentication.getPrincipal(),
try {
// WARNING: at this stage we assume that the java properties
// will have the same value
- String username = System.getProperty("user.name");
- Node userProfile = jcrSecurityModel.sync(nodeSession, username);
+ GrantedAuthority[] authorities = getBaseAuthorities();
+ String username = JVM_OSUSER;
+ Node userProfile = jcrSecurityModel.sync(nodeSession, username,
+ SecurityUtils.authoritiesToStringList(authorities));
JcrUserDetails.checkAccountStatus(userProfile);
- // each user should have a writable area in the default
- // workspace of the node
- // SecurityJcrUtils.createUserHomeIfNeeded(nodeSession,
- // username);
userDetails = new JcrUserDetails(userProfile, authen
- .getCredentials().toString(), getBaseAuthorities());
+ .getCredentials().toString(), authorities);
authen.setDetails(userDetails);
return authen;
} catch (RepositoryException e) {
}
}
- // public void setSecurityWorkspace(String securityWorkspace) {
- // this.securityWorkspace = securityWorkspace;
- // }
-
public void setRepository(Repository repository) {
this.repository = repository;
}
|| UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication);
}
-
-}
+}
\ No newline at end of file
--- /dev/null
+package org.argeo.security.jcr;
+
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.security.Privilege;
+import javax.jcr.version.VersionManager;
+
+import org.apache.commons.logging.Log;
+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.jcr.UserJcrUtils;
+
+/**
+ * Manages data expected by the Argeo security model, such as user home and
+ * profile.
+ */
+public class SimpleJcrSecurityModel implements JcrSecurityModel {
+ private final static Log log = LogFactory
+ .getLog(SimpleJcrSecurityModel.class);
+ // ArgeoNames not implemented as interface in order to ease derivation by
+ // Jackrabbit bundles
+
+ /** The home base path. */
+ private String homeBasePath = "/home";
+
+ public Node sync(Session session, String username, List<String> roles) {
+ // TODO check user name validity (e.g. should not start by ROLE_)
+
+ try {
+ Node userHome = UserJcrUtils.getUserHome(session, username);
+ if (userHome == null) {
+ String homePath = generateUserPath(homeBasePath, username);
+ userHome = JcrUtils.mkdirs(session, homePath);
+ // userHome = JcrUtils.mkfolders(session, homePath);
+ userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME);
+ userHome.setProperty(ArgeoNames.ARGEO_USER_ID, username);
+ session.save();
+
+ JcrUtils.clearAccessControList(session, homePath, username);
+ JcrUtils.addPrivilege(session, homePath, username,
+ Privilege.JCR_ALL);
+ } else {
+ // for backward compatibility with pre 1.0 security model
+ if (userHome.hasNode(ArgeoNames.ARGEO_PROFILE)) {
+ userHome.getNode(ArgeoNames.ARGEO_PROFILE).remove();
+ userHome.getSession().save();
+ }
+ }
+
+ // Remote roles
+ if (roles != null) {
+ //writeRemoteRoles(userHome, roles);
+ }
+
+ Node userProfile = UserJcrUtils.getUserProfile(session, username);
+ if (userProfile == null) {
+ String personPath = generateUserPath(
+ ArgeoJcrConstants.PEOPLE_BASE_PATH, username);
+ Node personBase = JcrUtils.mkdirs(session, personPath);
+ userProfile = personBase.addNode(ArgeoNames.ARGEO_PROFILE);
+ userProfile.addMixin(ArgeoTypes.ARGEO_USER_PROFILE);
+ userProfile.setProperty(ArgeoNames.ARGEO_USER_ID, username);
+ userProfile.setProperty(ArgeoNames.ARGEO_ENABLED, true);
+ userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED,
+ true);
+ userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED,
+ true);
+ userProfile.setProperty(
+ ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED, true);
+ session.save();
+
+ JcrUtils.clearAccessControList(session, userProfile.getPath(),
+ username);
+ JcrUtils.addPrivilege(session, userProfile.getPath(), username,
+ Privilege.JCR_READ);
+
+ VersionManager versionManager = session.getWorkspace()
+ .getVersionManager();
+ if (versionManager.isCheckedOut(userProfile.getPath()))
+ versionManager.checkin(userProfile.getPath());
+ }
+ return userProfile;
+ } catch (RepositoryException e) {
+ JcrUtils.discardQuietly(session);
+ throw new ArgeoException("Cannot sync node security model for "
+ + username, e);
+ }
+ }
+
+ /** Generate path for a new user home */
+ protected String generateUserPath(String base, String username) {
+ int atIndex = username.indexOf('@');
+ if (atIndex > 0) {
+ String domain = username.substring(0, atIndex);
+ String name = username.substring(atIndex + 1);
+ return base + '/' + JcrUtils.firstCharsToPath(domain, 2) + '/'
+ + domain + '/' + JcrUtils.firstCharsToPath(name, 2) + '/'
+ + name;
+ } else if (atIndex == 0 || atIndex == (username.length() - 1)) {
+ throw new ArgeoException("Unsupported username " + username);
+ } else {
+ return base + '/' + JcrUtils.firstCharsToPath(username, 2) + '/'
+ + username;
+ }
+ }
+
+ /** Write remote roles used by remote access in the home directory */
+ protected void writeRemoteRoles(Node userHome, List<String> roles)
+ throws RepositoryException {
+ boolean writeRoles = false;
+ if (userHome.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
+ Value[] remoteRoles = userHome.getProperty(
+ ArgeoNames.ARGEO_REMOTE_ROLES).getValues();
+ if (remoteRoles.length != roles.size())
+ writeRoles = true;
+ else
+ for (int i = 0; i < remoteRoles.length; i++)
+ if (!remoteRoles[i].getString().equals(roles.get(i)))
+ writeRoles = true;
+ } else
+ writeRoles = true;
+
+ if (writeRoles) {
+ userHome.getSession().getWorkspace().getVersionManager()
+ .checkout(userHome.getPath());
+ String[] roleIds = roles.toArray(new String[roles.size()]);
+ userHome.setProperty(ArgeoNames.ARGEO_REMOTE_ROLES, roleIds);
+ JcrUtils.updateLastModified(userHome);
+ userHome.getSession().save();
+ userHome.getSession().getWorkspace().getVersionManager()
+ .checkin(userHome.getPath());
+ if (log.isDebugEnabled())
+ log.debug("Wrote remote roles " + roles + " for "
+ + userHome.getProperty(ArgeoNames.ARGEO_USER_ID));
+ }
+
+ }
+
+ public void setHomeBasePath(String homeBasePath) {
+ this.homeBasePath = homeBasePath;
+ }
+
+}
/** Jackrabbit login mechanism based on Spring Security */
public class ArgeoLoginModule extends AbstractLoginModule {
- private String adminRole = "ROLE_ADMIN";
+ // private String adminRole = "ROLE_ADMIN";
@SuppressWarnings("unused")
@Override
for (GrantedAuthority ga : authen.getAuthorities()) {
principals.add(new GrantedAuthorityPrincipal(ga));
// FIXME: make it more generic
- if (adminRole.equals(ga.getAuthority()))
- principals.add(new AdminPrincipal(authen.getName()));
+ // if (adminRole.equals(ga.getAuthority()))
+ // principals.add(new AdminPrincipal(authen.getName()));
}
}
/** Integrates Spring Security and Jackrabbit Security users and roles. */
public class ArgeoSecurityManager extends DefaultSecurityManager {
+ /** Legacy security sync */
+ final static String PROPERTY_JACKRABBIT_SECURITY_SYNC_1_1 = "argeo.jackarabbit.securitySync.1.1";
+
private final static Log log = LogFactory
.getLog(ArgeoSecurityManager.class);
+ private static Boolean synchronize = Boolean.parseBoolean(System
+ .getProperty(PROPERTY_JACKRABBIT_SECURITY_SYNC_1_1, "false"));
+
/** TODO? use a bounded buffer */
private Map<String, String> userRolesCache = Collections
.synchronizedMap(new HashMap<String, String>());
@Override
public String getUserID(Subject subject, String workspaceName)
throws RepositoryException {
+ if (!synchronize)
+ return super.getUserID(subject, workspaceName);
+
if (log.isTraceEnabled())
log.trace(subject);
// skip anonymous user (no rights)
Authentication authen;
Set<Authentication> authens = subject
.getPrincipals(Authentication.class);
- String userId;
+ String userId = super.getUserID(subject, workspaceName);
if (authens.size() == 0) {
// make sure that logged-in user has a Principal, useful for testing
// using an admin user
- userId = super.getUserID(subject, workspaceName);
UserManager systemUm = getSystemUserManager(null);
if (systemUm.getAuthorizable(userId) == null)
systemUm.createUser(userId, "");
} else {// Spring Security
authen = authens.iterator().next();
- userId = authen.getName();
+ if (!userId.equals(authen.getName()))
+ log.warn("User ID is '" + userId + "' but authen is "
+ + authen.getName());
StringBuffer roles = new StringBuffer("");
GrantedAuthority[] authorities = authen.getAuthorities();
for (GrantedAuthority ga : authorities) {
* Make sure that the Jackrabbit security model contains this user and its
* granted authorities
*/
- static void syncSpringAndJackrabbitSecurity(UserManager systemUm,
+ static private void syncSpringAndJackrabbitSecurity(UserManager systemUm,
Authentication authen) throws RepositoryException {
long begin = System.currentTimeMillis();
package org.argeo.security.jackrabbit;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jackrabbit.api.JackrabbitSession;
+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.argeo.ArgeoException;
import org.argeo.jcr.ArgeoNames;
-import org.argeo.security.jcr.JcrSecurityModel;
+import org.argeo.security.jcr.SimpleJcrSecurityModel;
/** Make sure that user authorizable exists before syncing user directories. */
-public class JackrabbitSecurityModel extends JcrSecurityModel {
+public class JackrabbitSecurityModel extends SimpleJcrSecurityModel {
private final static Log log = LogFactory
.getLog(JackrabbitSecurityModel.class);
@Override
- public Node sync(Session session, String username) {
- User user = null;
+ public Node sync(Session session, String username, List<String> roles) {
+ if (!(session instanceof JackrabbitSession))
+ return super.sync(session, username, roles);
+
try {
- if (session instanceof JackrabbitSession) {
- UserManager userManager = ((JackrabbitSession) session)
- .getUserManager();
- user = (User) userManager.getAuthorizable(username);
- if (user != null) {
- String principalName = user.getPrincipal().getName();
- if (!principalName.equals(username)) {
- log.warn("Jackrabbit principal is '" + principalName
- + "' but username is '" + username
- + "'. Recreating...");
- user.remove();
- user = userManager.createUser(username, "");
- }
- } else {
- // create new principal
- userManager.createUser(username, "");
+ UserManager userManager = ((JackrabbitSession) session)
+ .getUserManager();
+ User user = (User) userManager.getAuthorizable(username);
+ if (user != null) {
+ String principalName = user.getPrincipal().getName();
+ if (!principalName.equals(username)) {
+ log.warn("Jackrabbit principal is '" + principalName
+ + "' but username is '" + username
+ + "'. Recreating...");
+ user.remove();
+ user = userManager.createUser(username, "");
}
+ } else {
+ // create new principal
+ user = userManager.createUser(username, "");
+ log.info(username + " added as Jackrabbit user " + user);
}
- Node userProfile = super.sync(session, username);
- if (user != null && userProfile != null) {
- Boolean enabled = userProfile.getProperty(
- ArgeoNames.ARGEO_ENABLED).getBoolean();
- if (enabled && user.isDisabled())
- user.disable(null);
- else if (!enabled && !user.isDisabled())
- user.disable(userProfile.getPath() + " is disabled");
- }
+
+ // generic JCR sync
+ Node userProfile = super.sync(session, username, roles);
+
+ Boolean enabled = userProfile.getProperty(ArgeoNames.ARGEO_ENABLED)
+ .getBoolean();
+ if (enabled && user.isDisabled())
+ user.disable(null);
+ else if (!enabled && !user.isDisabled())
+ user.disable(userProfile.getPath() + " is disabled");
+
+ // Sync Jackrabbit roles
+ if (roles != null)
+ syncRoles(userManager, user, roles);
+
return userProfile;
} catch (RepositoryException e) {
throw new ArgeoException(
"Cannot perform Jackrabbit specific operations", e);
}
}
+
+ /** Make sure Jackrabbit roles are in line with authentication */
+ void syncRoles(UserManager userManager, User user, List<String> roles)
+ throws RepositoryException {
+ List<String> userGroupIds = new ArrayList<String>();
+ for (String role : roles) {
+ Group group = (Group) userManager.getAuthorizable(role);
+ if (group == null) {
+ group = userManager.createGroup(role);
+ log.info(role + " added as " + group);
+ }
+ if (!group.isMember(user))
+ group.addMember(user);
+ userGroupIds.add(role);
+ }
+
+ // check if user has not been removed from some groups
+ for (Iterator<Group> it = user.declaredMemberOf(); it.hasNext();) {
+ Group group = it.next();
+ if (!userGroupIds.contains(group.getID()))
+ group.removeMember(user);
+ }
+ }
}
import org.argeo.jcr.ArgeoNames;
import org.argeo.jcr.ArgeoTypes;
import org.argeo.jcr.JcrUtils;
+import org.argeo.security.SecurityUtils;
import org.argeo.security.jcr.JcrSecurityModel;
import org.argeo.security.jcr.JcrUserDetails;
+import org.argeo.security.jcr.SimpleJcrSecurityModel;
import org.springframework.ldap.core.ContextExecutor;
import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.DirContextAdapter;
private Repository repository;
private JcrProfileListener jcrProfileListener;
- private JcrSecurityModel jcrSecurityModel = new JcrSecurityModel();
+ private JcrSecurityModel jcrSecurityModel = new SimpleJcrSecurityModel();
// Mapping
private Map<String, String> propertyToAttributes = new HashMap<String, String>();
// Node userProfile = SecurityJcrUtils.createUserProfileIfNeeded(
// securitySession, username);
- Node userProfile = jcrSecurityModel.sync(nodeSession, username);
+ Node userProfile = jcrSecurityModel.sync(nodeSession, username,
+ SecurityUtils.authoritiesToStringList(authorities));
// JcrUserDetails.checkAccountStatus(userProfile);
// password
// process
String username = ctx.getStringAttribute(usernameAttribute);
- Node userProfile = jcrSecurityModel.sync(session, username);
+ Node userProfile = jcrSecurityModel.sync(session, username, null);
Map<String, String> modifications = new HashMap<String, String>();
for (String jcrProperty : propertyToAttributes.keySet())
ldapToJcr(userProfile, jcrProperty, ctx, modifications);
/**
* Add privileges on a path to a {@link Principal}. The path must already
- * exist. Session is saved.
+ * exist. Session is saved. Synchronized to prevent concurrent modifications
+ * of the same node.
*/
- public static void addPrivileges(Session session, String path,
+ public synchronized static void addPrivileges(Session session, String path,
Principal principal, List<Privilege> privs)
throws RepositoryException {
+ // make sure the session is in line with the persisted state
+ session.refresh(false);
AccessControlManager acm = session.getAccessControlManager();
AccessControlList acl = getAccessControlList(acm, path);
acl.addAccessControlEntry(principal,
log.debug("Added privileges " + privBuf + " to " + principal
+ " on " + path);
}
+ session.refresh(true);
session.save();
}