2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.openecomp.policy.pdp.rest;
23 import java.io.BufferedReader;
24 import java.io.ByteArrayInputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.InputStreamReader;
28 import java.io.OutputStream;
29 import java.lang.reflect.Constructor;
30 import java.net.InetAddress;
31 import java.net.UnknownHostException;
32 import java.nio.file.Files;
33 import java.util.Properties;
34 import java.util.UUID;
35 import java.util.concurrent.BlockingQueue;
36 import java.util.concurrent.LinkedBlockingQueue;
38 import javax.servlet.Servlet;
39 import javax.servlet.ServletConfig;
40 import javax.servlet.ServletException;
41 import javax.servlet.annotation.WebInitParam;
42 import javax.servlet.annotation.WebServlet;
43 import javax.servlet.http.HttpServlet;
44 import javax.servlet.http.HttpServletRequest;
45 import javax.servlet.http.HttpServletResponse;
47 import org.apache.commons.io.IOUtils;
48 import org.apache.commons.logging.Log;
49 import org.apache.commons.logging.LogFactory;
50 import org.apache.http.entity.ContentType;
51 import org.openecomp.policy.api.PolicyParameters;
52 import org.openecomp.policy.common.im.AdministrativeStateException;
53 import org.openecomp.policy.common.im.ForwardProgressException;
54 import org.openecomp.policy.common.im.IntegrityMonitor;
55 import org.openecomp.policy.common.im.IntegrityMonitorProperties;
56 import org.openecomp.policy.common.im.StandbyStatusException;
57 import org.openecomp.policy.common.logging.ECOMPLoggingContext;
58 import org.openecomp.policy.common.logging.ECOMPLoggingUtils;
59 import org.openecomp.policy.common.logging.eelf.MessageCodes;
60 import org.openecomp.policy.common.logging.eelf.PolicyLogger;
61 import org.openecomp.policy.pdp.rest.jmx.PdpRestMonitor;
62 import org.openecomp.policy.rest.XACMLRest;
63 import org.openecomp.policy.rest.XACMLRestProperties;
64 import org.openecomp.policy.xacml.api.XACMLErrorConstants;
65 import org.openecomp.policy.xacml.pdp.std.functions.PolicyList;
66 import org.openecomp.policy.xacml.std.pap.StdPDPStatus;
68 import com.att.research.xacml.api.Request;
69 import com.att.research.xacml.api.Response;
70 import com.att.research.xacml.api.pap.PDPStatus.Status;
71 import com.att.research.xacml.api.pdp.PDPEngine;
72 import com.att.research.xacml.api.pdp.PDPException;
73 import com.att.research.xacml.std.dom.DOMRequest;
74 import com.att.research.xacml.std.dom.DOMResponse;
75 import com.att.research.xacml.std.json.JSONRequest;
76 import com.att.research.xacml.std.json.JSONResponse;
77 import com.att.research.xacml.util.XACMLProperties;
78 import com.fasterxml.jackson.databind.ObjectMapper;
81 * Servlet implementation class XacmlPdpServlet
83 * This is an implementation of the XACML 3.0 RESTful Interface with added features to support
84 * simple PAP RESTful API for policy publishing and PIP configuration changes.
86 * If you are running this the first time, then we recommend you look at the xacml.pdp.properties file.
87 * This properties file has all the default parameter settings. If you are running the servlet as is,
88 * then we recommend setting up you're container to run it on port 8080 with context "/pdp". Wherever
89 * the default working directory is set to, a "config" directory will be created that holds the policy
90 * and pip cache. This setting is located in the xacml.pdp.properties file.
92 * When you are ready to customize, you can create a separate xacml.pdp.properties on you're local file
93 * system and setup the parameters as you wish. Just set the Java VM System variable to point to that file:
95 * -Dxacml.properties=/opt/app/xacml/etc/xacml.pdp.properties
97 * Or if you only want to change one or two properties, simply set the Java VM System variable for that property.
99 * -Dxacml.rest.pdp.register=false
104 description = "Implements the XACML PDP RESTful API and client PAP API.",
105 urlPatterns = { "/" },
108 @WebInitParam(name = "XACML_PROPERTIES_NAME", value = "xacml.pdp.properties", description = "The location of the PDP xacml.pdp.properties file holding configuration information.")
110 public class XACMLPdpServlet extends HttpServlet implements Runnable {
111 private static final long serialVersionUID = 1L;
112 private static final String DEFAULT_MAX_CONTENT_LENGTH = "999999999"; //32767
113 private static final String CREATE_UPDATE_POLICY_SERVICE = "org.openecomp.policy.pdp.rest.api.services.CreateUpdatePolicyServiceImpl";
115 // Our application debug log
117 private static final Log logger = LogFactory.getLog(XACMLPdpServlet.class);
119 // This logger is specifically only for Xacml requests and their corresponding response.
120 // It's output ideally should be sent to a separate file from the application logger.
122 private static final Log requestLogger = LogFactory.getLog("xacml.request");
125 private static final Log auditLogger = LogFactory.getLog("auditLogger");
127 private static final PdpRestMonitor monitor = PdpRestMonitor.singleton;
130 // This thread may getting invoked on startup, to let the PAP know
131 // that we are up and running.
133 private Thread registerThread = null;
134 private XACMLPdpRegisterThread registerRunnable = null;
136 // This is our PDP engine pointer. There is a synchronized lock used
137 // for access to the pointer. In case we are servicing PEP requests while
138 // an update is occurring from the PAP.
140 private static PDPEngine pdpEngine = null;
141 private static final Object pdpEngineLock = new Object();
143 // This is our PDP's status. What policies are loaded (or not) and
144 // what PIP configurations are loaded (or not).
145 // There is a synchronized lock used for access to the object.
147 private static volatile StdPDPStatus status = new StdPDPStatus();
148 private static final Object pdpStatusLock = new Object();
149 private static Constructor<?> createUpdatePolicyConstructor;
151 private static final String ENVIORNMENT_HEADER = "Environment";
152 private static String environment = null;
154 // Queue of PUT requests
156 public static class PutRequest {
157 public Properties policyProperties = null;
158 public Properties pipConfigProperties = null;
160 PutRequest(Properties policies, Properties pips) {
161 this.policyProperties = policies;
162 this.pipConfigProperties = pips;
165 public static volatile BlockingQueue<PutRequest> queue = null;
166 // For notification Delay.
167 private static int notificationDelay = 0;
168 public static int getNotificationDelay(){
169 return XACMLPdpServlet.notificationDelay;
172 private static String pdpResourceName;
173 private static String dependencyGroups = null;
174 private static String[] dependencyNodes = null;
177 // This is our configuration thread that attempts to load
178 // a new configuration request.
180 private Thread configThread = null;
181 private volatile boolean configThreadTerminate = false;
182 private ECOMPLoggingContext baseLoggingContext = null;
183 private IntegrityMonitor im;
184 private String createUpdateResourceName = null;
186 * Default constructor.
188 public XACMLPdpServlet() {
192 * @see Servlet#init(ServletConfig)
194 public void init(ServletConfig config) throws ServletException {
198 XACMLRest.xacmlInit(config);
199 // Load the Notification Delay.
201 XACMLPdpServlet.notificationDelay = Integer.parseInt(XACMLProperties.getProperty(XACMLRestProperties.PROP_NOTIFICATION_DELAY));
203 logger.info("Notification Delay Not set. Keeping it 0 as default.");
206 int queueSize = 5; // Set default Queue Size here.
207 queueSize = Integer.parseInt(XACMLProperties.getProperty("REQUEST_BUFFER_SIZE",String.valueOf(queueSize)));
208 queue = new LinkedBlockingQueue<PutRequest>(queueSize);
209 // Load our engine - this will use the latest configuration
210 // that was saved to disk and set our initial status object.
212 PDPEngine engine = XACMLPdpLoader.loadEngine(XACMLPdpServlet.status, null, null);
213 if (engine != null) {
214 synchronized(pdpEngineLock) {
221 baseLoggingContext = new ECOMPLoggingContext();
222 // fixed data that will be the same in all logging output goes here
224 String hostname = InetAddress.getLocalHost().getCanonicalHostName();
225 baseLoggingContext.setServer(hostname);
226 } catch (UnknownHostException e) {
227 logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Unable to get hostname for logging");
230 Properties properties;
232 properties = XACMLProperties.getProperties();
233 } catch (IOException e) {
234 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e,
235 "Error loading properties with: XACMLProperties.getProperties()");
236 throw new ServletException(e.getMessage(), e.getCause());
238 if(properties.getProperty(XACMLRestProperties.PDP_RESOURCE_NAME)==null){
239 XACMLProperties.reloadProperties();
241 properties = XACMLProperties.getProperties();
242 } catch (IOException e) {
243 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e,
244 "Error loading properties with: XACMLProperties.getProperties()");
245 throw new ServletException(e.getMessage(), e.getCause());
247 PolicyLogger.info("\n Properties Given : \n" + properties.toString());
249 pdpResourceName = properties.getProperty(XACMLRestProperties.PDP_RESOURCE_NAME);
250 if(pdpResourceName == null){
251 PolicyLogger.error(MessageCodes.MISS_PROPERTY_ERROR, XACMLRestProperties.PDP_RESOURCE_NAME, "xacml.pdp");
252 throw new ServletException("pdpResourceName is null");
255 dependencyGroups = properties.getProperty(IntegrityMonitorProperties.DEPENDENCY_GROUPS);
256 if(dependencyGroups == null){
257 PolicyLogger.error(MessageCodes.MISS_PROPERTY_ERROR, IntegrityMonitorProperties.DEPENDENCY_GROUPS, "xacml.pdp");
258 throw new ServletException("dependency_groups is null");
260 // dependency_groups is a semicolon-delimited list of groups, and
261 // each group is a comma-separated list of nodes. For our purposes
262 // we just need a list of dependencies without regard to grouping,
263 // so split the list into nodes separated by either comma or semicolon.
264 dependencyNodes = dependencyGroups.split("[;,]");
265 for (int i = 0 ; i < dependencyNodes.length ; i++){
266 dependencyNodes[i] = dependencyNodes[i].trim();
269 // CreateUpdatePolicy ResourceName
270 createUpdateResourceName = properties.getProperty("createUpdatePolicy.impl.className", CREATE_UPDATE_POLICY_SERVICE);
272 Class<?> createUpdateclass = Class.forName(createUpdateResourceName);
273 createUpdatePolicyConstructor = createUpdateclass.getConstructor(PolicyParameters.class, String.class, boolean.class);
275 PolicyLogger.error(MessageCodes.MISS_PROPERTY_ERROR, "createUpdatePolicy.impl.className", "xacml.pdp.init");
276 throw new ServletException("Could not find the Class name : " +createUpdateResourceName + "\n" +e.getMessage());
279 // Create an IntegrityMonitor
281 logger.info("Creating IntegrityMonitor");
282 im = IntegrityMonitor.getInstance(pdpResourceName, properties);
283 } catch (Exception e) {
284 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, "Failed to create IntegrityMonitor");
285 throw new ServletException(e);
288 environment = XACMLProperties.getProperty("ENVIRONMENT", "DEVL");
290 // Kick off our thread to register with the PAP servlet.
292 if (Boolean.parseBoolean(XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_REGISTER))) {
293 this.registerRunnable = new XACMLPdpRegisterThread(baseLoggingContext);
294 this.registerThread = new Thread(this.registerRunnable);
295 this.registerThread.start();
298 // This is our thread that manages incoming configuration
301 this.configThread = new Thread(this);
302 this.configThread.start();
306 * @see Servlet#destroy()
308 public void destroy() {
310 logger.info("Destroying....");
312 // Make sure the register thread is not running
314 if (this.registerRunnable != null) {
316 this.registerRunnable.terminate();
317 if (this.registerThread != null) {
318 this.registerThread.interrupt();
319 this.registerThread.join();
321 } catch (InterruptedException e) {
322 logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + e);
323 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, "");
327 // Make sure the configure thread is not running
329 this.configThreadTerminate = true;
331 this.configThread.interrupt();
332 this.configThread.join();
333 } catch (InterruptedException e) {
334 logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + e);
335 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, "");
337 logger.info("Destroyed.");
341 * PUT - The PAP engine sends configuration information using HTTP PUT request.
343 * One parameter is expected:
345 * config=[policy|pip|all]
347 * policy - Expect a properties file that contains updated lists of the root and referenced policies that the PDP should
348 * be using for PEP requests.
350 * Specifically should AT LEAST contain the following properties:
352 * xacml.referencedPolicies
354 * In addition, any relevant information needed by the PDP to load or retrieve the policies to store in its cache.
357 * xacml.rootPolicies=PolicyA.1, PolicyB.1
359 * PolicyA.1.url=http://localhost:9090/PAP?id=b2d7b86d-d8f1-4adf-ba9d-b68b2a90bee1&version=1
360 * PolicyB.1.url=http://localhost:9090/PAP/id=be962404-27f6-41d8-9521-5acb7f0238be&version=1
362 * xacml.referencedPolicies=RefPolicyC.1, RefPolicyD.1
364 * RefPolicyC.1.url=http://localhost:9090/PAP?id=foobar&version=1
365 * RefPolicyD.1.url=http://localhost:9090/PAP/id=example&version=1
367 * pip - Expect a properties file that contain PIP engine configuration properties.
369 * Specifically should AT LEAST the following property:
372 * In addition, any relevant information needed by the PDP to load and configure the PIPs.
375 * xacml.pip.engines=foo,bar
377 * foo.classname=com.foo
382 * bar.classname=com.bar
385 * all - Expect ALL new configuration properties for the PDP
387 * @see HttpServlet#doPut(HttpServletRequest request, HttpServletResponse response)
389 protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
390 ECOMPLoggingContext loggingContext = ECOMPLoggingUtils.getLoggingContextForRequest(request, baseLoggingContext);
391 loggingContext.transactionStarted();
392 if ((loggingContext.getRequestID() == null) || (loggingContext.getRequestID() == "")){
393 UUID requestID = UUID.randomUUID();
394 loggingContext.setRequestID(requestID.toString());
395 PolicyLogger.info("requestID not provided in call to XACMLPdpSrvlet (doPut) so we generated one");
397 PolicyLogger.info("requestID was provided in call to XACMLPdpSrvlet (doPut)");
399 loggingContext.metricStarted();
400 loggingContext.metricEnded();
401 PolicyLogger.metrics("Metric example posted here - 1 of 2");
402 loggingContext.metricStarted();
403 loggingContext.metricEnded();
404 PolicyLogger.metrics("Metric example posted here - 2 of 2");
406 // Dump our request out
408 if (logger.isDebugEnabled()) {
409 XACMLRest.dumpRequest(request);
413 im.startTransaction();
415 catch (AdministrativeStateException | StandbyStatusException e) {
416 String message = e.toString();
417 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message);
418 loggingContext.transactionEnded();
419 PolicyLogger.audit("Transaction Failed - See Error.log");
420 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message);
424 // What is being PUT?
426 String cache = request.getParameter("cache");
428 // Should be a list of policy and pip configurations in Java properties format
430 if (cache != null && request.getContentType().equals("text/x-java-properties")) {
431 loggingContext.setServiceName("PDP.putConfig");
432 if (request.getContentLength() > Integer.parseInt(XACMLProperties.getProperty("MAX_CONTENT_LENGTH", DEFAULT_MAX_CONTENT_LENGTH))) {
433 String message = "Content-Length larger than server will accept.";
434 logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + message);
435 loggingContext.transactionEnded();
436 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, message);
437 PolicyLogger.audit("Transaction Failed - See Error.log");
438 response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
442 this.doPutConfig(cache, request, response, loggingContext);
443 loggingContext.transactionEnded();
444 PolicyLogger.audit("Transaction ended");
448 String message = "Invalid cache: '" + cache + "' or content-type: '" + request.getContentType() + "'";
449 logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + message);
450 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message);
451 loggingContext.transactionEnded();
452 PolicyLogger.audit("Transaction Failed - See Error.log");
453 response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
459 protected void doPutConfig(String config, HttpServletRequest request, HttpServletResponse response, ECOMPLoggingContext loggingContext) throws ServletException, IOException {
461 // prevent multiple configuration changes from stacking up
462 if (XACMLPdpServlet.queue.remainingCapacity() <= 0) {
463 logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Queue capacity reached");
464 PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, "Queue capacity reached");
465 loggingContext.transactionEnded();
466 PolicyLogger.audit("Transaction Failed - See Error.log");
467 response.sendError(HttpServletResponse.SC_CONFLICT, "Multiple configuration changes waiting processing.");
471 // Read the properties data into an object.
473 Properties newProperties = new Properties();
474 newProperties.load(request.getInputStream());
475 // should have something in the request
476 if (newProperties.size() == 0) {
477 logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "No properties in PUT");
478 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "No properties in PUT");
479 loggingContext.transactionEnded();
480 PolicyLogger.audit("Transaction Failed - See Error.log");
481 response.sendError(HttpServletResponse.SC_BAD_REQUEST, "PUT must contain at least one property");
485 // Which set of properties are they sending us? Whatever they send gets
486 // put on the queue (if there is room).
487 // For audit logging purposes, we consider the transaction done once the
488 // the request gets put on the queue.
490 if (config.equals("policies")) {
491 newProperties = XACMLProperties.getPolicyProperties(newProperties, true);
492 if (newProperties.size() == 0) {
493 logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "No policy properties in PUT");
494 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "No policy properties in PUT");
495 loggingContext.transactionEnded();
496 PolicyLogger.audit("Transaction Failed - See Error.log");
497 response.sendError(HttpServletResponse.SC_BAD_REQUEST, "PUT with cache=policies must contain at least one policy property");
500 XACMLPdpServlet.queue.offer(new PutRequest(newProperties, null));
501 loggingContext.transactionEnded();
502 auditLogger.info("Success");
503 PolicyLogger.audit("Success");
504 } else if (config.equals("pips")) {
505 newProperties = XACMLProperties.getPipProperties(newProperties);
506 if (newProperties.size() == 0) {
507 logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "No pips properties in PUT");
508 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "No pips properties in PUT");
509 loggingContext.transactionEnded();
510 PolicyLogger.audit("Transaction Failed - See Error.log");
511 response.sendError(HttpServletResponse.SC_BAD_REQUEST, "PUT with cache=pips must contain at least one pip property");
514 XACMLPdpServlet.queue.offer(new PutRequest(null, newProperties));
515 loggingContext.transactionEnded();
516 auditLogger.info("Success");
517 PolicyLogger.audit("Success");
518 } else if (config.equals("all")) {
519 Properties newPolicyProperties = XACMLProperties.getPolicyProperties(newProperties, true);
520 if (newPolicyProperties.size() == 0) {
521 logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "No policy properties in PUT");
522 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "No policy properties in PUT");
523 loggingContext.transactionEnded();
524 PolicyLogger.audit("Transaction Failed - See Error.log");
525 response.sendError(HttpServletResponse.SC_BAD_REQUEST, "PUT with cache=all must contain at least one policy property");
528 Properties newPipProperties = XACMLProperties.getPipProperties(newProperties);
529 if (newPipProperties.size() == 0) {
530 logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "No pips properties in PUT");
531 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "No pips properties in PUT");
532 loggingContext.transactionEnded();
533 PolicyLogger.audit("Transaction Failed - See Error.log");
534 response.sendError(HttpServletResponse.SC_BAD_REQUEST, "PUT with cache=all must contain at least one pip property");
537 XACMLPdpServlet.queue.offer(new PutRequest(newPolicyProperties, newPipProperties));
538 loggingContext.transactionEnded();
539 auditLogger.info("Success");
540 PolicyLogger.audit("Success");
545 logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Invalid config value: " + config);
546 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "Invalid config value: " + config);
547 loggingContext.transactionEnded();
548 PolicyLogger.audit("Transaction Failed - See Error.log");
549 response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Config must be one of 'policies', 'pips', 'all'");
552 } catch (Exception e) {
553 logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Failed to process new configuration.", e);
554 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, "Failed to process new configuration");
555 loggingContext.transactionEnded();
556 PolicyLogger.audit("Transaction Failed - See Error.log");
557 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
564 * Parameters: type=hb|config|Status
566 * 1. HeartBeat Status
568 * OK - All Policies are Loaded, All PIPs are Loaded
569 * LOADING_IN_PROGRESS - Currently loading a new policy set/pip configuration
570 * LAST_UPDATE_FAILED - Need to track the items that failed during last update
571 * LOAD_FAILURE - ??? Need to determine what information is sent and how
574 * return the StdPDPStatus object in the Response content
577 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
579 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
580 ECOMPLoggingContext loggingContext = ECOMPLoggingUtils.getLoggingContextForRequest(request, baseLoggingContext);
581 loggingContext.transactionStarted();
582 if ((loggingContext.getRequestID() == null) || (loggingContext.getRequestID() == "")){
583 UUID requestID = UUID.randomUUID();
584 loggingContext.setRequestID(requestID.toString());
585 PolicyLogger.info("requestID not provided in call to XACMLPdpSrvlet (doGet) so we generated one");
587 PolicyLogger.info("requestID was provided in call to XACMLPdpSrvlet (doGet)");
589 loggingContext.metricStarted();
590 loggingContext.metricEnded();
591 PolicyLogger.metrics("Metric example posted here - 1 of 2");
592 loggingContext.metricStarted();
593 loggingContext.metricEnded();
594 PolicyLogger.metrics("Metric example posted here - 2 of 2");
596 XACMLRest.dumpRequest(request);
598 String pathInfo = request.getRequestURI();
599 if (pathInfo != null){
600 // health check from Global Site Selector (iDNS).
601 // DO NOT do a im.startTransaction for the test request
602 if (pathInfo.equals("/pdp/test")) {
603 loggingContext.setServiceName("iDNS:PDP.test");
606 //If we make it this far, all is well
607 String message = "GET:/pdp/test called and PDP " + pdpResourceName + " is OK";
608 PolicyLogger.debug(message);
609 loggingContext.transactionEnded();
610 PolicyLogger.audit("Success");
611 response.setStatus(HttpServletResponse.SC_OK);
613 } catch (ForwardProgressException fpe){
614 //No forward progress is being made
615 String message = "GET:/pdp/test called and PDP " + pdpResourceName + " is not making forward progress."
616 + " Exception Message: " + fpe.getMessage();
617 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message );
618 loggingContext.transactionEnded();
619 PolicyLogger.audit("Transaction Failed - See Error.log");
620 // PolicyLogger.audit(MessageCodes.ERROR_SYSTEM_ERROR, message );
621 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message);
623 }catch (AdministrativeStateException ase){
624 //Administrative State is locked
625 String message = "GET:/pdp/test called and PDP " + pdpResourceName + " Administrative State is LOCKED "
626 + " Exception Message: " + ase.getMessage();
627 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message );
628 loggingContext.transactionEnded();
629 PolicyLogger.audit("Transaction Failed - See Error.log");
630 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message);
632 }catch (StandbyStatusException sse){
633 //Administrative State is locked
634 String message = "GET:/pdp/test called and PDP " + pdpResourceName + " Standby Status is NOT PROVIDING SERVICE "
635 + " Exception Message: " + sse.getMessage();
636 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message );
637 loggingContext.transactionEnded();
638 PolicyLogger.audit("Transaction Failed - See Error.log");
639 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message);
641 } catch (Exception e) {
642 //A subsystem is not making progress or is not responding
643 String eMsg = e.getMessage();
645 eMsg = "No Exception Message";
647 String message = "GET:/pdp/test called and PDP " + pdpResourceName + " has had a subsystem failure."
648 + " Exception Message: " + eMsg;
649 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message );
650 //Get the specific list of subsystems that failed
651 String failedNodeList = null;
652 for(String node : dependencyNodes){
653 if(eMsg.contains(node)){
654 if(failedNodeList == null){
655 failedNodeList = node;
657 failedNodeList = failedNodeList.concat(","+node);
661 if(failedNodeList == null){
662 failedNodeList = "UnknownSubSystem";
664 response.addHeader("X-ECOMP-SubsystemFailure", failedNodeList);
665 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message);
666 loggingContext.transactionEnded();
667 PolicyLogger.audit("Transaction Failed - See Error.log");
674 im.startTransaction();
676 catch (AdministrativeStateException | StandbyStatusException e) {
677 String message = e.toString();
678 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message);
679 loggingContext.transactionEnded();
680 PolicyLogger.audit("Transaction Failed - See Error.log");
681 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message);
685 // What are they requesting?
687 boolean returnHB = false;
688 response.setHeader("Cache-Control", "no-cache");
689 String type = request.getParameter("type");
690 // type might be null, so use equals on string constants
691 if ("config".equals(type)) {
692 loggingContext.setServiceName("PDP.getConfig");
693 response.setContentType("text/x-java-properties");
695 String lists = XACMLProperties.PROP_ROOTPOLICIES + "=" + XACMLProperties.getProperty(XACMLProperties.PROP_ROOTPOLICIES, "");
696 lists = lists + "\n" + XACMLProperties.PROP_REFERENCEDPOLICIES + "=" + XACMLProperties.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES, "") + "\n";
697 try (InputStream listInputStream = new ByteArrayInputStream(lists.getBytes());
698 InputStream pipInputStream = Files.newInputStream(XACMLPdpLoader.getPIPConfig());
699 OutputStream os = response.getOutputStream()) {
700 IOUtils.copy(listInputStream, os);
701 IOUtils.copy(pipInputStream, os);
703 loggingContext.transactionEnded();
704 auditLogger.info("Success");
705 PolicyLogger.audit("Success");
706 response.setStatus(HttpServletResponse.SC_OK);
707 } catch (Exception e) {
708 logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Failed to copy property file", e);
709 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, "Failed to copy property file");
710 loggingContext.transactionEnded();
711 PolicyLogger.audit("Transaction Failed - See Error.log");
712 response.sendError(400, "Failed to copy Property file");
715 } else if ("hb".equals(type)) {
717 response.setStatus(HttpServletResponse.SC_NO_CONTENT);
719 } else if ("Status".equals(type)) {
720 loggingContext.setServiceName("PDP.getStatus");
721 // convert response object to JSON and include in the response
722 synchronized(pdpStatusLock) {
723 ObjectMapper mapper = new ObjectMapper();
724 mapper.writeValue(response.getOutputStream(), status);
726 response.setStatus(HttpServletResponse.SC_OK);
727 loggingContext.transactionEnded();
728 auditLogger.info("Success");
729 PolicyLogger.audit("Success");
732 logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Invalid type value: " + type);
733 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "Invalid type value: " + type);
734 loggingContext.transactionEnded();
735 PolicyLogger.audit("Transaction Failed - See Error.log");
736 response.sendError(HttpServletResponse.SC_BAD_REQUEST, "type not 'config' or 'hb'");
739 synchronized(pdpStatusLock) {
740 response.addHeader(XACMLRestProperties.PROP_PDP_HTTP_HEADER_HB, status.getStatus().toString());
743 loggingContext.transactionEnded();
744 PolicyLogger.audit("Transaction Ended");
750 * POST - We expect XACML requests to be posted by PEP applications. They can be in the form of XML or JSON according
751 * to the XACML 3.0 Specifications for both.
754 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
756 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
758 ECOMPLoggingContext loggingContext = ECOMPLoggingUtils.getLoggingContextForRequest(request, baseLoggingContext);
759 loggingContext.transactionStarted();
760 loggingContext.setServiceName("PDP.decide");
761 if ((loggingContext.getRequestID() == null) || (loggingContext.getRequestID() == "")){
762 UUID requestID = UUID.randomUUID();
763 loggingContext.setRequestID(requestID.toString());
764 PolicyLogger.info("requestID not provided in call to XACMLPdpSrvlet (doPost) so we generated one");
766 PolicyLogger.info("requestID was provided in call to XACMLPdpSrvlet (doPost)");
768 loggingContext.metricStarted();
769 loggingContext.metricEnded();
770 PolicyLogger.metrics("Metric example posted here - 1 of 2");
771 loggingContext.metricStarted();
772 loggingContext.metricEnded();
773 PolicyLogger.metrics("Metric example posted here - 2 of 2");
774 monitor.pdpEvaluationAttempts();
777 im.startTransaction();
779 catch (AdministrativeStateException | StandbyStatusException e) {
780 String message = e.toString();
781 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message);
782 loggingContext.transactionEnded();
783 PolicyLogger.audit("Transaction Failed - See Error.log");
784 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message);
788 // no point in doing any work if we know from the get-go that we cannot do anything with the request
790 if (status.getLoadedRootPolicies().size() == 0) {
791 logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Request from PEP at " + request.getRequestURI() + " for service when PDP has No Root Policies loaded");
792 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, "Request from PEP at " + request.getRequestURI() + " for service when PDP has No Root Policies loaded");
793 loggingContext.transactionEnded();
794 PolicyLogger.audit("Transaction Failed - See Error.log");
795 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
800 XACMLRest.dumpRequest(request);
802 // Set our no-cache header
804 response.setHeader("Cache-Control", "no-cache");
806 // They must send a Content-Type
808 if (request.getContentType() == null) {
809 logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Must specify a Content-Type");
810 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "Must specify a Content-Type");
811 loggingContext.transactionEnded();
812 PolicyLogger.audit("Transaction Failed - See Error.log");
813 response.sendError(HttpServletResponse.SC_BAD_REQUEST, "no content-type given");
818 // Limit the Content-Length to something reasonable
820 if (request.getContentLength() > Integer.parseInt(XACMLProperties.getProperty("MAX_CONTENT_LENGTH", "32767"))) {
821 String message = "Content-Length larger than server will accept.";
822 logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + message);
823 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, message);
824 loggingContext.transactionEnded();
825 PolicyLogger.audit("Transaction Failed - See Error.log");
826 response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
830 if (request.getContentLength() <= 0) {
831 String message = "Content-Length is negative";
832 logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + message);
833 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, message);
834 loggingContext.transactionEnded();
835 PolicyLogger.audit("Transaction Failed - See Error.log");
836 response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
840 ContentType contentType = null;
842 contentType = ContentType.parse(request.getContentType());
844 catch (Exception e) {
845 String message = "Parsing Content-Type: " + request.getContentType() + ", error=" + e.getMessage();
846 logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + message, e);
847 loggingContext.transactionEnded();
848 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, message);
849 PolicyLogger.audit("Transaction Failed - See Error.log");
850 response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
855 // What exactly did they send us?
857 String incomingRequestString = null;
858 Request pdpRequest = null;
859 if (contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_JSON.getMimeType()) ||
860 contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_XML.getMimeType()) ||
861 contentType.getMimeType().equalsIgnoreCase("application/xacml+xml") ) {
863 // Read in the string
865 StringBuilder buffer = new StringBuilder();
866 BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
868 while((line = reader.readLine()) != null){
871 incomingRequestString = buffer.toString();
872 logger.info(incomingRequestString);
874 // Parse into a request
877 if (contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_JSON.getMimeType())) {
878 pdpRequest = JSONRequest.load(incomingRequestString);
879 } else if ( contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_XML.getMimeType()) ||
880 contentType.getMimeType().equalsIgnoreCase("application/xacml+xml")) {
881 pdpRequest = DOMRequest.load(incomingRequestString);
885 logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Could not parse request", e);
886 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "Could not parse request");
887 loggingContext.transactionEnded();
888 PolicyLogger.audit("Transaction Failed - See Error.log");
889 response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
894 String message = "unsupported content type" + request.getContentType();
895 logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + message);
896 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, message);
897 loggingContext.transactionEnded();
898 PolicyLogger.audit("Transaction Failed - See Error.log");
899 response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
904 // Did we successfully get and parse a request?
906 if (pdpRequest == null || pdpRequest.getRequestAttributes() == null || pdpRequest.getRequestAttributes().size() <= 0) {
907 String message = "Zero Attributes found in the request";
908 logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + message);
909 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, message);
910 loggingContext.transactionEnded();
911 PolicyLogger.audit("Transaction Failed - See Error.log");
912 response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
921 // Authenticating the Request here.
923 if(!authorizeRequest(request, pdpRequest)){
924 String message = "PEP not Authorized for making this Request!! \n Contact Administrator for this Scope. ";
925 logger.error(XACMLErrorConstants.ERROR_PERMISSIONS + message );
926 PolicyLogger.error(MessageCodes.ERROR_PERMISSIONS, message);
927 loggingContext.transactionEnded();
928 PolicyLogger.audit("Transaction Failed - See Error.log");
929 response.sendError(HttpServletResponse.SC_FORBIDDEN, message);
934 // Get the pointer to the PDP Engine
936 PDPEngine myEngine = null;
937 synchronized(pdpEngineLock) {
938 myEngine = XACMLPdpServlet.pdpEngine;
940 if (myEngine == null) {
941 String message = "No engine loaded.";
942 logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + message);
943 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message);
944 loggingContext.transactionEnded();
945 PolicyLogger.audit("Transaction Failed - See Error.log");
946 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message);
951 // Send the request and save the response
953 long lTimeStart, lTimeEnd;
954 Response pdpResponse = null;
956 //TODO - Make this unnecessary
957 //TODO It seems that the PDP Engine is not thread-safe, so when a configuration change occurs in the middle of processing
958 //TODO a PEP Request, that Request fails (it throws a NullPointerException in the decide() method).
959 //TODO Using synchronize will slow down processing of PEP requests, possibly by a significant amount.
960 //TODO Since configuration changes are rare, it would be A Very Good Thing if we could eliminate this sychronized block.
962 //TODO This problem was found by starting one PDP then
963 //TODO RestLoadTest switching between 2 configurations, 1 second apart
964 //TODO both configurations contain the datarouter policy
965 //TODO both configurations already have all policies cached in the PDPs config directory
966 //TODO RestLoadTest started with the Datarouter test requests, 5 threads, no interval
967 //TODO With that configuration this code (without the synchronized) throws a NullPointerException
968 //TODO within a few seconds.
970 synchronized(pdpEngineLock) {
971 myEngine = XACMLPdpServlet.pdpEngine;
973 PolicyList.clearPolicyList();
974 lTimeStart = System.currentTimeMillis();
975 pdpResponse = myEngine.decide(pdpRequest);
976 lTimeEnd = System.currentTimeMillis();
977 } catch (PDPException e) {
978 String message = "Exception during decide: " + e.getMessage();
979 logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + message);
980 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message);
981 loggingContext.transactionEnded();
982 PolicyLogger.audit("Transaction Failed - See Error.log");
983 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message);
988 monitor.computeLatency(lTimeEnd - lTimeStart);
989 requestLogger.info(lTimeStart + "=" + incomingRequestString);
990 for(String policy : PolicyList.getpolicyList()){
991 monitor.policyCountAdd(policy, 1);
995 logger.info("PolicyID triggered in Request: " + PolicyList.getpolicyList());
997 //need to go through the list and find out if the value is unique and then add it other wise
998 // monitor.policyCountAdd(PolicyList.getpolicyList(), 1);
1000 if (logger.isDebugEnabled()) {
1001 logger.debug("Request time: " + (lTimeEnd - lTimeStart) + "ms");
1004 // Convert Response to appropriate Content-Type
1006 if (pdpResponse == null) {
1007 requestLogger.info(lTimeStart + "=" + "{}");
1008 throw new Exception("Failed to get response from PDP engine.");
1011 // Set our content-type
1013 response.setContentType(contentType.getMimeType());
1015 // Convert the PDP response object to a String to
1016 // return to our caller as well as dump to our loggers.
1018 String outgoingResponseString = "";
1019 if (contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_JSON.getMimeType())) {
1021 // Get it as a String. This is not very efficient but we need to log our
1022 // results for auditing.
1024 outgoingResponseString = JSONResponse.toString(pdpResponse, logger.isDebugEnabled());
1025 if (logger.isDebugEnabled()) {
1026 logger.debug(outgoingResponseString);
1028 // Get rid of whitespace
1030 outgoingResponseString = JSONResponse.toString(pdpResponse, false);
1032 } else if ( contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_XML.getMimeType()) ||
1033 contentType.getMimeType().equalsIgnoreCase("application/xacml+xml")) {
1035 // Get it as a String. This is not very efficient but we need to log our
1036 // results for auditing.
1038 outgoingResponseString = DOMResponse.toString(pdpResponse, logger.isDebugEnabled());
1039 if (logger.isDebugEnabled()) {
1040 logger.debug(outgoingResponseString);
1042 // Get rid of whitespace
1044 outgoingResponseString = DOMResponse.toString(pdpResponse, false);
1047 // adding the jmx values for NA, Permit and Deny
1049 if (outgoingResponseString.contains("NotApplicable") || outgoingResponseString.contains("Decision not a Permit")){
1050 monitor.pdpEvaluationNA();
1053 if (outgoingResponseString.contains("Permit") && !outgoingResponseString.contains("Decision not a Permit")){
1054 monitor.pdpEvaluationPermit();
1057 if (outgoingResponseString.contains("Deny")){
1058 monitor.pdpEvaluationDeny();
1061 // lTimeStart is used as an ID within the requestLogger to match up
1062 // request's with responses.
1064 requestLogger.info(lTimeStart + "=" + outgoingResponseString);
1065 response.getWriter().print(outgoingResponseString);
1067 catch (Exception e) {
1068 String message = "Exception executing request: " + e;
1069 logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + message, e);
1070 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, message);
1071 loggingContext.transactionEnded();
1072 PolicyLogger.audit("Transaction Failed - See Error.log");
1073 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message);
1077 monitor.pdpEvaluationSuccess();
1078 response.setStatus(HttpServletResponse.SC_OK);
1080 loggingContext.transactionEnded();
1081 auditLogger.info("Success");
1082 PolicyLogger.audit("Success");
1087 * Added for Authorizing the PEP Requests for Environment check.
1089 private boolean authorizeRequest(HttpServletRequest request, Request pepRequest) {
1090 if(request instanceof HttpServletRequest) {
1091 // Get the client Credentials from the Request header.
1092 HttpServletRequest httpServletRequest = (HttpServletRequest) request;
1093 String clientCredentials = httpServletRequest.getHeader(ENVIORNMENT_HEADER);
1094 if(clientCredentials!=null && clientCredentials.equalsIgnoreCase(environment)){
1107 // Keep running until we are told to terminate
1110 // variable not used, but constructor has needed side-effects so don't remove:
1111 @SuppressWarnings("unused")
1112 ECOMPLoggingContext loggingContext = new ECOMPLoggingContext(baseLoggingContext);
1113 while (! this.configThreadTerminate) {
1114 PutRequest request = XACMLPdpServlet.queue.take();
1115 StdPDPStatus newStatus = new StdPDPStatus();
1117 //TODO - This is related to the problem discussed in the doPost() method about the PDPEngine not being thread-safe.
1118 //TODO See that discussion, and when the PDPEngine is made thread-safe it should be ok to move the loadEngine out of
1119 //TODO the synchronized block.
1120 //TODO However, since configuration changes should be rare we may not care about changing this.
1121 PDPEngine newEngine = null;
1122 synchronized(pdpStatusLock) {
1123 XACMLPdpServlet.status.setStatus(Status.UPDATING_CONFIGURATION);
1124 newEngine = XACMLPdpLoader.loadEngine(newStatus, request.policyProperties, request.pipConfigProperties);
1126 // PDPEngine newEngine = XACMLPdpLoader.loadEngine(newStatus, request.policyProperties, request.pipConfigProperties);
1127 if (newEngine != null) {
1128 synchronized(XACMLPdpServlet.pdpEngineLock) {
1129 XACMLPdpServlet.pdpEngine = newEngine;
1131 logger.info("Saving configuration.");
1132 if (request.policyProperties != null) {
1133 try (OutputStream os = Files.newOutputStream(XACMLPdpLoader.getPDPPolicyCache())) {
1134 request.policyProperties.store(os, "");
1137 if (request.pipConfigProperties != null) {
1138 try (OutputStream os = Files.newOutputStream(XACMLPdpLoader.getPIPConfig())) {
1139 request.pipConfigProperties.store(os, "");
1142 newStatus.setStatus(Status.UP_TO_DATE);
1143 } catch (Exception e) {
1144 logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to store new properties.");
1145 PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, "Failed to store new properties");
1146 newStatus.setStatus(Status.LOAD_ERRORS);
1147 newStatus.addLoadWarning("Unable to save configuration: " + e.getMessage());
1151 newStatus.setStatus(Status.LAST_UPDATE_FAILED);
1153 synchronized(pdpStatusLock) {
1154 XACMLPdpServlet.status.set(newStatus);
1157 } catch (InterruptedException e) {
1158 logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "interrupted");
1159 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, "interrupted");
1163 public static PDPEngine getPDPEngine(){
1164 PDPEngine myEngine = null;
1165 synchronized(pdpEngineLock) {
1166 myEngine = XACMLPdpServlet.pdpEngine;
1171 public static Constructor<?> getCreateUpdatePolicyConstructor(){
1172 return createUpdatePolicyConstructor;