diff options
Diffstat (limited to 'ECOMP-PDP-REST/src/main/java/org/openecomp')
18 files changed, 4163 insertions, 0 deletions
diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/PapUrlResolver.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/PapUrlResolver.java new file mode 100644 index 000000000..d9a3688b4 --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/PapUrlResolver.java @@ -0,0 +1,387 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.pdp.rest; + +import java.net.URI; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.NoSuchElementException; +import java.util.Properties; + +import org.openecomp.policy.rest.XACMLRestProperties; + +import com.att.research.xacml.util.XACMLProperties; + +import org.openecomp.policy.common.logging.flexlogger.*; + +public class PapUrlResolver { + private static final Logger logger = FlexLogger.getLogger(PapUrlResolver.class); + //how long to keep a pap failed before making it un-failed, in milli-seconds + private static final long FAIL_TIMEOUT = 18000000; + + //thread locks + public static Object propertyLock = new Object(); + + public static void setPapUrls(String[] papUrls){ + + } + //keeping this here for backward compatibility + public static String extractIdFromUrl(String url){ + return extractQuery(url); + } + public static String extractQuery(String url){ + try{ + return URI.create(url).getQuery(); + } catch(Exception e){ + return ""; + } + } + public static String modifyUrl(String idUrl, String serverUrl){ + URI one = URI.create(idUrl); + String host = one.getPath()+one.getQuery(); + URI two = URI.create(serverUrl); + two.resolve(host); + return two.toString(); + } + + //get an instance of a new PapUrlResolver, using XACMLProperties to get the url lists + public static PapUrlResolver getInstance(){ + return new PapUrlResolver(null,null,null,true); + } + + //get an instance of a new PapUrlResolver, using the provides strings for the url lists + public static PapUrlResolver getInstance(String urlList, String failedList, String succeededList){ + return new PapUrlResolver(urlList, failedList, succeededList,false); + } + + //keeps track of our current location in the list of urls, allows for iterating + private int pointer; + + //should the XACML property lists be updated after anything changes or should we wait for the update + //method to be called. + private boolean autoUpdateProperties; + + //this list keeps the sorted, priority of PAP URLs + private PapUrlNode[] sortedUrlNodes; + //this list keeps the original list of nodes so that they can be entered into the property list correctly + private PapUrlNode[] originalUrlNodes; + + //private constructor to make an instance of a PapUrlResolver, called by static method getInstance. + //If the list property strings are not defined, we get the values from XACMLProperties. + //The instance acts as an iterator, with hasNext and next methods, but does not implement Iterable, + //because it is used for a difference purpose. + private PapUrlResolver(String urlList, String failedList, String succeededList, boolean autoUpdateProperties){ + this.autoUpdateProperties = autoUpdateProperties; + //synchronized(propertyLock){ + if(urlList == null){ + urlList = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_URLS); + if(urlList == null){ + urlList = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_URL); + } + failedList = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_FAILED_URLS); + succeededList = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_SUCCEEDED_URLS); + } + //} + String[] urls = urlList.split(","); + if(urls.length == 0){ + //log error + } + String[] failed = emptyOrSplit(failedList,urls.length); + String[] succeeded = emptyOrSplit(succeededList,urls.length); + + sortedUrlNodes = new PapUrlNode[urls.length]; + for(int i=0;i<urls.length;i++){ + + String userId = null; + String pass = null; + userId = XACMLProperties.getProperty(urls[i]+"."+XACMLRestProperties.PROP_PAP_USERID); + pass = XACMLProperties.getProperty(urls[i]+"."+XACMLRestProperties.PROP_PAP_PASS); + if(userId == null || pass == null){ + userId = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_USERID); + pass = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_PASS); + } + if(userId == null || pass == null){ + userId = ""; + pass = ""; + } + PapUrlNode newNode = new PapUrlNode(urls[i],userId,pass); + newNode.setFailedTime(failed[i]); + newNode.setSucceededTime(succeeded[i]); + if(sortedUrlNodes[i] == null){ + sortedUrlNodes[i] = newNode; + } + + } + originalUrlNodes = sortedUrlNodes.clone(); + sort(sortedUrlNodes); + pointer = 0; + } + + + //either split a list by commas, or fill an array to the expected length, if the property list is not long enough + private String[] emptyOrSplit(String list,int expectedLength){ + String[] ret; + if(list == null){ + ret = new String[expectedLength]; + for(int i=0;i<expectedLength;i++){ + ret[i] = "-1"; + } + } else { + ret = list.split(","); + if(ret.length != expectedLength){ + ret = emptyOrSplit(null,expectedLength); + } + } + return ret; + } + + private void sort(PapUrlNode[] array){ + + //O(n^2) double-loop most likely the best in this case, since number of records will be VERY small + for(int i=0;i<array.length;i++){ + for(int j=i;j<array.length;j++){ + if(array[j].compareTo(array[i])<0){ + PapUrlNode temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + } + } + } + + //returns whether this PapUrlResolver object has more PAP urls that can be tried + public boolean hasMoreUrls(){ + return pointer < sortedUrlNodes.length; + } + + //sets the current PAP url as being failed + //this will set the failed time to now and remove any succeeded time + public void failed(){ + logger.error("PAP Server FAILED: "+sortedUrlNodes[pointer].getUrl()); + + sortedUrlNodes[pointer].setFailedTime(new Date()); + sortedUrlNodes[pointer].setSucceededTime(null); + propertiesUpdated(); + } + + //sets the current PAP url as being working + //this will set the succeeded time to now and remove any failed time + //Also, this will cause hasMoreUrls to return false, since a working one has been found + + public void succeeded(){ + registered(); + pointer = sortedUrlNodes.length; + } + public void registered(){ + sortedUrlNodes[pointer].setFailedTime(null); + sortedUrlNodes[pointer].setSucceededTime(new Date()); + logger.info("PAP server SUCCEEDED "+sortedUrlNodes[pointer].getUrl()); + propertiesUpdated(); + } + + //returns a properties object with the properties that pertain to PAP urls + public Properties getProperties(){ + String failedPropertyString = ""; + String succeededPropertyString = ""; + String urlPropertyString = ""; + for(int i=0;i<originalUrlNodes.length;i++){ + failedPropertyString = failedPropertyString.concat(",").concat(originalUrlNodes[i].getFailedTime()); + succeededPropertyString = succeededPropertyString.concat(",").concat(originalUrlNodes[i].getSucceededTime()); + urlPropertyString = urlPropertyString.concat(",").concat(originalUrlNodes[i].getUrl()); + } + Properties prop = new Properties(); + failedPropertyString = failedPropertyString.substring(1); + succeededPropertyString = succeededPropertyString.substring(1); + urlPropertyString = urlPropertyString.substring(1); + prop.setProperty(XACMLRestProperties.PROP_PAP_FAILED_URLS,failedPropertyString); + prop.setProperty(XACMLRestProperties.PROP_PAP_URLS,urlPropertyString); + prop.setProperty(XACMLRestProperties.PROP_PAP_SUCCEEDED_URLS,succeededPropertyString); + return prop; + } + + //saves the updates urls to the correct properties + private void propertiesUpdated(){ + if(!autoUpdateProperties){ + return; + } + Properties prop = getProperties(); + + logger.debug("Failed PAP Url List: "+prop.getProperty(XACMLRestProperties.PROP_PAP_FAILED_URLS)); + logger.debug("Succeeded PAP Url List: "+prop.getProperty(XACMLRestProperties.PROP_PAP_SUCCEEDED_URLS)); + XACMLProperties.setProperty(XACMLRestProperties.PROP_PAP_FAILED_URLS,prop.getProperty(XACMLRestProperties.PROP_PAP_FAILED_URLS)); + XACMLProperties.setProperty(XACMLRestProperties.PROP_PAP_SUCCEEDED_URLS,prop.getProperty(XACMLRestProperties.PROP_PAP_SUCCEEDED_URLS)); + } + + //iterates to the next available PAP url, according to the priority order + public void getNext(){ + pointer++; + } + + //returns the url of the current PAP server that we are iterating over + //will append the provided policy id to the url + public String getUrl(String query){ + if(sortedUrlNodes[pointer]== null){ + throw new NoSuchElementException(); + } else { + String finalUrl = sortedUrlNodes[pointer].getUrl().concat("?").concat(query); + return finalUrl; + } + } + + //returns the url of the current PAP server that we are iterating over + //Just returns the url, with no id appended to it + public String getUrl(){ + if(sortedUrlNodes[pointer]== null){ + throw new NoSuchElementException(); + } else { + + return sortedUrlNodes[pointer].getUrl(); + } + } + public String getUserId(){ + if(sortedUrlNodes[pointer]== null){ + throw new NoSuchElementException(); + } else { + + return sortedUrlNodes[pointer].getUserId(); + } + } + public String getPass(){ + if(sortedUrlNodes[pointer]== null){ + throw new NoSuchElementException(); + } else { + + return sortedUrlNodes[pointer].getPass(); + } + } + + + //This is the class to hold the details of a single PAP URL + //including: the url itself, the last time it failed, and the last time it succeeded + //It also includes the custom comparer which can compare based on failed and succeeded times, and takes into account + //the timeout on failures. + private class PapUrlNode implements Comparable<PapUrlNode> { + private String papUrl; + private Date failedTime; + private Date succeededTime; + private String userId; + private String pass; + + public PapUrlNode(String url){ + this.papUrl = url; + failedTime = null; + this.succeededTime = null; + this.userId = ""; + this.pass = ""; + + } + public PapUrlNode(String url,String userId,String pass){ + this.papUrl = url; + failedTime = null; + this.succeededTime = null; + this.userId = userId; + this.pass = pass; + + } + public String getUserId(){ + return this.userId; + } + public String getPass(){ + return this.pass; + } + + public void setFailedTime(Object time){ + Date failedTimeAsDate = setHandler(time); + if(failedTimeAsDate == null){ + this.failedTime = null; + } else { + long timeDifference = new Date().getTime() - failedTimeAsDate.getTime(); + if(timeDifference < FAIL_TIMEOUT){ + this.failedTime = failedTimeAsDate; + } else { + this.failedTime = null; + } + } + } + + //set the time that this url succeeded at + public void setSucceededTime(Object time){ + this.succeededTime = setHandler(time); + } + + //parses string into a date or a null date, if the url never failed/succeeded (since -1 will be in the property) + private Date setHandler(Object time){ + if(time instanceof String){ + if(((String)time).equals("-1")){ + return null; + } + try { + DateFormat df = new SimpleDateFormat(); + Date parsedTime = df.parse((String)time); + return parsedTime; + } catch (ParseException e) { + return null; + } + } + if(time instanceof Date){ + return (Date)time; + } + return null; + } + + + public String getFailedTime(){ + return formatTime(this.failedTime); + } + + public String getSucceededTime(){ + return formatTime(this.succeededTime); + } + + //formats a Date into a string or a -1 if there is not date (-1 is used in properties for no date) + private String formatTime(Date d){ + if(d == null){ + return "-1"; + } + DateFormat df = new SimpleDateFormat(); + return df.format(d); + } + + public String getUrl(){ + return papUrl; + } + + public int compareTo(PapUrlNode other){ + if(this.failedTime == null && other.failedTime != null){ + return -1; + } + if(this.failedTime != null && other.failedTime == null){ + return 1; + } + if(this.failedTime != null){ + return this.failedTime.compareTo(other.failedTime); + } + return 0; + } + } +} diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/XACMLPdpLoader.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/XACMLPdpLoader.java new file mode 100644 index 000000000..9c5b120c3 --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/XACMLPdpLoader.java @@ -0,0 +1,695 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.pdp.rest; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.ConcurrentModificationException; +import java.util.HashMap; +import java.util.Properties; +import java.util.Set; + +import org.apache.commons.io.IOUtils; +import org.openecomp.policy.pdp.rest.notifications.NotificationController; +import org.openecomp.policy.rest.XACMLRest; +import org.openecomp.policy.rest.XACMLRestProperties; +import org.openecomp.policy.common.logging.flexlogger.FlexLogger; +import org.openecomp.policy.common.logging.flexlogger.Logger; + +import org.openecomp.policy.xacml.api.XACMLErrorConstants; +import com.att.research.xacml.api.pap.PAPException; +import com.att.research.xacml.api.pap.PDPStatus; +import com.att.research.xacml.api.pap.PDPStatus.Status; +import com.att.research.xacml.api.pdp.PDPEngine; +import com.att.research.xacml.api.pdp.PDPEngineFactory; +import com.att.research.xacml.api.pip.PIPEngine; +import com.att.research.xacml.api.pip.PIPException; +import com.att.research.xacml.api.pip.PIPFinder; +import com.att.research.xacml.api.pip.PIPFinderFactory; +import org.openecomp.policy.xacml.std.pap.StdPDPPIPConfig; +import org.openecomp.policy.xacml.std.pap.StdPDPPolicy; +import org.openecomp.policy.xacml.std.pap.StdPDPStatus; +import com.att.research.xacml.util.FactoryException; +import com.att.research.xacml.util.XACMLProperties; +import com.att.research.xacmlatt.pdp.policy.PolicyDef; +import com.att.research.xacmlatt.pdp.policy.dom.DOMPolicyDef; +import com.att.research.xacmlatt.pdp.std.StdPolicyFinderFactory; +import com.google.common.base.Splitter; + +/** + * Does the work for loading policy and PIP configurations sent from the PAP + * servlet. + * + * + * + */ +public class XACMLPdpLoader { + private static final Logger logger = FlexLogger.getLogger(XACMLPdpLoader.class); + private static NotificationController notificationController = new NotificationController(); + private static final Long notifyDelay = (long) XACMLPdpServlet.getNotificationDelay(); + + + public static synchronized PDPEngine loadEngine(StdPDPStatus status, + Properties policyProperties, Properties pipProperties) { + logger.info("loadEngine: " + policyProperties + " " + pipProperties); + // + // First load our policies + // + try { + // + // Were we given some properties? + // + if (policyProperties == null) { + // + // On init we have no incoming configuration, so just + // Load our current saved configuration + // + policyProperties = new Properties(); + try (InputStream is = Files.newInputStream(getPDPPolicyCache())) { + policyProperties.load(is); + } + } + + // + // Get our policy cache up-to-date + // + // Side effects of this include: + // - downloading of policies from remote locations, and + // - creating new "<PolicyId>.file" properties for files existing + // local + // + XACMLPdpLoader.cachePolicies(policyProperties); + // + // Validate the policies + // + XACMLPdpLoader.validatePolicies(policyProperties, status); + if (logger.isDebugEnabled()) { + logger.debug("Status: " + status); + } + } catch (ConcurrentModificationException e) { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + e.getMessage()); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e, ""); + } catch (Exception e) { + String error = "Failed to load Policy Cache properties file: " + + e.getMessage(); + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + error, e); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e, error); + status.addLoadError(error); + status.setStatus(PDPStatus.Status.LOAD_ERRORS); + } + // + // Load our PIP configuration + // + try { + // + // Were we given some properties to use? + // + if (pipProperties == null) { + // + // Load our current saved configuration + // + pipProperties = new Properties(); + try (InputStream is = Files.newInputStream(getPIPConfig())) { + pipProperties.load(is); + } + } + // + // Validate our PIP configurations + // + XACMLPdpLoader.validatePipConfiguration(pipProperties, status); + if (logger.isDebugEnabled()) { + logger.debug("Status: " + status); + } + } catch (Exception e) { + String error = "Failed to load/validate Pip Config properties file: " + + e.getMessage(); + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + error, e); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, error); + status.addLoadError(XACMLErrorConstants.ERROR_PROCESS_FLOW + error); + status.setStatus(PDPStatus.Status.LOAD_ERRORS); + } + // + // Were they validated? + // + if (status.getStatus() == Status.LOAD_ERRORS) { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW +"there were load errors"); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE,"there were load errors"); + return null; + } + // + // Reset our official properties the PDP factory + // uses to configure the PDP engine. + // + XACMLRest.loadXacmlProperties(policyProperties, pipProperties); + // + // Dump ALL our properties that we are trying to load + // + try { + logger.info(XACMLProperties.getProperties().toString()); + } catch (IOException e) { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to get XACML Properties", e); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e, "Failed to get XACML Properties"); + } + // + // Now load the PDP engine + // + PDPEngineFactory factory = null; + PDPEngine engine = null; + try { + factory = PDPEngineFactory.newInstance(); + engine = factory.newEngine(); + logger.info("Loaded new PDP engine."); + status.setStatus(Status.UP_TO_DATE); + } catch (FactoryException e) { + String error = "Failed to create new PDP Engine"; + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR +error, e); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, error); + status.addLoadError(error); + } + // Notification will be Sent Here. + sendNotification(); + return engine; + } + + private static HashMap<String, PolicyDef> policyContainer = null; + + private static void sendNotification(){ + Thread notify = new Thread(){ + public void run(){ + try{ + Thread.sleep(notifyDelay); + NotificationController.sendNotification(); + }catch(Exception e){ + logger.error(XACMLErrorConstants.ERROR_UNKNOWN + e); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_UNKNOWN, e, ""); + } + } + }; + notify.start(); + } + + public static synchronized void validatePolicies(Properties properties, + StdPDPStatus status) throws PAPException { + Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties); + Set<String> refPolicies = XACMLProperties + .getReferencedPolicyIDs(properties); + policyContainer = new HashMap<String, PolicyDef>(); + + for (String id : rootPolicies) { + loadPolicy(properties, status, id, true); + } + // remember which policies were root policies + status.addAllLoadedRootPolicies(status.getLoadedPolicies()); + + for (String id : refPolicies) { + loadPolicy(properties, status, id, false); + } + logger.info("Loaded " + status.getLoadedPolicies().size() + + " policies, failed to load " + + status.getFailedPolicies().size() + " policies, " + + status.getLoadedRootPolicies().size() + " root policies"); + // TODO Notification Controller is here.. + notificationController.check(status, policyContainer); + if (status.getLoadedRootPolicies().size() == 0) { + logger.warn(XACMLErrorConstants.ERROR_PROCESS_FLOW +"NO ROOT POLICIES LOADED!!! Cannot serve PEP Requests."); + status.addLoadWarning("NO ROOT POLICIES LOADED!!! Cannot serve PEP Requests."); + } + policyContainer.clear(); + } + + + public static synchronized void loadPolicy(Properties properties, + StdPDPStatus status, String id, boolean isRoot) throws PAPException { + PolicyDef policy = null; + String location = null; + URI locationURI = null; + boolean isFile = false; + boolean rougeFile = false; + try { + location = properties.getProperty(id + ".file"); + if(location != null){ + isFile = true; + locationURI = Paths.get(location).toUri(); + try (InputStream is = Files.newInputStream(Paths.get(location))) { + policy = DOMPolicyDef.load(is); + } catch (Exception e){ + // This Happens if a any issue with the error policyFile. Lets remove it. + try { + logger.error("Corrupted policy file, deleting: " + location); + Files.delete(Paths.get(location)); + properties.remove(id + ".file"); + rougeFile = true; + } catch (IOException e1) { + logger.error(e1); + } + } + } + if(location==null || rougeFile){ + if(rougeFile){ + rougeFile = false; + } + location = properties.getProperty(id + ".url"); + if (location != null) { + // + // Construct the URL + // + int errorCount=0; + boolean error= false; + do{ + error=false; + PapUrlResolver papUrls = PapUrlResolver.getInstance(); + while(papUrls.hasMoreUrls()){ + String papID = papUrls.getUserId(); + String papPass = papUrls.getPass(); + Base64.Encoder encoder = Base64.getEncoder(); + String encoding = encoder.encodeToString((papID+":"+papPass).getBytes(StandardCharsets.UTF_8)); + locationURI = URI.create(papUrls.getUrl(PapUrlResolver.extractIdFromUrl(location))); + URL url = locationURI.toURL(); + //FIXME: modify me + URLConnection urlConnection = null; + try{ + urlConnection = url.openConnection(); + } catch (IOException e){ + papUrls.failed(); + papUrls.getNext(); + break; + } + urlConnection.setRequestProperty(XACMLRestProperties.PROP_PDP_HTTP_HEADER_ID, + XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_ID)); + urlConnection.setRequestProperty("Authorization", "Basic " + encoding); + // + // Now construct the output file name + // + Path outFile = Paths.get(getPDPConfig().toAbsolutePath() + .toString(), id); + // + // Copy it to disk + // + try (FileOutputStream fos = new FileOutputStream( + outFile.toFile())) { + IOUtils.copy(urlConnection.getInputStream(), fos); + } catch(IOException e){ + papUrls.failed(); + papUrls.getNext(); + break; + } + // + // Now try to load + // + isFile = true; + try (InputStream fis = Files.newInputStream(outFile)) { + policy = DOMPolicyDef.load(fis); + }catch(Exception e){ + try { + logger.error("Corrupted policy file, deleting: " + location); + Files.delete(outFile); + error = true; + errorCount++; + break; + } catch (IOException e1) { + logger.error(e1); + } + } + // + // Save it + // + properties.setProperty(id + ".file", outFile + .toAbsolutePath().toString()); + error = false; + break; + } + }while(error && errorCount>2); + } + } + if (policy != null) { + status.addLoadedPolicy(new StdPDPPolicy(id, isRoot, + locationURI, properties)); + logger.info("Loaded policy: " + policy.getIdentifier() + + " version: " + policy.getVersion().stringValue()); + // Sending the policy objects to the Notification Controller. + policyContainer.put(id, policy); + } else { + String error = "Failed to load policy " + location; + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + error); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, error); + status.setStatus(PDPStatus.Status.LOAD_ERRORS); + status.addLoadError(error); + status.addFailedPolicy(new StdPDPPolicy(id, isRoot)); + } + } catch (Exception e) { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW +"Failed to load policy '" + id + "' from location '" + + location + "'", e); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e, "Failed to load policy '" + id + "' from location '" + // + location + "'"); + status.setStatus(PDPStatus.Status.LOAD_ERRORS); + status.addFailedPolicy(new StdPDPPolicy(id, isRoot)); + // + // Is it a file? + // + if (isFile) { + // + // Let's remove it + // + try { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Corrupted policy file, deleting: " + location); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, "Corrupted policy file, deleting: " + location); + Files.delete(Paths.get(location)); + + } catch (IOException e1) { + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + e1); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e1, ""); + } + } + //throw new PAPException("Failed to load policy '" + id + "' from location '" + location + "'"); + } + } + + public static synchronized void validatePipConfiguration( + Properties properties, StdPDPStatus status) throws PAPException { + try { + PIPFinderFactory factory = PIPFinderFactory.newInstance(properties); + if (factory == null) { + throw new FactoryException( + "Could not create PIP Finder Factory: " + + properties + .getProperty(XACMLProperties.PROP_PIPFINDERFACTORY)); + } + PIPFinder finder = factory.getFinder(properties); + // + // Check for this, although it should always return something + // + if (finder == null) { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "pip finder factory returned a null engine."); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, "pip finder factory returned a null engine."); + throw new PIPException("Could not create PIP Finder"); + } else { + logger.info("Loaded PIP finder"); + } + for (PIPEngine engine : finder.getPIPEngines()) { + logger.info("Configured PIP Engine: " + engine.getName()); + StdPDPPIPConfig config = new StdPDPPIPConfig(); + config.setName(engine.getName()); + status.addLoadedPipConfig(config); + } + } catch (FactoryException | PIPException e) { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "validate PIP configuration failed: " + + e.getLocalizedMessage()); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e.getLocalizedMessage(), "validate PIP configuration failed"); + status.addLoadError(e.getLocalizedMessage()); + status.setStatus(Status.LOAD_ERRORS); + throw new PAPException(e); + } + } + + /** + * Iterates the policies defined in the props object to ensure they are + * loaded locally. Policies are searched for in the following order: - see + * if the current properties has a "<PolicyID>.file" entry and that + * file exists in the local directory - if not, see if the file exists in + * the local directory; if so create a ".file" property for it. - if not, + * get the "<PolicyID>.url" property and try to GET the policy from + * that location (and set the ".file" property) + * + * If the ".file" property is created, then true is returned to tell the + * caller that the props object changed. + * + * @param props + * @return true/false if anything was changed in the props object + * @throws PAPException + */ + public static synchronized boolean cachePolicies(Properties props) + throws PAPException { + boolean changed = false; + String[] lists = new String[2]; + lists[0] = props.getProperty(XACMLProperties.PROP_ROOTPOLICIES); + lists[1] = props.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES); + for (String list : lists) { + // + // Check for a null or empty parameter + // + if (list == null || list.length() == 0) { + continue; + } + Iterable<String> policies = Splitter.on(',').trimResults() + .omitEmptyStrings().split(list); + for (String policy : policies) { + boolean policyExists = false; + + // First look for ".file" property and verify the file exists + String propLocation = props.getProperty(policy + + StdPolicyFinderFactory.PROP_FILE); + if (propLocation != null) { + // + // Does it exist? + // + policyExists = Files.exists(Paths.get(propLocation)); + if (policyExists == false) { + logger.warn(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Policy file " + policy + " expected at " + + propLocation + " does NOT exist."); + } + } + + // If ".file" property does not exist, try looking for the local + // file anyway + // (it might exist without having a ".file" property set for it) + if (policyExists == false) { + // + // Now construct the output file name + // + Path outFile = Paths.get(getPDPConfig().toAbsolutePath() + .toString(), policy); + // + // Double check to see if we pulled it at some point + // + policyExists = Files.exists(outFile); + if (policyExists) { + // + // Set the property so the PDP engine doesn't have + // to pull it from the URL but rather the FILE. + // + logger.info("Policy does exist: " + + outFile.toAbsolutePath().toString()); + props.setProperty(policy + + StdPolicyFinderFactory.PROP_FILE, outFile + .toAbsolutePath().toString()); + // + // Indicate that there were changes made to the + // properties + // + changed = true; + } else { + + // File does not exist locally, so we need to get it + // from the location given in the ".url" property (which + // MUST exist) + + // + // There better be a URL to retrieve it + // + propLocation = props.getProperty(policy + + StdPolicyFinderFactory.PROP_URL); + if (propLocation != null) { + // + // Get it + // + PapUrlResolver papUrls = PapUrlResolver.getInstance(); + while(papUrls.hasMoreUrls()){ + String papID = papUrls.getUserId(); + String papPass = papUrls.getPass(); + Base64.Encoder encoder = Base64.getEncoder(); + String encoding = encoder.encodeToString((papID+":"+papPass).getBytes(StandardCharsets.UTF_8)); + URL url = null; + try { + // + // Create the URL + // + url = new URL(papUrls.getUrl(PapUrlResolver.extractIdFromUrl(propLocation))); + logger.info("Pulling " + url.toString()); + // + // Open the connection + // + URLConnection urlConnection = url.openConnection(); + urlConnection.setRequestProperty(XACMLRestProperties.PROP_PDP_HTTP_HEADER_ID, + XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_ID)); + urlConnection.setRequestProperty("Authorization", "Basic " + encoding); + // + // Copy it to disk + // + try (InputStream is = urlConnection + .getInputStream(); + OutputStream os = new FileOutputStream( + outFile.toFile())) { + IOUtils.copy(is, os); + } + // + // Now save it in the properties as a .file + // + logger.info("Pulled policy: " + + outFile.toAbsolutePath().toString()); + props.setProperty(policy + + StdPolicyFinderFactory.PROP_FILE, + outFile.toAbsolutePath().toString()); + papUrls.succeeded(); + // + // Indicate that there were changes made to the + // properties + // + changed = true; + } catch (Exception e) { + papUrls.failed(); + if (e instanceof MalformedURLException) { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Policy '" + + policy + + "' had bad URL in new configuration, URL='" + + propLocation + "'"); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, "Policy '" + // + policy + // + "' had bad URL in new configuration, URL='" + // + propLocation + "'"); + + } else { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Error while retrieving policy " + + policy + + " from URL " + + url.toString() + ", e=" + e); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e, "Error while retrieving policy " + // + policy + // + " from URL " + // + url.toString()); + } + } + papUrls.getNext(); + } + } else { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Policy " + policy + + " does NOT exist and does NOT have a URL"); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, "Policy " + policy + // + " does NOT exist and does NOT have a URL"); + } + } + } + } + } + return changed; + } + + public static synchronized Path getPDPPolicyCache() throws PAPException { + Path config = getPDPConfig(); + Path policyProperties = Paths.get(config.toAbsolutePath().toString(), + "xacml.policy.properties"); + if (Files.notExists(policyProperties)) { + logger.warn(XACMLErrorConstants.ERROR_PROCESS_FLOW + policyProperties.toAbsolutePath().toString() + + " does NOT exist."); + // + // Try to create the file + // + try { + Files.createFile(policyProperties); + } catch (IOException e) { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to create policy properties file: " + + policyProperties.toAbsolutePath().toString()); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, "Failed to create policy properties file: " + // + policyProperties.toAbsolutePath().toString()); + throw new PAPException( + "Failed to create policy properties file: " + + policyProperties.toAbsolutePath().toString()); + } + } + return policyProperties; + } + + public static synchronized Path getPIPConfig() throws PAPException { + Path config = getPDPConfig(); + Path pipConfigProperties = Paths.get( + config.toAbsolutePath().toString(), "xacml.pip.properties"); + if (Files.notExists(pipConfigProperties)) { + logger.warn(XACMLErrorConstants.ERROR_PROCESS_FLOW + pipConfigProperties.toAbsolutePath().toString() + + " does NOT exist."); + // + // Try to create the file + // + try { + Files.createFile(pipConfigProperties); + } catch (IOException e) { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to create pip properties file: " + + pipConfigProperties.toAbsolutePath().toString()); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, "Failed to create pip properties file: " + //+ pipConfigProperties.toAbsolutePath().toString()); + throw new PAPException("Failed to create pip properties file: " + + pipConfigProperties.toAbsolutePath().toString()); + } + } + return pipConfigProperties; + } + + public static synchronized Path getPDPConfig() throws PAPException { + Path config = Paths.get(XACMLProperties + .getProperty(XACMLRestProperties.PROP_PDP_CONFIG)); + if (Files.notExists(config)) { + logger.warn(XACMLErrorConstants.ERROR_PROCESS_FLOW + config.toAbsolutePath().toString() + " does NOT exist."); + // + // Try to create the directory + // + try { + Files.createDirectories(config); + } catch (IOException e) { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to create config directory: " + + config.toAbsolutePath().toString(), e); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e, "Failed to create config directory: " + //+ config.toAbsolutePath().toString()); + throw new PAPException("Failed to create config directory: " + + config.toAbsolutePath().toString()); + } + } + return config; + } + +} diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/XACMLPdpRegisterThread.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/XACMLPdpRegisterThread.java new file mode 100644 index 000000000..83b9f3cf7 --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/XACMLPdpRegisterThread.java @@ -0,0 +1,333 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + + +package org.openecomp.policy.pdp.rest; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Base64; +import java.util.Properties; +import java.util.UUID; + +import org.apache.commons.io.IOUtils; +import org.openecomp.policy.pdp.rest.XACMLPdpServlet.PutRequest; +import org.openecomp.policy.rest.XACMLRestProperties; + +import org.openecomp.policy.xacml.api.XACMLErrorConstants; +import com.att.research.xacml.util.XACMLProperties; + +import org.openecomp.policy.common.logging.ECOMPLoggingContext; +import org.openecomp.policy.common.logging.eelf.PolicyLogger; +import org.openecomp.policy.common.logging.flexlogger.*; + +public class XACMLPdpRegisterThread implements Runnable { + private static final Logger logger = FlexLogger.getLogger(XACMLPdpRegisterThread.class); + private static final Logger auditLogger = FlexLogger.getLogger("auditLogger"); + private ECOMPLoggingContext baseLoggingContext = null; + + + + public volatile boolean isRunning = false; + + public XACMLPdpRegisterThread(ECOMPLoggingContext baseLoggingContext) { + this.baseLoggingContext = baseLoggingContext; + } + + public synchronized boolean isRunning() { + return this.isRunning; + } + + public synchronized void terminate() { + this.isRunning = false; + } + + /** + * + * This is our thread that runs on startup to tell the PAP server we are up-and-running. + * + */ + @Override + public void run() { + synchronized(this) { + this.isRunning = true; + } + // get a new logging context for the thread + ECOMPLoggingContext loggingContext = new ECOMPLoggingContext(baseLoggingContext); + loggingContext.setServiceName("PDP:PAP.register"); + //are we registered with at least one + boolean registered = false; + boolean interrupted = false; + /* + int seconds; + try { + seconds = Integer.parseInt(XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_REGISTER_SLEEP)); + } catch (NumberFormatException e) { + logger.error( XACMLErrorConstants.ERROR_SYSTEM_ERROR +"REGISTER_SLEEP: ", e); + seconds = 5; + } + if (seconds < 5) { + seconds = 5; + } + int retries; + try { + retries = Integer.parseInt(XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_REGISTER_RETRIES)); + } catch (NumberFormatException e) { + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR +" REGISTER_SLEEP: ", e); + retries = -1; + } + */ + PapUrlResolver papUrls = PapUrlResolver.getInstance(); + //while (! registered && ! interrupted && this.isRunning()) { + String tempRootPoliciesProperty = XACMLProperties.getProperty(XACMLProperties.PROP_ROOTPOLICIES); + String tempReferencedPoliciesProperty = XACMLProperties.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES); + Properties tempPipConfigProperties = new Properties(); + try(InputStream pipFile = Files.newInputStream(XACMLPdpLoader.getPIPConfig())){ + tempPipConfigProperties.load(pipFile); + } catch(Exception e){ + logger.error("Failed to open PIP property file", e); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.EXCEPTION_ERROR, e, "XACMLPdpRegisterThread", "Failed to open PIP property file"); + } + while(papUrls.hasMoreUrls()){ + String papID = papUrls.getUserId(); + String papPass = papUrls.getPass(); + Base64.Encoder encoder = Base64.getEncoder(); + String encoding = encoder.encodeToString((papID+":"+papPass).getBytes(StandardCharsets.UTF_8)); + HttpURLConnection connection = null; + try { + // get a new transaction (request) ID and update the logging context. + // each time through the outer loop is considered a new transaction. + // each time through the inner loop (which handles redirects) is a + // continuation of the same transaction. + UUID requestID = UUID.randomUUID(); + loggingContext.setRequestID(requestID.toString()); + //PolicyLogger.info("Request Id generated in XACMLPdpRegisterThread under XACML-PDP-REST"); + loggingContext.transactionStarted(); + // + // Get the list of PAP Servlet URLs from the property file + // + //String papUrlList = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_URL); + //String[] papUrls = papUrlList.split(","); + //PapUrlResolver.setPapUrls(papUrls); + URL url = new URL(papUrls.getUrl()); + logger.info("Registering with " + url.toString()); + //PolicyLogger.info("new transaction (request) ID and update to logging context in XACMLPdpRegisterThread"); + boolean finished = false; + while (! finished) { + // + // Open up the connection + // + connection = (HttpURLConnection)url.openConnection(); + // + // Setup our method and headers + // + connection.setRequestMethod("POST"); + connection.setRequestProperty("Authorization", "Basic " + encoding); + connection.setRequestProperty("Accept", "text/x-java-properties"); + connection.setRequestProperty("Content-Type", "text/x-java-properties"); + connection.setRequestProperty(XACMLRestProperties.PROP_PDP_HTTP_HEADER_ID, XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_ID)); + connection.setRequestProperty(XACMLRestProperties.PROP_PDP_HTTP_HEADER_JMX_PORT, XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_JMX_PORT)); + connection.setRequestProperty("X-ECOMP-RequestID", requestID.toString()); + 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. + // + connection.setInstanceFollowRedirects(false); + connection.setDoOutput(true); + connection.setDoInput(true); + try { + // + // Send our current policy configuration + // + String lists = XACMLProperties.PROP_ROOTPOLICIES + "=" + tempRootPoliciesProperty; + lists = lists + "\n" + XACMLProperties.PROP_REFERENCEDPOLICIES + "=" + tempReferencedPoliciesProperty + "\n"; + try (InputStream listsInputStream = new ByteArrayInputStream(lists.getBytes()); + OutputStream os = connection.getOutputStream()) { + IOUtils.copy(listsInputStream, os); + + // + // Send our current PIP configuration + // + //IOUtils.copy(pipInputStream, os); + tempPipConfigProperties.store(os, ""); + } + } catch (Exception e) { + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR +"Failed to send property file", e); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, "Failed to send property file"); + } + // + // Do the connect + // + connection.connect(); + if (connection.getResponseCode() == 204) { + logger.info("Success. We are configured correctly."); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.info("Success. We are configured correctly."); + loggingContext.transactionEnded(); + // auditLogger.info("Success. We are configured correctly."); + // TODO:EELF Cleanup - Remove logger + PolicyLogger.audit("Success. We are configured correctly."); + papUrls.registered(); + finished = true; + registered = true; + } else if (connection.getResponseCode() == 200) { + logger.info("Success. We have a new configuration."); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.info("Success. We have a new configuration."); + loggingContext.transactionEnded(); + // TODO:EELF Cleanup - Remove logger + //auditLogger.info("Success. We have a new configuration."); + PolicyLogger.audit("Success. We have a new configuration."); + papUrls.registered(); + Properties properties = new Properties(); + properties.load(connection.getInputStream()); + logger.info("New properties: " + properties.toString()); + // + // Queue it + // + // The incoming properties does NOT include urls + //FIXME: problem here is that we need the properties to be filled in BEFORE this thread continues and registers with another pap + Properties returnedPolicyProperties = XACMLProperties.getPolicyProperties(properties, false); + tempRootPoliciesProperty = new String(returnedPolicyProperties.getProperty(XACMLProperties.PROP_ROOTPOLICIES)); + tempReferencedPoliciesProperty = new String(returnedPolicyProperties.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES)); + Properties returnedPipProperties = XACMLProperties.getPipProperties(properties); + Properties threadSafeReturnedPipProperties = new Properties(); + ByteArrayOutputStream threadSafeReturnedPipPropertiesOs = new ByteArrayOutputStream(); + returnedPipProperties.store(threadSafeReturnedPipPropertiesOs, ""); + InputStream threadSafeReturnedPipPropertiesIs = new ByteArrayInputStream(threadSafeReturnedPipPropertiesOs.toByteArray()); + threadSafeReturnedPipProperties.load(threadSafeReturnedPipPropertiesIs); + tempPipConfigProperties = threadSafeReturnedPipProperties; + //FIXME: how will pipproperties respond to threading? + + PutRequest req = new PutRequest(returnedPolicyProperties,returnedPipProperties); + XACMLPdpServlet.queue.offer(req); + // + // We are now registered + // + finished = true; + registered=true; + } else if (connection.getResponseCode() >= 300 && connection.getResponseCode() <= 399) { + // + // Re-direction + // + String newLocation = connection.getHeaderField("Location"); + if (newLocation == null || newLocation.isEmpty()) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR +"Did not receive a valid re-direction location"); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.warn(MessageCodes.ERROR_SYSTEM_ERROR, "Did not receive a valid re-direction location"); + loggingContext.transactionEnded(); + auditLogger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR +"Did not receive a valid re-direction location"); + // TODO:EELF Cleanup - Remove logger + PolicyLogger.audit("Transaction Failed - See Error.log"); + finished = true; + } else { + //FIXME: how to handle this + logger.info("New Location: " + newLocation); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.info("New Location: " + newLocation); + url = new URL(newLocation); + } + } else { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Failed: " + connection.getResponseCode() + " message: " + connection.getResponseMessage()); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.warn(MessageCodes.ERROR_SYSTEM_ERROR, "Failed: " + connection.getResponseCode() + " message: " + connection.getResponseMessage()); + loggingContext.transactionEnded(); + auditLogger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Failed: " + connection.getResponseCode() + " message: " + connection.getResponseMessage()); + // TODO:EELF Cleanup - Remove logger + PolicyLogger.audit("Transaction Failed - See Error.log"); + finished = true; + papUrls.failed(); + } + } + } catch (Exception e) { + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + e); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, ""); + PolicyLogger.audit("Transaction Failed - See Error.log"); + loggingContext.transactionEnded(); + // TODO:EELF look at this error going to audit. decide what to do. + //auditLogger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + e); + // TODO:EELF Cleanup - Remove logger + papUrls.failed(); + } finally { + // cleanup the connection + if (connection != null) { + try { + // For some reason trying to get the inputStream from the connection + // throws an exception rather than returning null when the InputStream does not exist. + InputStream is = null; + try { + is = connection.getInputStream(); + } catch (Exception e1) { + // ignore this + } + if (is != null) { + is.close(); + } + + } catch (IOException ex) { + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Failed to close connection: " + ex, ex); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, ex, "Failed to close connection"); + } + connection.disconnect(); + } + } + // + // Wait a little while to try again + // + /* + try { + if (registered == false) { + if (retries > 0) { + retries--; + } else if (retries == 0) { + break; + } + Thread.sleep(seconds * 1000); + } + } catch (InterruptedException e) { + interrupted = true; + this.terminate(); + } + */ + //end of hasMoreUrls while loop + papUrls.getNext(); + } + synchronized(this) { + this.isRunning = false; + } + logger.info("Thread exiting...(registered=" + registered + ", interrupted=" + interrupted + ", isRunning=" + this.isRunning() + ", retries=" + "0" + ")"); + } + +} diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/XACMLPdpServlet.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/XACMLPdpServlet.java new file mode 100644 index 000000000..963fcd127 --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/XACMLPdpServlet.java @@ -0,0 +1,1138 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.pdp.rest; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.file.Files; +import java.util.Properties; +import java.util.UUID; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import javax.servlet.Servlet; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebInitParam; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.entity.ContentType; +import org.openecomp.policy.pdp.rest.jmx.PdpRestMonitor; +import org.openecomp.policy.rest.XACMLRest; +import org.openecomp.policy.rest.XACMLRestProperties; +import org.openecomp.policy.common.logging.ECOMPLoggingContext; +import org.openecomp.policy.common.logging.ECOMPLoggingUtils; +import org.openecomp.policy.common.logging.eelf.MessageCodes; +import org.openecomp.policy.common.logging.eelf.PolicyLogger; +import org.openecomp.policy.common.im.AdministrativeStateException; +import org.openecomp.policy.common.im.ForwardProgressException; +import org.openecomp.policy.common.im.IntegrityMonitor; +import org.openecomp.policy.common.im.IntegrityMonitorProperties; +import org.openecomp.policy.common.im.StandbyStatusException; + +import com.att.research.xacml.api.Request; +import com.att.research.xacml.api.Response; +import org.openecomp.policy.xacml.api.XACMLErrorConstants; +import com.att.research.xacml.api.pap.PDPStatus.Status; +import com.att.research.xacml.api.pdp.PDPEngine; +import com.att.research.xacml.api.pdp.PDPException; +import com.att.research.xacml.std.dom.DOMRequest; +import com.att.research.xacml.std.dom.DOMResponse; +import com.att.research.xacml.std.json.JSONRequest; +import com.att.research.xacml.std.json.JSONResponse; +import com.att.research.xacml.util.XACMLProperties; +import org.openecomp.policy.xacml.pdp.std.functions.PolicyList; +import org.openecomp.policy.xacml.std.pap.StdPDPStatus; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Servlet implementation class XacmlPdpServlet + * + * This is an implementation of the XACML 3.0 RESTful Interface with added features to support + * simple PAP RESTful API for policy publishing and PIP configuration changes. + * + * If you are running this the first time, then we recommend you look at the xacml.pdp.properties file. + * This properties file has all the default parameter settings. If you are running the servlet as is, + * then we recommend setting up you're container to run it on port 8080 with context "/pdp". Wherever + * the default working directory is set to, a "config" directory will be created that holds the policy + * and pip cache. This setting is located in the xacml.pdp.properties file. + * + * When you are ready to customize, you can create a separate xacml.pdp.properties on you're local file + * system and setup the parameters as you wish. Just set the Java VM System variable to point to that file: + * + * -Dxacml.properties=/opt/app/xacml/etc/xacml.pdp.properties + * + * Or if you only want to change one or two properties, simply set the Java VM System variable for that property. + * + * -Dxacml.rest.pdp.register=false + * + * + */ +@WebServlet( + description = "Implements the XACML PDP RESTful API and client PAP API.", + urlPatterns = { "/" }, + loadOnStartup=1, + initParams = { + @WebInitParam(name = "XACML_PROPERTIES_NAME", value = "xacml.pdp.properties", description = "The location of the PDP xacml.pdp.properties file holding configuration information.") + }) +public class XACMLPdpServlet extends HttpServlet implements Runnable { + private static final long serialVersionUID = 1L; + private static final String DEFAULT_MAX_CONTENT_LENGTH = "999999999"; //32767 + // + // Our application debug log + // + private static final Log logger = LogFactory.getLog(XACMLPdpServlet.class); + // + // This logger is specifically only for Xacml requests and their corresponding response. + // It's output ideally should be sent to a separate file from the application logger. + // + private static final Log requestLogger = LogFactory.getLog("xacml.request"); + // + // audit logger + private static final Log auditLogger = LogFactory.getLog("auditLogger"); + + private static final PdpRestMonitor monitor = PdpRestMonitor.singleton; + + // + // This thread may getting invoked on startup, to let the PAP know + // that we are up and running. + // + private Thread registerThread = null; + private XACMLPdpRegisterThread registerRunnable = null; + // + // This is our PDP engine pointer. There is a synchronized lock used + // for access to the pointer. In case we are servicing PEP requests while + // an update is occurring from the PAP. + // + private PDPEngine pdpEngine = null; + private static final Object pdpEngineLock = new Object(); + // + // This is our PDP's status. What policies are loaded (or not) and + // what PIP configurations are loaded (or not). + // There is a synchronized lock used for access to the object. + // + private static volatile StdPDPStatus status = new StdPDPStatus(); + private static final Object pdpStatusLock = new Object(); + + private static final String ENVIORNMENT_HEADER = "Environment"; + private static String environment = null; + // + // Queue of PUT requests + // + public static class PutRequest { + public Properties policyProperties = null; + public Properties pipConfigProperties = null; + + PutRequest(Properties policies, Properties pips) { + this.policyProperties = policies; + this.pipConfigProperties = pips; + } + } + public static volatile BlockingQueue<PutRequest> queue = null; + // For notification Delay. + private static int notificationDelay = 0; + public static int getNotificationDelay(){ + return XACMLPdpServlet.notificationDelay; + } + + private static String pdpResourceName; + private static String dependencyGroups = null; + private static String[] dependencyNodes = null; + + // + // This is our configuration thread that attempts to load + // a new configuration request. + // + private Thread configThread = null; + private volatile boolean configThreadTerminate = false; + private ECOMPLoggingContext baseLoggingContext = null; + private IntegrityMonitor im; + /** + * Default constructor. + */ + public XACMLPdpServlet() { + } + + /** + * @see Servlet#init(ServletConfig) + */ + public void init(ServletConfig config) throws ServletException { + // + // Initialize + // + XACMLRest.xacmlInit(config); + // Load the Notification Delay. + try{ + XACMLPdpServlet.notificationDelay = Integer.parseInt(XACMLProperties.getProperty(XACMLRestProperties.PROP_NOTIFICATION_DELAY)); + }catch(Exception e){ + logger.info("Notification Delay Not set. Keeping it 0 as default."); + } + // Load Queue size. + int queueSize = 5; // Set default Queue Size here. + queueSize = Integer.parseInt(XACMLProperties.getProperty("REQUEST_BUFFER_SIZE",String.valueOf(queueSize))); + queue = new LinkedBlockingQueue<PutRequest>(queueSize); + // Load our engine - this will use the latest configuration + // that was saved to disk and set our initial status object. + // + PDPEngine engine = XACMLPdpLoader.loadEngine(XACMLPdpServlet.status, null, null); + if (engine != null) { + synchronized(pdpEngineLock) { + pdpEngine = engine; + } + } + // + // Logging stuff.... + // + baseLoggingContext = new ECOMPLoggingContext(); + // fixed data that will be the same in all logging output goes here + try { + String hostname = InetAddress.getLocalHost().getCanonicalHostName(); + baseLoggingContext.setServer(hostname); + } catch (UnknownHostException e) { + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Unable to get hostname for logging"); + } + + Properties properties; + try { + properties = XACMLProperties.getProperties(); + } catch (IOException e) { + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, + "Error loading properties with: XACMLProperties.getProperties()"); + throw new ServletException(e.getMessage(), e.getCause()); + } + + pdpResourceName = properties.getProperty(XACMLRestProperties.PDP_RESOURCE_NAME); + if(pdpResourceName == null){ + PolicyLogger.error(MessageCodes.MISS_PROPERTY_ERROR, XACMLRestProperties.PDP_RESOURCE_NAME, "xacml.pdp"); + throw new ServletException("pdpResourceName is null"); + } + + dependencyGroups = properties.getProperty(IntegrityMonitorProperties.DEPENDENCY_GROUPS); + if(dependencyGroups == null){ + PolicyLogger.error(MessageCodes.MISS_PROPERTY_ERROR, IntegrityMonitorProperties.DEPENDENCY_GROUPS, "xacml.pdp"); + throw new ServletException("dependency_groups is null"); + } + // dependency_groups is a semicolon-delimited list of groups, and + // each group is a comma-separated list of nodes. For our purposes + // we just need a list of dependencies without regard to grouping, + // so split the list into nodes separated by either comma or semicolon. + dependencyNodes = dependencyGroups.split("[;,]"); + for (int i = 0 ; i < dependencyNodes.length ; i++){ + dependencyNodes[i] = dependencyNodes[i].trim(); + } + + + // Create an IntegrityMonitor + try { + logger.info("Creating IntegrityMonitor"); + im = IntegrityMonitor.getInstance(pdpResourceName, properties); + } catch (Exception e) { + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, "Failed to create IntegrityMonitor"); + throw new ServletException(e); + } + + environment = XACMLProperties.getProperty("ENVIRONMENT", "DEVL"); + // + // Kick off our thread to register with the PAP servlet. + // + if (Boolean.parseBoolean(XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_REGISTER))) { + this.registerRunnable = new XACMLPdpRegisterThread(baseLoggingContext); + this.registerThread = new Thread(this.registerRunnable); + this.registerThread.start(); + } + // + // This is our thread that manages incoming configuration + // changes. + // + this.configThread = new Thread(this); + this.configThread.start(); + } + + /** + * @see Servlet#destroy() + */ + public void destroy() { + super.destroy(); + logger.info("Destroying...."); + // + // Make sure the register thread is not running + // + if (this.registerRunnable != null) { + try { + this.registerRunnable.terminate(); + if (this.registerThread != null) { + this.registerThread.interrupt(); + this.registerThread.join(); + } + } catch (InterruptedException e) { + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + e); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, ""); + } + } + // + // Make sure the configure thread is not running + // + this.configThreadTerminate = true; + try { + this.configThread.interrupt(); + this.configThread.join(); + } catch (InterruptedException e) { + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + e); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, ""); + } + logger.info("Destroyed."); + } + + /** + * PUT - The PAP engine sends configuration information using HTTP PUT request. + * + * One parameter is expected: + * + * config=[policy|pip|all] + * + * policy - Expect a properties file that contains updated lists of the root and referenced policies that the PDP should + * be using for PEP requests. + * + * Specifically should AT LEAST contain the following properties: + * xacml.rootPolicies + * xacml.referencedPolicies + * + * In addition, any relevant information needed by the PDP to load or retrieve the policies to store in its cache. + * + * EXAMPLE: + * xacml.rootPolicies=PolicyA.1, PolicyB.1 + * + * PolicyA.1.url=http://localhost:9090/PAP?id=b2d7b86d-d8f1-4adf-ba9d-b68b2a90bee1&version=1 + * PolicyB.1.url=http://localhost:9090/PAP/id=be962404-27f6-41d8-9521-5acb7f0238be&version=1 + * + * xacml.referencedPolicies=RefPolicyC.1, RefPolicyD.1 + * + * RefPolicyC.1.url=http://localhost:9090/PAP?id=foobar&version=1 + * RefPolicyD.1.url=http://localhost:9090/PAP/id=example&version=1 + * + * pip - Expect a properties file that contain PIP engine configuration properties. + * + * Specifically should AT LEAST the following property: + * xacml.pip.engines + * + * In addition, any relevant information needed by the PDP to load and configure the PIPs. + * + * EXAMPLE: + * xacml.pip.engines=foo,bar + * + * foo.classname=com.foo + * foo.sample=abc + * foo.example=xyz + * ...... + * + * bar.classname=com.bar + * ...... + * + * all - Expect ALL new configuration properties for the PDP + * + * @see HttpServlet#doPut(HttpServletRequest request, HttpServletResponse response) + */ + protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + ECOMPLoggingContext loggingContext = ECOMPLoggingUtils.getLoggingContextForRequest(request, baseLoggingContext); + loggingContext.transactionStarted(); + if ((loggingContext.getRequestID() == null) || (loggingContext.getRequestID() == "")){ + UUID requestID = UUID.randomUUID(); + loggingContext.setRequestID(requestID.toString()); + PolicyLogger.info("requestID not provided in call to XACMLPdpSrvlet (doPut) so we generated one"); + } else { + PolicyLogger.info("requestID was provided in call to XACMLPdpSrvlet (doPut)"); + } + loggingContext.metricStarted(); + loggingContext.metricEnded(); + PolicyLogger.metrics("Metric example posted here - 1 of 2"); + loggingContext.metricStarted(); + loggingContext.metricEnded(); + PolicyLogger.metrics("Metric example posted here - 2 of 2"); + // + // Dump our request out + // + if (logger.isDebugEnabled()) { + XACMLRest.dumpRequest(request); + } + + try { + im.startTransaction(); + } + catch (AdministrativeStateException | StandbyStatusException e) { + String message = e.toString(); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message); + return; + } + // + // What is being PUT? + // + String cache = request.getParameter("cache"); + // + // Should be a list of policy and pip configurations in Java properties format + // + if (cache != null && request.getContentType().equals("text/x-java-properties")) { + loggingContext.setServiceName("PDP.putConfig"); + if (request.getContentLength() > Integer.parseInt(XACMLProperties.getProperty("MAX_CONTENT_LENGTH", DEFAULT_MAX_CONTENT_LENGTH))) { + String message = "Content-Length larger than server will accept."; + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + message); + loggingContext.transactionEnded(); + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, message); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); + im.endTransaction(); + return; + } + this.doPutConfig(cache, request, response, loggingContext); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction ended"); + + im.endTransaction(); + } else { + String message = "Invalid cache: '" + cache + "' or content-type: '" + request.getContentType() + "'"; + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + message); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); + im.endTransaction(); + return; + } + } + + protected void doPutConfig(String config, HttpServletRequest request, HttpServletResponse response, ECOMPLoggingContext loggingContext) throws ServletException, IOException { + try { + // prevent multiple configuration changes from stacking up + if (XACMLPdpServlet.queue.remainingCapacity() <= 0) { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Queue capacity reached"); + PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, "Queue capacity reached"); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_CONFLICT, "Multiple configuration changes waiting processing."); + return; + } + // + // Read the properties data into an object. + // + Properties newProperties = new Properties(); + newProperties.load(request.getInputStream()); + // should have something in the request + if (newProperties.size() == 0) { + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "No properties in PUT"); + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "No properties in PUT"); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "PUT must contain at least one property"); + return; + } + // + // Which set of properties are they sending us? Whatever they send gets + // put on the queue (if there is room). + // For audit logging purposes, we consider the transaction done once the + // the request gets put on the queue. + // + if (config.equals("policies")) { + newProperties = XACMLProperties.getPolicyProperties(newProperties, true); + if (newProperties.size() == 0) { + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "No policy properties in PUT"); + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "No policy properties in PUT"); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "PUT with cache=policies must contain at least one policy property"); + return; + } + XACMLPdpServlet.queue.offer(new PutRequest(newProperties, null)); + loggingContext.transactionEnded(); + auditLogger.info("Success"); + PolicyLogger.audit("Success"); + } else if (config.equals("pips")) { + newProperties = XACMLProperties.getPipProperties(newProperties); + if (newProperties.size() == 0) { + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "No pips properties in PUT"); + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "No pips properties in PUT"); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "PUT with cache=pips must contain at least one pip property"); + return; + } + XACMLPdpServlet.queue.offer(new PutRequest(null, newProperties)); + loggingContext.transactionEnded(); + auditLogger.info("Success"); + PolicyLogger.audit("Success"); + } else if (config.equals("all")) { + Properties newPolicyProperties = XACMLProperties.getPolicyProperties(newProperties, true); + if (newPolicyProperties.size() == 0) { + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "No policy properties in PUT"); + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "No policy properties in PUT"); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "PUT with cache=all must contain at least one policy property"); + return; + } + Properties newPipProperties = XACMLProperties.getPipProperties(newProperties); + if (newPipProperties.size() == 0) { + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "No pips properties in PUT"); + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "No pips properties in PUT"); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "PUT with cache=all must contain at least one pip property"); + return; + } + XACMLPdpServlet.queue.offer(new PutRequest(newPolicyProperties, newPipProperties)); + loggingContext.transactionEnded(); + auditLogger.info("Success"); + PolicyLogger.audit("Success"); + } else { + // + // Invalid value + // + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Invalid config value: " + config); + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "Invalid config value: " + config); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Config must be one of 'policies', 'pips', 'all'"); + return; + } + } catch (Exception e) { + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Failed to process new configuration.", e); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, "Failed to process new configuration"); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return; + } + + } + + /** + * Parameters: type=hb|config|Status + * + * 1. HeartBeat Status + * HeartBeat + * OK - All Policies are Loaded, All PIPs are Loaded + * LOADING_IN_PROGRESS - Currently loading a new policy set/pip configuration + * LAST_UPDATE_FAILED - Need to track the items that failed during last update + * LOAD_FAILURE - ??? Need to determine what information is sent and how + * 2. Configuration + * 3. Status + * return the StdPDPStatus object in the Response content + * + * + * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) + */ + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + ECOMPLoggingContext loggingContext = ECOMPLoggingUtils.getLoggingContextForRequest(request, baseLoggingContext); + loggingContext.transactionStarted(); + if ((loggingContext.getRequestID() == null) || (loggingContext.getRequestID() == "")){ + UUID requestID = UUID.randomUUID(); + loggingContext.setRequestID(requestID.toString()); + PolicyLogger.info("requestID not provided in call to XACMLPdpSrvlet (doGet) so we generated one"); + } else { + PolicyLogger.info("requestID was provided in call to XACMLPdpSrvlet (doGet)"); + } + loggingContext.metricStarted(); + loggingContext.metricEnded(); + PolicyLogger.metrics("Metric example posted here - 1 of 2"); + loggingContext.metricStarted(); + loggingContext.metricEnded(); + PolicyLogger.metrics("Metric example posted here - 2 of 2"); + + XACMLRest.dumpRequest(request); + + String pathInfo = request.getRequestURI(); + if (pathInfo != null){ + // health check from Global Site Selector (iDNS). + // DO NOT do a im.startTransaction for the test request + if (pathInfo.equals("/pdp/test")) { + loggingContext.setServiceName("iDNS:PDP.test"); + try { + im.evaluateSanity(); + //If we make it this far, all is well + String message = "GET:/pdp/test called and PDP " + pdpResourceName + " is OK"; + PolicyLogger.debug(message); + loggingContext.transactionEnded(); + PolicyLogger.audit("Success"); + response.setStatus(HttpServletResponse.SC_OK); + return; + } catch (ForwardProgressException fpe){ + //No forward progress is being made + String message = "GET:/pdp/test called and PDP " + pdpResourceName + " is not making forward progress." + + " Exception Message: " + fpe.getMessage(); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message ); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + // PolicyLogger.audit(MessageCodes.ERROR_SYSTEM_ERROR, message ); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message); + return; + }catch (AdministrativeStateException ase){ + //Administrative State is locked + String message = "GET:/pdp/test called and PDP " + pdpResourceName + " Administrative State is LOCKED " + + " Exception Message: " + ase.getMessage(); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message ); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message); + return; + }catch (StandbyStatusException sse){ + //Administrative State is locked + String message = "GET:/pdp/test called and PDP " + pdpResourceName + " Standby Status is NOT PROVIDING SERVICE " + + " Exception Message: " + sse.getMessage(); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message ); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message); + return; + } catch (Exception e) { + //A subsystem is not making progress or is not responding + String eMsg = e.getMessage(); + if(eMsg == null){ + eMsg = "No Exception Message"; + } + String message = "GET:/pdp/test called and PDP " + pdpResourceName + " has had a subsystem failure." + + " Exception Message: " + eMsg; + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message ); + //Get the specific list of subsystems that failed + String failedNodeList = null; + for(String node : dependencyNodes){ + if(eMsg.contains(node)){ + if(failedNodeList == null){ + failedNodeList = node; + }else{ + failedNodeList = failedNodeList.concat(","+node); + } + } + } + if(failedNodeList == null){ + failedNodeList = "UnknownSubSystem"; + } + response.addHeader("X-ECOMP-SubsystemFailure", failedNodeList); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + return; + } + } + } + + try { + im.startTransaction(); + } + catch (AdministrativeStateException | StandbyStatusException e) { + String message = e.toString(); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message); + return; + } + // + // What are they requesting? + // + boolean returnHB = false; + response.setHeader("Cache-Control", "no-cache"); + String type = request.getParameter("type"); + // type might be null, so use equals on string constants + if ("config".equals(type)) { + loggingContext.setServiceName("PDP.getConfig"); + response.setContentType("text/x-java-properties"); + try { + String lists = XACMLProperties.PROP_ROOTPOLICIES + "=" + XACMLProperties.getProperty(XACMLProperties.PROP_ROOTPOLICIES, ""); + lists = lists + "\n" + XACMLProperties.PROP_REFERENCEDPOLICIES + "=" + XACMLProperties.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES, "") + "\n"; + try (InputStream listInputStream = new ByteArrayInputStream(lists.getBytes()); + InputStream pipInputStream = Files.newInputStream(XACMLPdpLoader.getPIPConfig()); + OutputStream os = response.getOutputStream()) { + IOUtils.copy(listInputStream, os); + IOUtils.copy(pipInputStream, os); + } + loggingContext.transactionEnded(); + auditLogger.info("Success"); + PolicyLogger.audit("Success"); + response.setStatus(HttpServletResponse.SC_OK); + } catch (Exception e) { + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Failed to copy property file", e); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, "Failed to copy property file"); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(400, "Failed to copy Property file"); + } + + } else if ("hb".equals(type)) { + returnHB = true; + response.setStatus(HttpServletResponse.SC_NO_CONTENT); + + } else if ("Status".equals(type)) { + loggingContext.setServiceName("PDP.getStatus"); + // convert response object to JSON and include in the response + synchronized(pdpStatusLock) { + ObjectMapper mapper = new ObjectMapper(); + mapper.writeValue(response.getOutputStream(), status); + } + response.setStatus(HttpServletResponse.SC_OK); + loggingContext.transactionEnded(); + auditLogger.info("Success"); + PolicyLogger.audit("Success"); + + } else { + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Invalid type value: " + type); + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "Invalid type value: " + type); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "type not 'config' or 'hb'"); + } + if (returnHB) { + synchronized(pdpStatusLock) { + response.addHeader(XACMLRestProperties.PROP_PDP_HTTP_HEADER_HB, status.getStatus().toString()); + } + } + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Ended"); + im.endTransaction(); + + } + + /** + * POST - We expect XACML requests to be posted by PEP applications. They can be in the form of XML or JSON according + * to the XACML 3.0 Specifications for both. + * + * + * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) + */ + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + ECOMPLoggingContext loggingContext = ECOMPLoggingUtils.getLoggingContextForRequest(request, baseLoggingContext); + loggingContext.transactionStarted(); + loggingContext.setServiceName("PDP.decide"); + if ((loggingContext.getRequestID() == null) || (loggingContext.getRequestID() == "")){ + UUID requestID = UUID.randomUUID(); + loggingContext.setRequestID(requestID.toString()); + PolicyLogger.info("requestID not provided in call to XACMLPdpSrvlet (doPost) so we generated one"); + } else { + PolicyLogger.info("requestID was provided in call to XACMLPdpSrvlet (doPost)"); + } + loggingContext.metricStarted(); + loggingContext.metricEnded(); + PolicyLogger.metrics("Metric example posted here - 1 of 2"); + loggingContext.metricStarted(); + loggingContext.metricEnded(); + PolicyLogger.metrics("Metric example posted here - 2 of 2"); + monitor.pdpEvaluationAttempts(); + + try { + im.startTransaction(); + } + catch (AdministrativeStateException | StandbyStatusException e) { + String message = e.toString(); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message); + return; + } + // + // no point in doing any work if we know from the get-go that we cannot do anything with the request + // + if (status.getLoadedRootPolicies().size() == 0) { + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Request from PEP at " + request.getRequestURI() + " for service when PDP has No Root Policies loaded"); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, "Request from PEP at " + request.getRequestURI() + " for service when PDP has No Root Policies loaded"); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + im.endTransaction(); + return; + } + + XACMLRest.dumpRequest(request); + // + // Set our no-cache header + // + response.setHeader("Cache-Control", "no-cache"); + // + // They must send a Content-Type + // + if (request.getContentType() == null) { + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Must specify a Content-Type"); + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, "Must specify a Content-Type"); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "no content-type given"); + im.endTransaction(); + return; + } + // + // Limit the Content-Length to something reasonable + // + if (request.getContentLength() > Integer.parseInt(XACMLProperties.getProperty("MAX_CONTENT_LENGTH", "32767"))) { + String message = "Content-Length larger than server will accept."; + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + message); + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, message); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); + im.endTransaction(); + return; + } + if (request.getContentLength() <= 0) { + String message = "Content-Length is negative"; + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + message); + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, message); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); + im.endTransaction(); + return; + } + ContentType contentType = null; + try { + contentType = ContentType.parse(request.getContentType()); + } + catch (Exception e) { + String message = "Parsing Content-Type: " + request.getContentType() + ", error=" + e.getMessage(); + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + message, e); + loggingContext.transactionEnded(); + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, message); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); + im.endTransaction(); + return; + } + // + // What exactly did they send us? + // + String incomingRequestString = null; + Request pdpRequest = null; + if (contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_JSON.getMimeType()) || + contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_XML.getMimeType()) || + contentType.getMimeType().equalsIgnoreCase("application/xacml+xml") ) { + // + // Read in the string + // + StringBuilder buffer = new StringBuilder(); + BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream())); + String line; + while((line = reader.readLine()) != null){ + buffer.append(line); + } + incomingRequestString = buffer.toString(); + logger.info(incomingRequestString); + // + // Parse into a request + // + try { + if (contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_JSON.getMimeType())) { + pdpRequest = JSONRequest.load(incomingRequestString); + } else if ( contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_XML.getMimeType()) || + contentType.getMimeType().equalsIgnoreCase("application/xacml+xml")) { + pdpRequest = DOMRequest.load(incomingRequestString); + } + } + catch(Exception e) { + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Could not parse request", e); + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "Could not parse request"); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); + im.endTransaction(); + return; + } + } else { + String message = "unsupported content type" + request.getContentType(); + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + message); + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, message); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); + im.endTransaction(); + return; + } + // + // Did we successfully get and parse a request? + // + if (pdpRequest == null || pdpRequest.getRequestAttributes() == null || pdpRequest.getRequestAttributes().size() <= 0) { + String message = "Zero Attributes found in the request"; + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + message); + PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, message); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); + im.endTransaction(); + return; + } + // + // Run it + // + try { + // + // Authenticating the Request here. + // + if(!authorizeRequest(request, pdpRequest)){ + String message = "PEP not Authorized for making this Request!! \n Contact Administrator for this Scope. "; + logger.error(XACMLErrorConstants.ERROR_PERMISSIONS + message ); + PolicyLogger.error(MessageCodes.ERROR_PERMISSIONS, message); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_FORBIDDEN, message); + im.endTransaction(); + return; + } + // + // Get the pointer to the PDP Engine + // + PDPEngine myEngine = null; + synchronized(pdpEngineLock) { + myEngine = this.pdpEngine; + } + if (myEngine == null) { + String message = "No engine loaded."; + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + message); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message); + im.endTransaction(); + return; + } + // + // Send the request and save the response + // + long lTimeStart, lTimeEnd; + Response pdpResponse = null; + +//TODO - Make this unnecessary +//TODO It seems that the PDP Engine is not thread-safe, so when a configuration change occurs in the middle of processing +//TODO a PEP Request, that Request fails (it throws a NullPointerException in the decide() method). +//TODO Using synchronize will slow down processing of PEP requests, possibly by a significant amount. +//TODO Since configuration changes are rare, it would be A Very Good Thing if we could eliminate this sychronized block. +//TODO +//TODO This problem was found by starting one PDP then +//TODO RestLoadTest switching between 2 configurations, 1 second apart +//TODO both configurations contain the datarouter policy +//TODO both configurations already have all policies cached in the PDPs config directory +//TODO RestLoadTest started with the Datarouter test requests, 5 threads, no interval +//TODO With that configuration this code (without the synchronized) throws a NullPointerException +//TODO within a few seconds. +// +synchronized(pdpEngineLock) { + myEngine = this.pdpEngine; + try { + PolicyList.clearPolicyList(); + lTimeStart = System.currentTimeMillis(); + pdpResponse = myEngine.decide(pdpRequest); + lTimeEnd = System.currentTimeMillis(); + } catch (PDPException e) { + String message = "Exception during decide: " + e.getMessage(); + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + message); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, message); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message); + im.endTransaction(); + return; + } +} + monitor.computeLatency(lTimeEnd - lTimeStart); + requestLogger.info(lTimeStart + "=" + incomingRequestString); + for(String policy : PolicyList.getpolicyList()){ + monitor.policyCountAdd(policy, 1); + } + + + logger.info("PolicyID triggered in Request: " + PolicyList.getpolicyList()); + + //need to go through the list and find out if the value is unique and then add it other wise +// monitor.policyCountAdd(PolicyList.getpolicyList(), 1); + + if (logger.isDebugEnabled()) { + logger.debug("Request time: " + (lTimeEnd - lTimeStart) + "ms"); + } + // + // Convert Response to appropriate Content-Type + // + if (pdpResponse == null) { + requestLogger.info(lTimeStart + "=" + "{}"); + throw new Exception("Failed to get response from PDP engine."); + } + // + // Set our content-type + // + response.setContentType(contentType.getMimeType()); + // + // Convert the PDP response object to a String to + // return to our caller as well as dump to our loggers. + // + String outgoingResponseString = ""; + if (contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_JSON.getMimeType())) { + // + // Get it as a String. This is not very efficient but we need to log our + // results for auditing. + // + outgoingResponseString = JSONResponse.toString(pdpResponse, logger.isDebugEnabled()); + if (logger.isDebugEnabled()) { + logger.debug(outgoingResponseString); + // + // Get rid of whitespace + // + outgoingResponseString = JSONResponse.toString(pdpResponse, false); + } + } else if ( contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_XML.getMimeType()) || + contentType.getMimeType().equalsIgnoreCase("application/xacml+xml")) { + // + // Get it as a String. This is not very efficient but we need to log our + // results for auditing. + // + outgoingResponseString = DOMResponse.toString(pdpResponse, logger.isDebugEnabled()); + if (logger.isDebugEnabled()) { + logger.debug(outgoingResponseString); + // + // Get rid of whitespace + // + outgoingResponseString = DOMResponse.toString(pdpResponse, false); + } + } + // adding the jmx values for NA, Permit and Deny + // + if (outgoingResponseString.contains("NotApplicable") || outgoingResponseString.contains("Decision not a Permit")){ + monitor.pdpEvaluationNA(); + } + + if (outgoingResponseString.contains("Permit") && !outgoingResponseString.contains("Decision not a Permit")){ + monitor.pdpEvaluationPermit(); + } + + if (outgoingResponseString.contains("Deny")){ + monitor.pdpEvaluationDeny(); + } + // + // lTimeStart is used as an ID within the requestLogger to match up + // request's with responses. + // + requestLogger.info(lTimeStart + "=" + outgoingResponseString); + response.getWriter().print(outgoingResponseString); + } + catch (Exception e) { + String message = "Exception executing request: " + e; + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + message, e); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, message); + loggingContext.transactionEnded(); + PolicyLogger.audit("Transaction Failed - See Error.log"); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message); + return; + } + + monitor.pdpEvaluationSuccess(); + response.setStatus(HttpServletResponse.SC_OK); + + loggingContext.transactionEnded(); + auditLogger.info("Success"); + PolicyLogger.audit("Success"); + +} + + /* + * Added for Authorizing the PEP Requests for Environment check. + */ + private boolean authorizeRequest(HttpServletRequest request, Request pepRequest) { + if(request instanceof HttpServletRequest) { + // Get the client Credentials from the Request header. + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + String clientCredentials = httpServletRequest.getHeader(ENVIORNMENT_HEADER); + if(clientCredentials!=null && clientCredentials.equalsIgnoreCase(environment)){ + return true; + }else{ + return false; + } + }else{ + return false; + } + } + + @Override + public void run() { + // + // Keep running until we are told to terminate + // + try { + // variable not used, but constructor has needed side-effects so don't remove: + ECOMPLoggingContext loggingContext = new ECOMPLoggingContext(baseLoggingContext); + while (! this.configThreadTerminate) { + PutRequest request = XACMLPdpServlet.queue.take(); + StdPDPStatus newStatus = new StdPDPStatus(); + +//TODO - This is related to the problem discussed in the doPost() method about the PDPEngine not being thread-safe. +//TODO See that discussion, and when the PDPEngine is made thread-safe it should be ok to move the loadEngine out of +//TODO the synchronized block. +//TODO However, since configuration changes should be rare we may not care about changing this. +PDPEngine newEngine = null; + synchronized(pdpStatusLock) { + XACMLPdpServlet.status.setStatus(Status.UPDATING_CONFIGURATION); +newEngine = XACMLPdpLoader.loadEngine(newStatus, request.policyProperties, request.pipConfigProperties); + } +// PDPEngine newEngine = XACMLPdpLoader.loadEngine(newStatus, request.policyProperties, request.pipConfigProperties); + if (newEngine != null) { + synchronized(XACMLPdpServlet.pdpEngineLock) { + this.pdpEngine = newEngine; + try { + logger.info("Saving configuration."); + if (request.policyProperties != null) { + try (OutputStream os = Files.newOutputStream(XACMLPdpLoader.getPDPPolicyCache())) { + request.policyProperties.store(os, ""); + } + } + if (request.pipConfigProperties != null) { + try (OutputStream os = Files.newOutputStream(XACMLPdpLoader.getPIPConfig())) { + request.pipConfigProperties.store(os, ""); + } + } + newStatus.setStatus(Status.UP_TO_DATE); + } catch (Exception e) { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to store new properties."); + PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, "Failed to store new properties"); + newStatus.setStatus(Status.LOAD_ERRORS); + newStatus.addLoadWarning("Unable to save configuration: " + e.getMessage()); + } + } + } else { + newStatus.setStatus(Status.LAST_UPDATE_FAILED); + } + synchronized(pdpStatusLock) { + XACMLPdpServlet.status.set(newStatus); + } + } + } catch (InterruptedException e) { + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "interrupted"); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, "interrupted"); + } + } + +} diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/impl/XACMLPdpPIPFinderFactory.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/impl/XACMLPdpPIPFinderFactory.java new file mode 100644 index 000000000..3c8b67f31 --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/impl/XACMLPdpPIPFinderFactory.java @@ -0,0 +1,88 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.openecomp.policy.pdp.rest.impl; + +import java.util.Properties; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.openecomp.policy.common.logging.eelf.MessageCodes; +import org.openecomp.policy.common.logging.eelf.PolicyLogger; + +import org.openecomp.policy.xacml.api.XACMLErrorConstants; +import com.att.research.xacml.api.pip.PIPException; +import com.att.research.xacml.api.pip.PIPFinder; +import com.att.research.xacml.api.pip.PIPFinderFactory; +import com.att.research.xacml.std.pip.finders.ConfigurableEngineFinder; +import com.att.research.xacml.util.XACMLProperties; + +public class XACMLPdpPIPFinderFactory extends PIPFinderFactory { + private ConfigurableEngineFinder pipFinder; + + private static Log logger = LogFactory.getLog(XACMLPdpPIPFinderFactory.class); + + public XACMLPdpPIPFinderFactory() { + } + + public XACMLPdpPIPFinderFactory(Properties properties) { + } + + @Override + public PIPFinder getFinder() throws PIPException { + if (pipFinder == null) { + synchronized(this) { + if (pipFinder == null) { + if (logger.isDebugEnabled()) { + logger.debug("Creating default configurable engine finder"); + } + pipFinder = new ConfigurableEngineFinder(); + Properties xacmlProperties = null; + try { + xacmlProperties = XACMLProperties.getProperties(); + } catch (Exception ex) { + logger.error( XACMLErrorConstants.ERROR_SYSTEM_ERROR+ "Exception getting XACML properties: " + ex.getMessage(), ex); + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, ex, "Exception getting XACML properties"); + return null; + } + if (xacmlProperties != null) { + ((ConfigurableEngineFinder)pipFinder).configure(xacmlProperties); + } + } + } + } + return pipFinder; + } + + @Override + public PIPFinder getFinder(Properties properties) throws PIPException { + if (pipFinder == null) { + synchronized(this) { + if (pipFinder == null) { + if (logger.isDebugEnabled()) { + logger.debug("Creating configurable engine finder using: " + properties); + } + pipFinder = new ConfigurableEngineFinder(); + ((ConfigurableEngineFinder)pipFinder).configure(properties); + } + } + } + return this.pipFinder; + } +} diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/impl/XACMLPdpPolicyFinderFactory.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/impl/XACMLPdpPolicyFinderFactory.java new file mode 100644 index 000000000..9335bef6c --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/impl/XACMLPdpPolicyFinderFactory.java @@ -0,0 +1,208 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.pdp.rest.impl; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.openecomp.policy.xacml.api.XACMLErrorConstants; +import com.att.research.xacml.std.StdStatusCode; +import com.att.research.xacml.std.dom.DOMStructureException; +import com.att.research.xacml.util.FactoryException; +import com.att.research.xacml.util.XACMLProperties; +import com.att.research.xacmlatt.pdp.policy.Policy; +import com.att.research.xacmlatt.pdp.policy.PolicyDef; +import com.att.research.xacmlatt.pdp.policy.PolicyFinder; +import com.att.research.xacmlatt.pdp.policy.PolicyFinderFactory; +import com.att.research.xacmlatt.pdp.policy.dom.DOMPolicyDef; +import com.att.research.xacmlatt.pdp.std.StdPolicyFinder; +import com.google.common.base.Splitter; + +public class XACMLPdpPolicyFinderFactory extends PolicyFinderFactory { + public static final String PROP_FILE = ".file"; + public static final String PROP_URL = ".url"; + + private static Log logger = LogFactory.getLog(XACMLPdpPolicyFinderFactory.class); + private List<PolicyDef> rootPolicies; + private List<PolicyDef> referencedPolicies; + private boolean needsInit = true; + + private Properties properties = null; + + public XACMLPdpPolicyFinderFactory() { + // + // Here we differ from the StdPolicyFinderFactory in that we initialize right away. + // We do not wait for a policy request to happen to look for and load policies. + // + this.init(); + } + + public XACMLPdpPolicyFinderFactory(Properties properties) { + // + // Save our properties + // + this.properties = properties; + // + // Here we differ from the StdPolicyFinderFactory in that we initialize right away. + // We do not wait for a policy request to happen to look for and load policies. + // + this.init(); + } + + /** + * Loads the <code>PolicyDef</code> for the given <code>String</code> identifier by looking first + * for a ".file" property associated with the ID and using that to load from a <code>File</code> and + * looking for a ".url" property associated with the ID and using that to load from a <code>URL</code>. + * + * @param policyId the <code>String</code> identifier for the policy + * @return a <code>PolicyDef</code> loaded from the given identifier + */ + protected PolicyDef loadPolicyDef(String policyId) { + String propLocation = null; + if (this.properties == null) { + propLocation = XACMLProperties.getProperty(policyId + PROP_FILE); + } else { + propLocation = this.properties.getProperty(policyId + PROP_FILE); + } + if (propLocation != null) { + File fileLocation = new File(propLocation); + if (!fileLocation.exists()) { + XACMLPdpPolicyFinderFactory.logger.error("Policy file " + fileLocation.getAbsolutePath() + " does not exist."); + } else if (!fileLocation.canRead()) { + XACMLPdpPolicyFinderFactory.logger.error("Policy file " + fileLocation.getAbsolutePath() + " cannot be read."); + } else { + try { + XACMLPdpPolicyFinderFactory.logger.info("Loading policy file " + fileLocation); + PolicyDef policyDef = DOMPolicyDef.load(fileLocation); + if (policyDef != null) { + return policyDef; + } + } catch (DOMStructureException ex) { + XACMLPdpPolicyFinderFactory.logger.error( XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Error loading policy file " + fileLocation.getAbsolutePath() + ": " + ex.getMessage(), ex); + return new Policy(StdStatusCode.STATUS_CODE_SYNTAX_ERROR, ex.getMessage()); + } + } + } + if (this.properties == null) { + propLocation = XACMLProperties.getProperty(policyId + PROP_URL); + } else { + propLocation = this.properties.getProperty(policyId + PROP_URL); + } + if (propLocation != null) { + InputStream is = null; + try { + URL url = new URL(propLocation); + URLConnection urlConnection = url.openConnection(); + XACMLPdpPolicyFinderFactory.logger.info("Loading policy file " + url.toString()); + is = urlConnection.getInputStream(); + PolicyDef policyDef = DOMPolicyDef.load(is); + if (policyDef != null) { + return policyDef; + } + } catch (MalformedURLException ex) { + XACMLPdpPolicyFinderFactory.logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Invalid URL " + propLocation + ": " + ex.getMessage(), ex); + } catch (IOException ex) { + XACMLPdpPolicyFinderFactory.logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "IOException opening URL " + propLocation + ": " + ex.getMessage(), ex); + } catch (DOMStructureException ex) { + XACMLPdpPolicyFinderFactory.logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Invalid Policy " + propLocation + ": " + ex.getMessage(), ex); + return new Policy(StdStatusCode.STATUS_CODE_SYNTAX_ERROR, ex.getMessage()); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + XACMLPdpPolicyFinderFactory.logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Exception closing InputStream for GET of url " + propLocation + " : " + e.getMessage() + " (May be memory leak)", e); + } + } + } + } + + XACMLPdpPolicyFinderFactory.logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR +"No known location for Policy " + policyId); + return null; + } + + /** + * Finds the identifiers for all of the policies referenced by the given property name in the + * <code>XACMLProperties</code> and loads them using the requested loading method. + * + * @param propertyName the <code>String</code> name of the property containing the list of policy identifiers + * @return a <code>List</code> of <code>PolicyDef</code>s loaded from the given property name + */ + protected List<PolicyDef> getPolicyDefs(String propertyName) { + String policyIds = XACMLProperties.getProperty(propertyName); + if (policyIds == null || policyIds.length() == 0) { + return null; + } + + Iterable<String> policyIdArray = Splitter.on(',').trimResults().omitEmptyStrings().split(policyIds); + if (policyIdArray == null) { + return null; + } + + List<PolicyDef> listPolicyDefs = new ArrayList<PolicyDef>(); + for (String policyId : policyIdArray) { + PolicyDef policyDef = this.loadPolicyDef(policyId); + if (policyDef != null) { + listPolicyDefs.add(policyDef); + } + } + return listPolicyDefs; + } + + protected synchronized void init() { + if (this.needsInit) { + if (XACMLPdpPolicyFinderFactory.logger.isDebugEnabled()) { + XACMLPdpPolicyFinderFactory.logger.debug("Initializing"); + } + this.rootPolicies = this.getPolicyDefs(XACMLProperties.PROP_ROOTPOLICIES); + this.referencedPolicies = this.getPolicyDefs(XACMLProperties.PROP_REFERENCEDPOLICIES); + if (XACMLPdpPolicyFinderFactory.logger.isDebugEnabled()) { + XACMLPdpPolicyFinderFactory.logger.debug("Root Policies: " + this.rootPolicies); + XACMLPdpPolicyFinderFactory.logger.debug("Referenced Policies: " + this.referencedPolicies); + } + this.needsInit = false; + } + } + + @Override + public PolicyFinder getPolicyFinder() throws FactoryException { + // + // Force using any properties that were passed upon construction + // + return new StdPolicyFinder(this.rootPolicies, this.referencedPolicies, this.properties); + } + + @Override + public PolicyFinder getPolicyFinder(Properties properties) throws FactoryException { + return new StdPolicyFinder(this.rootPolicies, this.referencedPolicies, properties); + } + +} diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/jmx/PdpRestMBeanListener.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/jmx/PdpRestMBeanListener.java new file mode 100644 index 000000000..309bd1502 --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/jmx/PdpRestMBeanListener.java @@ -0,0 +1,87 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.pdp.rest.jmx; + +import java.lang.management.ManagementFactory; + +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.annotation.WebListener; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.openecomp.policy.common.logging.flexlogger.*; + +import org.openecomp.policy.xacml.api.XACMLErrorConstants; +/** + * + * + */ + +@WebListener +public class PdpRestMBeanListener implements ServletContextListener { + private static final String JMX_OBJECT_NAME = "PdpRest:type=PdpRestMonitor"; + private static final Logger logger = FlexLogger.getLogger(PdpRestMBeanListener.class); + + private ObjectName objectName; + + @Override + public void contextInitialized(ServletContextEvent contextEvent) { + if (logger.isInfoEnabled()) + logger.info("Registering."); + + final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + try { + objectName = new ObjectName(JMX_OBJECT_NAME); + server.registerMBean(PdpRestMonitor.singleton, objectName); + logger.info("MBean registered: " + objectName); + } catch (Exception e) { + + logger.warn(e.getMessage(), e); + + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Unable to Register " +e.getMessage(), e); + + } + } + // mark + @Override + public void contextDestroyed(ServletContextEvent contextEvent) { + if (logger.isInfoEnabled()) + logger.info("Unregistering"); + final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + try { + objectName = new ObjectName(JMX_OBJECT_NAME); + server.unregisterMBean(objectName); + if (logger.isInfoEnabled()) + logger.info("MBean unregistered: " + objectName); + } catch (Exception e) { + + logger.warn(e.getMessage(), e); + + logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Unable to Destroy Context" +e.getMessage(), e); + + } + } + +} + diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/jmx/PdpRestMonitor.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/jmx/PdpRestMonitor.java new file mode 100644 index 000000000..93dfbe6a0 --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/jmx/PdpRestMonitor.java @@ -0,0 +1,161 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.pdp.rest.jmx; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BinaryOperator; + +import org.openecomp.policy.xacml.util.MetricsUtil.AvgLatency; +import org.openecomp.policy.xacml.util.MetricsUtil.MaxLatency; +import org.openecomp.policy.xacml.util.MetricsUtil.MinLatency; + +public class PdpRestMonitor implements PdpRestMonitorMBean { + public static PdpRestMonitor singleton = new PdpRestMonitor(); + + private final AtomicLong pdpEvaluationAttempts = new AtomicLong(); + private final AtomicLong pdpEvaluationSuccesses = new AtomicLong(); + private final AtomicLong pdpEvaluationNA = new AtomicLong(); + private final AtomicLong pdpEvaluationPermit = new AtomicLong(); + private final AtomicLong pdpEvaluationDeny = new AtomicLong(); + private final Map<String, Integer> policyCount = new HashMap<>(); + + private final MinLatency pdpEngineDecisionMinLatency = new MinLatency(); + private final MaxLatency pdpEngineDecisionMaxLatency = new MaxLatency(); + private final AvgLatency pdpEngineDecisionAvgLatency = new AvgLatency(); + + private volatile long lastDecisionLatency = 0; + + @Override + public long getPdpEvaluationAttempts() { + return pdpEvaluationAttempts.longValue(); + } + @Override + public long getPdpEvaluationPermit() { + return pdpEvaluationPermit.longValue(); + } + @Override + public long getPdpEvaluationDeny() { + return pdpEvaluationDeny.longValue(); + } + @Override + public long getPdpEvaluationSuccesses() { + return pdpEvaluationSuccesses.longValue(); + } + + @Override + public long getpdpEvaluationNA() { + return pdpEvaluationNA.longValue(); + } + @Override + public long getLastDecisionLatency() { + return lastDecisionLatency; + } + + /** + * @return the pdpEngineDecisionMinLatency + */ + @Override + public long getPdpEngineDecisionMinLatency() { + return pdpEngineDecisionMinLatency.min(); + } + + /** + * @return the pdpEngineDecisionMaxLatency + */ + @Override + public long getPdpEngineDecisionMaxLatency() { + return pdpEngineDecisionMaxLatency.max(); + } + + /** + * @return the pdpEngineDecisionAvgLatency + */ + @Override + public long getPdpEngineDecisionAvgLatency() { + return pdpEngineDecisionAvgLatency.avg(); + } + + @Override + public synchronized void resetLatency() { + this.lastDecisionLatency = 0; + this.pdpEngineDecisionMinLatency.reset(); + this.pdpEngineDecisionMaxLatency.reset(); + this.pdpEngineDecisionAvgLatency.reset(); + } + + @Override + public synchronized void resetCounters() { + this.pdpEvaluationAttempts.set(0); + this.pdpEvaluationSuccesses.set(0); + this.pdpEvaluationNA.set(0); + this.policyCount.clear(); + } + + public void pdpEvaluationAttempts() { + pdpEvaluationAttempts.incrementAndGet(); + } + + public void pdpEvaluationSuccess() { + pdpEvaluationSuccesses.incrementAndGet(); + } + + public void pdpEvaluationNA(){ + pdpEvaluationNA.incrementAndGet(); + } + public void pdpEvaluationPermit(){ + pdpEvaluationPermit.incrementAndGet(); + } + public void pdpEvaluationDeny(){ + pdpEvaluationDeny.incrementAndGet(); + } + + public synchronized void computeLatency(long latency) { + this.lastDecisionLatency = latency; + this.pdpEngineDecisionMinLatency.compute(latency); + this.pdpEngineDecisionMaxLatency.compute(latency); + this.pdpEngineDecisionAvgLatency.compute(latency); + } + + public void policyCountAdd(String policyID, Integer count){ + if (policyCount.containsKey(policyID)){ + count = count + policyCount.get(policyID); + } + policyCount.put(policyID, count); + } + public Map<String, Integer> getpolicyMap() { + return policyCount; + } + public Integer getpolicyCount(String policyID) { + // TODO Auto-generated method stub + if (policyCount.containsKey(policyID)){ + return policyCount.get(policyID); + } + return null; + } + + + +} diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/jmx/PdpRestMonitorMBean.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/jmx/PdpRestMonitorMBean.java new file mode 100644 index 000000000..e7db09d24 --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/jmx/PdpRestMonitorMBean.java @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.pdp.rest.jmx; + +import java.util.Map; + +public interface PdpRestMonitorMBean { + public long getPdpEvaluationAttempts(); + public long getPdpEvaluationSuccesses(); + public long getLastDecisionLatency(); + public long getPdpEngineDecisionMinLatency(); + public long getPdpEngineDecisionMaxLatency(); + public long getPdpEngineDecisionAvgLatency(); + public Integer getpolicyCount(String policyID); + + public void resetLatency(); + public void resetCounters(); + public long getpdpEvaluationNA(); + public long getPdpEvaluationPermit(); + public long getPdpEvaluationDeny(); + public Map<String, Integer> getpolicyMap(); +} diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/ManualNotificationUpdateThread.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/ManualNotificationUpdateThread.java new file mode 100644 index 000000000..6b8857273 --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/ManualNotificationUpdateThread.java @@ -0,0 +1,151 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.pdp.rest.notifications; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.util.LinkedList; +import java.util.UUID; + +import org.openecomp.policy.rest.XACMLRestProperties; + +import com.att.nsa.cambria.client.CambriaClientFactory; +import com.att.nsa.cambria.client.CambriaConsumer; +import com.att.nsa.cambria.client.CambriaPublisher; +import org.openecomp.policy.xacml.api.XACMLErrorConstants; +import com.att.research.xacml.util.XACMLProperties; + +import org.openecomp.policy.common.logging.flexlogger.*; + +public class ManualNotificationUpdateThread implements Runnable { + private static final Logger logger = FlexLogger.getLogger(ManualNotificationUpdateThread.class); +// private static List<String> uebURLList = null; + private static String topic = null; + private static CambriaConsumer CConsumer = null; +// private static Collection<String> clusterList = null; + private static String clusterList = null; +// private Collection<String> urlList = null; + private static String update = null; + + public volatile boolean isRunning = false; + + public synchronized boolean isRunning() { + return this.isRunning; + } + + public synchronized void terminate() { + this.isRunning = false; + } + + /** + * + * This is our thread that runs on startup if the system is configured to UEB to accept manual update requests + * + */ + @Override + public void run() { + synchronized(this) { + this.isRunning = true; + } + URL aURL = null; + String group = UUID.randomUUID ().toString (); + String id = "0"; + String returnTopic = null; + try { + ManualNotificationUpdateThread.clusterList = XACMLProperties.getProperty(XACMLRestProperties.PROP_NOTIFICATION_UEB_CLUSTER); + String url = XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_ID); + aURL = new URL(url); + topic = aURL.getHost() + aURL.getPort(); + } catch (NumberFormatException e) { + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Unable to get UEB cluster list or pdp url: ", e); + this.isRunning = false; + } catch (MalformedURLException e) { + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Error in processing URL to create topic for Notification ", e); + } + String consumerTopic = aURL.getHost() + aURL.getPort() + "UpdateRequest"; + SendMessage(consumerTopic, "Starting-Topic"); + final LinkedList<String> urlList = new LinkedList<String> (); + for ( String u : clusterList.split ( "," ) ){ + urlList.add ( u ); + } + + try { + CConsumer = CambriaClientFactory.createConsumer ( null, urlList, consumerTopic , group, id, 20*1000, 1000 ); + } catch (MalformedURLException | GeneralSecurityException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + + + while (this.isRunning()) { + logger.debug("While loop test _ take out "); + try { + for ( String msg : CConsumer.fetch () ){ + logger.debug("Manual Notification Recieved Message " + msg + " from UEB cluster : "); + returnTopic = processMessage(msg); + if(returnTopic != null){ + SendMessage(returnTopic, update); + } + } + } catch (IOException e) { + logger.debug(XACMLErrorConstants.ERROR_DATA_ISSUE + "Error in processing UEB message"); + } + } + logger.debug("Stopping UEB Consuer loop will not logger fetch messages from the cluser"); + + } + + private void SendMessage( String topic, String message) { + CambriaPublisher pub = null; + try { + pub = CambriaClientFactory.createSimplePublisher (null, clusterList, topic ); + } catch (MalformedURLException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } catch (GeneralSecurityException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + try { + pub.send( "pdpReturnMessage", message ); + logger.debug("Sending to Message to tpoic" + topic); + } catch (IOException e) { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW+ "Error sending notification update"); + } + pub.close(); + } + + private String processMessage(String msg) { + logger.debug("notification message: " + msg); + String[] UID = msg.split("=")[1].split("\""); + String returnTopic = topic + UID[0]; + if(msg.contains("Starting-Topic")){ + return null; + } + return returnTopic; + } + public static void setUpdate(String update) { + ManualNotificationUpdateThread.update = update; + } + +} diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/Notification.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/Notification.java new file mode 100644 index 000000000..5ab165b0a --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/Notification.java @@ -0,0 +1,52 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.pdp.rest.notifications; + +import java.util.Collection; + +/** + * Notification is the POJO which will be used to send the Notifications to the Server. + * Notification must contain the Removal and Updated policies. + * + * @version 0.1 + * + */ +public class Notification { + private Collection<Removed> removedPolicies = null; + private Collection<Updated> loadedPolicies = null; + + public Collection<Removed> getRemovedPolicies() { + return removedPolicies; + } + + public void setRemovedPolicies(Collection<Removed> removedPolicies) { + this.removedPolicies = removedPolicies; + } + + public Collection<Updated> getLoadedPolicies() { + return loadedPolicies; + } + + public void setLoadedPolicies(Collection<Updated> loadedPolicies) { + this.loadedPolicies = loadedPolicies; + } + +} diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/NotificationController.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/NotificationController.java new file mode 100644 index 000000000..1dfd07422 --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/NotificationController.java @@ -0,0 +1,391 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.pdp.rest.notifications; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.filefilter.WildcardFileFilter; +import org.openecomp.policy.pdp.rest.PapUrlResolver; +import org.openecomp.policy.rest.XACMLRestProperties; +import org.openecomp.policy.common.logging.flexlogger.FlexLogger; +import org.openecomp.policy.common.logging.flexlogger.Logger; + +import org.openecomp.policy.xacml.api.XACMLErrorConstants; +import com.att.research.xacml.api.pap.PDPPolicy; +import com.att.research.xacml.api.pap.PDPStatus; +import com.att.research.xacml.util.XACMLProperties; +import com.att.research.xacmlatt.pdp.policy.AllOf; +import com.att.research.xacmlatt.pdp.policy.AnyOf; +import com.att.research.xacmlatt.pdp.policy.Match; +import com.att.research.xacmlatt.pdp.policy.PolicyDef; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; + +/** + * NotificationController Checks for the Updated and Removed policies. It + * notifies the Server to send Notifications to the Client. + * + * @version 0.2 + * + */ +public class NotificationController { + private static final Logger logger = FlexLogger.getLogger(NotificationController.class); + private static Notification record = new Notification(); + private PDPStatus oldStatus = null; + private Removed removed = null; + private Updated updated = null; + private ManualNotificationUpdateThread registerMaunualNotificationRunnable = null; + private Thread manualNotificationThread = null; + private boolean manualThreadStarted = false; + + private static String notificationJSON = null; + private static String propNotificationType = null; + private static String pdpURL = null; + private static Boolean notificationFlag = false; + + public void check(PDPStatus newStatus,HashMap<String, PolicyDef> policyContainer) { + boolean updated = false; + boolean removed = false; + Notification notification = new Notification(); + HashSet<Removed> removedPolicies = new HashSet<Removed>(); + HashSet<Updated> updatedPolicies = new HashSet<Updated>(); + + if (oldStatus == null) { + oldStatus = newStatus; + } + // Debugging purpose only. + logger.debug("old config Status :" + oldStatus.getStatus()); + logger.debug("new config Status :" + newStatus.getStatus()); + + // Depending on the above condition taking the Change as an Update. + if (oldStatus.getStatus().toString() != newStatus.getStatus().toString()) { + logger.info("There is an Update to the PDP"); + logger.debug(oldStatus.getLoadedPolicies()); + logger.debug(newStatus.getLoadedPolicies()); + // Check if there is an Update/additions in the policy. + for (PDPPolicy newPolicy : newStatus.getLoadedPolicies()) { + boolean change = true; + for (PDPPolicy oldPolicy : oldStatus.getLoadedPolicies()) { + // Check if there are same policies. + if (oldPolicy.getId().equals(newPolicy.getId())) { + // Check if they have same version. + if (oldPolicy.getVersion().equals(newPolicy.getVersion())) { + change = false; + } + } + } + // if there is a change Send the notifications to the Client. + if (change) { + sendUpdate(newPolicy, policyContainer); + updated = true; + updatedPolicies.add(this.updated); + } + } + // Check if there is any removal of policy. + for (PDPPolicy oldPolicy : oldStatus.getLoadedPolicies()) { + boolean change = true; + for (PDPPolicy newPolicy : newStatus.getLoadedPolicies()) { + // Check if there are same policies. + if (oldPolicy.getId().equals(newPolicy.getId())) { + // Check if they have same version. + if (oldPolicy.getVersion().equals(newPolicy.getVersion())) { + change = false; + } + } + } + // if there is a change Send the notifications to the Client. + if (change) { + sendremove(oldPolicy); + removed = true; + removedPolicies.add(this.removed); + } + } + } + // At the end the oldStatus must be updated with the newStatus. + oldStatus = newStatus; + // Sending Notification to the Server to pass over to the clients + if (updated || removed) { + // Call the Notification Server.. + notification.setRemovedPolicies(removedPolicies); + notification.setLoadedPolicies(updatedPolicies); + ObjectWriter om = new ObjectMapper().writer(); + try { + notificationJSON = om.writeValueAsString(notification); + logger.info(notificationJSON); + // NotificationServer Method here. + propNotificationType = XACMLProperties.getProperty(XACMLRestProperties.PROP_NOTIFICATION_TYPE); + pdpURL = XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_ID); + if (propNotificationType!=null && propNotificationType.equals("ueb") && !manualThreadStarted) { + logger.debug("Starting Thread to accept UEB notfications."); + this.registerMaunualNotificationRunnable = new ManualNotificationUpdateThread(); + this.manualNotificationThread = new Thread(this.registerMaunualNotificationRunnable); + this.manualNotificationThread.start(); + manualThreadStarted = true; + } + String notificationJSON= null; + notificationFlag = true; + try{ + notificationJSON= record(notification); + }catch(Exception e){ + logger.error(e); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, ""); + } + NotificationServer.setUpdate(notificationJSON); + ManualNotificationUpdateThread.setUpdate(notificationJSON); + } catch (JsonProcessingException e) { + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + e.getMessage()); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, ""); + } + } + } + + public static void sendNotification(){ + if(notificationFlag){ + NotificationServer.sendNotification(notificationJSON, propNotificationType, pdpURL); + notificationFlag = false; + } + } + + private void sendremove(PDPPolicy oldPolicy) { + removed = new Removed(); + // Want to know what is removed ? + // logger.info("The Policy removed is: " + oldPolicy.getId()); + // logger.info("The version no. is: " + oldPolicy.getVersion()); + logger.info("Policy removed: " + oldPolicy.getId()+ " with version number: " + oldPolicy.getVersion()); + removed.setPolicyName(oldPolicy.getId()); + removed.setVersionNo(oldPolicy.getVersion()); + removeFile(oldPolicy); + } + + private void sendUpdate(PDPPolicy newPolicy,HashMap<String, PolicyDef> policyContainer) { + updated = new Updated(); + // Want to know what is new ? + logger.info("The new Policy is: " + newPolicy.getId()); + logger.info("The version no. is: " + newPolicy.getVersion()); + updated.setPolicyName(newPolicy.getId()); + updated.setVersionNo(newPolicy.getVersion()); + // If the policy is of Config type then retrieve its matches. + if (newPolicy.getName().startsWith("Config")) { + // Take a Configuration copy to PDP webapps. + final String urlStart = "attributeId=URLID,expression"; + final String urlEnd = "}}},{"; + String policy = policyContainer.get(newPolicy.getId()).toString(); + if(policy.contains(urlStart)){ + String urlFinePartOne = policy.substring(policy.indexOf(urlStart)+urlStart.length()); + String urlFinePart = urlFinePartOne.substring(0,urlFinePartOne.indexOf(urlEnd)); + String urlString = urlFinePart.substring(urlFinePart.indexOf("value=$URL")+6); + callPap(urlString, "Config"); + } + Iterator<AnyOf> anyOfs = policyContainer.get(newPolicy.getId()).getTarget().getAnyOfs(); + while (anyOfs.hasNext()) { + AnyOf anyOf = anyOfs.next(); + Iterator<AllOf> allOfs = anyOf.getAllOfs(); + while (allOfs.hasNext()) { + AllOf allOf = allOfs.next(); + Iterator<Match> matches = allOf.getMatches(); + HashMap<String, String> matchValues = new HashMap<String, String>(); + while (matches.hasNext()) { + Match match = matches.next(); + logger.info("Attribute Value is: "+ match.getAttributeValue().getValue().toString()); + String[] result = match.getAttributeRetrievalBase().toString().split("attributeId="); + result[1] = result[1].replaceAll("}", ""); + if (!result[1].equals("urn:oasis:names:tc:xacml:1.0:subject:subject-id")) { + logger.info("Attribute id is: " + result[1]); + } + matchValues.put(result[1], match.getAttributeValue().getValue().toString()); + logger.info("Match is : "+ result[1]+ " , " + match.getAttributeValue().getValue().toString()); + } + updated.setMatches(matchValues); + } + } + }else if(newPolicy.getName().startsWith("Action")){ + // Take Configuration copy to PDP Webapps. + // Action policies have .json as extension. + String urlString = "$URL/Action/" + newPolicy.getId().substring(0, newPolicy.getId().lastIndexOf(".")) + ".json"; + callPap(urlString, "Action"); + } + } + + // Adding this for Recording the changes to serve Polling requests.. + public static String record(Notification notification) throws Exception { + // Initialization with updates. + if (record.getRemovedPolicies() == null || record.getLoadedPolicies() == null) { + record.setRemovedPolicies(notification.getRemovedPolicies()); + record.setLoadedPolicies(notification.getLoadedPolicies()); + } else { + // Check if there is anything new and update the record.. + if (record.getLoadedPolicies() != null || record.getRemovedPolicies() != null) { + HashSet<Removed> removedPolicies = (HashSet<Removed>) record.getRemovedPolicies(); + HashSet<Updated> updatedPolicies = (HashSet<Updated>) record.getLoadedPolicies(); + + // Checking with New updated policies. + if (notification.getLoadedPolicies() != null && !notification.getLoadedPolicies().isEmpty()) { + for (Updated newUpdatedPolicy : notification.getLoadedPolicies()) { + // If it was removed earlier then we need to remove from our record + Iterator<Removed> oldRemovedPolicy = removedPolicies.iterator(); + while (oldRemovedPolicy.hasNext()) { + Removed policy = oldRemovedPolicy.next(); + if (newUpdatedPolicy.getPolicyName().equals(policy.getPolicyName())) { + if (newUpdatedPolicy.getVersionNo().equals(policy.getVersionNo())) { + oldRemovedPolicy.remove(); + } + } + } + // If it was previously updated need to Overwrite it to the record. + Iterator<Updated> oldUpdatedPolicy = updatedPolicies.iterator(); + while (oldUpdatedPolicy.hasNext()) { + Updated policy = oldUpdatedPolicy.next(); + if (newUpdatedPolicy.getPolicyName().equals(policy.getPolicyName())) { + if (newUpdatedPolicy.getVersionNo().equals(policy.getVersionNo())) { + oldUpdatedPolicy.remove(); + } + } + } + updatedPolicies.add(newUpdatedPolicy); + } + } + // Checking with New Removed policies. + if (notification.getRemovedPolicies() != null && !notification.getRemovedPolicies().isEmpty()) { + for (Removed newRemovedPolicy : notification.getRemovedPolicies()) { + // If it was previously removed Overwrite it to the record. + Iterator<Removed> oldRemovedPolicy = removedPolicies.iterator(); + while (oldRemovedPolicy.hasNext()) { + Removed policy = oldRemovedPolicy.next(); + if (newRemovedPolicy.getPolicyName().equals(policy.getPolicyName())) { + if (newRemovedPolicy.getVersionNo().equals(policy.getVersionNo())) { + oldRemovedPolicy.remove(); + } + } + } + // If it was added earlier then we need to remove from our record. + Iterator<Updated> oldUpdatedPolicy = updatedPolicies.iterator(); + while (oldUpdatedPolicy.hasNext()) { + Updated policy = oldUpdatedPolicy.next(); + if (newRemovedPolicy.getPolicyName().equals(policy.getPolicyName())) { + if (newRemovedPolicy.getVersionNo().equals(policy.getVersionNo())) { + oldUpdatedPolicy.remove(); + } + } + } + removedPolicies.add(newRemovedPolicy); + } + } + record.setRemovedPolicies(removedPolicies); + record.setLoadedPolicies(updatedPolicies); + } + } + // Send the Result to the caller. + ObjectWriter om = new ObjectMapper().writer(); + String json = null; + try { + json = om.writeValueAsString(record); + } catch (JsonProcessingException e) { + logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + e.getMessage()); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, ""); + } + logger.info(json); + return json; + } + + private void removeFile(PDPPolicy oldPolicy) { + try{ + Path removedPolicyFile = Paths.get(XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_CONFIG)+File.separator+oldPolicy.getId()); + Files.deleteIfExists(removedPolicyFile); + boolean delete=false; + File dir= null; + if(oldPolicy.getName().startsWith("Config")){ + delete = true; + dir = new File(XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_WEBAPPS)+File.separator+"Config"); + }else if(oldPolicy.getName().startsWith("Action")){ + delete = true; + dir = new File(XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_WEBAPPS)+File.separator+"Action"); + } + if(delete && dir!=null){ + FileFilter fileFilter = new WildcardFileFilter(oldPolicy.getId().substring(0, oldPolicy.getId().lastIndexOf("."))+".*"); + File[] configFile = dir.listFiles(fileFilter); + if(configFile.length==1){ + Files.deleteIfExists(configFile[0].toPath()); + } + } + }catch(Exception e){ + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Couldn't remove the policy/config file " + oldPolicy.getName()); + // TODO:EELF Cleanup - Remove logger + //PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e, "Couldn't remove the policy file " + oldPolicy.getName()); + } + } + + private void callPap(String urlString, String type) { + Path configLocation = Paths.get(XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_WEBAPPS)+File.separator+type); + if(Files.notExists(configLocation)){ + try { + Files.createDirectories(configLocation); + } catch (IOException e) { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW +"Failed to create config directory: " + configLocation.toAbsolutePath().toString(), e); + } + } + PapUrlResolver papUrls = PapUrlResolver.getInstance(); + while(papUrls.hasMoreUrls()){ + String papPath = papUrls.getUrl(); + papPath = papPath.substring(0, papPath.lastIndexOf("/pap")); + String papAddress= urlString.replace("$URL", papPath); + String fileName = papAddress.substring(papAddress.lastIndexOf("/")+1); + String fileLocation = configLocation.toString() + File.separator + fileName; + try { + URL papURL = new URL(papAddress); + logger.info("Calling " +papAddress + " for Configuration Copy."); + URLConnection urlConnection = papURL.openConnection(); + File file= new File(fileLocation); + try (InputStream is = urlConnection.getInputStream(); + OutputStream os = new FileOutputStream(file)) { + IOUtils.copy(is, os); + break; + } + } catch (MalformedURLException e) { + logger.error(e + e.getMessage()); + } catch(FileNotFoundException e){ + logger.error(e + e.getMessage()); + } catch (IOException e) { + logger.error(e + e.getMessage()); + } + papUrls.getNext(); + } + } +} diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/NotificationServer.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/NotificationServer.java new file mode 100644 index 000000000..d6cda7491 --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/NotificationServer.java @@ -0,0 +1,141 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.pdp.rest.notifications; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.ServerEndpoint; + +import org.openecomp.policy.rest.XACMLRestProperties; +import org.openecomp.policy.common.logging.eelf.MessageCodes; +import org.openecomp.policy.common.logging.eelf.PolicyLogger; +import com.att.nsa.cambria.client.CambriaClientFactory; +import com.att.nsa.cambria.client.CambriaPublisher; +import org.openecomp.policy.xacml.api.XACMLErrorConstants; +import com.att.research.xacml.util.XACMLProperties; + +import org.openecomp.policy.common.logging.flexlogger.*; + + +/** + * The NotificationServer sends the Server Notifications to the Clients once there is any Event. + * WebSockets is being used as a medium for sending Notifications. + * UEB is being used as a medium for sending Notifications. + * + * @version 0.1 + * + **/ +@ServerEndpoint(value = "/notifications") +public class NotificationServer { + private static final Logger logger = FlexLogger.getLogger(NotificationServer.class); + private static Queue<Session> queue = new ConcurrentLinkedQueue<Session>(); + private static String update = null; + private static String hosts = null; + private static URL aURL = null; + + @OnOpen + public void openConnection(Session session) { + logger.info("Session Connected: " + session.getId()); + queue.add(session); + } + + @OnClose + public void closeConnection(Session session) { + queue.remove(session); + } + + @OnError + public void error(Session session, Throwable t) { + queue.remove(session); + logger.info(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Session Error for : " + session.getId() + " Error: " + t.getMessage()); + + } + + @OnMessage + public void Message(String message, Session session) { + + if(message.equalsIgnoreCase("Manual")) { + try { + session.getBasicRemote().sendText(update); + session.close(); + } catch (IOException e) { + logger.info(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Error in sending the Event Notification: "+ e.getMessage()); + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Error sending Message update"); + } + } + } + + public static void sendNotification(String notification, String propNotificationType, String pdpURL){ + + logger.debug("Notification set to " + propNotificationType); + if (propNotificationType.equals("ueb")){ + String topic = null; + try { + aURL = new URL(pdpURL); + topic = aURL.getHost() + aURL.getPort(); + } catch (MalformedURLException e1) { + pdpURL = pdpURL.replace("/", ""); + topic = pdpURL.replace(":", ""); + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Error in parsing out pdpURL for UEB notfication "); + PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e1, "Error in parsing out pdpURL for UEB notfication "); + } + hosts = XACMLProperties.getProperty(XACMLRestProperties.PROP_NOTIFICATION_UEB_CLUSTER); + logger.debug("Creating Publisher for host: " + hosts + " with topic: " + topic); + CambriaPublisher pub = null; + try { + pub = CambriaClientFactory.createSimplePublisher (null, hosts, topic ); + } catch (MalformedURLException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } catch (GeneralSecurityException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + try { + pub.send( "MyPartitionKey", notification ); + } catch (IOException e) { + logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Error sending notification update"); + } + pub.close(); + } + for(Session session: queue) { + try { + session.getBasicRemote().sendText(notification); + } catch (IOException e) { + logger.info(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Error in sending the Event Notification: "+ e.getMessage()); + } + } + } + + public static void setUpdate(String update) { + NotificationServer.update = update; + } +} diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/Removed.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/Removed.java new file mode 100644 index 000000000..6ba073815 --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/Removed.java @@ -0,0 +1,52 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.pdp.rest.notifications; + + +/** + * Removal is the POJO for removal updates of the Policy. + * It must have the Policy removed and its Version number. + * + * @version 0.1 + * + */ +public class Removed { + + private String policyName = null; + private String versionNo = null; + + public String getVersionNo() { + return versionNo; + } + + public void setVersionNo(String versionNo) { + this.versionNo = versionNo; + } + + public String getPolicyName() { + return policyName; + } + + public void setPolicyName(String policyName) { + this.policyName = policyName; + } + +} diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/Updated.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/Updated.java new file mode 100644 index 000000000..39236bada --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/Updated.java @@ -0,0 +1,61 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.pdp.rest.notifications; + +import java.util.HashMap; + +/** + * Updated is the POJO which consists of any new or Updated Policy information. + * It must hold the Policy Name, version Number, Matches. + * + * @version 0.1 + * + */ +public class Updated { + private String policyName = null; + private String versionNo = null; + private HashMap<String,String> matches = null; + + public String getPolicyName() { + return policyName; + } + + public void setPolicyName(String policyName) { + this.policyName = policyName; + } + + public String getVersionNo() { + return versionNo; + } + + public void setVersionNo(String versionNo) { + this.versionNo = versionNo; + } + + public HashMap<String,String> getMatches() { + return matches; + } + + public void setMatches(HashMap<String,String> matches) { + this.matches = matches; + } + +} diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/package-info.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/package-info.java new file mode 100644 index 000000000..7d3995ec6 --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/notifications/package-info.java @@ -0,0 +1,28 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +/** + * This is the new Addition to the PDP Server added for sending Notifications to the Clients + * about the policy Updates/ Removals. + * + * @version 0.1 + * + */ +package org.openecomp.policy.pdp.rest.notifications; diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/restAuth/AuthenticationService.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/restAuth/AuthenticationService.java new file mode 100644 index 000000000..c426b6002 --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/restAuth/AuthenticationService.java @@ -0,0 +1,72 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.pdp.rest.restAuth; + +import java.util.Base64; +import java.util.StringTokenizer; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.openecomp.policy.rest.XACMLRestProperties; + +import org.openecomp.policy.xacml.api.XACMLErrorConstants; +import com.att.research.xacml.util.XACMLProperties; + +import org.openecomp.policy.common.logging.eelf.MessageCodes; +import org.openecomp.policy.common.logging.eelf.PolicyLogger; + +public class AuthenticationService { + private String pdpID = XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_USERID); + private String pdpPass = XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_PASS); + private static final Log logger = LogFactory.getLog(AuthenticationService.class); + + public boolean authenticate(String authCredentials) { + + if (null == authCredentials) + return false; + // header value format will be "Basic encodedstring" for Basic authentication. + final String encodedUserPassword = authCredentials.replaceFirst("Basic" + " ", ""); + String usernameAndPassword = null; + try { + byte[] decodedBytes = Base64.getDecoder().decode(encodedUserPassword); + usernameAndPassword = new String(decodedBytes, "UTF-8"); + } catch (Exception e) { + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + e); + // TODO:EELF Cleanup - Remove logger + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, ""); + return false; + } + try { + final StringTokenizer tokenizer = new StringTokenizer(usernameAndPassword, ":"); + final String username = tokenizer.nextToken(); + final String password = tokenizer.nextToken(); + + boolean authenticationStatus = pdpID.equals(username) && pdpPass.equals(password); + return authenticationStatus; + }catch (Exception e){ + logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + e); + // TODO:EELF Cleanup - Remove logger + PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, ""); + return false; + } + } + +} diff --git a/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/restAuth/PDPAuthenticationFilter.java b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/restAuth/PDPAuthenticationFilter.java new file mode 100644 index 000000000..b3b931eaa --- /dev/null +++ b/ECOMP-PDP-REST/src/main/java/org/openecomp/policy/pdp/rest/restAuth/PDPAuthenticationFilter.java @@ -0,0 +1,78 @@ +/*- + * ============LICENSE_START======================================================= + * ECOMP-PDP-REST + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.policy.pdp.rest.restAuth; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.policy.pdp.rest.restAuth.AuthenticationService; + +/** + * Servlet Filter implementation class PDPAuthenticationFilter + */ +@WebFilter("/*") +public class PDPAuthenticationFilter implements Filter { + + public static final String AUTHENTICATION_HEADER = "Authorization"; + + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain filter) throws IOException, ServletException { + if (request instanceof HttpServletRequest) { + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + String authCredentials = httpServletRequest.getHeader(AUTHENTICATION_HEADER); + String path = ((HttpServletRequest) request).getRequestURI(); + // better injected + AuthenticationService authenticationService = new AuthenticationService(); + + boolean authenticationStatus = authenticationService.authenticate(authCredentials); + + if (authenticationStatus) { + filter.doFilter(request, response); + } else if(path.contains("notifications")){ + filter.doFilter(request, response); + } else { + if (response instanceof HttpServletResponse) { + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + } + } + } + } + + @Override + public void destroy() { + } + + @Override + public void init(FilterConfig arg0) throws ServletException { + } + +} |