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