diff --git a/source/java/org/alfresco/repo/avm/wf/AVMSubmitPackageHandler.java b/source/java/org/alfresco/repo/avm/wf/AVMSubmitPackageHandler.java index 1e5833042b..8739c9a1c8 100644 --- a/source/java/org/alfresco/repo/avm/wf/AVMSubmitPackageHandler.java +++ b/source/java/org/alfresco/repo/avm/wf/AVMSubmitPackageHandler.java @@ -30,6 +30,7 @@ import java.util.Map; import org.alfresco.repo.avm.AVMDAOs; import org.alfresco.repo.avm.AVMNodeConverter; import org.alfresco.repo.domain.PropertyValue; +import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.workflow.jbpm.JBPMNode; import org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler; @@ -47,9 +48,7 @@ import org.apache.commons.logging.LogFactory; import org.jbpm.graph.exe.ExecutionContext; import org.springframework.beans.factory.BeanFactory; -public class AVMSubmitPackageHandler - extends JBPMSpringActionHandler - implements Serializable +public class AVMSubmitPackageHandler extends JBPMSpringActionHandler implements Serializable { private static final long serialVersionUID = 4113360751217684995L; @@ -57,7 +56,7 @@ public class AVMSubmitPackageHandler /** The AVMService instance. */ private AVMService fAVMService; - + /** The AVMSyncService instance. */ private AVMSyncService fAVMSyncService; @@ -65,21 +64,22 @@ public class AVMSubmitPackageHandler private AVMLockingService fAVMLockingService; /** - * The AVMSubmitTransactionListener instance - * (for JMX notification of virtualization server after commit/rollback). + * The AVMSubmitTransactionListener instance (for JMX notification of virtualization server after commit/rollback). */ private AVMSubmitTransactionListener fAVMSubmitTransactionListener; - + /** * Initialize service references. - * @param factory The BeanFactory to get references from. + * + * @param factory + * The BeanFactory to get references from. */ @Override - protected void initialiseHandler(final BeanFactory factory) + protected void initialiseHandler(final BeanFactory factory) { - fAVMService = (AVMService)factory.getBean(ServiceRegistry.AVM_SERVICE.getLocalName()); - fAVMSyncService = (AVMSyncService)factory.getBean(ServiceRegistry.AVM_SYNC_SERVICE.getLocalName()); - fAVMLockingService = (AVMLockingService)factory.getBean(ServiceRegistry.AVM_LOCKING_SERVICE.getLocalName()); + fAVMService = (AVMService) factory.getBean(ServiceRegistry.AVM_SERVICE.getLocalName()); + fAVMSyncService = (AVMSyncService) factory.getBean(ServiceRegistry.AVM_SYNC_SERVICE.getLocalName()); + fAVMLockingService = (AVMLockingService) factory.getBean(ServiceRegistry.AVM_LOCKING_SERVICE.getLocalName()); fAVMSubmitTransactionListener = (AVMSubmitTransactionListener) factory.getBean("AVMSubmitTransactionListener"); AlfrescoTransactionSupport.bindListener(fAVMSubmitTransactionListener); @@ -87,25 +87,25 @@ public class AVMSubmitPackageHandler /** * Do the actual work. - * @param executionContext The context to get stuff from. + * + * @param executionContext + * The context to get stuff from. */ - public void execute(final ExecutionContext executionContext) - throws Exception + public void execute(final ExecutionContext executionContext) throws Exception { // TODO: Allow submit parameters to be passed into this action handler - // rather than pulling directly from execution context - final NodeRef pkg = ((JBPMNode)executionContext.getContextInstance().getVariable("bpm_package")).getNodeRef(); + // rather than pulling directly from execution context + final NodeRef pkg = ((JBPMNode) executionContext.getContextInstance().getVariable("bpm_package")).getNodeRef(); final Pair pkgPath = AVMNodeConverter.ToAVMVersionPath(pkg); final AVMNodeDescriptor pkgDesc = fAVMService.lookup(pkgPath.getFirst(), pkgPath.getSecond()); - final String from = (String)executionContext.getContextInstance().getVariable("wcmwf_fromPath"); + final String from = (String) executionContext.getContextInstance().getVariable("wcmwf_fromPath"); final String targetPath = pkgDesc.getIndirection(); if (logger.isDebugEnabled()) logger.debug("handling submit of " + pkgPath.getSecond() + " from " + from + " to " + targetPath); // submit the package changes - final String description = (String)executionContext.getContextInstance().getVariable("bpm_workflowDescription"); - final String tag = (String)executionContext.getContextInstance().getVariable("wcmwf_label"); - + final String description = (String) executionContext.getContextInstance().getVariable("bpm_workflowDescription"); + final String tag = (String) executionContext.getContextInstance().getVariable("wcmwf_label"); final Map dnsProperties = this.fAVMService.queryStorePropertyKey(targetPath.split(":")[0], QName.createQName(null, ".dns%")); String webProject = dnsProperties.keySet().iterator().next().getLocalName(); @@ -116,31 +116,40 @@ public class AVMSubmitPackageHandler String p = diff.getSourcePath(); if (from != null && from.length() != 0) { - p = from + p.substring(pkgPath.getSecond().length()); + p = from + p.substring(pkgPath.getSecond().length()); } - this.recursivelyRemoveLocks(webProject, -1, p); + this.recursivelyRemoveLocks(webProject, -1, p); } - + // Allow AVMSubmitTransactionListener to inspect the staging diffs // so it can notify the virtualization server via JMX if when this - // submit succeeds or fails. This allows virtual webapps devoted + // submit succeeds or fails. This allows virtual webapps devoted // to the workarea to be destroyed, and staging to be updated in // the event that some of the files alter the behavior of the // webapp itself (e.g.: WEB-INF/web.xml, WEB-INF/lib/*.jar), etc. AlfrescoTransactionSupport.bindResource("staging_diffs", stagingDiffs); - fAVMSyncService.update(stagingDiffs, null, false, false, true, true, tag, description); - AVMDAOs.Instance().fAVMNodeDAO.flush(); - fAVMSyncService.flatten(pkgPath.getSecond(), targetPath); + // Workflow does this as system as the staging area has restricted access + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + + public Object doWork() throws Exception + { + fAVMSyncService.update(stagingDiffs, null, false, false, true, true, tag, description); + AVMDAOs.Instance().fAVMNodeDAO.flush(); + fAVMSyncService.flatten(pkgPath.getSecond(), targetPath); + return null; + } + }, AuthenticationUtil.getSystemUserName()); // flatten source folder where changes were submitted from if (from != null && from.length() > 0) { - // first, submit changes back to sandbox forcing addition of edits in workflow (and submission + // first, submit changes back to sandbox forcing addition of edits in workflow (and submission // flag removal). second, flatten sandbox, removing modified items that have been submitted // TODO: Without locking on the sandbox, it's possible that a change to a "submitted" item - // may get lost when the item is finally approved + // may get lost when the item is finally approved final List sandboxDiffs = fAVMSyncService.compare(pkgPath.getFirst(), pkgPath.getSecond(), -1, from, null); fAVMSyncService.update(sandboxDiffs, null, true, true, false, false, tag, description); AVMDAOs.Instance().fAVMNodeDAO.flush(); @@ -149,8 +158,7 @@ public class AVMSubmitPackageHandler } /** - * Recursively remove locks from a path. Walking child folders looking for files - * to remove locks from. + * Recursively remove locks from a path. Walking child folders looking for files to remove locks from. */ private void recursivelyRemoveLocks(final String webProject, final int version, final String path) { @@ -161,16 +169,16 @@ public class AVMSubmitPackageHandler { fAVMLockingService.removeLock(webProject, path.substring(path.indexOf(":") + 1)); } - else + else { - if (desc.isDeletedDirectory() == false) - { - Map list = fAVMService.getDirectoryListing(desc, true); - for (AVMNodeDescriptor child : list.values()) - { - recursivelyRemoveLocks(webProject, version, child.getPath()); - } - } + if (desc.isDeletedDirectory() == false) + { + Map list = fAVMService.getDirectoryListing(desc, true); + for (AVMNodeDescriptor child : list.values()) + { + recursivelyRemoveLocks(webProject, version, child.getPath()); + } + } } } } diff --git a/source/java/org/alfresco/repo/lock/LockServiceImpl.java b/source/java/org/alfresco/repo/lock/LockServiceImpl.java index 759a3fb065..5d6fb0aa76 100644 --- a/source/java/org/alfresco/repo/lock/LockServiceImpl.java +++ b/source/java/org/alfresco/repo/lock/LockServiceImpl.java @@ -474,8 +474,9 @@ public class LockServiceImpl implements LockService, // Ensure we have found a node reference if (nodeRef != null && userName != null) { + String effectiveUserName = AuthenticationUtil.getCurrentEffectiveUserName(); // Check to see if should just ignore this node - note: special MT System due to AuditableAspect - if (!(this.ignoreNodeRefs.contains(nodeRef) || tenantService.getBaseNameUser(userName).equals(AuthenticationUtil.getSystemUserName()))) + if (!(this.ignoreNodeRefs.contains(nodeRef) || tenantService.getBaseNameUser(effectiveUserName).equals(AuthenticationUtil.getSystemUserName()))) { try { diff --git a/source/java/org/alfresco/repo/security/authentication/AlfrescoSecureContext.java b/source/java/org/alfresco/repo/security/authentication/AlfrescoSecureContext.java new file mode 100644 index 0000000000..b219a3a7ac --- /dev/null +++ b/source/java/org/alfresco/repo/security/authentication/AlfrescoSecureContext.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.security.authentication; + +import net.sf.acegisecurity.Authentication; +import net.sf.acegisecurity.context.security.SecureContext; + +/** + * Extensions for the Alfresco security context. + * + * This is based on the Linux model and supports real, effective and stored authorities + * + * The real authority is used for auditing and reporting who the user is etc. + * The effective authority is used for permission checks. + * + * RunAs support leaves the real authority and changes only the effective authority + * That means "special" code can run code as system but still be audited as Joe + * + * In the future scrips etc can support a setUId flag and run as the owner of the script. + * If the script chooses to do this .... + * A method invocation could do the same (after entry security checks) + * + * TODO: extent runAs to take a nodeRef context - it can then set the stored atc and set this as effective if required. + * + * @author andyh + * + */ +public interface AlfrescoSecureContext extends SecureContext +{ + /** + * Get the effective authentication - used for permission checks + * @return + */ + public Authentication getEffectiveAuthentication(); + + /** + * Get the real authenticaiton - used for auditing and everything else + * @return + */ + public Authentication getRealAuthentication(); + + /** + * Get the store authentication - used for setuid scripts and methods + * @return + */ + public Authentication getStoredAuthentication(); + + /** + * Set the effective authentication held by the context + * + * @param effictiveAuthentication + */ + public void setEffectiveAuthentication(Authentication effictiveAuthentication); + + /** + * Set the real authentication held by the context + * + * @param realAuthentication + */ + public void setRealAuthentication(Authentication realAuthentication); + + /** + * Set the stored authentication held by the context + * + * @param storedAuthentication + */ + public void setStoredAuthentication(Authentication storedAuthentication); + +} diff --git a/source/java/org/alfresco/repo/security/authentication/AlfrescoSecureContextImpl.java b/source/java/org/alfresco/repo/security/authentication/AlfrescoSecureContextImpl.java new file mode 100644 index 0000000000..cff606a330 --- /dev/null +++ b/source/java/org/alfresco/repo/security/authentication/AlfrescoSecureContextImpl.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.security.authentication; + +import net.sf.acegisecurity.Authentication; +import net.sf.acegisecurity.context.ContextInvalidException; + +/** + * Hold an Alfresco extended security context + * + * @author andyh + * + */ +public class AlfrescoSecureContextImpl implements AlfrescoSecureContext +{ + Authentication storedAuthentication; + + Authentication realAuthentication; + + Authentication effectiveAuthentication; + + /** + * ACEGI + */ + public Authentication getAuthentication() + { + return getEffectiveAuthentication(); + } + + /** + * ACEGI + */ + public void setAuthentication(Authentication newAuthentication) + { + setEffectiveAuthentication(newAuthentication); + setRealAuthentication(newAuthentication); + } + + /** + * ACEGI + */ + public void validate() throws ContextInvalidException + { + if (effectiveAuthentication == null) + { + throw new ContextInvalidException("Effective authentication not set"); + } + } + + public Authentication getEffectiveAuthentication() + { + return effectiveAuthentication; + } + + public Authentication getRealAuthentication() + { + return realAuthentication; + } + + public Authentication getStoredAuthentication() + { + return storedAuthentication; + } + + public void setEffectiveAuthentication(Authentication effictiveAuthentication) + { + this.effectiveAuthentication = effictiveAuthentication; + } + + public void setRealAuthentication(Authentication realAuthentication) + { + this.realAuthentication = realAuthentication; + } + + public void setStoredAuthentication(Authentication storedAuthentication) + { + this.storedAuthentication = storedAuthentication; + } + + @Override + public int hashCode() + { + final int PRIME = 31; + int result = 1; + result = PRIME * result + ((effectiveAuthentication == null) ? 0 : effectiveAuthentication.hashCode()); + result = PRIME * result + ((realAuthentication == null) ? 0 : realAuthentication.hashCode()); + result = PRIME * result + ((storedAuthentication == null) ? 0 : storedAuthentication.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final AlfrescoSecureContextImpl other = (AlfrescoSecureContextImpl) obj; + if (effectiveAuthentication == null) + { + if (other.effectiveAuthentication != null) + return false; + } + else if (!effectiveAuthentication.equals(other.effectiveAuthentication)) + return false; + if (realAuthentication == null) + { + if (other.realAuthentication != null) + return false; + } + else if (!realAuthentication.equals(other.realAuthentication)) + return false; + if (storedAuthentication == null) + { + if (other.storedAuthentication != null) + return false; + } + else if (!storedAuthentication.equals(other.storedAuthentication)) + return false; + return true; + } + + @Override + public String toString() + { + StringBuilder builder = new StringBuilder(); + + if (realAuthentication == null) + { + builder.append("Real authenticaion = null"); + } + else + { + builder.append("Real authenticaion = " + realAuthentication.toString()); + } + builder.append(", "); + + if (effectiveAuthentication == null) + { + builder.append("Effective authenticaion = null"); + } + else + { + builder.append("Effective authenticaion = " + effectiveAuthentication.toString()); + } + builder.append(", "); + + if (storedAuthentication == null) + { + builder.append("Stored authenticaion = null"); + } + else + { + builder.append("Stored authenticaion = " + storedAuthentication.toString()); + } + + return builder.toString(); + } + +} diff --git a/source/java/org/alfresco/repo/security/authentication/AuthenticationUtil.java b/source/java/org/alfresco/repo/security/authentication/AuthenticationUtil.java index c3f434f1c6..314ee9258b 100644 --- a/source/java/org/alfresco/repo/security/authentication/AuthenticationUtil.java +++ b/source/java/org/alfresco/repo/security/authentication/AuthenticationUtil.java @@ -53,7 +53,7 @@ public abstract class AuthenticationUtil } public static final String SYSTEM_USER_NAME = "System"; - + private static boolean mtEnabled = false; private AuthenticationUtil() @@ -63,32 +63,47 @@ public abstract class AuthenticationUtil public static void setMtEnabled(boolean mtEnabled) { - if (! AuthenticationUtil.mtEnabled) - { - AuthenticationUtil.mtEnabled = mtEnabled; - } + if (!AuthenticationUtil.mtEnabled) + { + AuthenticationUtil.mtEnabled = mtEnabled; + } } - + public static boolean isMtEnabled() { return AuthenticationUtil.mtEnabled; } - + public static Authentication setCurrentUser(String userName) { return setCurrentUser(userName, getDefaultUserDetails(userName)); } + public static Authentication setCurrentRealUser(String userName) + { + return setCurrentRealUser(userName, getDefaultUserDetails(userName)); + } + + public static Authentication setCurrentEffectiveUser(String userName) + { + return setCurrentEffectiveUser(userName, getDefaultUserDetails(userName)); + } + + public static Authentication setCurrentStoredUser(String userName) + { + return setCurrentStoredUser(userName, getDefaultUserDetails(userName)); + } + /** * Explicitly set the current user to be authenticated. * - * @param userName - String user id - * @param providedDetails - provided details for the user - * + * @param userName - + * String user id + * @param providedDetails - + * provided details for the user * @return Authentication */ - public static Authentication setCurrentUser(String userName, UserDetails providedDetails) - throws AuthenticationException + public static Authentication setCurrentUser(String userName, UserDetails providedDetails) throws AuthenticationException { if (userName == null) { @@ -97,34 +112,7 @@ public abstract class AuthenticationUtil try { - UserDetails ud = null; - if (userName.equals(SYSTEM_USER_NAME)) - { - GrantedAuthority[] gas = new GrantedAuthority[1]; - gas[0] = new GrantedAuthorityImpl("ROLE_SYSTEM"); - ud = new User(SYSTEM_USER_NAME, "", true, true, true, true, gas); - } - else if (userName.equalsIgnoreCase(PermissionService.GUEST_AUTHORITY)) - { - GrantedAuthority[] gas = new GrantedAuthority[0]; - ud = new User(PermissionService.GUEST_AUTHORITY.toLowerCase(), "", true, true, true, true, gas); - } - else - { - if (providedDetails.getUsername().equals(userName)) - { - ud = providedDetails; - } - else - { - throw new AuthenticationException("Provided user details do not match the user name"); - } - } - - UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(ud, "", ud - .getAuthorities()); - auth.setDetails(ud); - auth.setAuthenticated(true); + UsernamePasswordAuthenticationToken auth = getAuthenticationToken(userName, providedDetails); return setCurrentAuthentication(auth); } catch (net.sf.acegisecurity.AuthenticationException ae) @@ -133,6 +121,92 @@ public abstract class AuthenticationUtil } } + public static Authentication setCurrentRealUser(String userName, UserDetails providedDetails) throws AuthenticationException + { + if (userName == null) + { + throw new AuthenticationException("Null user name"); + } + + try + { + UsernamePasswordAuthenticationToken auth = getAuthenticationToken(userName, providedDetails); + return setCurrentRealAuthentication(auth); + } + catch (net.sf.acegisecurity.AuthenticationException ae) + { + throw new AuthenticationException(ae.getMessage(), ae); + } + } + + public static Authentication setCurrentEffectiveUser(String userName, UserDetails providedDetails) throws AuthenticationException + { + if (userName == null) + { + throw new AuthenticationException("Null user name"); + } + + try + { + UsernamePasswordAuthenticationToken auth = getAuthenticationToken(userName, providedDetails); + return setCurrentEffectiveAuthentication(auth); + } + catch (net.sf.acegisecurity.AuthenticationException ae) + { + throw new AuthenticationException(ae.getMessage(), ae); + } + } + + public static Authentication setCurrentStoredUser(String userName, UserDetails providedDetails) throws AuthenticationException + { + if (userName == null) + { + throw new AuthenticationException("Null user name"); + } + + try + { + UsernamePasswordAuthenticationToken auth = getAuthenticationToken(userName, providedDetails); + return setCurrentStoredAuthentication(auth); + } + catch (net.sf.acegisecurity.AuthenticationException ae) + { + throw new AuthenticationException(ae.getMessage(), ae); + } + } + + private static UsernamePasswordAuthenticationToken getAuthenticationToken(String userName, UserDetails providedDetails) + { + UserDetails ud = null; + if (userName.equals(SYSTEM_USER_NAME)) + { + GrantedAuthority[] gas = new GrantedAuthority[1]; + gas[0] = new GrantedAuthorityImpl("ROLE_SYSTEM"); + ud = new User(SYSTEM_USER_NAME, "", true, true, true, true, gas); + } + else if (userName.equalsIgnoreCase(PermissionService.GUEST_AUTHORITY)) + { + GrantedAuthority[] gas = new GrantedAuthority[0]; + ud = new User(PermissionService.GUEST_AUTHORITY.toLowerCase(), "", true, true, true, true, gas); + } + else + { + if (providedDetails.getUsername().equals(userName)) + { + ud = providedDetails; + } + else + { + throw new AuthenticationException("Provided user details do not match the user name"); + } + } + + UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(ud, "", ud.getAuthorities()); + auth.setDetails(ud); + auth.setAuthenticated(true); + return auth; + } + /** * Default implementation that makes an ACEGI object on the fly * @@ -163,51 +237,130 @@ public abstract class AuthenticationUtil else { Context context = ContextHolder.getContext(); - SecureContext sc = null; - if ((context == null) || !(context instanceof SecureContext)) + AlfrescoSecureContext sc = null; + if ((context == null) || !(context instanceof AlfrescoSecureContext)) { - sc = new SecureContextImpl(); + sc = new AlfrescoSecureContextImpl(); ContextHolder.setContext(sc); } else { - sc = (SecureContext) context; + sc = (AlfrescoSecureContext) context; } authentication.setAuthenticated(true); + // Sets real and effective sc.setAuthentication(authentication); // Support for logging tenant domain / username (via log4j NDC) String userName = SYSTEM_USER_NAME; if (authentication.getPrincipal() instanceof UserDetails) { - userName = ((UserDetails) authentication.getPrincipal()).getUsername(); + userName = ((UserDetails) authentication.getPrincipal()).getUsername(); } - + logNDC(userName); - + return authentication; } } - + public static void logNDC(String userName) { NDC.remove(); - + if (isMtEnabled()) { int idx = userName.indexOf(TenantService.SEPARATOR); - if ((idx != -1) && (idx < (userName.length()-1))) + if ((idx != -1) && (idx < (userName.length() - 1))) { - NDC.push("Tenant:"+userName.substring(idx+1)+" User:"+userName.substring(0,idx)); + NDC.push("Tenant:" + userName.substring(idx + 1) + " User:" + userName.substring(0, idx)); } else { - NDC.push("User:"+userName); + NDC.push("User:" + userName); } } else { - NDC.push("User:"+userName); + NDC.push("User:" + userName); + } + } + + public static Authentication setCurrentRealAuthentication(Authentication authentication) + { + if (authentication == null) + { + clearCurrentSecurityContext(); + return null; + } + else + { + Context context = ContextHolder.getContext(); + AlfrescoSecureContext sc = null; + if ((context == null) || !(context instanceof AlfrescoSecureContext)) + { + sc = new AlfrescoSecureContextImpl(); + ContextHolder.setContext(sc); + } + else + { + sc = (AlfrescoSecureContext) context; + } + authentication.setAuthenticated(true); + sc.setRealAuthentication(authentication); + return authentication; + } + } + + public static Authentication setCurrentEffectiveAuthentication(Authentication authentication) + { + if (authentication == null) + { + clearCurrentSecurityContext(); + return null; + } + else + { + Context context = ContextHolder.getContext(); + AlfrescoSecureContext sc = null; + if ((context == null) || !(context instanceof AlfrescoSecureContext)) + { + sc = new AlfrescoSecureContextImpl(); + ContextHolder.setContext(sc); + } + else + { + sc = (AlfrescoSecureContext) context; + } + authentication.setAuthenticated(true); + sc.setEffectiveAuthentication(authentication); + return authentication; + } + } + + public static Authentication setCurrentStoredAuthentication(Authentication authentication) + { + if (authentication == null) + { + clearCurrentSecurityContext(); + return null; + } + else + { + Context context = ContextHolder.getContext(); + AlfrescoSecureContext sc = null; + if ((context == null) || !(context instanceof AlfrescoSecureContext)) + { + sc = new AlfrescoSecureContextImpl(); + ContextHolder.setContext(sc); + } + else + { + sc = (AlfrescoSecureContext) context; + } + authentication.setAuthenticated(true); + sc.setStoredAuthentication(authentication); + return authentication; } } @@ -218,13 +371,56 @@ public abstract class AuthenticationUtil * @throws AuthenticationException */ public static Authentication getCurrentAuthentication() throws AuthenticationException + { + return getCurrentRealAuthentication(); + } + + /** + * Get the current real authentication context + * + * @return Authentication + * @throws AuthenticationException + */ + public static Authentication getCurrentRealAuthentication() throws AuthenticationException { Context context = ContextHolder.getContext(); - if ((context == null) || !(context instanceof SecureContext)) + if ((context == null) || !(context instanceof AlfrescoSecureContext)) { return null; } - return ((SecureContext) context).getAuthentication(); + return ((AlfrescoSecureContext) context).getRealAuthentication(); + } + + /** + * Get the current effective authentication context + * + * @return Authentication + * @throws AuthenticationException + */ + public static Authentication getCurrentEffectiveAuthentication() throws AuthenticationException + { + Context context = ContextHolder.getContext(); + if ((context == null) || !(context instanceof AlfrescoSecureContext)) + { + return null; + } + return ((AlfrescoSecureContext) context).getEffectiveAuthentication(); + } + + /** + * Get the current stored authentication context + * + * @return Authentication + * @throws AuthenticationException + */ + public static Authentication getCurrentStoredAuthentication() throws AuthenticationException + { + Context context = ContextHolder.getContext(); + if ((context == null) || !(context instanceof AlfrescoSecureContext)) + { + return null; + } + return ((AlfrescoSecureContext) context).getStoredAuthentication(); } /** @@ -234,13 +430,53 @@ public abstract class AuthenticationUtil * @throws AuthenticationException */ public static String getCurrentUserName() throws AuthenticationException + { + return getCurrentRealUserName(); + } + + public static String getCurrentRealUserName() throws AuthenticationException { Context context = ContextHolder.getContext(); - if ((context == null) || !(context instanceof SecureContext)) + if ((context == null) || !(context instanceof AlfrescoSecureContext)) { return null; } - return getUserName(((SecureContext) context).getAuthentication()); + AlfrescoSecureContext ctx = (AlfrescoSecureContext) context; + if (ctx.getRealAuthentication() == null) + { + return null; + } + return getUserName(ctx.getRealAuthentication()); + } + + public static String getCurrentEffectiveUserName() throws AuthenticationException + { + Context context = ContextHolder.getContext(); + if ((context == null) || !(context instanceof AlfrescoSecureContext)) + { + return null; + } + AlfrescoSecureContext ctx = (AlfrescoSecureContext) context; + if (ctx.getEffectiveAuthentication() == null) + { + return null; + } + return getUserName(ctx.getEffectiveAuthentication()); + } + + public static String getCurrentStoredUserName() throws AuthenticationException + { + Context context = ContextHolder.getContext(); + if ((context == null) || !(context instanceof AlfrescoSecureContext)) + { + return null; + } + AlfrescoSecureContext ctx = (AlfrescoSecureContext) context; + if (ctx.getStoredAuthentication() == null) + { + return null; + } + return getUserName(ctx.getStoredAuthentication()); } /** @@ -304,33 +540,42 @@ public abstract class AuthenticationUtil } /** - * Execute a unit of work as a given user. The thread's authenticated user will be - * returned to its normal state after the call. + * Execute a unit of work as a given user. The thread's authenticated user will be returned to its normal state + * after the call. * - * @param runAsWork the unit of work to do - * @param uid the user ID - * @return Returns the work's return value + * @param runAsWork + * the unit of work to do + * @param uid + * the user ID + * @return Returns the work's return value */ public static R runAs(RunAsWork runAsWork, String uid) { - String currentUser = AuthenticationUtil.getCurrentUserName(); + String effectiveUser = AuthenticationUtil.getCurrentEffectiveUserName(); + String realUser = AuthenticationUtil.getCurrentRealUserName(); R result = null; try { - if ((currentUser != null) && (isMtEnabled())) - { - int idx = currentUser.indexOf(TenantService.SEPARATOR); - if ((idx != -1) && (idx < (currentUser.length()-1))) + + if ((realUser != null) && (isMtEnabled())) { + int idx = realUser.indexOf(TenantService.SEPARATOR); + if ((idx != -1) && (idx < (realUser.length() - 1))) + { if (uid.equals(AuthenticationUtil.getSystemUserName())) { - uid = uid + TenantService.SEPARATOR + currentUser.substring(idx+1); + uid = uid + TenantService.SEPARATOR + realUser.substring(idx + 1); } } } - - AuthenticationUtil.setCurrentUser(uid); + + if (realUser == null) + { + AuthenticationUtil.setCurrentRealUser(uid); + } + AuthenticationUtil.setCurrentEffectiveUser(uid); + result = runAsWork.doWork(); return result; } @@ -349,10 +594,13 @@ public abstract class AuthenticationUtil } finally { - AuthenticationUtil.clearCurrentSecurityContext(); - if (currentUser != null) + if (realUser == null) { - AuthenticationUtil.setCurrentUser(currentUser); + AuthenticationUtil.clearCurrentSecurityContext(); + } + else + { + AuthenticationUtil.setCurrentEffectiveUser(effectiveUser); } } } diff --git a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java index 20df811789..80baebc943 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java @@ -392,9 +392,19 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing perm = permIn; } + if(AuthenticationUtil.getCurrentEffectiveUserName() == null) + { + return AccessStatus.DENIED; + } + + if(AuthenticationUtil.getCurrentEffectiveUserName().equals(AuthenticationUtil.getSystemUserName())) + { + return AccessStatus.ALLOWED; + } + // Get the current authentications // Use the smart authentication cache to improve permissions performance - Authentication auth = authenticationComponent.getCurrentAuthentication(); + Authentication auth = AuthenticationUtil.getCurrentEffectiveAuthentication(); final Set authorisations = getAuthorisations(auth, nodeRef); // If the node does not support the given permission there is no point @@ -496,9 +506,19 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing return AccessStatus.DENIED; } + if(AuthenticationUtil.getCurrentEffectiveUserName() == null) + { + return AccessStatus.DENIED; + } + + if(AuthenticationUtil.getCurrentEffectiveUserName().equals(AuthenticationUtil.getSystemUserName())) + { + return AccessStatus.ALLOWED; + } + // Get the current authentications // Use the smart authentication cache to improve permissions performance - Authentication auth = authenticationComponent.getCurrentAuthentication(); + Authentication auth = AuthenticationUtil.getCurrentEffectiveAuthentication(); if (auth == null) { throw new IllegalStateException("Unauthenticated"); diff --git a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java index 0aa7eceb31..82d5ac506e 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java @@ -89,6 +89,34 @@ public class PermissionServiceTest extends AbstractPermissionTest allowAndyReadChildren = new SimplePermissionEntry(rootNodeRef, getPermission(PermissionService.READ_CHILDREN), "andy", AccessStatus.ALLOWED); } + public void testRunAsRealAndEffectiveUsers() + { + runAs("admin"); + + final NodeRef n1 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}one"), ContentModel.TYPE_FOLDER).getChildRef(); + + runAs("andy"); + assertTrue(permissionService.hasPermission(n1, getPermission(PermissionService.CONTRIBUTOR)) == AccessStatus.DENIED); + + assertEquals("andy", AuthenticationUtil.getCurrentRealUserName()); + assertEquals("andy", AuthenticationUtil.getCurrentEffectiveUserName()); + + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() { + + public Object doWork() throws Exception + { + assertTrue(permissionService.hasPermission(n1, getPermission(PermissionService.CONTRIBUTOR)) == AccessStatus.ALLOWED); + + assertEquals("andy", AuthenticationUtil.getCurrentRealUserName()); + assertEquals("admin", AuthenticationUtil.getCurrentEffectiveUserName()); + return null; + }}, "admin"); + + assertEquals("andy", AuthenticationUtil.getCurrentRealUserName()); + assertEquals("andy", AuthenticationUtil.getCurrentEffectiveUserName()); + + } + public void testDefaultModelPermissions() { runAs("admin");