You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@openaz.apache.org by pd...@apache.org on 2015/04/13 17:38:42 UTC
[42/51] [partial] incubator-openaz git commit: Initial seed of merged
of AT&T and JP Morgan code
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/src/main/java/com/att/research/xacml/rest/XACMLPapServlet.java
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/src/main/java/com/att/research/xacml/rest/XACMLPapServlet.java b/openaz-xacml-pap-rest/src/main/java/com/att/research/xacml/rest/XACMLPapServlet.java
new file mode 100755
index 0000000..e57a36b
--- /dev/null
+++ b/openaz-xacml-pap-rest/src/main/java/com/att/research/xacml/rest/XACMLPapServlet.java
@@ -0,0 +1,1529 @@
+/*
+ * 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 com.att.research.xacml.rest;
+
+
+import com.att.research.xacml.api.pap.*;
+import com.att.research.xacml.std.pap.StdPDP;
+import com.att.research.xacml.std.pap.StdPDPGroup;
+import com.att.research.xacml.std.pap.StdPDPItemSetChangeNotifier.StdItemSetChangeListener;
+import com.att.research.xacml.std.pap.StdPDPStatus;
+import com.att.research.xacml.util.FactoryException;
+import com.att.research.xacml.util.XACMLProperties;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Splitter;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+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 java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.*;
+import java.util.*;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Servlet implementation class XacmlPapServlet
+ *
+ *
+ * @author pameladragosh
+ */
+@WebServlet(
+ description = "Implements the XACML PAP RESTful API.",
+ urlPatterns = { "/" },
+ loadOnStartup=1,
+ initParams = {
+ @WebInitParam(name = "XACML_PROPERTIES_NAME", value = "xacml.pap.properties", description = "The location of the properties file holding configuration information.")
+ })
+
+public class XACMLPapServlet extends HttpServlet implements StdItemSetChangeListener, Runnable {
+ private static final long serialVersionUID = 1L;
+ private static final Log logger = LogFactory.getLog(XACMLPapServlet.class);
+
+ /*
+ *
+ * papEngine - This is our engine workhorse that manages the PDP Groups and Nodes.
+ */
+ private PAPEngine papEngine = null;
+
+ /*
+ * This PAP instance's own URL.
+ *
+ * Need this when creating URLs to send to the PDPs so they can GET the Policy files from this process.
+ */
+ private static String papURL = null;
+
+ /*
+ * List of Admin Console URLs.
+ * Used to send notifications when configuration changes.
+ *
+ * The CopyOnWriteArrayList *should* protect from concurrency errors.
+ * This list is seldom changed but often read, so the costs of this approach make sense.
+ */
+ private static final CopyOnWriteArrayList<String> adminConsoleURLStringList = new CopyOnWriteArrayList<String>();
+
+ /*
+ * This thread may be invoked upon startup to initiate sending PDP policy/pip configuration when
+ * this servlet starts. Its configurable by the admin.
+ */
+ private Thread initiateThread = null;
+
+ /*
+ // The heartbeat thread.
+ */
+ private static Heartbeat heartbeat = null;
+ private static Thread heartbeatThread = null;
+
+ /**
+ * @see HttpServlet#HttpServlet()
+ */
+ public XACMLPapServlet() {
+ super();
+ }
+
+ /**
+ * @see Servlet#init(ServletConfig)
+ */
+ public void init(ServletConfig config) throws ServletException {
+ try {
+ //
+ // Initialize
+ //
+ XACMLRest.xacmlInit(config);
+ //
+ // Load the properties
+ //
+ XACMLRest.loadXacmlProperties(null, null);
+ //
+ // Load our PAP engine, first create a factory
+ //
+ PAPEngineFactory factory = PAPEngineFactory.newInstance(XACMLProperties.getProperty(XACMLProperties.PROP_PAP_PAPENGINEFACTORY));
+ //
+ // The factory knows how to go about creating a PAP Engine
+ //
+ this.papEngine = factory.newEngine();
+ //
+ // we are about to call the PDPs and give them their configuration.
+ // To do that we need to have the URL of this PAP so we can construct the Policy file URLs
+ //
+ XACMLPapServlet.papURL = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_URL);
+ //
+ // Sanity check that a URL was defined somewhere, its essential.
+ //
+ // How to check that its valid? We can validate the form, but since we are in the init() method we
+ // are not fully loaded yet so we really couldn't ping ourself to see if the URL will work. One
+ // will have to look for errors in the PDP logs to determine if they are failing to initiate a
+ // request to this servlet.
+ //
+ if (XACMLPapServlet.papURL == null) {
+ throw new PAPException("The property " + XACMLRestProperties.PROP_PAP_URL + " is not valid: " + XACMLPapServlet.papURL);
+ }
+ //
+ // Configurable - have the PAP servlet initiate sending the latest PDP policy/pip configuration
+ // to all its known PDP nodes.
+ //
+ // Note: parseBoolean will return false if there is no property defined. This is fine for a default.
+ //
+ if (Boolean.parseBoolean(XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_INITIATE_PDP_CONFIG))) {
+ this.initiateThread = new Thread(this);
+ this.initiateThread.start();
+ }
+ //
+ // After startup, the PAP does Heartbeats to each of the PDPs periodically
+ //
+ XACMLPapServlet.heartbeat = new Heartbeat(this.papEngine);
+ XACMLPapServlet.heartbeatThread = new Thread(XACMLPapServlet.heartbeat);
+ XACMLPapServlet.heartbeatThread.start();
+ } catch (FactoryException | PAPException e) {
+ logger.error("Failed to create engine", e);
+ throw new ServletException ("PAP not initialized; error: "+e);
+ } catch (Exception e) {
+ logger.error("Failed to create engine - unexpected error: ", e);
+ throw new ServletException ("PAP not initialized; unexpected error: "+e); }
+ }
+
+ /**
+ * Thread used only during PAP startup to initiate change messages to all known PDPs.
+ * This must be on a separate thread so that any GET requests from the PDPs during this update can be serviced.
+ */
+ @Override
+ public void run() {
+ //
+ // send the current configuration to all the PDPs that we know about
+ //
+ changed();
+ }
+
+
+ /**
+ * @see Servlet#destroy()
+ *
+ * Depending on how this servlet is run, we may or may not care about cleaning up the resources.
+ * For now we assume that we do care.
+ */
+ public void destroy() {
+ //
+ // Make sure our threads are destroyed
+ //
+ if (XACMLPapServlet.heartbeatThread != null) {
+ //
+ // stop the heartbeat
+ //
+ try {
+ if (XACMLPapServlet.heartbeat != null) {
+ XACMLPapServlet.heartbeat.terminate();
+ }
+ XACMLPapServlet.heartbeatThread.interrupt();
+ XACMLPapServlet.heartbeatThread.join();
+ } catch (InterruptedException e) {
+ logger.error(e);
+ }
+ }
+ if (this.initiateThread != null) {
+ try {
+ this.initiateThread.interrupt();
+ this.initiateThread.join();
+ } catch (InterruptedException e) {
+ logger.error(e);
+ }
+ }
+ }
+
+ /**
+ *
+ * Called by:
+ * - PDP nodes to register themselves with the PAP, and
+ * - Admin Console to make changes in the PDP Groups.
+ *
+ * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
+ */
+ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ try {
+
+ XACMLRest.dumpRequest(request);
+
+ // since getParameter reads the content string, explicitly get the content before doing that.
+ // Simply getting the inputStream seems to protect it against being consumed by getParameter.
+ request.getInputStream();
+
+ //
+ // Is this from the Admin Console?
+ //
+ String groupId = request.getParameter("groupId");
+ if (groupId != null) {
+ //
+ // this is from the Admin Console, so handle separately
+ //
+ doACPost(request, response, groupId);
+ return;
+ }
+
+ //
+ // Request is from a PDP.
+ // It is coming up and asking for its config
+ //
+
+ //
+ // Get the PDP's ID
+ //
+ String id = this.getPDPID(request);
+ logger.info("doPost from: " + id);
+ //
+ // Get the PDP Object
+ //
+ PDP pdp = this.papEngine.getPDP(id);
+ //
+ // Is it known?
+ //
+ if (pdp == null) {
+ logger.info("Unknown PDP: " + id);
+ try {
+ this.papEngine.newPDP(id, this.papEngine.getDefaultGroup(), id, "Registered on first startup");
+ } catch (NullPointerException | PAPException e) {
+ logger.error("Failed to create new PDP", e);
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
+ return;
+ }
+ // get the PDP we just created
+ pdp = this.papEngine.getPDP(id);
+ if (pdp == null) {
+ String message = "Failed to create new PDP for id: " + id;
+ logger.error(message);
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message);
+ return;
+ }
+ }
+ //
+ // Get the PDP's Group
+ //
+ PDPGroup group = this.papEngine.getPDPGroup(pdp);
+ if (group == null) {
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "PDP not associated with any group, even the default");
+ return;
+ }
+ //
+ // Determine what group the PDP node is in and get
+ // its policy/pip properties.
+ //
+ Properties policies = group.getPolicyProperties();
+ Properties pipconfig = group.getPipConfigProperties();
+ //
+ // Get the current policy/pip configuration that the PDP has
+ //
+ Properties pdpProperties = new Properties();
+ pdpProperties.load(request.getInputStream());
+ logger.info("PDP Current Properties: " + pdpProperties.toString());
+ logger.info("Policies: " + (policies != null ? policies.toString() : "null"));
+ logger.info("Pip config: " + (pipconfig != null ? pipconfig.toString() : "null"));
+ //
+ // Validate the node's properties
+ //
+ boolean isCurrent = this.isPDPCurrent(policies, pipconfig, pdpProperties);
+ //
+ // Send back current configuration
+ //
+ if (isCurrent == false) {
+ //
+ // Tell the PDP we are sending back the current policies/pip config
+ //
+ logger.info("PDP configuration NOT current.");
+ if (policies != null) {
+ //
+ // Put URL's into the properties in case the PDP needs to
+ // retrieve them.
+ //
+ this.populatePolicyURL(request.getRequestURL(), policies);
+ //
+ // Copy the properties to the output stream
+ //
+ policies.store(response.getOutputStream(), "");
+ }
+ if (pipconfig != null) {
+ //
+ // Copy the properties to the output stream
+ //
+ pipconfig.store(response.getOutputStream(), "");
+ }
+ //
+ // We are good - and we are sending them information
+ //
+ response.setStatus(HttpServletResponse.SC_OK);
+//TODO - Correct?
+ setPDPSummaryStatus(pdp, PDPStatus.Status.OUT_OF_SYNCH);
+ } else {
+ //
+ // Tell them they are good
+ //
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+
+ //TODO - Correct?
+ setPDPSummaryStatus(pdp, PDPStatus.Status.UP_TO_DATE);
+
+ }
+ //
+ // tell the AC that something changed
+ //
+ notifyAC();
+ } catch (PAPException e) {
+ logger.debug("POST exception: " + e, e);
+ response.sendError(500, e.getMessage());
+ return;
+ }
+ }
+
+
+ /**
+ * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
+ */
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ try {
+ XACMLRest.dumpRequest(request);
+
+ // Is this from the Admin Console?
+ String groupId = request.getParameter("groupId");
+ if (groupId != null) {
+ // this is from the Admin Console, so handle separately
+ doACGet(request, response, groupId);
+ return;
+ }
+ //
+ // Get the PDP's ID
+ //
+ String id = this.getPDPID(request);
+ logger.info("doGet from: " + id);
+ //
+ // Get the PDP Object
+ //
+ PDP pdp = this.papEngine.getPDP(id);
+ //
+ // Is it known?
+ //
+ if (pdp == null) {
+ //
+ // Check if request came from localhost
+ //
+ String message = "Unknown PDP: " + id + " from " + request.getRemoteHost() + " us: " + request.getLocalAddr();
+ logger.info(message);
+ if (request.getRemoteHost().equals("localhost") ||
+ request.getRemoteHost().equals("127.0.0.1") ||
+ request.getRemoteHost().equals(request.getLocalAddr())) {
+ //
+ // Return status information - basically all the groups
+ //
+ Set<PDPGroup> groups = papEngine.getPDPGroups();
+
+ // convert response object to JSON and include in the response
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(response.getOutputStream(), groups);
+ response.setHeader("content-type", "application/json");
+ response.setStatus(HttpServletResponse.SC_OK);
+ return;
+ }
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, message);
+ return;
+ }
+ //
+ // Get the PDP's Group
+ //
+ PDPGroup group = this.papEngine.getPDPGroup(pdp);
+ if (group == null) {
+ String message = "No group associated with pdp " + pdp.getId();
+ logger.warn(message);
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, message);
+ return;
+ }
+ //
+ // Which policy do they want?
+ //
+ String policyId = request.getParameter("id");
+ if (policyId == null) {
+ String message = "Did not specify an id for the policy";
+ logger.warn(message);
+ response.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ return;
+ }
+ PDPPolicy policy = group.getPolicy(policyId);
+ if (policy == null) {
+ String message = "Unknown policy: " + policyId;
+ logger.warn(message);
+ response.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ return;
+ }
+ //
+ // Get its stream
+ //
+ try (InputStream is = policy.getStream(); OutputStream os = response.getOutputStream()) {
+ //
+ // Send the policy back
+ //
+ IOUtils.copy(is, os);
+
+ response.setStatus(HttpServletResponse.SC_OK);
+ } catch (PAPException e) {
+ String message = "Failed to open policy id " + policyId;
+ logger.error(message);
+ response.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ }
+ } catch (PAPException e) {
+ logger.error("GET exception: " + e, e);
+ response.sendError(500, e.getMessage());
+ return;
+ }
+ }
+
+ protected String getPDPID(HttpServletRequest request) {
+ String pdpURL = request.getHeader(XACMLRestProperties.PROP_PDP_HTTP_HEADER_ID);
+ if (pdpURL == null || pdpURL.isEmpty()) {
+ //
+ // Should send back its port for identification
+ //
+ logger.warn("PDP did not send custom header");
+ pdpURL = "";
+ }
+ return pdpURL;
+ }
+
+ private boolean isPDPCurrent(Properties policies, Properties pipconfig, Properties pdpProperties) {
+ String localRootPolicies = policies.getProperty(XACMLProperties.PROP_ROOTPOLICIES);
+ String localReferencedPolicies = policies.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES);
+ if (localRootPolicies == null || localReferencedPolicies == null) {
+ logger.warn("Missing property on PAP server: RootPolicies="+localRootPolicies+" ReferencedPolicies="+localReferencedPolicies);
+ return false;
+ }
+ //
+ // Compare the policies and pipconfig properties to the pdpProperties
+ //
+ try {
+ //
+ // the policy properties includes only xacml.rootPolicies and
+ // xacml.referencedPolicies without any .url entries
+ //
+ Properties pdpPolicies = XACMLProperties.getPolicyProperties(pdpProperties, false);
+ Properties pdpPipConfig = XACMLProperties.getPipProperties(pdpProperties);
+ if (localRootPolicies.equals(pdpPolicies.getProperty(XACMLProperties.PROP_ROOTPOLICIES)) &&
+ localReferencedPolicies.equals(pdpPolicies.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES)) &&
+ pdpPipConfig.equals(pipconfig)) {
+ //
+ // The PDP is current
+ //
+ return true;
+ }
+ } catch (Exception e) {
+ // we get here if the PDP did not include either xacml.rootPolicies or xacml.pip.engines,
+ // or if there are policies that do not have a corresponding ".url" property.
+ // Either of these cases means that the PDP is not up-to-date, so just drop-through to return false.
+ }
+ return false;
+ }
+
+ private void populatePolicyURL(StringBuffer urlPath, Properties policies) {
+ String lists[] = new String[2];
+ lists[0] = policies.getProperty(XACMLProperties.PROP_ROOTPOLICIES);
+ lists[1] = policies.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES);
+ for (String list : lists) {
+ if (list != null && list.isEmpty() == false) {
+ for (String id : Splitter.on(',').trimResults().omitEmptyStrings().split(list)) {
+ String url = urlPath + "?id=" + id;
+ logger.info("Policy URL for " + id + ": " + url);
+ policies.setProperty(id + ".url", url);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * @see HttpServlet#doPut(HttpServletRequest request, HttpServletResponse response)
+ */
+ protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ XACMLRest.dumpRequest(request);
+ //
+ // since getParameter reads the content string, explicitly get the content before doing that.
+ // Simply getting the inputStream seems to protect it against being consumed by getParameter.
+ //
+ request.getInputStream();
+ //
+ // See if this is Admin Console registering itself with us
+ //
+ String acURLString = request.getParameter("adminConsoleURL");
+ if (acURLString != null) {
+ //
+ // remember this Admin Console for future updates
+ //
+ if ( ! adminConsoleURLStringList.contains(acURLString)) {
+ adminConsoleURLStringList.add(acURLString);
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("Admin Console registering with URL: " + acURLString);
+ }
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ return;
+ }
+ //
+ // Is this some other operation from the Admin Console?
+ //
+ String groupId = request.getParameter("groupId");
+ if (groupId != null) {
+ //
+ // this is from the Admin Console, so handle separately
+ //
+ doACPut(request, response, groupId);
+ return;
+ }
+ //
+ // We do not expect anything from anywhere else.
+ // This method is here in case we ever need to support other operations.
+ //
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Request does not have groupId");
+ }
+
+ /**
+ * @see HttpServlet#doDelete(HttpServletRequest request, HttpServletResponse response)
+ */
+ protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ XACMLRest.dumpRequest(request);
+ //
+ // Is this from the Admin Console?
+ //
+ String groupId = request.getParameter("groupId");
+ if (groupId != null) {
+ //
+ // this is from the Admin Console, so handle separately
+ //
+ doACDelete(request, response, groupId);
+ return;
+ }
+ //
+ // We do not expect anything from anywhere else.
+ // This method is here in case we ever need to support other operations.
+ //
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Request does not have groupId");
+ }
+ //
+ // Admin Console request handling
+ //
+
+ /**
+ * Requests from the Admin Console to GET info about the Groups and PDPs
+ *
+ * @param request
+ * @param response
+ * @param groupId
+ * @throws ServletException
+ * @throws java.io.IOException
+ */
+ private void doACGet(HttpServletRequest request, HttpServletResponse response, String groupId) throws ServletException, IOException {
+ try {
+ String parameterDefault = request.getParameter("default");
+ String pdpId = request.getParameter("pdpId");
+ String pdpGroup = request.getParameter("getPDPGroup");
+ if ("".equals(groupId)) {
+ // request IS from AC but does not identify a group by name
+ if (parameterDefault != null) {
+ // Request is for the Default group (whatever its id)
+ PDPGroup group = papEngine.getDefaultGroup();
+
+ // convert response object to JSON and include in the response
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(response.getOutputStream(), group);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("GET Default group req from '" + request.getRequestURL() + "'");
+ }
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setHeader("content-type", "application/json");
+ response.getOutputStream().close();
+ return;
+
+ } else if (pdpId != null) {
+ // Request is related to a PDP
+ if (pdpGroup == null) {
+ // Request is for the PDP itself
+ // Request is for the (unspecified) group containing a given PDP
+ PDP pdp = papEngine.getPDP(pdpId);
+
+ // convert response object to JSON and include in the response
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(response.getOutputStream(), pdp);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("GET pdp '" + pdpId + "' req from '" + request.getRequestURL() + "'");
+ }
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setHeader("content-type", "application/json");
+ response.getOutputStream().close();
+ return;
+
+ } else {
+ // Request is for the (unspecified) group containing a given PDP
+ PDP pdp = papEngine.getPDP(pdpId);
+ PDPGroup group = papEngine.getPDPGroup(pdp);
+
+ // convert response object to JSON and include in the response
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(response.getOutputStream(), group);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("GET PDP '" + pdpId + "' Group req from '" + request.getRequestURL() + "'");
+ }
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setHeader("content-type", "application/json");
+ response.getOutputStream().close();
+ return;
+ }
+
+ } else {
+ // request is for top-level properties about all groups
+ Set<PDPGroup> groups = papEngine.getPDPGroups();
+
+ // convert response object to JSON and include in the response
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(response.getOutputStream(), groups);
+
+//TODO
+// In "notification" section, ALSO need to tell AC about other changes (made by other ACs)?'
+//TODO add new PDP notification (or just "config changed" notification) in appropriate place
+ if (logger.isDebugEnabled()) {
+ logger.debug("GET All groups req");
+ }
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setHeader("content-type", "application/json");
+ response.getOutputStream().close();
+ return;
+ }
+ }
+
+ // for all other GET operations the group must exist before the operation can be done
+ PDPGroup group = papEngine.getGroup(groupId);
+ if (group == null) {
+ logger.error("Unknown groupId '" + groupId + "'");
+ response.sendError(HttpServletResponse.SC_NOT_FOUND, "Unknown groupId '" + groupId +"'");
+ return;
+ }
+
+
+ // Figure out which request this is based on the parameters
+ String policyId = request.getParameter("policyId");
+
+ if (policyId != null) {
+// // retrieve a policy
+// PDPPolicy policy = papEngine.getPDPPolicy(policyId);
+//
+// // convert response object to JSON and include in the response
+// ObjectMapper mapper = new ObjectMapper();
+// mapper.writeValue(response.getOutputStream(), pdp);
+//
+// logger.debug("GET group '" + group.getId() + "' req from '" + request.getRequestURL() + "'");
+// response.setStatus(HttpServletResponse.SC_OK);
+// response.setHeader("content-type", "application/json");
+// response.getOutputStream().close();
+// return;
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "GET Policy not implemented");
+
+ } else {
+ // No other parameters, so return the identified Group
+
+ // convert response object to JSON and include in the response
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(response.getOutputStream(), group);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("GET group '" + group.getId() + "' req from '" + request.getRequestURL() + "'");
+ }
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setHeader("content-type", "application/json");
+ response.getOutputStream().close();
+ return;
+ }
+
+ //
+ // Currently there are no other GET calls from the AC.
+ // The AC uses the "GET All Groups" operation to fill its local cache and uses that cache for all other GETs without calling the PAP.
+ // Other GETs that could be called:
+ // Specific Group (groupId=<groupId>)
+ // A Policy (groupId=<groupId> policyId=<policyId>)
+ // A PDP (groupId=<groupId> pdpId=<pdpId>)
+
+ //TODO - implement other GET operations if needed
+
+ logger.error("UNIMPLEMENTED ");
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "UNIMPLEMENTED");
+ } catch (PAPException e) {
+ logger.error("AC Get exception: " + e, e);
+ response.sendError(500, e.getMessage());
+ return;
+ }
+
+ }
+
+
+ /**
+ * Requests from the Admin Console for operations not on single specific objects
+ *
+ * @param request
+ * @param response
+ * @param groupId
+ * @throws ServletException
+ * @throws java.io.IOException
+ */
+ private void doACPost(HttpServletRequest request, HttpServletResponse response, String groupId) throws ServletException, IOException {
+ try {
+ String groupName = request.getParameter("groupName");
+ String groupDescription = request.getParameter("groupDescription");
+ if (groupName != null && groupDescription != null) {
+ // Args: group=<groupId> groupName=<name> groupDescription=<description> <= create a new group
+ String unescapedName = URLDecoder.decode(groupName, "UTF-8");
+ String unescapedDescription = URLDecoder.decode(groupDescription, "UTF-8");
+ try {
+ papEngine.newGroup(unescapedName, unescapedDescription);
+ } catch (Exception e) {
+ logger.error("Unable to create new group: " + e.getLocalizedMessage());
+ response.sendError(500, "Unable to create new group '" + groupId + "'");
+ return;
+ }
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ if (logger.isDebugEnabled()) {
+ logger.debug("New Group '" + groupId + "' created");
+ }
+ // tell the Admin Consoles there is a change
+ notifyAC();
+ // new group by definition has no PDPs, so no need to notify them of changes
+ return;
+ }
+
+ // for all remaining POST operations the group must exist before the operation can be done
+ PDPGroup group = papEngine.getGroup(groupId);
+ if (group == null) {
+ logger.error("Unknown groupId '" + groupId + "'");
+ response.sendError(HttpServletResponse.SC_NOT_FOUND, "Unknown groupId '" + groupId +"'");
+ return;
+ }
+
+ // determine the operation needed based on the parameters in the request
+ if (request.getParameter("policyId") != null) {
+ // Args: group=<groupId> policy=<policyId> <= copy file
+ // copy a policy from the request contents into a file in the group's directory on this machine
+ String policyId = request.getParameter("policyId");
+ try {
+ ((StdPDPGroup) group).copyPolicyToFile(policyId, request.getInputStream());
+ } catch (Exception e) {
+ String message = "Policy '" + policyId + "' not copied to group '" + groupId +"': " + e;
+ logger.error(message);
+ response.sendError(500, message);
+ return;
+ }
+ // policy file copied ok
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ if (logger.isDebugEnabled()) {
+ logger.debug("policy '" + policyId + "' copied to directory for group '" + groupId + "'");
+ }
+ return;
+
+ } else if (request.getParameter("default") != null) {
+ // Args: group=<groupId> default=true <= make default
+ // change the current default group to be the one identified in the request.
+ //
+ // This is a POST operation rather than a PUT "update group" because of the side-effect that the current default group is also changed.
+ // It should never be the case that multiple groups are currently marked as the default, but protect against that anyway.
+ try {
+ papEngine.SetDefaultGroup(group);
+ } catch (Exception e) {
+ logger.error("Unable to set group: " + e.getLocalizedMessage());
+ response.sendError(500, "Unable to set group '" + groupId + "' to default");
+ return;
+ }
+
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Group '" + groupId + "' set to be default");
+ }
+ // Notify the Admin Consoles that something changed
+ // For now the AC cannot handle anything more detailed than the whole set of PDPGroups, so just notify on that
+//TODO - Future: FIGURE OUT WHAT LEVEL TO NOTIFY: 2 groups or entire set - currently notify AC to update whole configuration of all groups
+ notifyAC();
+ // This does not affect any PDPs in the existing groups, so no need to notify them of this change
+ return;
+
+ } else if (request.getParameter("pdpId") != null) {
+ // Args: group=<groupId> pdpId=<pdpId> <= move PDP to group
+ String pdpId = request.getParameter("pdpId");
+ PDP pdp = papEngine.getPDP(pdpId);
+
+ PDPGroup originalGroup = papEngine.getPDPGroup(pdp);
+
+ papEngine.movePDP(pdp, group);
+
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ if (logger.isDebugEnabled()) {
+ logger.debug("PDP '" + pdp.getId() +"' moved to group '" + group.getId() + "' set to be default");
+ }
+
+ // update the status of both the original group and the new one
+ ((StdPDPGroup)originalGroup).resetStatus();
+ ((StdPDPGroup)group).resetStatus();
+
+ // Notify the Admin Consoles that something changed
+ // For now the AC cannot handle anything more detailed than the whole set of PDPGroups, so just notify on that
+ notifyAC();
+ // Need to notify the PDP that it's config may have changed
+ pdpChanged(pdp);
+ return;
+
+
+ }
+ } catch (PAPException e) {
+ logger.error("AC POST exception: " + e, e);
+ response.sendError(500, e.getMessage());
+ return;
+ }
+ }
+
+ /**
+ * Requests from the Admin Console to create new items or update existing ones
+ *
+ * @param request
+ * @param response
+ * @param groupId
+ * @throws ServletException
+ * @throws java.io.IOException
+ */
+ private void doACPut(HttpServletRequest request, HttpServletResponse response, String groupId) throws ServletException, IOException {
+ try {
+
+
+ // for PUT operations the group may or may not need to exist before the operation can be done
+ PDPGroup group = papEngine.getGroup(groupId);
+
+ // determine the operation needed based on the parameters in the request
+
+ // for remaining operations the group must exist before the operation can be done
+ if (group == null) {
+ logger.error("Unknown groupId '" + groupId + "'");
+ response.sendError(HttpServletResponse.SC_NOT_FOUND, "Unknown groupId '" + groupId +"'");
+ return;
+ }
+ if (request.getParameter("policy") != null) {
+ // group=<groupId> policy=<policyId> contents=policy file <= Create new policy file in group dir, or replace it if it already exists (do not touch properties)
+ //TODO - currently this is done by the AC, but it should be done here by getting the policy file out of the contents and saving to disk
+ logger.error("PARTIALLY IMPLEMENTED!!! ACTUAL CHANGES SHOULD BE MADE BY PAP SERVLET!!! ");
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ return;
+ } else if (request.getParameter("pdpId") != null) {
+ // ARGS: group=<groupId> pdpId=<pdpId/URL> <= create a new PDP or Update an Existing one
+
+ String pdpId = request.getParameter("pdpId");
+
+ // get the request content into a String
+ String json = null;
+ // read the inputStream into a buffer (trick found online scans entire input looking for end-of-file)
+ java.util.Scanner scanner = new java.util.Scanner(request.getInputStream());
+ scanner.useDelimiter("\\A");
+ json = scanner.hasNext() ? scanner.next() : "";
+ scanner.close();
+ logger.info("JSON request from AC: " + json);
+
+ // convert Object sent as JSON into local object
+ ObjectMapper mapper = new ObjectMapper();
+
+ Object objectFromJSON = mapper.readValue(json, StdPDP.class);
+
+ if (pdpId == null ||
+ objectFromJSON == null ||
+ ! (objectFromJSON instanceof StdPDP) ||
+ ((StdPDP)objectFromJSON).getId() == null ||
+ ! ((StdPDP)objectFromJSON).getId().equals(pdpId)) {
+ logger.error("PDP new/update had bad input. pdpId=" + pdpId + " objectFromJSON="+objectFromJSON);
+ response.sendError(500, "Bad input, pdpid="+pdpId+" object="+objectFromJSON);
+ }
+ StdPDP pdp = (StdPDP) objectFromJSON;
+
+ if (papEngine.getPDP(pdpId) == null) {
+ // this is a request to create a new PDP object
+ papEngine.newPDP(pdp.getId(), group, pdp.getName(), pdp.getDescription());
+ } else {
+ // this is a request to update the pdp
+ papEngine.updatePDP(pdp);
+ }
+
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ if (logger.isDebugEnabled()) {
+ logger.debug("PDP '" + pdpId + "' created/updated");
+ }
+
+ // adjust the group's state including the new PDP
+ ((StdPDPGroup)group).resetStatus();
+
+ // tell the Admin Consoles there is a change
+ notifyAC();
+ // this might affect the PDP, so notify it of the change
+ pdpChanged(pdp);
+ return;
+ } else if (request.getParameter("pipId") != null) {
+ // group=<groupId> pipId=<pipEngineId> contents=pip properties <= add a PIP to pip config, or replace it if it already exists (lenient operation)
+ //TODO
+ logger.error("UNIMPLEMENTED ");
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "UNIMPLEMENTED");
+ return;
+ } else {
+ // Assume that this is an update of an existing PDP Group
+ // ARGS: group=<groupId> <= Update an Existing Group
+
+ // get the request content into a String
+ String json = null;
+ // read the inputStream into a buffer (trick found online scans entire input looking for end-of-file)
+ java.util.Scanner scanner = new java.util.Scanner(request.getInputStream());
+ scanner.useDelimiter("\\A");
+ json = scanner.hasNext() ? scanner.next() : "";
+ scanner.close();
+ logger.info("JSON request from AC: " + json);
+
+ // convert Object sent as JSON into local object
+ ObjectMapper mapper = new ObjectMapper();
+
+ Object objectFromJSON = mapper.readValue(json, StdPDPGroup.class);
+
+ if (objectFromJSON == null ||
+ ! (objectFromJSON instanceof StdPDPGroup) ||
+ ! ((StdPDPGroup)objectFromJSON).getId().equals(group.getId())) {
+ logger.error("Group update had bad input. id=" + group.getId() + " objectFromJSON="+objectFromJSON);
+ response.sendError(500, "Bad input, id="+group.getId() +" object="+objectFromJSON);
+ }
+
+ // The Path on the PAP side is not carried on the RESTful interface with the AC
+ // (because it is local to the PAP)
+ // so we need to fill that in before submitting the group for update
+ ((StdPDPGroup)objectFromJSON).setDirectory(((StdPDPGroup)group).getDirectory());
+
+ papEngine.updateGroup((StdPDPGroup)objectFromJSON);
+
+
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Group '" + group.getId() + "' updated");
+ }
+ // tell the Admin Consoles there is a change
+ notifyAC();
+ // Group changed, which might include changing the policies
+ groupChanged(group);
+ return;
+ }
+ } catch (PAPException e) {
+ logger.error("AC PUT exception: " + e, e);
+ response.sendError(500, e.getMessage());
+ return;
+ }
+ }
+
+ /**
+ * Requests from the Admin Console to delete/remove items
+ *
+ * @param request
+ * @param response
+ * @param groupId
+ * @throws ServletException
+ * @throws java.io.IOException
+ */
+ private void doACDelete(HttpServletRequest request, HttpServletResponse response, String groupId) throws ServletException, IOException {
+ try {
+ // for all DELETE operations the group must exist before the operation can be done
+ PDPGroup group = papEngine.getGroup(groupId);
+ if (group == null) {
+ logger.error("Unknown groupId '" + groupId + "'");
+ response.sendError(HttpServletResponse.SC_NOT_FOUND, "Unknown groupId '" + groupId +"'");
+ return;
+ }
+ // determine the operation needed based on the parameters in the request
+ if (request.getParameter("policy") != null) {
+ // group=<groupId> policy=<policyId> [delete=<true|false>] <= delete policy file from group
+ //TODO
+ logger.error("UNIMPLEMENTED ");
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "UNIMPLEMENTED");
+ return;
+ } else if (request.getParameter("pdpId") != null) {
+ // ARGS: group=<groupId> pdpId=<pdpId> <= delete PDP
+ String pdpId = request.getParameter("pdpId");
+ PDP pdp = papEngine.getPDP(pdpId);
+
+ papEngine.removePDP(pdp);
+
+ // adjust the status of the group, which may have changed when we removed this PDP
+ ((StdPDPGroup)group).resetStatus();
+
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ notifyAC();
+
+ // update the PDP and tell it that it has NO Policies (which prevents it from serving PEP Requests)
+ pdpChanged(pdp);
+ return;
+ } else if (request.getParameter("pipId") != null) {
+ // group=<groupId> pipId=<pipEngineId> <= delete PIP config for given engine
+ //TODO
+ logger.error("UNIMPLEMENTED ");
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "UNIMPLEMENTED");
+ return;
+ } else {
+ // ARGS: group=<groupId> movePDPsToGroupId=<movePDPsToGroupId> <= delete a group and move all its PDPs to the given group
+ String moveToGroupId = request.getParameter("movePDPsToGroupId");
+ PDPGroup moveToGroup = null;
+ if (moveToGroupId != null) {
+ moveToGroup = papEngine.getGroup(moveToGroupId);
+ }
+
+ // get list of PDPs in the group being deleted so we can notify them that they got changed
+ Set<PDP> movedPDPs = new HashSet<PDP>();
+ movedPDPs.addAll(group.getPdps());
+
+ // do the move/remove
+ papEngine.removeGroup(group, moveToGroup);
+
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ notifyAC();
+ // notify any PDPs in the removed set that their config may have changed
+ for (PDP pdp : movedPDPs) {
+ pdpChanged(pdp);
+ }
+ return;
+ }
+
+ } catch (PAPException e) {
+ logger.error("AC DELETE exception: " + e, e);
+ response.sendError(500, e.getMessage());
+ return;
+ }
+ }
+
+ //
+ // Heartbeat thread - periodically check on PDPs' status
+ //
+
+ /**
+ * Heartbeat with all known PDPs.
+ *
+ * Implementation note:
+ *
+ * The PDPs are contacted Sequentially, not in Parallel.
+ *
+ * If we did this in parallel using multiple threads we would simultaneously use
+ * - 1 thread and
+ * - 1 connection
+ * for EACH PDP.
+ * This could become a resource problem since we already use multiple threads and connections for updating the PDPs
+ * when user changes occur.
+ * Using separate threads can also make it tricky dealing with timeouts on PDPs that are non-responsive.
+ *
+ * The Sequential operation does a heartbeat request to each PDP one at a time.
+ * This has the flaw that any PDPs that do not respond will hold up the entire heartbeat sequence until they timeout.
+ * If there are a lot of non-responsive PDPs and the timeout is large-ish (the default is 20 seconds)
+ * it could take a long time to cycle through all of the PDPs.
+ * That means that this may not notice a PDP being down in a predictable time.
+ *
+ * @author glenngriffin
+ *
+ */
+ private class Heartbeat implements Runnable {
+ private PAPEngine papEngine;
+ private Set<PDP> pdps = new HashSet<PDP>();
+ private int heartbeatInterval;
+ private int heartbeatTimeout;
+
+ public volatile boolean isRunning = false;
+
+ public synchronized boolean isRunning() {
+ return this.isRunning;
+ }
+
+ public synchronized void terminate() {
+ this.isRunning = false;
+ }
+
+ public Heartbeat(PAPEngine engine) {
+ this.papEngine = engine;
+ this.heartbeatInterval = Integer.parseInt(XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_HEARTBEAT_INTERVAL, "10000"));
+ this.heartbeatTimeout = Integer.parseInt(XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_HEARTBEAT_TIMEOUT, "10000"));
+ }
+
+ @Override
+ public void run() {
+ //
+ // Set ourselves as running
+ //
+ synchronized(this) {
+ this.isRunning = true;
+ }
+ HashMap<String, URL> idToURLMap = new HashMap<String, URL>();
+ try {
+ while (this.isRunning()) {
+ // Wait the given time
+ Thread.sleep(heartbeatInterval);
+
+ // get the list of PDPs (may have changed since last time)
+ pdps.clear();
+ synchronized(papEngine) {
+ try {
+ for (PDPGroup g : papEngine.getPDPGroups()) {
+ for (PDP p : g.getPdps()) {
+ pdps.add(p);
+ }
+ }
+ } catch (PAPException e) {
+ logger.error("Heartbeat unable to read PDPs from PAPEngine: " + e.getMessage(), e);
+ }
+ }
+ //
+ // Check for shutdown
+ //
+ if (this.isRunning() == false) {
+ logger.info("isRunning is false, getting out of loop.");
+ break;
+ }
+
+ // try to get the summary status from each PDP
+ boolean changeSeen = false;
+ for (PDP pdp : pdps) {
+ //
+ // Check for shutdown
+ //
+ if (this.isRunning() == false) {
+ logger.info("isRunning is false, getting out of loop.");
+ break;
+ }
+ // the id of the PDP is its url (though we add a query parameter)
+ URL pdpURL = idToURLMap.get(pdp.getId());
+ if (pdpURL == null) {
+ // haven't seen this PDP before
+ String fullURLString = null;
+ try {
+ fullURLString = pdp.getId() + "?type=hb";
+ pdpURL = new URL(fullURLString);
+ idToURLMap.put(pdp.getId(), pdpURL);
+ } catch (MalformedURLException e) {
+ logger.error("PDP id '" + fullURLString + "' is not a valid URL: " + e, e);
+ continue;
+ }
+ }
+
+ // Do a GET with type HeartBeat
+ String newStatus = "";
+
+ HttpURLConnection connection = null;
+ try {
+
+ //
+ // Open up the connection
+ //
+ connection = (HttpURLConnection)pdpURL.openConnection();
+ //
+ // Setup our method and headers
+ //
+ connection.setRequestMethod("GET");
+ connection.setConnectTimeout(heartbeatTimeout);
+ //
+ // Do the connect
+ //
+ connection.connect();
+ if (connection.getResponseCode() == 204) {
+ newStatus = connection.getHeaderField(XACMLRestProperties.PROP_PDP_HTTP_HEADER_HB);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Heartbeat '" + pdp.getId() + "' status='" + newStatus + "'");
+ }
+ } else {
+ // anything else is an unexpected result
+ newStatus = PDPStatus.Status.UNKNOWN.toString();
+ logger.error("Heartbeat connect response code " + connection.getResponseCode() + ": " + pdp.getId());
+ }
+ } catch (UnknownHostException e) {
+ newStatus = PDPStatus.Status.NO_SUCH_HOST.toString();
+ logger.error("Heartbeat '" + pdp.getId() + "' NO_SUCH_HOST");
+ } catch (SocketTimeoutException e) {
+ newStatus = PDPStatus.Status.CANNOT_CONNECT.toString();
+ logger.error("Heartbeat '" + pdp.getId() + "' connection timeout: " + e );
+ } catch (ConnectException e) {
+ newStatus = PDPStatus.Status.CANNOT_CONNECT.toString();
+ logger.error("Heartbeat '" + pdp.getId() + "' cannot connect: " + e );
+ } catch (Exception e) {
+ newStatus = PDPStatus.Status.UNKNOWN.toString();
+ logger.error("Heartbeat '" + pdp.getId() + "' connect exception: " + e, e);
+ } finally {
+ // cleanup the connection
+ connection.disconnect();
+ }
+
+ if ( ! pdp.getStatus().getStatus().toString().equals(newStatus)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("previous status='" + pdp.getStatus().getStatus()+"' new Status='" + newStatus + "'");
+ }
+ try {
+ setPDPSummaryStatus(pdp, newStatus);
+ } catch (PAPException e) {
+ logger.error("Unable to set state for PDP '" + pdp.getId() + "': " + e, e);
+ }
+ changeSeen = true;
+ }
+
+ }
+ //
+ // Check for shutdown
+ //
+ if (this.isRunning() == false) {
+ logger.info("isRunning is false, getting out of loop.");
+ break;
+ }
+
+ // if any of the PDPs changed state, tell the ACs to update
+ if (changeSeen) {
+ notifyAC();
+ }
+
+ }
+ } catch (InterruptedException e) {
+ logger.error("Heartbeat interrupted. Shutting down");
+ this.terminate();
+ }
+ }
+ }
+
+
+ //
+ // HELPER to change Group status when PDP status is changed
+ //
+ // (Must NOT be called from a method that is synchronized on the papEngine or it may deadlock)
+ //
+
+ private void setPDPSummaryStatus(PDP pdp, PDPStatus.Status newStatus) throws PAPException {
+ setPDPSummaryStatus(pdp, newStatus.toString());
+ }
+
+ private void setPDPSummaryStatus(PDP pdp, String newStatus) throws PAPException {
+ synchronized(papEngine) {
+ StdPDPStatus status = (StdPDPStatus)pdp.getStatus();
+ status.setStatus(PDPStatus.Status.valueOf(newStatus));
+ ((StdPDP)pdp).setStatus(status);
+
+ // now adjust the group
+ StdPDPGroup group = (StdPDPGroup)papEngine.getPDPGroup(pdp);
+ // if the PDP was just deleted it may transiently exist but not be in a group
+ if (group != null) {
+ group.resetStatus();
+ }
+ }
+ }
+
+
+ //
+ // Callback methods telling this servlet to notify PDPs of changes made by the PAP StdEngine
+ // in the PDP group directories
+ //
+
+ @Override
+ public void changed() {
+ // all PDPs in all groups need to be updated/sync'd
+ Set<PDPGroup> groups;
+ try {
+ groups = papEngine.getPDPGroups();
+ } catch (PAPException e) {
+ logger.error("getPDPGroups failed: " + e.getLocalizedMessage());
+ throw new RuntimeException("Unable to get Groups: " + e);
+ }
+ for (PDPGroup group : groups) {
+ groupChanged(group);
+ }
+ }
+
+ @Override
+ public void groupChanged(PDPGroup group) {
+ // all PDPs within one group need to be updated/sync'd
+ for (PDP pdp : group.getPdps()) {
+ pdpChanged(pdp);
+ }
+ }
+
+ @Override
+ public void pdpChanged(PDP pdp) {
+ // kick off a thread to do an event notification for each PDP.
+ // This needs to be on a separate thread so that PDPs that do not respond (down, non-existent, etc)
+ // do not block the PSP response to the AC, which would freeze the GUI until all PDPs sequentially respond or time-out.
+ Thread t = new Thread(new UpdatePDPThread(pdp));
+ t.start();
+ }
+
+ private class UpdatePDPThread implements Runnable {
+ private PDP pdp;
+
+ // remember which PDP to notify
+ public UpdatePDPThread(PDP pdp) {
+ this.pdp = pdp;
+ }
+
+ public void run() {
+ // send the current configuration to one PDP
+ HttpURLConnection connection = null;
+ try {
+
+ //
+ // the Id of the PDP is its URL
+ //
+ if (logger.isDebugEnabled()) {
+ logger.debug("creating url for id '" + pdp.getId() + "'");
+ }
+ //TODO - currently always send both policies and pips. Do we care enough to add code to allow sending just one or the other?
+ //TODO (need to change "cache=", implying getting some input saying which to change)
+ URL url = new URL(pdp.getId() + "?cache=all");
+
+ //
+ // Open up the connection
+ //
+ connection = (HttpURLConnection)url.openConnection();
+ //
+ // Setup our method and headers
+ //
+ connection.setRequestMethod("PUT");
+ // connection.setRequestProperty("Accept", "text/x-java-properties");
+ connection.setRequestProperty("Content-Type", "text/x-java-properties");
+ // connection.setUseCaches(false);
+ //
+ // Adding this in. It seems the HttpUrlConnection class does NOT
+ // properly forward our headers for POST re-direction. It does so
+ // for a GET re-direction.
+ //
+ // So we need to handle this ourselves.
+ //
+ //TODO - is this needed for a PUT? seems better to leave in for now?
+// connection.setInstanceFollowRedirects(false);
+ //
+ // PLD - MUST be able to handle re-directs.
+ //
+ connection.setInstanceFollowRedirects(true);
+ connection.setDoOutput(true);
+ // connection.setDoInput(true);
+ try (OutputStream os = connection.getOutputStream()) {
+
+ PDPGroup group = papEngine.getPDPGroup(pdp);
+ // if the PDP was just deleted, there is no group, but we want to send an update anyway
+ if (group == null) {
+ // create blank properties files
+ Properties policyProperties = new Properties();
+ policyProperties.put(XACMLProperties.PROP_ROOTPOLICIES, "");
+ policyProperties.put(XACMLProperties.PROP_REFERENCEDPOLICIES, "");
+ policyProperties.store(os, "");
+
+ Properties pipProps = new Properties();
+ pipProps.setProperty(XACMLProperties.PROP_PIP_ENGINES, "");
+ pipProps.store(os, "");
+
+ } else {
+ // send properties from the current group
+ group.getPolicyProperties().store(os, "");
+ Properties policyLocations = new Properties();
+ for (PDPPolicy policy : group.getPolicies()) {
+ policyLocations.put(policy.getId() + ".url", XACMLPapServlet.papURL + "?id=" + policy.getId());
+ }
+ policyLocations.store(os, "");
+ group.getPipConfigProperties().store(os, "");
+ }
+
+ } catch (Exception e) {
+ logger.error("Failed to send property file to " + pdp.getId(), e);
+ // Since this is a server-side error, it probably does not reflect a problem on the client,
+ // so do not change the PDP status.
+ return;
+ }
+ //
+ // Do the connect
+ //
+ connection.connect();
+ if (connection.getResponseCode() == 204) {
+ logger.info("Success. We are configured correctly.");
+ setPDPSummaryStatus(pdp, PDPStatus.Status.UP_TO_DATE);
+ } else if (connection.getResponseCode() == 200) {
+ logger.info("Success. PDP needs to update its configuration.");
+ setPDPSummaryStatus(pdp, PDPStatus.Status.OUT_OF_SYNCH);
+ } else {
+ logger.warn("Failed: " + connection.getResponseCode() + " message: " + connection.getResponseMessage());
+ setPDPSummaryStatus(pdp, PDPStatus.Status.UNKNOWN);
+ }
+ } catch (Exception e) {
+ logger.error("Unable to sync config with PDP '" + pdp.getId() + "': " + e, e);
+ try {
+ setPDPSummaryStatus(pdp, PDPStatus.Status.UNKNOWN);
+ } catch (PAPException e1) {
+ logger.error("Unable to set status of PDP '" + pdp.getId() + "' to UNKNOWN: " + e, e);
+ }
+ } finally {
+ // cleanup the connection
+ connection.disconnect();
+
+ // tell the AC to update it's status info
+ notifyAC();
+ }
+
+ }
+ }
+
+ //
+ // RESTful Interface from PAP to ACs notifying them of changes
+ //
+
+ private void notifyAC() {
+ // kick off a thread to do one event notification for all registered ACs
+ // This needs to be on a separate thread so that ACs can make calls back to PAP to get the updated Group data
+ // as part of processing this message on their end.
+ Thread t = new Thread(new NotifyACThread());
+ t.start();
+ }
+
+ private class NotifyACThread implements Runnable {
+
+ public void run() {
+ List<String> disconnectedACs = new ArrayList<String>();
+// logger.debug("LIST SIZE="+adminConsoleURLStringList.size());
+
+ // There should be no Concurrent exception here because the list is a CopyOnWriteArrayList.
+ // The "for each" loop uses the collection's iterator under the covers, so it should be correct.
+ for (String acURL : adminConsoleURLStringList) {
+ HttpURLConnection connection = null;
+ try {
+
+ acURL += "?PAPNotification=true";
+
+//TODO - Currently we just tell AC that "Something changed" without being specific. Do we want to tell it which group/pdp changed?
+//TODO - If so, put correct parameters into the Query string here
+ acURL += "&objectType=all" + "&action=update";
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("creating url for id '" + acURL + "'");
+ }
+//TODO - currently always send both policies and pips. Do we care enough to add code to allow sending just one or the other?
+//TODO (need to change "cache=", implying getting some input saying which to change)
+
+ URL url = new URL(acURL );
+
+ //
+ // Open up the connection
+ //
+ connection = (HttpURLConnection)url.openConnection();
+ //
+ // Setup our method and headers
+ //
+ connection.setRequestMethod("PUT");
+ connection.setRequestProperty("Content-Type", "text/x-java-properties");
+ //
+ // Adding this in. It seems the HttpUrlConnection class does NOT
+ // properly forward our headers for POST re-direction. It does so
+ // for a GET re-direction.
+ //
+ // So we need to handle this ourselves.
+ //
+ //TODO - is this needed for a PUT? seems better to leave in for now?
+ connection.setInstanceFollowRedirects(false);
+ //
+ // Do not include any data in the PUT because this is just a
+ // notification to the AC.
+ // The AC will use GETs back to the PAP to get what it needs
+ // to fill in the screens.
+ //
+
+ //
+ // Do the connect
+ //
+ connection.connect();
+ if (connection.getResponseCode() == 204) {
+ logger.info("Success. We updated correctly.");
+ } else {
+ logger.warn("Failed: " + connection.getResponseCode() + " message: " + connection.getResponseMessage());
+ }
+
+ } catch (Exception e) {
+ logger.error("Unable to sync config AC '" + acURL + "': " + e, e);
+ disconnectedACs.add(acURL);
+ } finally {
+ // cleanup the connection
+ connection.disconnect();
+ }
+ }
+
+ // remove any ACs that are no longer connected
+ if (disconnectedACs.size() > 0) {
+ adminConsoleURLStringList.removeAll(disconnectedACs);
+ }
+
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/src/main/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/src/main/resources/log4j.properties b/openaz-xacml-pap-rest/src/main/resources/log4j.properties
new file mode 100755
index 0000000..b45fa2f
--- /dev/null
+++ b/openaz-xacml-pap-rest/src/main/resources/log4j.properties
@@ -0,0 +1,22 @@
+#
+# Use this properties for debugging and development.
+#
+#
+# Set root logger level to DEBUG and its only appender to A1.
+log4j.rootLogger=DEBUG, MAIN_LOG
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.MAIN_LOG=org.apache.log4j.ConsoleAppender
+
+# A1 uses PatternLayout.
+log4j.appender.MAIN_LOG.layout=org.apache.log4j.PatternLayout
+log4j.appender.MAIN_LOG.layout.ConversionPattern=%d{yyyy_MM_dd_HH_mm_ss_SSS} [%t] %-5p %l- %m%n
+
+#
+# This is specifically for Xacml request/response logging
+#
+log4j.logger.xacml.request=INFO, REQUEST_LOG
+
+log4j.appender.REQUEST_LOG=org.apache.log4j.ConsoleAppender
+log4j.appender.REQUEST_LOG.layout=org.apache.log4j.PatternLayout
+log4j.appender.REQUEST_LOG.layout.ConversionPattern=%d{yyyy_MM_dd_HH_mm_ss_SSS} %m%n
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$1.class
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$1.class b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$1.class
new file mode 100644
index 0000000..352e4c6
Binary files /dev/null and b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$1.class differ
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$Heartbeat.class
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$Heartbeat.class b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$Heartbeat.class
new file mode 100644
index 0000000..aac4b91
Binary files /dev/null and b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$Heartbeat.class differ
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$NotifyACThread.class
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$NotifyACThread.class b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$NotifyACThread.class
new file mode 100644
index 0000000..3d756fd
Binary files /dev/null and b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$NotifyACThread.class differ
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$UpdatePDPThread.class
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$UpdatePDPThread.class b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$UpdatePDPThread.class
new file mode 100644
index 0000000..ffd1592
Binary files /dev/null and b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$UpdatePDPThread.class differ
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet.class
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet.class b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet.class
new file mode 100644
index 0000000..d8d63b5
Binary files /dev/null and b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet.class differ
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/classes/log4j.properties
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/classes/log4j.properties b/openaz-xacml-pap-rest/target/classes/log4j.properties
new file mode 100644
index 0000000..b45fa2f
--- /dev/null
+++ b/openaz-xacml-pap-rest/target/classes/log4j.properties
@@ -0,0 +1,22 @@
+#
+# Use this properties for debugging and development.
+#
+#
+# Set root logger level to DEBUG and its only appender to A1.
+log4j.rootLogger=DEBUG, MAIN_LOG
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.MAIN_LOG=org.apache.log4j.ConsoleAppender
+
+# A1 uses PatternLayout.
+log4j.appender.MAIN_LOG.layout=org.apache.log4j.PatternLayout
+log4j.appender.MAIN_LOG.layout.ConversionPattern=%d{yyyy_MM_dd_HH_mm_ss_SSS} [%t] %-5p %l- %m%n
+
+#
+# This is specifically for Xacml request/response logging
+#
+log4j.logger.xacml.request=INFO, REQUEST_LOG
+
+log4j.appender.REQUEST_LOG=org.apache.log4j.ConsoleAppender
+log4j.appender.REQUEST_LOG.layout=org.apache.log4j.PatternLayout
+log4j.appender.REQUEST_LOG.layout.ConversionPattern=%d{yyyy_MM_dd_HH_mm_ss_SSS} %m%n
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/maven-archiver/pom.properties
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/maven-archiver/pom.properties b/openaz-xacml-pap-rest/target/maven-archiver/pom.properties
new file mode 100644
index 0000000..022f696
--- /dev/null
+++ b/openaz-xacml-pap-rest/target/maven-archiver/pom.properties
@@ -0,0 +1,5 @@
+#Generated by Maven
+#Tue Apr 07 07:42:37 EDT 2015
+version=0.0.1-SNAPSHOT
+groupId=org.openliberty.openaz
+artifactId=openaz-xacml-pap-rest
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
new file mode 100644
index 0000000..4cfb3ef
--- /dev/null
+++ b/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
@@ -0,0 +1,5 @@
+com/att/research/xacml/rest/XACMLPapServlet$Heartbeat.class
+com/att/research/xacml/rest/XACMLPapServlet$1.class
+com/att/research/xacml/rest/XACMLPapServlet.class
+com/att/research/xacml/rest/XACMLPapServlet$UpdatePDPThread.class
+com/att/research/xacml/rest/XACMLPapServlet$NotifyACThread.class
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
new file mode 100644
index 0000000..9a51391
--- /dev/null
+++ b/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
@@ -0,0 +1 @@
+/Users/ajith/IdeaProjects/openaz/openaz-xacml-pap-rest/src/main/java/com/att/research/xacml/rest/XACMLPapServlet.java
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst b/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/openaz-xacml-pap-rest-0.0.1-SNAPSHOT.jar
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/openaz-xacml-pap-rest-0.0.1-SNAPSHOT.jar b/openaz-xacml-pap-rest/target/openaz-xacml-pap-rest-0.0.1-SNAPSHOT.jar
new file mode 100644
index 0000000..d8cdce7
Binary files /dev/null and b/openaz-xacml-pap-rest/target/openaz-xacml-pap-rest-0.0.1-SNAPSHOT.jar differ
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/xacml.pap.properties
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/xacml.pap.properties b/openaz-xacml-pap-rest/xacml.pap.properties
new file mode 100755
index 0000000..05fc0c5
--- /dev/null
+++ b/openaz-xacml-pap-rest/xacml.pap.properties
@@ -0,0 +1,35 @@
+#
+# This is our factory that will create our engine
+#
+xacml.PAP.papEngineFactory=com.att.research.xacml.std.pap.StdEngineFactory
+
+#
+# Where we store our PAP PDP Group/Node information
+#
+xacml.pap.pdps=pdps
+#
+# Need the PAP's url (how PDPs will reach it) configured here
+# because we need it to generate the URLs of the Policy Files
+# sent to the PDPs in the configuration when the PAP is first brought up.
+# (In other cases, such as the PDP calling the PAP, we could generate this URL,
+# but for startup there is no other way to get it.)
+#
+#
+xacml.rest.pap.url=http://localhost:9090/pap/
+
+#
+# Upon startup, have the PAP servlet send latest configuration information to all
+# the PDP nodes it knows about.
+#
+xacml.rest.pap.initiate.pdp=true
+#
+# Heartbeat from PAP to PDPs
+#
+# How much time (in milliseconds) between heartbeats
+# (i.e. the time between completing the heartbeat with all PDPs and starting the next cycle)
+#
+xacml.rest.pap.heartbeat.interval=10000
+#
+# Heartbeat connection timeout (in milliseconds)
+#
+xacml.rest.pap.heartbeat.timeout=10000
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pdp-rest/WebContent/META-INF/MANIFEST.MF
----------------------------------------------------------------------
diff --git a/openaz-xacml-pdp-rest/WebContent/META-INF/MANIFEST.MF b/openaz-xacml-pdp-rest/WebContent/META-INF/MANIFEST.MF
new file mode 100755
index 0000000..58630c0
--- /dev/null
+++ b/openaz-xacml-pdp-rest/WebContent/META-INF/MANIFEST.MF
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pdp-rest/WebContent/WEB-INF/lib/sqljdbc4.jar
----------------------------------------------------------------------
diff --git a/openaz-xacml-pdp-rest/WebContent/WEB-INF/lib/sqljdbc4.jar b/openaz-xacml-pdp-rest/WebContent/WEB-INF/lib/sqljdbc4.jar
new file mode 100755
index 0000000..d6b7f6d
Binary files /dev/null and b/openaz-xacml-pdp-rest/WebContent/WEB-INF/lib/sqljdbc4.jar differ
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pdp-rest/config/xacml.pip.properties
----------------------------------------------------------------------
diff --git a/openaz-xacml-pdp-rest/config/xacml.pip.properties b/openaz-xacml-pdp-rest/config/xacml.pip.properties
new file mode 100755
index 0000000..0c16eb3
--- /dev/null
+++ b/openaz-xacml-pdp-rest/config/xacml.pip.properties
@@ -0,0 +1,219 @@
+# PIP Engine Definition
+#
+xacml.pip.engines=csv1,csv2,hyper1,sql1,ldap1
+
+csv1.classname=com.att.research.xacml.std.pip.engines.csv.CSVEngine
+csv1.name=Master
+csv1.description=Sean Lahman Basebase stats - Player names, DOB, and biographical info
+csv1.issuer=com:att:research:xacml:test:csv
+csv1.source=../XACML-TEST/testsets/pip/configurable-csv/adminDB/Master.txt
+csv1.maxsize=4000000
+csv1.delimiter=,
+csv1.quote="
+csv1.skip=0
+
+csv1.resolvers=data
+
+csv1.resolver.data.classname=com.att.research.xacml.std.pip.engines.csv.ConfigurableCSVResolver
+csv1.resolver.data.name=Player Resolver
+csv1.resolver.data.description=This resolver finds player information in the Master table.
+csv1.resolver.data.fields=firstname,lastname,deathyear,deathmonth,deathday,debut,finalgame
+csv1.resolver.data.field.firstname.column=16
+csv1.resolver.data.field.firstname.id=com:att:research:xacml:test:csv:subject:firstname
+csv1.resolver.data.field.firstname.datatype=http://www.w3.org/2001/XMLSchema#string
+csv1.resolver.data.field.firstname.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv1.resolver.data.field.lastname.column=17
+csv1.resolver.data.field.lastname.id=com:att:research:xacml:test:csv:subject:lastname
+csv1.resolver.data.field.lastname.datatype=http://www.w3.org/2001/XMLSchema#string
+csv1.resolver.data.field.lastname.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv1.resolver.data.field.deathyear.column=10
+csv1.resolver.data.field.deathyear.id=com:att:research:xacml:test:csv:subject:deathyear
+csv1.resolver.data.field.deathyear.datatype=http://www.w3.org/2001/XMLSchema#integer
+csv1.resolver.data.field.deathyear.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv1.resolver.data.field.deathmonth.column=11
+csv1.resolver.data.field.deathmonth.id=com:att:research:xacml:test:csv:subject:deathmonth
+csv1.resolver.data.field.deathmonth.datatype=http://www.w3.org/2001/XMLSchema#integer
+csv1.resolver.data.field.deathmonth.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv1.resolver.data.field.deathday.column=12
+csv1.resolver.data.field.deathday.id=com:att:research:xacml:test:csv:subject:deathday
+csv1.resolver.data.field.deathday.datatype=http://www.w3.org/2001/XMLSchema#integer
+csv1.resolver.data.field.deathday.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv1.resolver.data.field.debut.column=25
+csv1.resolver.data.field.debut.id=com:att:research:xacml:test:csv:subject:debut
+csv1.resolver.data.field.debut.datatype=http://www.w3.org/2001/XMLSchema#date
+csv1.resolver.data.field.debut.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv1.resolver.data.field.finalgame.column=26
+csv1.resolver.data.field.finalgame.id=com:att:research:xacml:test:csv:subject:finalgame
+csv1.resolver.data.field.finalgame.datatype=http://www.w3.org/2001/XMLSchema#date
+csv1.resolver.data.field.finalgame.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv1.resolver.data.parameters=playerid
+csv1.resolver.data.parameter.playerid.column=1
+csv1.resolver.data.parameter.playerid.id=urn:oasis:names:tc:xacml:1.0:subject:subject-id
+csv1.resolver.data.parameter.playerid.datatype=http://www.w3.org/2001/XMLSchema#string
+csv1.resolver.data.parameter.playerid.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv2.classname=com.att.research.xacml.std.pip.engines.csv.CSVEngine
+csv2.name=Appearances
+csv2.description=Sean Lahman Basebase stats - Player appearances for a team in a given year.
+#csv2.issuer=
+csv2.source=../XACML-TEST/testsets/pip/configurable-csv/adminDB/Appearances.txt
+csv2.maxsize=4000000
+csv2.delimiter=,
+csv2.quote="
+csv2.skip=0
+
+csv2.resolvers=data
+
+csv2.resolver.data.classname=com.att.research.xacml.std.pip.engines.csv.ConfigurableCSVResolver
+csv2.resolver.data.name=Appearance Resolver
+csv2.resolver.data.description=This resolver returns all the appearances for a player from the appearance table.
+csv2.resolver.data.fields=appearance
+csv2.resolver.data.field.appearance.column=0
+csv2.resolver.data.field.appearance.id=com:att:research:xacml:test:csv:subject:appearance
+csv2.resolver.data.field.appearance.datatype=http://www.w3.org/2001/XMLSchema#integer
+csv2.resolver.data.field.appearance.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+csv2.resolver.data.field.appearance.issuer=com:att:research:xacml:test:csv
+
+csv2.resolver.data.parameters=playerid
+csv2.resolver.data.parameter.playerid.column=3
+csv2.resolver.data.parameter.playerid.id=urn:oasis:names:tc:xacml:1.0:subject:subject-id
+csv2.resolver.data.parameter.playerid.datatype=http://www.w3.org/2001/XMLSchema#string
+csv2.resolver.data.parameter.playerid.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+#csv1.resolver.data.parameter.playerid.issuer=
+
+hyper1.classname=com.att.research.xacml.std.pip.engines.csv.HyperCSVEngine
+hyper1.name=World Marriage Age Limits
+hyper1.description=Minimum age for female/male marriages with or without their parental consent.
+hyper1.source=../XACML-TEST/testsets/pip/configurable-csv-hyper/marriage.csv
+hyper1.target=marriage
+hyper1.definition=country VARCHAR(80) PRIMARY KEY, wofemale INT, womale INT, wfemale INT, wmale INT, year INT, source VARCHAR(20)
+
+hyper1.resolvers=age_consent
+
+hyper1.resolver.age_consent.classname=com.att.research.xacml.std.pip.engines.jdbc.ConfigurableJDBCResolver
+hyper1.resolver.age_consent.name=Ages
+hyper1.resolver.age_consent.description=This returns all the age's for consent or no consent for a country.
+hyper1.resolver.age_consent.select=SELECT wofemale,womale,wfemale,wmale FROM marriage WHERE country=?
+hyper1.resolver.age_consent.fields=wofemale,womale,wfemale,wmale
+
+hyper1.resolver.age_consent.field.wofemale.id=com:att:research:xacml:test:csv:country:no-consent:female
+hyper1.resolver.age_consent.field.wofemale.datatype=http://www.w3.org/2001/XMLSchema#integer
+hyper1.resolver.age_consent.field.wofemale.category=com:att:research:xacml:test:csv:category:country
+hyper1.resolver.age_consent.field.wofemale.issuer=com:att:research:xacml:test:csv
+
+hyper1.resolver.age_consent.field.womale.id=com:att:research:xacml:test:csv:country:no-consent:male
+hyper1.resolver.age_consent.field.womale.datatype=http://www.w3.org/2001/XMLSchema#integer
+hyper1.resolver.age_consent.field.womale.category=com:att:research:xacml:test:csv:category:country
+hyper1.resolver.age_consent.field.womale.issuer=com:att:research:xacml:test:csv
+
+hyper1.resolver.age_consent.field.wfemale.id=com:att:research:xacml:test:csv:country:consent:female
+hyper1.resolver.age_consent.field.wfemale.datatype=http://www.w3.org/2001/XMLSchema#integer
+hyper1.resolver.age_consent.field.wfemale.category=com:att:research:xacml:test:csv:category:country
+hyper1.resolver.age_consent.field.wfemale.issuer=com:att:research:xacml:test:csv
+
+hyper1.resolver.age_consent.field.wmale.id=com:att:research:xacml:test:csv:country:consent:male
+hyper1.resolver.age_consent.field.wmale.datatype=http://www.w3.org/2001/XMLSchema#integer
+hyper1.resolver.age_consent.field.wmale.category=com:att:research:xacml:test:csv:category:country
+hyper1.resolver.age_consent.field.wmale.issuer=com:att:research:xacml:test:csv
+
+hyper1.resolver.age_consent.parameters=country
+hyper1.resolver.age_consent.parameter.country.id=com:att:research:xacml:test:csv:country:name
+hyper1.resolver.age_consent.parameter.country.datatype=http://www.w3.org/2001/XMLSchema#string
+hyper1.resolver.age_consent.parameter.country.category=com:att:research:xacml:test:csv:category:country
+#hyper1.resolver.age_consent.parameter.country.issuer=
+
+sql1.classname=com.att.research.xacml.std.pip.engines.jdbc.JDBCEngine
+sql1.name=World
+sql1.description=World Database from MySQL website. Copyright Statistics Finland, http://www.stat.fi/worldinfigures.
+# This will be the default issuer for the resolvers. NOTE: Issuer only used for attributes provided by the engine.
+sql1.issuer=com:att:research:xacml:test:sql
+#
+# This is the configuration for JDBC. You will have to setup the database and run the data\world*.sql script to
+# create the tables and load the data.
+#
+sql1.type=jdbc
+sql1.jdbc.driver=org.postgresql.Driver
+sql1.jdbc.url=jdbc:postgresql://localhost:5432/world
+sql1.jdbc.conn.user=sa
+sql1.jdbc.conn.password=
+#
+# This is the configuration for JNDI datasource.
+#
+#sql1.type=jndi
+#sql1.datasource=jdbc/xacml
+
+sql1.resolvers=langer
+
+sql1.resolver.langer.classname=com.att.research.xacml.std.pip.engines.jdbc.ConfigurableJDBCResolver
+sql1.resolver.langer.name=Language
+sql1.resolver.langer.description=This returns the language for a city.
+sql1.resolver.langer.select=SELECT language FROM city INNER JOIN countrylanguage ON city.countrycode = countrylanguage.countrycode WHERE name=?
+sql1.resolver.langer.fields=language
+sql1.resolver.langer.field.language.id=com:att:research:xacml:test:sql:resource:city:language
+sql1.resolver.langer.field.language.datatype=http://www.w3.org/2001/XMLSchema#string
+sql1.resolver.langer.field.language.category=urn:oasis:names:tc:xacml:3.0:attribute-category:resource
+#You can override the default issuer that is set in the JDBCEngine definition if you want.
+#sql1.resolver.langer.field.language.issuer=com:att:research:xacml:test:sql
+sql1.resolver.langer.parameters=name
+sql1.resolver.langer.parameter.name.id=urn:oasis:names:tc:xacml:1.0:resource:resource-id
+sql1.resolver.langer.parameter.name.datatype=http://www.w3.org/2001/XMLSchema#string
+sql1.resolver.langer.parameter.name.category=urn:oasis:names:tc:xacml:3.0:attribute-category:resource
+
+
+ldap1.classname=com.att.research.xacml.std.pip.engines.ldap.LDAPEngine
+ldap1.name=LDAP PIP
+ldap1.description=The LDAP containing the seven seas sample LDIF data.
+ldap1.issuer=com:att:research:xacml:test:ldap
+ldap1.java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
+#
+# NOTE: You will have to setup a local LDAP server and load the data\apache-ds-tutorial.ldif before
+# this example will work.
+#
+ldap1.java.naming.provider.url=ldap://localhost:10389
+#ldap.java.naming.security.principal=
+#ldap.java.naming.security.credentials=
+ldap1.scope=subtree
+
+ldap1.resolvers=dn,ship
+
+ldap1.resolver.dn.classname=com.att.research.xacml.std.pip.engines.ldap.ConfigurableLDAPResolver
+ldap1.resolver.dn.name=Domain Names
+ldap1.resolver.dn.description=Find all the dn's for the subject id
+ldap1.resolver.dn.base=o=sevenseas
+ldap1.resolver.dn.base.parameters=
+ldap1.resolver.dn.filter=(|(uid=${uid})(mail=${uid}))
+ldap1.resolver.dn.filter.parameters=uid
+ldap1.resolver.dn.filter.parameters.uid.id=urn:oasis:names:tc:xacml:1.0:subject:subject-id
+ldap1.resolver.dn.filter.parameters.uid.datatype=http://www.w3.org/2001/XMLSchema#string
+ldap1.resolver.dn.filter.parameters.uid.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+#ldap1.resolver.dn.filter.parameters.uid.issuer=com:att:research:xacml:test:ldap
+ldap1.resolver.dn.filter.view=dn
+ldap1.resolver.dn.filter.view.dn.id=com:att:research:xacml:test:ldap:subject:dn
+ldap1.resolver.dn.filter.view.dn.datatype=http://www.w3.org/2001/XMLSchema#string
+ldap1.resolver.dn.filter.view.dn.category=urn:oasis:names:tc:xacml:3.0:attribute-category:resource
+ldap1.resolver.dn.filter.view.dn.issuer=com:att:research:xacml:test:ldap
+
+ldap1.resolver.ship.classname=com.att.research.xacml.std.pip.engines.ldap.ConfigurableLDAPResolver
+ldap1.resolver.ship.name=Ship Resolver
+ldap1.resolver.ship.description=This resolves a subject's dn to a ship.
+ldap1.resolver.ship.base=o=sevenseas
+ldap1.resolver.ship.base.parameters=
+ldap1.resolver.ship.filter=uniquemember=${dn}
+ldap1.resolver.ship.filter.parameters=dn
+ldap1.resolver.ship.filter.parameters.dn.id=com:att:research:xacml:test:ldap:subject:dn
+ldap1.resolver.ship.filter.parameters.dn.datatype=http://www.w3.org/2001/XMLSchema#string
+ldap1.resolver.ship.filter.parameters.dn.category=urn:oasis:names:tc:xacml:3.0:attribute-category:resource
+ldap1.resolver.ship.filter.parameters.dn.issuer=com:att:research:xacml:test:ldap
+ldap1.resolver.ship.filter.view=cn
+ldap1.resolver.ship.filter.view.cn.id=com:att:research:xacml:test:ldap:subject:ship
+ldap1.resolver.ship.filter.view.cn.datatype=http://www.w3.org/2001/XMLSchema#string
+ldap1.resolver.ship.filter.view.cn.category=urn:oasis:names:tc:xacml:3.0:attribute-category:resource
+ldap1.resolver.ship.filter.view.cn.issuer=com:att:research:xacml:test:ldap
+
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pdp-rest/config/xacml.policy.properties
----------------------------------------------------------------------
diff --git a/openaz-xacml-pdp-rest/config/xacml.policy.properties b/openaz-xacml-pdp-rest/config/xacml.policy.properties
new file mode 100755
index 0000000..d42c84c
--- /dev/null
+++ b/openaz-xacml-pdp-rest/config/xacml.policy.properties
@@ -0,0 +1,8 @@
+xacml.rootPolicies=annotation,ldap,csv,csvhyper,sql
+xacml.referencedPolicies=
+
+annotation.file=../XACML-TEST/testsets/annotation/AnnotationPolicy.v1.xml
+ldap.file=../XACML-TEST/testsets/pip/configurable-ldap/LDAP-Seven-Seas-v1.xml
+csv.file=../XACML-TEST/testsets/pip/configurable-csv/CSV-Baseball-Hall-Of-Fame-v1.xml
+csvhyper.file=../XACML-TEST/testsets/pip/configurable-csv-hyper/CSV-Legal-Age-Marriage-v1.xml
+sql.file=../XACML-TEST/testsets/pip/configurable-sql/SQL-World-Languages-v1.xml
http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pdp-rest/pom.xml
----------------------------------------------------------------------
diff --git a/openaz-xacml-pdp-rest/pom.xml b/openaz-xacml-pdp-rest/pom.xml
new file mode 100755
index 0000000..edb97b4
--- /dev/null
+++ b/openaz-xacml-pdp-rest/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>openaz</artifactId>
+ <groupId>org.openliberty.openaz</groupId>
+ <version>0.0.1-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>openaz-xacml-pdp-rest</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.openliberty.openaz</groupId>
+ <artifactId>openaz-xacml</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.openliberty.openaz</groupId>
+ <artifactId>openaz-xacml-rest</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.openliberty.openaz</groupId>
+ <artifactId>openaz-xacml-pdp</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-java</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>postgresql</groupId>
+ <artifactId>postgresql</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hsqldb</groupId>
+ <artifactId>hsqldb</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file