/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Nuxeo - initial API and implementation * * $Id$ */ package org.eclipse.ecr.core.security; import java.security.Principal; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.ecr.core.api.CoreSession; import org.eclipse.ecr.core.api.DocumentException; import org.eclipse.ecr.core.api.IdRef; import org.eclipse.ecr.core.api.NuxeoPrincipal; import org.eclipse.ecr.core.api.PathRef; import org.eclipse.ecr.core.api.security.ACL; import org.eclipse.ecr.core.api.security.ACP; import org.eclipse.ecr.core.api.security.Access; import org.eclipse.ecr.core.api.security.PermissionProvider; import org.eclipse.ecr.core.api.security.SecurityConstants; import org.eclipse.ecr.core.api.security.SecuritySummaryEntry; import org.eclipse.ecr.core.api.security.impl.SecuritySummaryEntryImpl; import org.eclipse.ecr.core.model.Document; import org.eclipse.ecr.core.model.Session; import org.eclipse.ecr.core.query.sql.model.SQLQuery; import org.eclipse.ecr.runtime.model.ComponentContext; import org.eclipse.ecr.runtime.model.ComponentInstance; import org.eclipse.ecr.runtime.model.ComponentName; import org.eclipse.ecr.runtime.model.DefaultComponent; /** * @author Bogdan Stefanescu * @author Olivier Grisel * @author Anahide Tchertchian */ // TODO: improve caching invalidation // TODO: remove "implements SecurityConstants" and check that it doesn't break // anything public class SecurityService extends DefaultComponent { public static final ComponentName NAME = new ComponentName( "org.eclipse.ecr.core.security.SecurityService"); public static final String PERMISSIONS_EXTENSION_POINT = "permissions"; private static final String PERMISSIONS_VISIBILITY_EXTENSION_POINT = "permissionsVisibility"; private static final String POLICIES_EXTENSION_POINT = "policies"; private static final Log log = LogFactory.getLog(SecurityService.class); private PermissionProviderLocal permissionProvider; private SecurityPolicyService securityPolicyService; // private SecurityManager securityManager; @Override public void activate(ComponentContext context) throws Exception { super.activate(context); permissionProvider = new DefaultPermissionProvider(); securityPolicyService = new SecurityPolicyServiceImpl(); } @Override public void deactivate(ComponentContext context) throws Exception { super.deactivate(context); permissionProvider = null; securityPolicyService = null; } @Override public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) throws Exception { if (PERMISSIONS_EXTENSION_POINT.equals(extensionPoint) && contribution instanceof PermissionDescriptor) { permissionProvider.registerDescriptor((PermissionDescriptor) contribution); } else if (PERMISSIONS_VISIBILITY_EXTENSION_POINT.equals(extensionPoint) && contribution instanceof PermissionVisibilityDescriptor) { permissionProvider.registerDescriptor((PermissionVisibilityDescriptor) contribution); } else if (POLICIES_EXTENSION_POINT.equals(extensionPoint) && contribution instanceof SecurityPolicyDescriptor) { securityPolicyService.registerDescriptor((SecurityPolicyDescriptor) contribution); } } @Override public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) throws Exception { if (PERMISSIONS_EXTENSION_POINT.equals(extensionPoint) && contribution instanceof PermissionDescriptor) { permissionProvider.unregisterDescriptor((PermissionDescriptor) contribution); } else if (PERMISSIONS_VISIBILITY_EXTENSION_POINT.equals(extensionPoint) && contribution instanceof PermissionVisibilityDescriptor) { permissionProvider.unregisterDescriptor((PermissionVisibilityDescriptor) contribution); } else if (POLICIES_EXTENSION_POINT.equals(extensionPoint) && contribution instanceof SecurityPolicyDescriptor) { securityPolicyService.unregisterDescriptor((SecurityPolicyDescriptor) contribution); } } public PermissionProvider getPermissionProvider() { return permissionProvider; } // Never used. Remove ? public static void invalidateCache(Session session, String username) { session.getRepository().getNuxeoSecurityManager().invalidateCache( session); } public boolean arePoliciesRestrictingPermission(String permission) { return securityPolicyService.arePoliciesRestrictingPermission(permission); } public boolean arePoliciesExpressibleInQuery(String repositoryName) { return securityPolicyService.arePoliciesExpressibleInQuery(repositoryName); } public Collection<SQLQuery.Transformer> getPoliciesQueryTransformers( String repositoryName) { return securityPolicyService.getPoliciesQueryTransformers(repositoryName); } public boolean checkPermission(Document doc, Principal principal, String permission) throws SecurityException { String username = principal.getName(); // system bypass // :FIXME: temporary workaround if (SecurityConstants.SYSTEM_USERNAME.equals(username)) { return true; } if (principal instanceof NuxeoPrincipal && ((NuxeoPrincipal) principal).isAdministrator()) { return true; } // get the security store SecurityManager securityManager = doc.getSession().getRepository().getNuxeoSecurityManager(); // fully check each ACE in turn String[] resolvedPermissions = getPermissionsToCheck(permission); String[] additionalPrincipals = getPrincipalsToCheck(principal); // get the ordered list of ACE ACP acp = securityManager.getMergedACP(doc); // check pluggable policies Access access = securityPolicyService.checkPermission(doc, acp, principal, permission, resolvedPermissions, additionalPrincipals); if (access != null && !Access.UNKNOWN.equals(access)) { return access.toBoolean(); } if (acp == null) { return false; // no ACP on that doc - by default deny } access = acp.getAccess(additionalPrincipals, resolvedPermissions); return access.toBoolean(); } /** * Provides the full list of all permissions or groups of permissions that * contain the given one (inclusive). * <p> * It is exposed remotely through {@link CoreSession#getPermissionsToCheck}. * * @param permission * @return the list, as an array of strings. */ public String[] getPermissionsToCheck(String permission) { String[] groups = permissionProvider.getPermissionGroups(permission); if (groups == null) { return new String[] { permission, SecurityConstants.EVERYTHING }; } else { String[] perms = new String[groups.length + 2]; perms[0] = permission; System.arraycopy(groups, 0, perms, 1, groups.length); perms[groups.length + 1] = SecurityConstants.EVERYTHING; return perms; } } public static String[] getPrincipalsToCheck(Principal principal) { List<String> userGroups = null; if (principal instanceof NuxeoPrincipal) { userGroups = ((NuxeoPrincipal) principal).getAllGroups(); } if (userGroups == null) { return new String[] { principal.getName(), SecurityConstants.EVERYONE }; } else { int size = userGroups.size(); String[] groups = new String[size + 2]; userGroups.toArray(groups); groups[size] = principal.getName(); groups[size + 1] = SecurityConstants.EVERYONE; return groups; } } public static List<SecuritySummaryEntry> getSecuritySummary(Document doc, Boolean includeParents) { List<SecuritySummaryEntry> result = new ArrayList<SecuritySummaryEntry>(); if (doc == null) { return result; } addChildrenToSecuritySummary(doc, result); // TODO: change API to use boolean instead if (includeParents) { addParentsToSecurirySummary(doc, result); } return result; } private static SecuritySummaryEntry createSecuritySummaryEntry(Document doc) throws DocumentException { return new SecuritySummaryEntryImpl(new IdRef(doc.getUUID()), new PathRef(doc.getPath()), doc.getSession().getSecurityManager().getACP(doc)); } private static void addParentsToSecurirySummary(Document doc, List<SecuritySummaryEntry> summary) { Document parent; try { parent = doc.getParent(); } catch (DocumentException e) { return; } if (parent == null) { return; } try { SecuritySummaryEntry entry = createSecuritySummaryEntry(parent); final ACP acp = entry.getAcp(); if (acp != null) { final ACL[] acls = acp.getACLs(); if (acls != null && acls.length > 0) { summary.add(0, entry); } } } catch (DocumentException e) { return; } addParentsToSecurirySummary(parent, summary); } private static void addChildrenToSecuritySummary(Document doc, List<SecuritySummaryEntry> summary) { try { SecuritySummaryEntry entry = createSecuritySummaryEntry(doc); ACP acp = entry.getAcp(); if (acp != null && acp.getACLs() != null && acp.getACLs().length > 0) { summary.add(entry); } } catch (DocumentException e) { return; } try { Iterator<Document> iter = doc.getChildren(); while (iter.hasNext()) { Document child = iter.next(); addChildrenToSecuritySummary(child, summary); } } catch (DocumentException e) { // TODO Auto-generated catch block return; } } @SuppressWarnings("unchecked") @Override public <T> T getAdapter(Class<T> adapter) { if (adapter.isAssignableFrom(PermissionProvider.class)) { return (T) permissionProvider; } else if (adapter.isAssignableFrom(SecurityPolicyService.class)) { return (T) securityPolicyService; } else { return adapter.cast(this); } } }