001/* 002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * Nuxeo - initial API and implementation 011 * 012 * $Id$ 013 */ 014 015package org.nuxeo.ecm.core.security; 016 017import java.security.Principal; 018import java.util.Collection; 019import java.util.List; 020 021import org.apache.commons.logging.Log; 022import org.apache.commons.logging.LogFactory; 023import org.nuxeo.ecm.core.api.CoreSession; 024import org.nuxeo.ecm.core.api.NuxeoPrincipal; 025import org.nuxeo.ecm.core.api.security.ACP; 026import org.nuxeo.ecm.core.api.security.Access; 027import org.nuxeo.ecm.core.api.security.PermissionProvider; 028import org.nuxeo.ecm.core.api.security.SecurityConstants; 029import org.nuxeo.ecm.core.model.Document; 030import org.nuxeo.ecm.core.query.sql.model.SQLQuery; 031import org.nuxeo.runtime.model.ComponentContext; 032import org.nuxeo.runtime.model.ComponentInstance; 033import org.nuxeo.runtime.model.ComponentName; 034import org.nuxeo.runtime.model.DefaultComponent; 035 036/** 037 * @author Bogdan Stefanescu 038 * @author Olivier Grisel 039 * @author Anahide Tchertchian 040 */ 041// TODO: improve caching invalidation 042// TODO: remove "implements SecurityConstants" and check that it doesn't break 043// anything 044public class SecurityService extends DefaultComponent { 045 046 public static final ComponentName NAME = new ComponentName("org.nuxeo.ecm.core.security.SecurityService"); 047 048 public static final String PERMISSIONS_EXTENSION_POINT = "permissions"; 049 050 private static final String PERMISSIONS_VISIBILITY_EXTENSION_POINT = "permissionsVisibility"; 051 052 private static final String POLICIES_EXTENSION_POINT = "policies"; 053 054 private static final Log log = LogFactory.getLog(SecurityService.class); 055 056 private PermissionProviderLocal permissionProvider; 057 058 private SecurityPolicyService securityPolicyService; 059 060 // private SecurityManager securityManager; 061 062 @Override 063 public void activate(ComponentContext context) { 064 super.activate(context); 065 permissionProvider = new DefaultPermissionProvider(); 066 securityPolicyService = new SecurityPolicyServiceImpl(); 067 } 068 069 @Override 070 public void deactivate(ComponentContext context) { 071 super.deactivate(context); 072 permissionProvider = null; 073 securityPolicyService = null; 074 } 075 076 @Override 077 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 078 if (PERMISSIONS_EXTENSION_POINT.equals(extensionPoint) && contribution instanceof PermissionDescriptor) { 079 permissionProvider.registerDescriptor((PermissionDescriptor) contribution); 080 } else if (PERMISSIONS_VISIBILITY_EXTENSION_POINT.equals(extensionPoint) 081 && contribution instanceof PermissionVisibilityDescriptor) { 082 permissionProvider.registerDescriptor((PermissionVisibilityDescriptor) contribution); 083 } else if (POLICIES_EXTENSION_POINT.equals(extensionPoint) && contribution instanceof SecurityPolicyDescriptor) { 084 securityPolicyService.registerDescriptor((SecurityPolicyDescriptor) contribution); 085 } 086 } 087 088 @Override 089 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 090 if (PERMISSIONS_EXTENSION_POINT.equals(extensionPoint) && contribution instanceof PermissionDescriptor) { 091 permissionProvider.unregisterDescriptor((PermissionDescriptor) contribution); 092 } else if (PERMISSIONS_VISIBILITY_EXTENSION_POINT.equals(extensionPoint) 093 && contribution instanceof PermissionVisibilityDescriptor) { 094 permissionProvider.unregisterDescriptor((PermissionVisibilityDescriptor) contribution); 095 } else if (POLICIES_EXTENSION_POINT.equals(extensionPoint) && contribution instanceof SecurityPolicyDescriptor) { 096 securityPolicyService.unregisterDescriptor((SecurityPolicyDescriptor) contribution); 097 } 098 } 099 100 public PermissionProvider getPermissionProvider() { 101 return permissionProvider; 102 } 103 104 public boolean arePoliciesRestrictingPermission(String permission) { 105 return securityPolicyService.arePoliciesRestrictingPermission(permission); 106 } 107 108 public boolean arePoliciesExpressibleInQuery(String repositoryName) { 109 return securityPolicyService.arePoliciesExpressibleInQuery(repositoryName); 110 } 111 112 public Collection<SQLQuery.Transformer> getPoliciesQueryTransformers(String repositoryName) { 113 return securityPolicyService.getPoliciesQueryTransformers(repositoryName); 114 } 115 116 public boolean checkPermission(Document doc, Principal principal, String permission) { 117 String username = principal.getName(); 118 119 // system bypass 120 // :FIXME: temporary workaround 121 if (SecurityConstants.SYSTEM_USERNAME.equals(username)) { 122 return true; 123 } 124 125 if (principal instanceof NuxeoPrincipal && ((NuxeoPrincipal) principal).isAdministrator()) { 126 return true; 127 } 128 129 // fully check each ACE in turn 130 String[] resolvedPermissions = getPermissionsToCheck(permission); 131 String[] additionalPrincipals = getPrincipalsToCheck(principal); 132 133 // get the ordered list of ACE 134 ACP acp = doc.getSession().getMergedACP(doc); 135 136 // check pluggable policies 137 Access access = securityPolicyService.checkPermission(doc, acp, principal, permission, resolvedPermissions, 138 additionalPrincipals); 139 if (access != null && !Access.UNKNOWN.equals(access)) { 140 return access.toBoolean(); 141 } 142 143 if (acp == null) { 144 return false; // no ACP on that doc - by default deny 145 } 146 access = acp.getAccess(additionalPrincipals, resolvedPermissions); 147 148 return access.toBoolean(); 149 } 150 151 /** 152 * Provides the full list of all permissions or groups of permissions that contain the given one (inclusive). 153 * <p> 154 * It is exposed remotely through {@link CoreSession#getPermissionsToCheck}. 155 * 156 * @param permission 157 * @return the list, as an array of strings. 158 */ 159 public String[] getPermissionsToCheck(String permission) { 160 String[] groups = permissionProvider.getPermissionGroups(permission); 161 if (groups == null) { 162 return new String[] { permission, SecurityConstants.EVERYTHING }; 163 } else { 164 String[] perms = new String[groups.length + 2]; 165 perms[0] = permission; 166 System.arraycopy(groups, 0, perms, 1, groups.length); 167 perms[groups.length + 1] = SecurityConstants.EVERYTHING; 168 return perms; 169 } 170 } 171 172 public static String[] getPrincipalsToCheck(Principal principal) { 173 List<String> userGroups = null; 174 if (principal instanceof NuxeoPrincipal) { 175 userGroups = ((NuxeoPrincipal) principal).getAllGroups(); 176 } 177 if (userGroups == null) { 178 return new String[] { principal.getName(), SecurityConstants.EVERYONE }; 179 } else { 180 int size = userGroups.size(); 181 String[] groups = new String[size + 2]; 182 userGroups.toArray(groups); 183 groups[size] = principal.getName(); 184 groups[size + 1] = SecurityConstants.EVERYONE; 185 return groups; 186 } 187 } 188 189 @SuppressWarnings("unchecked") 190 @Override 191 public <T> T getAdapter(Class<T> adapter) { 192 if (adapter.isAssignableFrom(PermissionProvider.class)) { 193 return (T) permissionProvider; 194 } else if (adapter.isAssignableFrom(SecurityPolicyService.class)) { 195 return (T) securityPolicyService; 196 } else { 197 return adapter.cast(this); 198 } 199 } 200 201}