X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=security%2Fruntime%2Forg.argeo.security.jackrabbit%2Fsrc%2Fmain%2Fjava%2Forg%2Fargeo%2Fsecurity%2Fjackrabbit%2FArgeoSecurityManager.java;h=3450c75d8dbd51f9e9418814a82023e8cdd4a205;hb=3a3d316af102ba410d1d9e6de349d0c8f7ac044f;hp=e5b83e3169d2afa3df0f675190d16fe69123d345;hpb=03db65bd74ce09b696a4c5af15a58df988e5368d;p=lgpl%2Fargeo-commons.git 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 e5b83e316..3450c75d8 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,9 +1,27 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.argeo.security.jackrabbit; import java.security.Principal; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import javax.jcr.RepositoryException; @@ -16,38 +34,117 @@ 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.AMContext; +import org.apache.jackrabbit.core.security.AccessManager; 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; +import org.springframework.security.context.SecurityContextHolder; -/** Intermediary class in order to have a consistent naming in config files. */ +/** Integrates Spring Security and Jackrabbit Security users and roles. */ public class ArgeoSecurityManager extends DefaultSecurityManager { - private Log log = LogFactory.getLog(ArgeoSecurityManager.class); + /** 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 userRolesCache = Collections + .synchronizedMap(new HashMap()); + + @Override + public AccessManager getAccessManager(Session session, AMContext amContext) + throws RepositoryException { + synchronized (getSystemSession()) { + return super.getAccessManager(session, amContext); + } + } @Override - /** Since this is called once when the session is created, we take the opportunity to synchronize Spring and Jackrabbit users and groups.*/ + public UserManager getUserManager(Session session) + throws RepositoryException { + synchronized (getSystemSession()) { + return super.getUserManager(session); + } + } + + /** + * Since this is called once when the session is created, we take the + * opportunity to make sure that Jackrabbit users and groups reflect Spring + * Security name and authorities. + */ + @Override public String getUserID(Subject subject, String workspaceName) throws RepositoryException { - long begin = System.currentTimeMillis(); + if (!synchronize) { + Authentication authentication = SecurityContextHolder.getContext() + .getAuthentication(); + if (authentication != null) + return authentication.getName(); + else + return super.getUserID(subject, workspaceName); + } - if (!subject.getPrincipals(SystemPrincipal.class).isEmpty()) + if (log.isTraceEnabled()) + log.trace(subject); + // skip anonymous user (no rights) + if (!subject.getPrincipals(AnonymousPrincipal.class).isEmpty()) + return super.getUserID(subject, workspaceName); + // skip Jackrabbit system user (all rights) + if (!subject.getPrincipals(ArgeoSystemPrincipal.class).isEmpty()) return super.getUserID(subject, workspaceName); + // retrieve Spring authentication from JAAS + // TODO? use Spring Security context holder Authentication authen; Set authens = subject .getPrincipals(Authentication.class); - if (authens.size() == 0) - throw new ArgeoException("No Spring authentication found in " - + subject); - else + 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 + UserManager systemUm = getSystemUserManager(null); + if (systemUm.getAuthorizable(userId) == null) + systemUm.createUser(userId, ""); + } else {// Spring Security authen = authens.iterator().next(); - UserManager systemUm = getSystemUserManager(workspaceName); + 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) { + roles.append(ga.toString()); + } + + // do not sync if not changed + if (userRolesCache.containsKey(userId) + && userRolesCache.get(userId).equals(roles.toString())) + return userId; + + // sync Spring and Jackrabbit + // workspace is irrelevant here + UserManager systemUm = getSystemUserManager(null); + syncSpringAndJackrabbitSecurity(systemUm, authen); + userRolesCache.put(userId, roles.toString()); + } + return userId; + } + + /** + * Make sure that the Jackrabbit security model contains this user and its + * granted authorities + */ + static private void syncSpringAndJackrabbitSecurity(UserManager systemUm, + Authentication authen) throws RepositoryException { + long begin = System.currentTimeMillis(); String userId = authen.getName(); User user = (User) systemUm.getAuthorizable(userId); @@ -57,12 +154,12 @@ public class ArgeoSecurityManager extends DefaultSecurityManager { log.info(userId + " added as " + user); } + // process groups List userGroupIds = new ArrayList(); for (GrantedAuthority ga : authen.getAuthorities()) { Group group = (Group) systemUm.getAuthorizable(ga.getAuthority()); if (group == null) { - group = systemUm.createGroup(ga.getAuthority(), - new GrantedAuthorityPrincipal(ga), null); + group = systemUm.createGroup(ga.getAuthority()); log.info(ga.getAuthority() + " added as " + group); } if (!group.isMember(user)) @@ -81,7 +178,6 @@ public class ArgeoSecurityManager extends DefaultSecurityManager { log.trace("Spring and Jackrabbit Security synchronized for user " + userId + " in " + (System.currentTimeMillis() - begin) + " ms"); - return userId; } @Override @@ -94,7 +190,6 @@ public class ArgeoSecurityManager extends DefaultSecurityManager { private class ArgeoWorkspaceAccessManagerImpl implements SecurityConstants, WorkspaceAccessManager { private final WorkspaceAccessManager wam; - private String defaultWorkspace; public ArgeoWorkspaceAccessManagerImpl(WorkspaceAccessManager wam) { super(); @@ -103,8 +198,6 @@ public class ArgeoSecurityManager extends DefaultSecurityManager { public void init(Session systemSession) throws RepositoryException { wam.init(systemSession); - defaultWorkspace = ((RepositoryImpl) getRepository()).getConfig() - .getDefaultWorkspaceName(); } public void close() throws RepositoryException { @@ -112,18 +205,8 @@ public class ArgeoSecurityManager extends DefaultSecurityManager { 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); + // TODO: implements finer access to workspaces + return true; } }