| /* |
| * 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) 2013 AT&T Knowledge Ventures |
| * Unpublished and Not for Publication |
| * All Rights Reserved |
| */ |
| package org.apache.openaz.xacml.rest; |
| |
| import java.io.BufferedReader; |
| import java.io.ByteArrayInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.OutputStream; |
| import java.nio.file.Files; |
| import java.util.Properties; |
| import java.util.concurrent.BlockingQueue; |
| import java.util.concurrent.LinkedBlockingQueue; |
| |
| import javax.servlet.Servlet; |
| import javax.servlet.ServletConfig; |
| import javax.servlet.ServletException; |
| import javax.servlet.annotation.WebInitParam; |
| import javax.servlet.annotation.WebServlet; |
| import javax.servlet.http.HttpServlet; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.commons.io.IOUtils; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.http.entity.ContentType; |
| import org.apache.openaz.xacml.api.Request; |
| import org.apache.openaz.xacml.api.Response; |
| import org.apache.openaz.xacml.api.pap.PDPStatus.Status; |
| import org.apache.openaz.xacml.api.pdp.PDPEngine; |
| import org.apache.openaz.xacml.api.pdp.PDPException; |
| import org.apache.openaz.xacml.std.dom.DOMRequest; |
| import org.apache.openaz.xacml.std.dom.DOMResponse; |
| import org.apache.openaz.xacml.std.json.JSONRequest; |
| import org.apache.openaz.xacml.std.json.JSONResponse; |
| import org.apache.openaz.xacml.std.pap.StdPDPStatus; |
| import org.apache.openaz.xacml.util.XACMLProperties; |
| |
| import com.fasterxml.jackson.databind.ObjectMapper; |
| |
| /** |
| * Servlet implementation class XacmlPdpServlet This is an implementation of the XACML 3.0 RESTful Interface |
| * with added features to support simple PAP RESTful API for policy publishing and PIP configuration changes. |
| * If you are running this the first time, then we recommend you look at the xacml.pdp.properties file. This |
| * properties file has all the default parameter settings. If you are running the servlet as is, then we |
| * recommend setting up you're container to run it on port 8080 with context "/pdp". Wherever the default |
| * working directory is set to, a "config" directory will be created that holds the policy and pip cache. This |
| * setting is located in the xacml.pdp.properties file. When you are ready to customize, you can create a |
| * separate xacml.pdp.properties on you're local file system and setup the parameters as you wish. Just set |
| * the Java VM System variable to point to that file: |
| * -Dxacml.properties=/opt/app/xacml/etc/xacml.pdp.properties Or if you only want to change one or two |
| * properties, simply set the Java VM System variable for that property. -Dxacml.rest.pdp.register=false |
| */ |
| @WebServlet(description = "Implements the XACML PDP RESTful API and client PAP API.", urlPatterns = { |
| "/" |
| }, loadOnStartup = 1, initParams = { |
| @WebInitParam(name = "XACML_PROPERTIES_NAME", value = "xacml.pdp.properties", description = "The location of the PDP xacml.pdp.properties file holding configuration information.") |
| }) |
| public class XACMLPdpServlet extends HttpServlet implements Runnable { |
| private static final long serialVersionUID = 1L; |
| // |
| // Our application debug log |
| // |
| private static final Log logger = LogFactory.getLog(XACMLPdpServlet.class); |
| // |
| // This logger is specifically only for Xacml requests and their corresponding response. |
| // It's output ideally should be sent to a separate file from the application logger. |
| // |
| private static final Log requestLogger = LogFactory.getLog("xacml.request"); |
| // |
| // This thread may getting invoked on startup, to let the PAP know |
| // that we are up and running. |
| // |
| private Thread registerThread = null; |
| private XACMLPdpRegisterThread registerRunnable = null; |
| // |
| // This is our PDP engine pointer. There is a synchronized lock used |
| // for access to the pointer. In case we are servicing PEP requests while |
| // an update is occurring from the PAP. |
| // |
| private PDPEngine pdpEngine = null; |
| private static final Object pdpEngineLock = new Object(); |
| // |
| // This is our PDP's status. What policies are loaded (or not) and |
| // what PIP configurations are loaded (or not). |
| // There is a synchronized lock used for access to the object. |
| // |
| private static volatile StdPDPStatus status = new StdPDPStatus(); |
| private static final Object pdpStatusLock = new Object(); |
| |
| // |
| // Queue of PUT requests |
| // |
| public static class PutRequest { |
| public Properties policyProperties = null; |
| public Properties pipConfigProperties = null; |
| |
| PutRequest(Properties policies, Properties pips) { |
| this.policyProperties = policies; |
| this.pipConfigProperties = pips; |
| } |
| } |
| |
| public static volatile BlockingQueue<PutRequest> queue = new LinkedBlockingQueue<PutRequest>(2); |
| // |
| // This is our configuration thread that attempts to load |
| // a new configuration request. |
| // |
| private Thread configThread = null; |
| private volatile boolean configThreadTerminate = false; |
| |
| /** |
| * Default constructor. |
| */ |
| public XACMLPdpServlet() { |
| } |
| |
| /** |
| * @see Servlet#init(ServletConfig) |
| */ |
| @Override |
| public void init(ServletConfig config) throws ServletException { |
| // |
| // Initialize |
| // |
| XACMLRest.xacmlInit(config); |
| // |
| // Load our engine - this will use the latest configuration |
| // that was saved to disk and set our initial status object. |
| // |
| PDPEngine engine = XACMLPdpLoader.loadEngine(XACMLPdpServlet.status, null, null); |
| if (engine != null) { |
| synchronized (pdpEngineLock) { |
| pdpEngine = engine; |
| } |
| } |
| // |
| // Kick off our thread to register with the PAP servlet. |
| // |
| if (Boolean.parseBoolean(XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_REGISTER))) { |
| this.registerRunnable = new XACMLPdpRegisterThread(); |
| this.registerThread = new Thread(this.registerRunnable); |
| this.registerThread.start(); |
| } |
| // |
| // This is our thread that manages incoming configuration |
| // changes. |
| // |
| this.configThread = new Thread(this); |
| this.configThread.start(); |
| } |
| |
| /** |
| * @see Servlet#destroy() |
| */ |
| @Override |
| public void destroy() { |
| super.destroy(); |
| logger.info("Destroying...."); |
| // |
| // Make sure the register thread is not running |
| // |
| if (this.registerRunnable != null) { |
| try { |
| this.registerRunnable.terminate(); |
| if (this.registerThread != null) { |
| this.registerThread.interrupt(); |
| this.registerThread.join(); |
| } |
| } catch (InterruptedException e) { |
| logger.error(e); |
| } |
| } |
| // |
| // Make sure the configure thread is not running |
| // |
| this.configThreadTerminate = true; |
| try { |
| this.configThread.interrupt(); |
| this.configThread.join(); |
| } catch (InterruptedException e) { |
| logger.error(e); |
| } |
| logger.info("Destroyed."); |
| } |
| |
| /** |
| * PUT - The PAP engine sends configuration information using HTTP PUT request. One parameter is expected: |
| * config=[policy|pip|all] policy - Expect a properties file that contains updated lists of the root and |
| * referenced policies that the PDP should be using for PEP requests. Specifically should AT LEAST contain |
| * the following properties: xacml.rootPolicies xacml.referencedPolicies In addition, any relevant |
| * information needed by the PDP to load or retrieve the policies to store in its cache. EXAMPLE: |
| * xacml.rootPolicies=PolicyA.1, PolicyB.1 |
| * PolicyA.1.url=http://localhost:9090/PAP?id=b2d7b86d-d8f1-4adf-ba9d-b68b2a90bee1&version=1 |
| * PolicyB.1.url=http://localhost:9090/PAP/id=be962404-27f6-41d8-9521-5acb7f0238be&version=1 |
| * xacml.referencedPolicies=RefPolicyC.1, RefPolicyD.1 |
| * RefPolicyC.1.url=http://localhost:9090/PAP?id=foobar&version=1 |
| * RefPolicyD.1.url=http://localhost:9090/PAP/id=example&version=1 pip - Expect a properties file that |
| * contain PIP engine configuration properties. Specifically should AT LEAST the following property: |
| * xacml.pip.engines In addition, any relevant information needed by the PDP to load and configure the |
| * PIPs. EXAMPLE: xacml.pip.engines=foo,bar foo.classname=com.foo foo.sample=abc foo.example=xyz ...... |
| * bar.classname=com.bar ...... all - Expect ALL new configuration properties for the PDP |
| * |
| * @see HttpServlet#doPut(HttpServletRequest request, HttpServletResponse response) |
| */ |
| @Override |
| protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, |
| IOException { |
| // |
| // Dump our request out |
| // |
| if (logger.isDebugEnabled()) { |
| XACMLRest.dumpRequest(request); |
| } |
| // |
| // What is being PUT? |
| // |
| String cache = request.getParameter("cache"); |
| // |
| // Should be a list of policy and pip configurations in Java properties format |
| // |
| if (cache != null && request.getContentType().equals("text/x-java-properties")) { |
| if (request.getContentLength() > Integer.parseInt(XACMLProperties |
| .getProperty("MAX_CONTENT_LENGTH", "32767"))) { |
| String message = "Content-Length larger than server will accept."; |
| logger.info(message); |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); |
| return; |
| } |
| this.doPutConfig(cache, request, response); |
| } else { |
| String message = "Invalid cache: '" + cache + "' or content-type: '" + request.getContentType() |
| + "'"; |
| logger.error(message); |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); |
| return; |
| } |
| } |
| |
| protected void doPutConfig(String config, HttpServletRequest request, HttpServletResponse response) |
| throws ServletException, IOException { |
| try { |
| // prevent multiple configuration changes from stacking up |
| if (XACMLPdpServlet.queue.remainingCapacity() <= 0) { |
| logger.error("Queue capacity reached"); |
| response.sendError(HttpServletResponse.SC_CONFLICT, |
| "Multiple configuration changes waiting processing."); |
| return; |
| } |
| // |
| // Read the properties data into an object. |
| // |
| Properties newProperties = new Properties(); |
| newProperties.load(request.getInputStream()); |
| // should have something in the request |
| if (newProperties.size() == 0) { |
| logger.error("No properties in PUT"); |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, |
| "PUT must contain at least one property"); |
| return; |
| } |
| // |
| // Which set of properties are they sending us? Whatever they send gets |
| // put on the queue (if there is room). |
| // |
| if (config.equals("policies")) { |
| newProperties = XACMLProperties.getPolicyProperties(newProperties, true); |
| if (newProperties.size() == 0) { |
| logger.error("No policy properties in PUT"); |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, |
| "PUT with cache=policies must contain at least one policy property"); |
| return; |
| } |
| XACMLPdpServlet.queue.offer(new PutRequest(newProperties, null)); |
| } else if (config.equals("pips")) { |
| newProperties = XACMLProperties.getPipProperties(newProperties); |
| if (newProperties.size() == 0) { |
| logger.error("No pips properties in PUT"); |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, |
| "PUT with cache=pips must contain at least one pip property"); |
| return; |
| } |
| XACMLPdpServlet.queue.offer(new PutRequest(null, newProperties)); |
| } else if (config.equals("all")) { |
| Properties newPolicyProperties = XACMLProperties.getPolicyProperties(newProperties, true); |
| if (newPolicyProperties.size() == 0) { |
| logger.error("No policy properties in PUT"); |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, |
| "PUT with cache=all must contain at least one policy property"); |
| return; |
| } |
| Properties newPipProperties = XACMLProperties.getPipProperties(newProperties); |
| if (newPipProperties.size() == 0) { |
| logger.error("No pips properties in PUT"); |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, |
| "PUT with cache=all must contain at least one pip property"); |
| return; |
| } |
| XACMLPdpServlet.queue.offer(new PutRequest(newPolicyProperties, newPipProperties)); |
| } else { |
| // |
| // Invalid value |
| // |
| logger.error("Invalid config value: " + config); |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, |
| "Config must be one of 'policies', 'pips', 'all'"); |
| return; |
| } |
| } catch (Exception e) { |
| logger.error("Failed to process new configuration.", e); |
| response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); |
| return; |
| } |
| } |
| |
| /** |
| * Parameters: type=hb|config|Status 1. HeartBeat Status HeartBeat OK - All Policies are Loaded, All PIPs |
| * are Loaded LOADING_IN_PROGRESS - Currently loading a new policy set/pip configuration |
| * LAST_UPDATE_FAILED - Need to track the items that failed during last update LOAD_FAILURE - ??? Need to |
| * determine what information is sent and how 2. Configuration 3. Status return the StdPDPStatus object in |
| * the Response content |
| * |
| * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) |
| */ |
| @Override |
| protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, |
| IOException { |
| XACMLRest.dumpRequest(request); |
| // |
| // What are they requesting? |
| // |
| boolean returnHB = false; |
| response.setHeader("Cache-Control", "no-cache"); |
| String type = request.getParameter("type"); |
| // type might be null, so use equals on string constants |
| if ("config".equals(type)) { |
| response.setContentType("text/x-java-properties"); |
| try { |
| String lists = XACMLProperties.PROP_ROOTPOLICIES + "=" |
| + XACMLProperties.getProperty(XACMLProperties.PROP_ROOTPOLICIES, ""); |
| lists = lists + "\n" + XACMLProperties.PROP_REFERENCEDPOLICIES + "=" |
| + XACMLProperties.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES, "") + "\n"; |
| try (InputStream listInputStream = new ByteArrayInputStream(lists.getBytes()); |
| InputStream pipInputStream = Files.newInputStream(XACMLPdpLoader.getPIPConfig()); |
| OutputStream os = response.getOutputStream()) { |
| IOUtils.copy(listInputStream, os); |
| IOUtils.copy(pipInputStream, os); |
| } |
| response.setStatus(HttpServletResponse.SC_OK); |
| } catch (Exception e) { |
| logger.error("Failed to copy property file", e); |
| response.sendError(400, "Failed to copy Property file"); |
| } |
| |
| } else if ("hb".equals(type)) { |
| returnHB = true; |
| response.setStatus(HttpServletResponse.SC_NO_CONTENT); |
| |
| } else if ("Status".equals(type)) { |
| // convert response object to JSON and include in the response |
| synchronized (pdpStatusLock) { |
| ObjectMapper mapper = new ObjectMapper(); |
| mapper.writeValue(response.getOutputStream(), status); |
| } |
| response.setStatus(HttpServletResponse.SC_OK); |
| |
| } else { |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, "type not 'config' or 'hb'"); |
| } |
| if (returnHB) { |
| synchronized (pdpStatusLock) { |
| response |
| .addHeader(XACMLRestProperties.PROP_PDP_HTTP_HEADER_HB, status.getStatus().toString()); |
| } |
| } |
| } |
| |
| /** |
| * POST - We expect XACML requests to be posted by PEP applications. They can be in the form of XML or |
| * JSON according to the XACML 3.0 Specifications for both. |
| * |
| * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) |
| */ |
| @Override |
| protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, |
| IOException { |
| // |
| // no point in doing any work if we know from the get-go that we cannot do anything with the request |
| // |
| if (status.getLoadedRootPolicies().size() == 0) { |
| logger.warn("Request from PEP at " + request.getRequestURI() |
| + " for service when PDP has No Root Policies loaded"); |
| response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); |
| return; |
| } |
| |
| XACMLRest.dumpRequest(request); |
| // |
| // Set our no-cache header |
| // |
| response.setHeader("Cache-Control", "no-cache"); |
| // |
| // They must send a Content-Type |
| // |
| if (request.getContentType() == null) { |
| logger.warn("Must specify a Content-Type"); |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, "no content-type given"); |
| return; |
| } |
| // |
| // Limit the Content-Length to something reasonable |
| // |
| if (request.getContentLength() > Integer.parseInt(XACMLProperties.getProperty("MAX_CONTENT_LENGTH", |
| "32767"))) { |
| String message = "Content-Length larger than server will accept."; |
| logger.info(message); |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); |
| return; |
| } |
| if (request.getContentLength() <= 0) { |
| String message = "Content-Length is negative"; |
| logger.info(message); |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); |
| return; |
| } |
| ContentType contentType = null; |
| try { |
| contentType = ContentType.parse(request.getContentType()); |
| } catch (Exception e) { |
| String message = "Parsing Content-Type: " + request.getContentType() + ", error=" |
| + e.getMessage(); |
| logger.error(message, e); |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); |
| return; |
| } |
| // |
| // What exactly did they send us? |
| // |
| String incomingRequestString = null; |
| Request pdpRequest = null; |
| if (contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_JSON.getMimeType()) |
| || contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_XML.getMimeType()) |
| || contentType.getMimeType().equalsIgnoreCase("application/xacml+xml")) { |
| // |
| // Read in the string |
| // |
| StringBuilder buffer = new StringBuilder(); |
| try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()))) { |
| String line; |
| while ((line = reader.readLine()) != null) { |
| buffer.append(line); |
| } |
| incomingRequestString = buffer.toString(); |
| } |
| logger.info(incomingRequestString); |
| // |
| // Parse into a request |
| // |
| try { |
| if (contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_JSON.getMimeType())) { |
| pdpRequest = JSONRequest.load(incomingRequestString); |
| } else if (contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_XML |
| .getMimeType()) |
| || contentType.getMimeType().equalsIgnoreCase("application/xacml+xml")) { |
| pdpRequest = DOMRequest.load(incomingRequestString); |
| } |
| } catch (Exception e) { |
| logger.error("Could not parse request", e); |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); |
| return; |
| } |
| } else { |
| String message = "unsupported content type" + request.getContentType(); |
| logger.error(message); |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); |
| return; |
| } |
| // |
| // Did we successfully get and parse a request? |
| // |
| if (pdpRequest == null || pdpRequest.getRequestAttributes() == null |
| || pdpRequest.getRequestAttributes().size() <= 0) { |
| String message = "Zero Attributes found in the request"; |
| logger.error(message); |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); |
| return; |
| } |
| // |
| // Run it |
| // |
| try { |
| // |
| // Get the pointer to the PDP Engine |
| // |
| PDPEngine myEngine = null; |
| synchronized (pdpEngineLock) { |
| myEngine = this.pdpEngine; |
| } |
| if (myEngine == null) { |
| String message = "No engine loaded."; |
| logger.error(message); |
| response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message); |
| return; |
| } |
| // |
| // Send the request and save the response |
| // |
| long lTimeStart, lTimeEnd; |
| Response pdpResponse = null; |
| |
| // TODO - Make this unnecessary |
| // TODO It seems that the PDP Engine is not thread-safe, so when a configuration change occurs in |
| // the middle of processing |
| // TODO a PEP Request, that Request fails (it throws a NullPointerException in the decide() |
| // method). |
| // TODO Using synchronize will slow down processing of PEP requests, possibly by a significant |
| // amount. |
| // TODO Since configuration changes are rare, it would be A Very Good Thing if we could eliminate |
| // this sychronized block. |
| // TODO |
| // TODO This problem was found by starting one PDP then |
| // TODO RestLoadTest switching between 2 configurations, 1 second apart |
| // TODO both configurations contain the datarouter policy |
| // TODO both configurations already have all policies cached in the PDPs config directory |
| // TODO RestLoadTest started with the Datarouter test requests, 5 threads, no interval |
| // TODO With that configuration this code (without the synchronized) throws a NullPointerException |
| // TODO within a few seconds. |
| // |
| synchronized (pdpEngineLock) { |
| myEngine = this.pdpEngine; |
| try { |
| lTimeStart = System.currentTimeMillis(); |
| pdpResponse = myEngine.decide(pdpRequest); |
| lTimeEnd = System.currentTimeMillis(); |
| } catch (PDPException e) { |
| String message = "Exception during decide: " + e.getMessage(); |
| logger.error(message); |
| response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message); |
| return; |
| } |
| } |
| requestLogger.info(lTimeStart + "=" + incomingRequestString); |
| if (logger.isDebugEnabled()) { |
| logger.debug("Request time: " + (lTimeEnd - lTimeStart) + "ms"); |
| } |
| // |
| // Convert Response to appropriate Content-Type |
| // |
| if (pdpResponse == null) { |
| requestLogger.info(lTimeStart + "=" + "{}"); |
| throw new Exception("Failed to get response from PDP engine."); |
| } |
| // |
| // Set our content-type |
| // |
| response.setContentType(contentType.getMimeType()); |
| // |
| // Convert the PDP response object to a String to |
| // return to our caller as well as dump to our loggers. |
| // |
| String outgoingResponseString = ""; |
| if (contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_JSON.getMimeType())) { |
| // |
| // Get it as a String. This is not very efficient but we need to log our |
| // results for auditing. |
| // |
| outgoingResponseString = JSONResponse.toString(pdpResponse, logger.isDebugEnabled()); |
| if (logger.isDebugEnabled()) { |
| logger.debug(outgoingResponseString); |
| // |
| // Get rid of whitespace |
| // |
| outgoingResponseString = JSONResponse.toString(pdpResponse, false); |
| } |
| } else if (contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_XML.getMimeType()) |
| || contentType.getMimeType().equalsIgnoreCase("application/xacml+xml")) { |
| // |
| // Get it as a String. This is not very efficient but we need to log our |
| // results for auditing. |
| // |
| outgoingResponseString = DOMResponse.toString(pdpResponse, logger.isDebugEnabled()); |
| if (logger.isDebugEnabled()) { |
| logger.debug(outgoingResponseString); |
| // |
| // Get rid of whitespace |
| // |
| outgoingResponseString = DOMResponse.toString(pdpResponse, false); |
| } |
| } |
| // |
| // lTimeStart is used as an ID within the requestLogger to match up |
| // request's with responses. |
| // |
| requestLogger.info(lTimeStart + "=" + outgoingResponseString); |
| response.getWriter().print(outgoingResponseString); |
| } catch (Exception e) { |
| String message = "Exception executing request: " + e; |
| logger.error(message, e); |
| response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message); |
| return; |
| } |
| response.setStatus(HttpServletResponse.SC_OK); |
| } |
| |
| @Override |
| public void run() { |
| // |
| // Keep running until we are told to terminate |
| // |
| try { |
| while (!this.configThreadTerminate) { |
| PutRequest request = XACMLPdpServlet.queue.take(); |
| StdPDPStatus newStatus = new StdPDPStatus(); |
| |
| // TODO - This is related to the problem discussed in the doPost() method about the PDPEngine |
| // not being thread-safe. |
| // TODO See that discussion, and when the PDPEngine is made thread-safe it should be ok to |
| // move the loadEngine out of |
| // TODO the synchronized block. |
| // TODO However, since configuration changes should be rare we may not care about changing |
| // this. |
| PDPEngine newEngine = null; |
| synchronized (pdpStatusLock) { |
| XACMLPdpServlet.status.setStatus(Status.UPDATING_CONFIGURATION); |
| newEngine = XACMLPdpLoader.loadEngine(newStatus, request.policyProperties, |
| request.pipConfigProperties); |
| } |
| // PDPEngine newEngine = XACMLPdpLoader.loadEngine(newStatus, request.policyProperties, |
| // request.pipConfigProperties); |
| if (newEngine != null) { |
| synchronized (XACMLPdpServlet.pdpEngineLock) { |
| this.pdpEngine = newEngine; |
| try { |
| logger.info("Saving configuration."); |
| if (request.policyProperties != null) { |
| try (OutputStream os = Files.newOutputStream(XACMLPdpLoader |
| .getPDPPolicyCache())) { |
| request.policyProperties.store(os, ""); |
| } |
| } |
| if (request.pipConfigProperties != null) { |
| try (OutputStream os = Files.newOutputStream(XACMLPdpLoader.getPIPConfig())) { |
| request.pipConfigProperties.store(os, ""); |
| } |
| } |
| newStatus.setStatus(Status.UP_TO_DATE); |
| |
| } catch (Exception e) { |
| logger.error("Failed to store new properties."); |
| newStatus.setStatus(Status.LOAD_ERRORS); |
| newStatus.addLoadWarning("Unable to save configuration: " + e.getMessage()); |
| } |
| } |
| } else { |
| newStatus.setStatus(Status.LAST_UPDATE_FAILED); |
| } |
| synchronized (pdpStatusLock) { |
| XACMLPdpServlet.status.set(newStatus); |
| } |
| } |
| } catch (InterruptedException e) { |
| logger.error("interrupted"); |
| } |
| } |
| } |