| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| |
| /* |
| * AT&T - PROPRIETARY |
| * THIS FILE CONTAINS PROPRIETARY INFORMATION OF |
| * AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN |
| * ACCORDANCE WITH APPLICABLE AGREEMENTS. |
| * |
| * Copyright (c) 2014 AT&T Knowledge Ventures |
| * Unpublished and Not for Publication |
| * All Rights Reserved |
| */ |
| package org.apache.openaz.xacml.rest; |
| |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.net.MalformedURLException; |
| import java.net.URI; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.Properties; |
| import java.util.Set; |
| |
| import org.apache.commons.io.IOUtils; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.openaz.xacml.api.pap.PAPException; |
| import org.apache.openaz.xacml.api.pap.PDPStatus; |
| import org.apache.openaz.xacml.api.pap.PDPStatus.Status; |
| import org.apache.openaz.xacml.api.pdp.PDPEngine; |
| import org.apache.openaz.xacml.api.pdp.PDPEngineFactory; |
| import org.apache.openaz.xacml.api.pip.PIPEngine; |
| import org.apache.openaz.xacml.api.pip.PIPException; |
| import org.apache.openaz.xacml.api.pip.PIPFinder; |
| import org.apache.openaz.xacml.api.pip.PIPFinderFactory; |
| import org.apache.openaz.xacml.pdp.policy.PolicyDef; |
| import org.apache.openaz.xacml.pdp.policy.dom.DOMPolicyDef; |
| import org.apache.openaz.xacml.pdp.std.StdPolicyFinderFactory; |
| import org.apache.openaz.xacml.std.pap.StdPDPPIPConfig; |
| import org.apache.openaz.xacml.std.pap.StdPDPPolicy; |
| import org.apache.openaz.xacml.std.pap.StdPDPStatus; |
| import org.apache.openaz.xacml.util.FactoryException; |
| import org.apache.openaz.xacml.util.XACMLProperties; |
| |
| import com.google.common.base.Splitter; |
| |
| /** |
| * Does the work for loading policy and PIP configurations sent from the PAP servlet. |
| */ |
| public class XACMLPdpLoader { |
| private static final Log logger = LogFactory.getLog(XACMLPdpLoader.class); |
| |
| public static synchronized PDPEngine loadEngine(StdPDPStatus status, Properties policyProperties, |
| Properties pipProperties) { |
| logger.info("loadEngine: " + policyProperties + " " + pipProperties); |
| // |
| // First load our policies |
| // |
| try { |
| // |
| // Were we given some properties? |
| // |
| if (policyProperties == null) { |
| // |
| // On init we have no incoming configuration, so just |
| // Load our current saved configuration |
| // |
| policyProperties = new Properties(); |
| try (InputStream is = Files.newInputStream(getPDPPolicyCache())) { |
| policyProperties.load(is); |
| } |
| } |
| |
| // |
| // Get our policy cache up-to-date |
| // |
| // Side effects of this include: |
| // - downloading of policies from remote locations, and |
| // - creating new "<PolicyId>.file" properties for files existing local |
| // |
| XACMLPdpLoader.cachePolicies(policyProperties); |
| // |
| // Validate the policies |
| // |
| XACMLPdpLoader.validatePolicies(policyProperties, status); |
| if (logger.isDebugEnabled()) { |
| logger.debug("Status: " + status); |
| } |
| } catch (Exception e) { |
| String error = "Failed to load Policy Cache properties file: " + e.getMessage(); |
| logger.error(error, e); |
| status.addLoadError(error); |
| status.setStatus(PDPStatus.Status.LOAD_ERRORS); |
| } |
| // |
| // Load our PIP configuration |
| // |
| try { |
| // |
| // Were we given some properties to use? |
| // |
| if (pipProperties == null) { |
| // |
| // Load our current saved configuration |
| // |
| pipProperties = new Properties(); |
| try (InputStream is = Files.newInputStream(getPIPConfig())) { |
| pipProperties.load(is); |
| } |
| } |
| // |
| // Validate our PIP configurations |
| // |
| XACMLPdpLoader.validatePipConfiguration(pipProperties, status); |
| if (logger.isDebugEnabled()) { |
| logger.debug("Status: " + status); |
| } |
| } catch (Exception e) { |
| String error = "Failed to load/validate Pip Config properties file: " + e.getMessage(); |
| logger.error(error, e); |
| status.addLoadError(error); |
| status.setStatus(PDPStatus.Status.LOAD_ERRORS); |
| } |
| // |
| // Were they validated? |
| // |
| if (status.getStatus() == Status.LOAD_ERRORS) { |
| logger.error("there were load errors"); |
| return null; |
| } |
| // |
| // Reset our official properties the PDP factory |
| // uses to configure the PDP engine. |
| // |
| XACMLRest.loadXacmlProperties(policyProperties, pipProperties); |
| // |
| // Dump ALL our properties that we are trying to load |
| // |
| try { |
| logger.info(XACMLProperties.getProperties().toString()); |
| } catch (IOException e) { |
| logger.error("Failed to get XACML Properties", e); |
| } |
| // |
| // Now load the PDP engine |
| // |
| PDPEngineFactory factory = null; |
| PDPEngine engine = null; |
| try { |
| factory = PDPEngineFactory.newInstance(); |
| engine = factory.newEngine(); |
| logger.info("Loaded new PDP engine."); |
| status.setStatus(Status.UP_TO_DATE); |
| } catch (FactoryException e) { |
| String error = "Failed to create new PDP Engine"; |
| logger.error(error, e); |
| status.addLoadError(error); |
| } |
| return engine; |
| } |
| |
| public static synchronized void validatePolicies(Properties properties, StdPDPStatus status) |
| throws PAPException { |
| Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties); |
| Set<String> refPolicies = XACMLProperties.getReferencedPolicyIDs(properties); |
| |
| for (String id : rootPolicies) { |
| loadPolicy(properties, status, id, true); |
| } |
| // remember which policies were root policies |
| status.addAllLoadedRootPolicies(status.getLoadedPolicies()); |
| |
| for (String id : refPolicies) { |
| loadPolicy(properties, status, id, false); |
| } |
| |
| logger.info("Loaded " + status.getLoadedPolicies().size() + " policies, failed to load " |
| + status.getFailedPolicies().size() + " policies, " |
| + status.getLoadedRootPolicies().size() + " root policies"); |
| if (status.getLoadedRootPolicies().size() == 0) { |
| logger.warn("NO ROOT POLICIES LOADED!!! Cannot serve PEP Requests."); |
| status.addLoadWarning("NO ROOT POLICIES LOADED!!! Cannot serve PEP Requests."); |
| } |
| } |
| |
| public static synchronized void loadPolicy(Properties properties, StdPDPStatus status, String id, |
| boolean isRoot) throws PAPException { |
| PolicyDef policy = null; |
| String location = null; |
| URI locationURI = null; |
| boolean isFile = false; |
| try { |
| location = properties.getProperty(id + ".file"); |
| if (location == null) { |
| location = properties.getProperty(id + ".url"); |
| if (location != null) { |
| // |
| // Construct the URL |
| // |
| locationURI = URI.create(location); |
| URL url = locationURI.toURL(); |
| URLConnection urlConnection = url.openConnection(); |
| urlConnection.setRequestProperty(XACMLRestProperties.PROP_PDP_HTTP_HEADER_ID, |
| XACMLProperties |
| .getProperty(XACMLRestProperties.PROP_PDP_ID)); |
| // |
| // Now construct the output file name |
| // |
| Path outFile = Paths.get(getPDPConfig().toAbsolutePath().toString(), id); |
| // |
| // Copy it to disk |
| // |
| try (FileOutputStream fos = new FileOutputStream(outFile.toFile())) { |
| IOUtils.copy(urlConnection.getInputStream(), fos); |
| } |
| // |
| // Now try to load |
| // |
| isFile = true; |
| try (InputStream fis = Files.newInputStream(outFile)) { |
| policy = DOMPolicyDef.load(fis); |
| } |
| // |
| // Save it |
| // |
| properties.setProperty(id + ".file", outFile.toAbsolutePath().toString()); |
| } |
| } else { |
| isFile = true; |
| locationURI = Paths.get(location).toUri(); |
| try (InputStream is = Files.newInputStream(Paths.get(location))) { |
| policy = DOMPolicyDef.load(is); |
| } |
| } |
| if (policy != null) { |
| status.addLoadedPolicy(new StdPDPPolicy(id, isRoot, locationURI, properties)); |
| logger.info("Loaded policy: " + policy.getIdentifier() + " version: " |
| + policy.getVersion().stringValue()); |
| } else { |
| String error = "Failed to load policy " + location; |
| logger.error(error); |
| status.setStatus(PDPStatus.Status.LOAD_ERRORS); |
| status.addLoadError(error); |
| status.addFailedPolicy(new StdPDPPolicy(id, isRoot)); |
| } |
| } catch (Exception e) { |
| logger.error("Failed to load policy '" + id + "' from location '" + location + "'", e); |
| status.setStatus(PDPStatus.Status.LOAD_ERRORS); |
| status.addFailedPolicy(new StdPDPPolicy(id, isRoot)); |
| // |
| // Is it a file? |
| // |
| if (isFile) { |
| // |
| // Let's remove it |
| // |
| try { |
| logger.error("Corrupted policy file, deleting: " + location); |
| Files.delete(Paths.get(location)); |
| } catch (IOException e1) { |
| logger.error(e1); |
| } |
| } |
| throw new PAPException("Failed to load policy '" + id + "' from location '" + location + "'"); |
| } |
| } |
| |
| public static synchronized void validatePipConfiguration(Properties properties, StdPDPStatus status) |
| throws PAPException { |
| try { |
| PIPFinderFactory factory = PIPFinderFactory.newInstance(properties); |
| if (factory == null) { |
| throw new FactoryException("Could not create PIP Finder Factory: " |
| + properties.getProperty(XACMLProperties.PROP_PIPFINDERFACTORY)); |
| } |
| PIPFinder finder = factory.getFinder(properties); |
| // |
| // Check for this, although it should always return something |
| // |
| if (finder == null) { |
| logger.error("pip finder factory returned a null engine."); |
| throw new PIPException("Could not create PIP Finder"); |
| } else { |
| logger.info("Loaded PIP finder"); |
| } |
| for (PIPEngine engine : finder.getPIPEngines()) { |
| logger.info("Configured PIP Engine: " + engine.getName()); |
| StdPDPPIPConfig config = new StdPDPPIPConfig(); |
| config.setName(engine.getName()); |
| status.addLoadedPipConfig(config); |
| } |
| } catch (FactoryException | PIPException e) { |
| logger.error("validate PIP configuration failed: " + e.getLocalizedMessage()); |
| status.addLoadError(e.getLocalizedMessage()); |
| status.setStatus(Status.LOAD_ERRORS); |
| throw new PAPException(e); |
| } |
| } |
| |
| /** |
| * Iterates the policies defined in the props object to ensure they are loaded locally. Policies are |
| * searched for in the following order: - see if the current properties has a "<PolicyID>.file" |
| * entry and that file exists in the local directory - if not, see if the file exists in the local |
| * directory; if so create a ".file" property for it. - if not, get the "<PolicyID>.url" property |
| * and try to GET the policy from that location (and set the ".file" property) If the ".file" property is |
| * created, then true is returned to tell the caller that the props object changed. |
| * |
| * @param props |
| * @return true/false if anything was changed in the props object |
| * @throws PAPException |
| */ |
| public static synchronized boolean cachePolicies(Properties props) throws PAPException { |
| boolean changed = false; |
| String[] lists = new String[2]; |
| lists[0] = props.getProperty(XACMLProperties.PROP_ROOTPOLICIES); |
| lists[1] = props.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES); |
| for (String list : lists) { |
| // |
| // Check for a null or empty parameter |
| // |
| if (list == null || list.length() == 0) { |
| continue; |
| } |
| Iterable<String> policies = Splitter.on(',').trimResults().omitEmptyStrings().split(list); |
| for (String policy : policies) { |
| boolean policyExists = false; |
| |
| // First look for ".file" property and verify the file exists |
| String propLocation = props.getProperty(policy + StdPolicyFinderFactory.PROP_FILE); |
| if (propLocation != null) { |
| // |
| // Does it exist? |
| // |
| policyExists = Files.exists(Paths.get(propLocation)); |
| if (!policyExists) { |
| logger.warn("Policy file " + policy + " expected at " + propLocation |
| + " does NOT exist."); |
| } |
| } |
| |
| // If ".file" property does not exist, try looking for the local file anyway |
| // (it might exist without having a ".file" property set for it) |
| if (!policyExists) { |
| // |
| // Now construct the output file name |
| // |
| Path outFile = Paths.get(getPDPConfig().toAbsolutePath().toString(), policy); |
| // |
| // Double check to see if we pulled it at some point |
| // |
| policyExists = Files.exists(outFile); |
| if (policyExists) { |
| // |
| // Set the property so the PDP engine doesn't have |
| // to pull it from the URL but rather the FILE. |
| // |
| logger.info("Policy does exist: " + outFile.toAbsolutePath().toString()); |
| props.setProperty(policy + StdPolicyFinderFactory.PROP_FILE, outFile.toAbsolutePath() |
| .toString()); |
| // |
| // Indicate that there were changes made to the properties |
| // |
| changed = true; |
| } else { |
| |
| // File does not exist locally, so we need to get it from the location given in the |
| // ".url" property (which MUST exist) |
| |
| // |
| // There better be a URL to retrieve it |
| // |
| propLocation = props.getProperty(policy + StdPolicyFinderFactory.PROP_URL); |
| if (propLocation != null) { |
| // |
| // Get it |
| // |
| URL url = null; |
| try { |
| // |
| // Create the URL |
| // |
| url = new URL(propLocation); |
| logger.info("Pulling " + url.toString()); |
| // |
| // Open the connection |
| // |
| URLConnection urlConnection = url.openConnection(); |
| urlConnection |
| .setRequestProperty(XACMLRestProperties.PROP_PDP_HTTP_HEADER_ID, |
| XACMLProperties |
| .getProperty(XACMLRestProperties.PROP_PDP_ID)); |
| // |
| // Copy it to disk |
| // |
| try (InputStream is = urlConnection.getInputStream(); |
| OutputStream os = new FileOutputStream(outFile.toFile())) { |
| IOUtils.copy(is, os); |
| } |
| // |
| // Now save it in the properties as a .file |
| // |
| logger.info("Pulled policy: " + outFile.toAbsolutePath().toString()); |
| props.setProperty(policy + StdPolicyFinderFactory.PROP_FILE, outFile |
| .toAbsolutePath().toString()); |
| // |
| // Indicate that there were changes made to the properties |
| // |
| changed = true; |
| } catch (Exception e) { |
| if (e instanceof MalformedURLException) { |
| logger.error("Policy '" + policy |
| + "' had bad URL in new configuration, URL='" + propLocation |
| + "'"); |
| } else { |
| logger.error("Error while retrieving policy " + policy + " from URL " |
| + url.toString() + ", e=" + e); |
| } |
| } |
| } else { |
| logger.error("Policy " + policy + " does NOT exist and does NOT have a URL"); |
| } |
| } |
| } |
| } |
| } |
| return changed; |
| } |
| |
| public static synchronized Path getPDPPolicyCache() throws PAPException { |
| Path config = getPDPConfig(); |
| Path policyProperties = Paths.get(config.toAbsolutePath().toString(), "xacml.policy.properties"); |
| if (Files.notExists(policyProperties)) { |
| logger.warn(policyProperties.toAbsolutePath().toString() + " does NOT exist."); |
| // |
| // Try to create the file |
| // |
| try { |
| Files.createFile(policyProperties); |
| } catch (IOException e) { |
| logger.error("Failed to create policy properties file: " |
| + policyProperties.toAbsolutePath().toString()); |
| throw new PAPException("Failed to create policy properties file: " |
| + policyProperties.toAbsolutePath().toString()); |
| } |
| } |
| return policyProperties; |
| } |
| |
| public static synchronized Path getPIPConfig() throws PAPException { |
| Path config = getPDPConfig(); |
| Path pipConfigProperties = Paths.get(config.toAbsolutePath().toString(), "xacml.pip.properties"); |
| if (Files.notExists(pipConfigProperties)) { |
| logger.warn(pipConfigProperties.toAbsolutePath().toString() + " does NOT exist."); |
| // |
| // Try to create the file |
| // |
| try { |
| Files.createFile(pipConfigProperties); |
| } catch (IOException e) { |
| logger.error("Failed to create pip properties file: " |
| + pipConfigProperties.toAbsolutePath().toString()); |
| throw new PAPException("Failed to create pip properties file: " |
| + pipConfigProperties.toAbsolutePath().toString()); |
| } |
| } |
| return pipConfigProperties; |
| } |
| |
| public static synchronized Path getPDPConfig() throws PAPException { |
| Path config = Paths.get(XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_CONFIG)); |
| if (Files.notExists(config)) { |
| logger.warn(config.toAbsolutePath().toString() + " does NOT exist."); |
| // |
| // Try to create the directory |
| // |
| try { |
| Files.createDirectories(config); |
| } catch (IOException e) { |
| logger.error("Failed to create config directory: " + config.toAbsolutePath().toString(), e); |
| throw new PAPException("Failed to create config directory: " |
| + config.toAbsolutePath().toString()); |
| } |
| } |
| return config; |
| } |
| |
| } |