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 * <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 011 * 012 * $Id$ 013 */ 014 015package org.nuxeo.ecm.core.security; 016 017import java.security.Principal; 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.Collections; 021import java.util.Hashtable; 022import java.util.LinkedList; 023import java.util.List; 024import java.util.Map; 025 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028import org.nuxeo.ecm.core.api.security.ACP; 029import org.nuxeo.ecm.core.api.security.Access; 030import org.nuxeo.ecm.core.model.Document; 031import org.nuxeo.ecm.core.query.sql.model.SQLQuery; 032 033/** 034 * Security policy service implementation. 035 * <p> 036 * Iterates over ordered policies. First policy to give a known access (grant or deny) applies. 037 * 038 * @author Anahide Tchertchian 039 */ 040public class SecurityPolicyServiceImpl implements SecurityPolicyService { 041 042 private static final long serialVersionUID = 482814921906794786L; 043 044 private static final Log log = LogFactory.getLog(SecurityPolicyServiceImpl.class); 045 046 private final Map<String, SecurityPolicyDescriptor> policyDescriptors; 047 048 private List<SecurityPolicy> policies; 049 050 public SecurityPolicyServiceImpl() { 051 policyDescriptors = new Hashtable<String, SecurityPolicyDescriptor>(); 052 } 053 054 private void computePolicies() { 055 policies = new ArrayList<SecurityPolicy>(); 056 List<SecurityPolicyDescriptor> orderedDescriptors = new ArrayList<SecurityPolicyDescriptor>(); 057 for (SecurityPolicyDescriptor descriptor : policyDescriptors.values()) { 058 if (descriptor.isEnabled()) { 059 orderedDescriptors.add(descriptor); 060 } 061 } 062 Collections.sort(orderedDescriptors); 063 List<String> policyNames = new ArrayList<String>(); 064 for (SecurityPolicyDescriptor descriptor : orderedDescriptors) { 065 if (descriptor.isEnabled()) { 066 try { 067 Object policy = descriptor.getPolicy().newInstance(); 068 if (policy instanceof SecurityPolicy) { 069 policies.add((SecurityPolicy) policy); 070 policyNames.add(descriptor.getName()); 071 } else { 072 log.error(String.format("Invalid contribution to security policy service %s:" 073 + " must implement SecurityPolicy interface", descriptor.getName())); 074 } 075 } catch (ReflectiveOperationException e) { 076 log.error(e, e); 077 } 078 } 079 } 080 log.debug("Ordered security policies: " + policyNames.toString()); 081 } 082 083 @Override 084 public synchronized List<SecurityPolicy> getPolicies() { 085 if (policies == null) { 086 computePolicies(); 087 } 088 return policies; 089 } 090 091 private void resetPolicies() { 092 policies = null; 093 } 094 095 @Override 096 public boolean arePoliciesRestrictingPermission(String permission) { 097 for (SecurityPolicy policy : getPolicies()) { 098 if (policy.isRestrictingPermission(permission)) { 099 return true; 100 } 101 } 102 return false; 103 } 104 105 @Override 106 public boolean arePoliciesExpressibleInQuery(String repositoryName) { 107 for (SecurityPolicy policy : getPolicies()) { 108 if (!policy.isExpressibleInQuery(repositoryName)) { 109 return false; 110 } 111 } 112 return true; 113 } 114 115 @Override 116 public Collection<SQLQuery.Transformer> getPoliciesQueryTransformers(String repositoryName) { 117 List<SQLQuery.Transformer> transformers = new LinkedList<SQLQuery.Transformer>(); 118 for (SecurityPolicy policy : getPolicies()) { 119 if (policy.isExpressibleInQuery(repositoryName)) { 120 transformers.add(policy.getQueryTransformer(repositoryName)); 121 } else { 122 log.warn(String.format("Security policy '%s' for repository '%s'" 123 + " cannot be expressed in SQL query.", policy.getClass().getName(), repositoryName)); 124 } 125 } 126 return transformers; 127 } 128 129 @Override 130 public void registerDescriptor(SecurityPolicyDescriptor descriptor) { 131 String id = descriptor.getName(); 132 if (policyDescriptors.containsKey(id)) { 133 log.info("Overriding security policy " + id); 134 } 135 policyDescriptors.put(id, descriptor); 136 resetPolicies(); 137 } 138 139 @Override 140 public void unregisterDescriptor(SecurityPolicyDescriptor descriptor) { 141 String id = descriptor.getName(); 142 if (policyDescriptors.containsKey(id)) { 143 policyDescriptors.remove(id); 144 resetPolicies(); 145 } 146 } 147 148 @Override 149 public Access checkPermission(Document doc, ACP mergedAcp, Principal principal, String permission, 150 String[] resolvedPermissions, String[] additionalPrincipals) { 151 Access access = Access.UNKNOWN; 152 List<SecurityPolicy> policies = getPolicies(); 153 for (SecurityPolicy policy : policies) { 154 Access policyAccess = policy.checkPermission(doc, mergedAcp, principal, permission, resolvedPermissions, 155 additionalPrincipals); 156 if (policyAccess != null && !Access.UNKNOWN.equals(policyAccess)) { 157 access = policyAccess; 158 break; 159 } 160 } 161 return access; 162 } 163 164}