001/* 002 * Licensed to DuraSpace under one or more contributor license agreements. 003 * See the NOTICE file distributed with this work for additional information 004 * regarding copyright ownership. 005 * 006 * DuraSpace licenses this file to you under the Apache License, 007 * Version 2.0 (the "License"); you may not use this file except in 008 * compliance with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.fcrepo.auth.xacml; 019 020import static org.fcrepo.auth.xacml.URIConstants.POLICY_URI_PREFIX; 021import static org.fcrepo.auth.xacml.URIConstants.XACML_POLICY_PROPERTY; 022import static org.fcrepo.kernel.modeshape.FedoraSessionImpl.getJcrSession; 023import static org.slf4j.LoggerFactory.getLogger; 024 025import java.net.URI; 026 027import javax.inject.Inject; 028import javax.jcr.Node; 029import javax.jcr.Property; 030import javax.jcr.RepositoryException; 031import javax.xml.parsers.DocumentBuilder; 032import javax.xml.parsers.DocumentBuilderFactory; 033 034import org.fcrepo.http.commons.session.SessionFactory; 035import org.fcrepo.kernel.api.FedoraSession; 036import org.fcrepo.kernel.api.FedoraTypes; 037import org.fcrepo.kernel.api.models.FedoraBinary; 038import org.fcrepo.kernel.api.models.FedoraResource; 039import org.fcrepo.kernel.api.services.BinaryService; 040import org.fcrepo.kernel.api.services.NodeService; 041import org.jboss.security.xacml.sunxacml.AbstractPolicy; 042import org.jboss.security.xacml.sunxacml.EvaluationCtx; 043import org.jboss.security.xacml.sunxacml.MatchResult; 044import org.jboss.security.xacml.sunxacml.Policy; 045import org.jboss.security.xacml.sunxacml.PolicyMetaData; 046import org.jboss.security.xacml.sunxacml.PolicySet; 047import org.jboss.security.xacml.sunxacml.VersionConstraints; 048import org.jboss.security.xacml.sunxacml.attr.AttributeValue; 049import org.jboss.security.xacml.sunxacml.cond.EvaluationResult; 050import org.jboss.security.xacml.sunxacml.finder.PolicyFinder; 051import org.jboss.security.xacml.sunxacml.finder.PolicyFinderModule; 052import org.jboss.security.xacml.sunxacml.finder.PolicyFinderResult; 053import org.slf4j.Logger; 054import org.springframework.stereotype.Component; 055import org.w3c.dom.Document; 056import org.w3c.dom.Element; 057 058 059/** 060 * Locates a policy in ModeShape by evaluation context or by URI. 061 * 062 * @author Gregory Jansen 063 * @author bbpennel 064 */ 065@Component("fedoraPolicyFinderModule") 066public class FedoraPolicyFinderModule extends PolicyFinderModule { 067 068 private static final Logger LOGGER = getLogger(FedoraPolicyFinderModule.class); 069 070 @Inject 071 private SessionFactory sessionFactory; 072 073 @Inject 074 private BinaryService binaryService; 075 076 @Inject 077 private NodeService nodeService; 078 079 private PolicyFinder finder; 080 081 /* 082 * This policy finder can find by request context. 083 * @see org.jboss.security.xacml.sunxacml.finder.PolicyFinderModule# 084 * isRequestSupported() 085 */ 086 @Override 087 public final boolean isRequestSupported() { 088 return true; 089 } 090 091 /* 092 * This policy finder can find by reference (URI) 093 * @see org.jboss.security.xacml.sunxacml.finder.PolicyFinderModule# 094 * isIdReferenceSupported() 095 */ 096 @Override 097 public final boolean isIdReferenceSupported() { 098 return true; 099 } 100 101 /** 102 * Retrieves the policy from the given policy node 103 * 104 * @param policyBinary 105 * @return 106 */ 107 private AbstractPolicy getPolicy(final FedoraBinary policyBinary) { 108 return loadPolicy(policyBinary); 109 } 110 111 /** 112 * Creates a new policy or policy set object from the given policy node 113 * 114 * @param policyBinary 115 * @return 116 */ 117 private AbstractPolicy loadPolicy(final FedoraBinary policyBinary) { 118 String policyName = "unparsed"; 119 try { 120 // create the factory 121 final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 122 factory.setIgnoringComments(true); 123 factory.setNamespaceAware(true); 124 factory.setValidating(false); 125 126 final DocumentBuilder db = factory.newDocumentBuilder(); 127 128 // Parse the policy content 129 final Document doc = db.parse(policyBinary.getContent()); 130 131 // handle the policy, if it's a known type 132 final Element root = doc.getDocumentElement(); 133 final String name = root.getTagName(); 134 135 policyName = PolicyUtil.getID(doc); 136 if (name.equals("Policy")) { 137 return Policy.getInstance(root); 138 } else if (name.equals("PolicySet")) { 139 return PolicySet.getInstance(root, finder); 140 } else { 141 // this isn't a root type that we know how to handle 142 throw new Exception("Unknown root document type: " + name); 143 } 144 } catch (final Exception e) { 145 LOGGER.error("Unable to parse policy from {}", policyName, e); 146 } 147 148 // a default fall-through in the case of an error 149 return null; 150 } 151 152 /* 153 * Find a policy in ModeShape that is appropriate for the evaluation 154 * context. 155 * @see 156 * org.jboss.security.xacml.sunxacml.finder.PolicyFinderModule#findPolicy 157 * (org.jboss.security.xacml.sunxacml.EvaluationCtx) 158 */ 159 @Override 160 public final PolicyFinderResult findPolicy(final EvaluationCtx context) { 161 final EvaluationResult ridEvalRes = context.getResourceAttribute( 162 URI.create("http://www.w3.org/2001/XMLSchema#string"), URIConstants.ATTRIBUTEID_RESOURCE_ID, null); 163 final AttributeValue resourceIdAttValue = ridEvalRes.getAttributeValue(); 164 String path = resourceIdAttValue.getValue().toString(); 165 166 LOGGER.debug("Finding policy for resource: {}", path); 167 168 if ("".equals(path.trim())) { 169 path = "/"; 170 } 171 172 try { 173 final FedoraSession internalSession = sessionFactory.getInternalSession(); 174 175 // Walk up the hierarchy to find the first node with a policy assigned 176 Node nodeWithPolicy = PolicyUtil.getFirstRealNode(path, getJcrSession(internalSession)); 177 while (nodeWithPolicy != null && !nodeWithPolicy.hasProperty(XACML_POLICY_PROPERTY)) { 178 nodeWithPolicy = nodeWithPolicy.getParent(); 179 } 180 181 // This should never happen, as PolicyUtil.getFirstRealNode() at least returns the root node. 182 if (null == nodeWithPolicy) { 183 LOGGER.warn("No policy found for: {}!", path); 184 return new PolicyFinderResult(); 185 } 186 187 final Property prop = nodeWithPolicy.getProperty(XACML_POLICY_PROPERTY); 188 189 final FedoraBinary policyBinary; 190 final FedoraResource resource = nodeService.find(internalSession, prop.getNode().getPath()); 191 if (resource.hasType(FedoraTypes.FEDORA_NON_RDF_SOURCE_DESCRIPTION)) { 192 policyBinary = binaryService.findOrCreate(internalSession, resource.getPath()); 193 194 } else { 195 LOGGER.warn("Policy Binary not found for: {}", path); 196 return new PolicyFinderResult(); 197 } 198 199 if (policyBinary == null) { 200 LOGGER.warn("Policy binary for path: {} was null!", nodeWithPolicy.getPath()); 201 return new PolicyFinderResult(); 202 } 203 204 final AbstractPolicy policy = getPolicy(policyBinary); 205 206 // Evaluate if the policy targets match the current context 207 final MatchResult match = policy.match(context); 208 final int result = match.getResult(); 209 210 if (result == MatchResult.INDETERMINATE) { 211 return new PolicyFinderResult(match.getStatus()); 212 } 213 214 // Found a good policy, return it 215 if (result == MatchResult.MATCH) { 216 return new PolicyFinderResult(policy); 217 } 218 219 return new PolicyFinderResult(); 220 } catch (final RepositoryException e) { 221 LOGGER.warn("Failed to retrieve a policy for {}", e, path); 222 return new PolicyFinderResult(); 223 } 224 } 225 226 /* 227 * Find a policy in ModeShape by reference URI. 228 * @see 229 * org.jboss.security.xacml.sunxacml.finder.PolicyFinderModule#findPolicy 230 * (java.net.URI, int, org.jboss.security.xacml.sunxacml.VersionConstraints, 231 * org.jboss.security.xacml.sunxacml.PolicyMetaData) 232 */ 233 @Override 234 public final PolicyFinderResult findPolicy(final URI idReference, 235 final int type, 236 final VersionConstraints constraints, 237 final PolicyMetaData parentMetaData) { 238 final String id = idReference.toString(); 239 if (!id.startsWith(POLICY_URI_PREFIX)) { 240 LOGGER.warn("Policy reference must begin with {}, but was {}", POLICY_URI_PREFIX, id); 241 return new PolicyFinderResult(); 242 } 243 244 final String path = PolicyUtil.getPathForId(id); 245 final FedoraSession internalSession = sessionFactory.getInternalSession(); 246 247 final FedoraBinary policyBinary; 248 final FedoraResource resource = nodeService.find(internalSession, path); 249 if (resource.hasType(FedoraTypes.FEDORA_NON_RDF_SOURCE_DESCRIPTION)) { 250 policyBinary = binaryService.findOrCreate(internalSession, resource.getPath()); 251 252 } else { 253 LOGGER.warn("Policy Binary not found for: {}", path); 254 return new PolicyFinderResult(); 255 } 256 257 final AbstractPolicy policy = getPolicy(policyBinary); 258 259 return new PolicyFinderResult(policy); 260 } 261 262 /* 263 * (non-Javadoc) 264 * @see 265 * org.jboss.security.xacml.sunxacml.finder.PolicyFinderModule#init(org. 266 * jboss.security.xacml.sunxacml.finder.PolicyFinder) 267 */ 268 @Override 269 public void init(final PolicyFinder finder) { 270 this.finder = finder; 271 } 272 273}