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