diff options
Diffstat (limited to 'controlloop/common/guard/src/main/java/org')
3 files changed, 386 insertions, 117 deletions
diff --git a/controlloop/common/guard/src/main/java/org/onap/policy/guard/CallGuardTask.java b/controlloop/common/guard/src/main/java/org/onap/policy/guard/CallGuardTask.java index 8ea4ec1b3..dde18ed31 100644 --- a/controlloop/common/guard/src/main/java/org/onap/policy/guard/CallGuardTask.java +++ b/controlloop/common/guard/src/main/java/org/onap/policy/guard/CallGuardTask.java @@ -42,9 +42,10 @@ public class CallGuardTask implements Runnable { String target; String requestId; - public CallGuardTask(String guardUrl, WorkingMemory wm, String cl, String act, String rec, String tar, String reqId) { - - restfulPdpUrl = guardUrl; + /* + * Guard url is grabbed from PolicyEngine.manager properties + */ + public CallGuardTask(WorkingMemory wm, String cl, String act, String rec, String tar, String reqId) { workingMemory = wm; clname = cl; actor = act; @@ -69,15 +70,13 @@ public class CallGuardTask implements Runnable { logger.debug("{}", request); logger.debug("********** XACML REQUEST END ********\n"); - String guardUrl = PolicyEngine.manager.getEnvironmentProperty("guard.url"); + String guardUrl = PolicyEngine.manager.getEnvironmentProperty(Util.PROP_GUARD_URL); String guardDecision = null; // - // Check if guard url property exists + // Make guard request // - if(guardUrl != null){ - guardDecision = PolicyGuardXacmlHelper.callPDP(guardUrl, xacmlReq); - } + guardDecision = new PolicyGuardXacmlHelper().callPDP(xacmlReq); logger.debug("\n********** XACML RESPONSE START ********"); logger.debug("{}", guardDecision); @@ -88,7 +87,7 @@ public class CallGuardTask implements Runnable { // if(guardDecision == null){ logger.error("********** XACML FAILED TO CONNECT ********"); - guardDecision = "Indeterminate"; + guardDecision = Util.INDETERMINATE; } PolicyGuardResponse guardResponse = new PolicyGuardResponse(guardDecision, UUID.fromString(this.requestId), this.recipe); diff --git a/controlloop/common/guard/src/main/java/org/onap/policy/guard/PolicyGuardXacmlHelper.java b/controlloop/common/guard/src/main/java/org/onap/policy/guard/PolicyGuardXacmlHelper.java index 826f05652..f5a93d8be 100644 --- a/controlloop/common/guard/src/main/java/org/onap/policy/guard/PolicyGuardXacmlHelper.java +++ b/controlloop/common/guard/src/main/java/org/onap/policy/guard/PolicyGuardXacmlHelper.java @@ -25,14 +25,19 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.Serializable; import java.net.HttpURLConnection; import java.net.URL; +import java.util.ArrayList; +import java.util.Base64; import java.util.Iterator; +import java.util.Properties; import java.util.UUID; import org.apache.commons.io.IOUtils; import org.apache.http.entity.ContentType; import org.json.JSONObject; +import org.onap.policy.drools.system.PolicyEngine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,49 +48,86 @@ import com.att.research.xacml.api.Result; public class PolicyGuardXacmlHelper { - - private static final Logger logger = LoggerFactory.getLogger(PolicyGuardXacmlHelper.class); - public static String callPDP(String restfulPdpUrl, PolicyGuardXacmlRequestAttributes xacmlReq) { + private static final Logger logger = LoggerFactory + .getLogger(PolicyGuardXacmlHelper.class); + + public PolicyGuardXacmlHelper() { + init(PolicyEngine.manager.getEnvironment()); + } + + // initialized from 'pdpx.url' property -- + // Each entry in 'restUrls' contains a destination URL, and an optional + // 'Authorization' header entry. 'restUrlIndex' indicates the next + // entry to try -- after each failure, the index is advanced to the + // next entry (wrapping to the beginning, if needed). + static private class URLEntry implements Serializable { + URL restURL; + String authorization = null; + String clientAuth = null; + String environment = null; + }; + + private URLEntry[] restUrls = null; + private int restUrlIndex = 0; + + // REST timeout, initialized from 'pdpx.timeout' property + private int timeout = 20000; + + + // initialized from 'guard.disabled', but may also be set to 'true' if + // there is an initialization error + private boolean disabled = false; + + // errors that forced 'disabled' to be set to 'true' + private String errorMessage = null; + + public String callPDP(PolicyGuardXacmlRequestAttributes xacmlReq) { // // Send it to the PDP // -// com.att.research.xacml.api.Response response = null; String response = null; - + + // + // Build the json request + // JSONObject attributes = new JSONObject(); attributes.put("actor", xacmlReq.getActor_id()); attributes.put("recipe", xacmlReq.getOperation_id()); attributes.put("target", xacmlReq.getTarget_id()); - if (xacmlReq.getClname_id() != null){ + if (xacmlReq.getClname_id() != null) { attributes.put("clname", xacmlReq.getClname_id()); } JSONObject jsonReq = new JSONObject(); jsonReq.put("decisionAttributes", attributes); jsonReq.put("onapName", "PDPD"); - + + URLEntry urlEntry = restUrls[restUrlIndex]; + try { // // Call RESTful PDP // - response = callRESTfulPDP(new ByteArrayInputStream(jsonReq.toString().getBytes()), new URL(restfulPdpUrl/*"https://localhost:8443/pdp/"*/)); + response = callRESTfulPDP(new ByteArrayInputStream(jsonReq + .toString().getBytes()), urlEntry.restURL, + urlEntry.authorization, urlEntry.clientAuth, + urlEntry.environment); } catch (Exception e) { logger.error("Error in sending RESTful request: ", e); } - - + return response; } - - + /** - * This makes an HTTP POST call to a running PDP RESTful servlet to get a decision. + * This makes an HTTP POST call to a running PDP RESTful servlet to get a + * decision. * * @param file * @return response from guard which contains "Permit" or "Deny" */ - private static String callRESTfulPDP(InputStream is, URL restURL) { -// com.att.research.xacml.api.Response response = null; + private String callRESTfulPDP(InputStream is, URL restURL, + String authorization, String clientauth, String environment) { String response = null; String rawDecision = null; HttpURLConnection connection = null; @@ -99,16 +141,28 @@ public class PolicyGuardXacmlHelper { // // Setup our method and headers // - connection.setRequestMethod("POST"); - 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.setRequestProperty("Accept", "application/json"); + if (authorization != null) { + connection.setRequestProperty("Authorization", authorization); + } + if (clientauth != null) { + connection.setRequestProperty("ClientAuth", clientauth); + } + if (environment != null) { + connection.setRequestProperty("Environment", environment); + } + connection.setConnectTimeout(timeout); + connection.setReadTimeout(timeout); + connection.setRequestMethod("POST"); + 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); // @@ -117,115 +171,291 @@ public class PolicyGuardXacmlHelper { try (OutputStream os = connection.getOutputStream()) { IOUtils.copy(is, os); } - // - // Do the connect - // - connection.connect(); - if (connection.getResponseCode() == 200) { - // - // Read the response - // - ContentType contentType = null; - try { - contentType = ContentType.parse(connection.getContentType()); - - if (contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_JSON.getMimeType())) { - InputStream iStream = connection.getInputStream(); - int contentLength = connection.getContentLength(); - - // if content length is -1, respose is chunked, and - // TCP connection will be dropped at the end - byte[] buf = - new byte[contentLength < 0 ? 1024 : contentLength]; - int offset = 0; - for ( ; ; ) - { - if (offset == contentLength) - { - // all expected bytes have been read - response = new String(buf); - break; + // + // Do the connect + // + connection.connect(); + if (connection.getResponseCode() == 200) { + // + // Read the response + // + ContentType contentType = null; + try { + contentType = ContentType + .parse(connection.getContentType()); + + if (contentType.getMimeType().equalsIgnoreCase( + ContentType.APPLICATION_JSON.getMimeType())) { + InputStream iStream = connection.getInputStream(); + int contentLength = connection.getContentLength(); + + // if content length is -1, respose is chunked, and + // TCP connection will be dropped at the end + byte[] buf = new byte[contentLength < 0 ? 1024 + : contentLength]; + int offset = 0; + for (;;) { + if (offset == contentLength) { + // all expected bytes have been read + response = new String(buf); + break; } - int size = iStream.read(buf, offset, - buf.length - offset); - if (size < 0) - { - if (contentLength > 0) - { - logger.error("partial input stream"); - } - else - { - // chunked response -- - // dropped connection is expected - response = new String(buf, 0, offset); + int size = iStream.read(buf, offset, buf.length + - offset); + if (size < 0) { + if (contentLength > 0) { + logger.error("partial input stream"); + } else { + // chunked response -- + // dropped connection is expected + response = new String(buf, 0, offset); } - break; + break; } - offset += size; + offset += size; } - } else { - logger.error("unknown content-type: " + contentType); - } + } else { + logger.error("unknown content-type: " + contentType); + } - } catch (Exception e) { - String message = "Parsing Content-Type: " + connection.getContentType(); + } catch (Exception e) { + String message = "Parsing Content-Type: " + + connection.getContentType(); logger.error(message, e); - } + } - } else { - logger.error(connection.getResponseCode() + " " + connection.getResponseMessage()); - } + } else { + logger.error(connection.getResponseCode() + " " + + connection.getResponseMessage()); + } } catch (Exception e) { - logger.error("Exception in 'PolicyGuardXacmlHelper.callRESTfulPDP'", e); + logger.error( + "Exception in 'PolicyGuardXacmlHelper.callRESTfulPDP'", e); + // + // Connection may have failed, return Indeterminate + // + if(response == null || "".equals(response)){ + return Util.INDETERMINATE; + } } rawDecision = new JSONObject(response).getString("decision"); - + return rawDecision; } - - - public static PolicyGuardResponse ParseXacmlPdpResponse(com.att.research.xacml.api.Response xacmlResponse){ - - if(xacmlResponse == null){ - + + public static PolicyGuardResponse ParseXacmlPdpResponse( + com.att.research.xacml.api.Response xacmlResponse) { + + if (xacmlResponse == null) { + // - //In case the actual XACML response was null, create an empty response object with decision "Indeterminate" + // In case the actual XACML response was null, create an empty + // response object with decision "Indeterminate" // return new PolicyGuardResponse("Indeterminate", null, ""); } - + Iterator<Result> it_res = xacmlResponse.getResults().iterator(); - - Result res = it_res.next(); + + Result res = it_res.next(); String decision_from_xacml_response = res.getDecision().toString(); - Iterator<AttributeCategory> it_attr_cat = res.getAttributes().iterator(); + Iterator<AttributeCategory> it_attr_cat = res.getAttributes() + .iterator(); UUID req_id_from_xacml_response = null; String operation_from_xacml_response = ""; - - while(it_attr_cat.hasNext()){ - Iterator<Attribute> it_attr = it_attr_cat.next().getAttributes().iterator(); - while(it_attr.hasNext()){ + + while (it_attr_cat.hasNext()) { + Iterator<Attribute> it_attr = it_attr_cat.next().getAttributes() + .iterator(); + while (it_attr.hasNext()) { Attribute current_attr = it_attr.next(); String s = current_attr.getAttributeId().stringValue(); - if(s.equals("urn:oasis:names:tc:xacml:1.0:request:request-id")){ - Iterator<AttributeValue<?>> it_values = current_attr.getValues().iterator(); - req_id_from_xacml_response = UUID.fromString(it_values.next().getValue().toString()); + if ("urn:oasis:names:tc:xacml:1.0:request:request-id".equals(s)) { + Iterator<AttributeValue<?>> it_values = current_attr + .getValues().iterator(); + req_id_from_xacml_response = UUID.fromString(it_values + .next().getValue().toString()); } - if(s.equals("urn:oasis:names:tc:xacml:1.0:operation:operation-id")){ - Iterator<AttributeValue<?>> it_values = current_attr.getValues().iterator(); - operation_from_xacml_response = it_values.next().getValue().toString(); + if ("urn:oasis:names:tc:xacml:1.0:operation:operation-id" + .equals(s)) { + Iterator<AttributeValue<?>> it_values = current_attr + .getValues().iterator(); + operation_from_xacml_response = it_values.next().getValue() + .toString(); } - + } } - - - return new PolicyGuardResponse(decision_from_xacml_response, req_id_from_xacml_response, operation_from_xacml_response); - + + return new PolicyGuardResponse(decision_from_xacml_response, + req_id_from_xacml_response, operation_from_xacml_response); + + } + + private void init(Properties properties) { + // used to store error messages + StringBuilder sb = new StringBuilder(); + + // fetch these parameters, if they exist + String timeoutString = properties.getProperty("pdpx.timeout"); + String disabledString = properties.getProperty("guard.disabled"); + + if (disabledString != null) { + // decode optional 'guard.disabled' parameter + disabled = new Boolean(disabledString); + if (disabled) { + // skip everything else + return; + } + } + + /* + * Decode 'pdpx.*' parameters + */ + + // first, the default parameters + String defaultUser = properties.getProperty("pdpx.username"); + String defaultPassword = properties + .getProperty("pdpx.password"); + String defaultClientUser = properties + .getProperty("pdpx.client.username"); + String defaultClientPassword = properties + .getProperty("pdpx.client.password"); + String defaultEnvironment = properties + .getProperty("pdpx.environment"); + + // now, see which numeric entries (1-9) exist + ArrayList<URLEntry> entries = new ArrayList<>(); + + for (int index = 0; index < 10; index += 1) { + String urlPrefix = "guard."; + String pdpxPrefix = "pdpx."; + if (index != 0) { + urlPrefix = urlPrefix + index + "."; + } + + // see if the associated URL exists + String restURLlist = properties.getProperty(urlPrefix + "url"); + if (nullOrEmpty(restURLlist)) { + // no entry for this index + continue; + } + + // support a list of entries separated by semicolons. Each entry + // can be: + // URL + // URL,user + // URL,user,password + for (String restURL : restURLlist.split("\\s*;\\s*")) { + String[] segments = restURL.split("\\s*,\\s*"); + String user = null; + String password = null; + + if (segments.length >= 2) { + // user id is provided + restURL = segments[0]; + user = segments[1]; + if (segments.length >= 3) { + // password is also provided + password = segments[2]; + } + } + + // URL does exist -- create the entry + URLEntry urlEntry = new URLEntry(); + try { + urlEntry.restURL = new URL(restURL); + } catch (java.net.MalformedURLException e) { + // if we don't have a URL, + // don't bother with the rest on this one + sb.append("'").append(urlPrefix).append("url' '") + .append(restURL).append("': ").append(e) + .append(","); + continue; + } + + if (nullOrEmpty(user)) { + // user id was not provided on '*.url' line -- + // extract it from a separate property + user = properties.getProperty(pdpxPrefix + "username", defaultUser); + } + if (nullOrEmpty(password)) { + // password was not provided on '*.url' line -- + // extract it from a separate property + password = properties.getProperty(pdpxPrefix + "password", + defaultPassword); + } + + // see if 'user' and 'password' entries both exist + if (!nullOrEmpty(user) && !nullOrEmpty(password)) { + urlEntry.authorization = "Basic " + + Base64.getEncoder().encodeToString( + (user + ":" + password).getBytes()); + } + + // see if 'client.user' and 'client.password' entries both exist + String clientUser = properties.getProperty(pdpxPrefix + + "client.username", defaultClientUser); + String clientPassword = properties.getProperty(pdpxPrefix + + "client.password", defaultClientPassword); + if (!nullOrEmpty(clientUser) && !nullOrEmpty(clientPassword)) { + urlEntry.clientAuth = "Basic " + + Base64.getEncoder().encodeToString( + (clientUser + ":" + clientPassword) + .getBytes()); + } + + // see if there is an 'environment' entry + String environment = properties.getProperty(pdpxPrefix + + "environment", defaultEnvironment); + if (!nullOrEmpty(environment)) { + urlEntry.environment = environment; + } + + // include this URLEntry in the list + entries.add(urlEntry); + } + } + + if (entries.size() == 0) { + sb.append("'pdpx.*' -- no URLs specified, "); + } else { + restUrls = entries.toArray(new URLEntry[0]); + } + + if (timeoutString != null) { + try { + // decode optional 'pdpx.timeout' parameter + timeout = Integer.valueOf(timeoutString); + } catch (NumberFormatException e) { + sb.append("'pdpx.timeout': " + e + ", "); + logger.trace(e.getLocalizedMessage()); + } + } + + + // if there are any errors, update 'errorMessage' & disable guard + // queries + if (sb.length() != 0) { + // remove the terminating ", ", and extract resulting error message + sb.setLength(sb.length() - 2); + errorMessage = sb.toString(); + disabled = true; + logger.error("Initialization failure: " + errorMessage); + } + } + + /** + * Check if a string is null or an empty string + * + * @param value + * the string to be tested + * @return 'true' if the string is 'null' or has a length of 0, 'false' + * otherwise + */ + static private boolean nullOrEmpty(String value) { + return (value == null || value.isEmpty()); } - - - + } diff --git a/controlloop/common/guard/src/main/java/org/onap/policy/guard/Util.java b/controlloop/common/guard/src/main/java/org/onap/policy/guard/Util.java index b594aff0a..f572cd7fa 100644 --- a/controlloop/common/guard/src/main/java/org/onap/policy/guard/Util.java +++ b/controlloop/common/guard/src/main/java/org/onap/policy/guard/Util.java @@ -31,12 +31,31 @@ import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; import org.onap.policy.controlloop.policy.ControlLoopPolicy; import org.onap.policy.controlloop.policy.guard.ControlLoopGuard; +import org.onap.policy.drools.system.PolicyEngine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.Constructor; public final class Util { + + /* + * Keys for guard properties + */ + public static final String PROP_GUARD_URL = "guard.url"; + public static final String PROP_GUARD_USER = "pdpx.username"; + public static final String PROP_GUARD_PASS = "pdpx.password"; + public static final String PROP_GUARD_CLIENT_USER = "pdpx.client.username"; + public static final String PROP_GUARD_CLIENT_PASS = "pdpx.client.password"; + public static final String PROP_GUARD_ENV = "pdpx.environment"; + + /* + * Guard responses + */ + public static final String INDETERMINATE = "Indeterminate"; + public static final String PERMIT = "Permit"; + public static final String DENY = "Deny"; + private static final Logger logger = LoggerFactory.getLogger(Util.class); public static class Pair<A, B> { @@ -83,5 +102,26 @@ public final class Util { } return null; } + + /** + * Sets Guard Properties. + * + * @see /guard/src/test/java/org/onap/policy/guard/UtilTest.java + * for setting test properties + */ + public static void setGuardEnvProps(String url, String username, String password, String clientName, String clientPassword, String environment){ + PolicyEngine.manager.setEnvironmentProperty(org.onap.policy.guard.Util.PROP_GUARD_URL, url); + PolicyEngine.manager.setEnvironmentProperty(org.onap.policy.guard.Util.PROP_GUARD_USER, username); + PolicyEngine.manager.setEnvironmentProperty(org.onap.policy.guard.Util.PROP_GUARD_PASS, password); + PolicyEngine.manager.setEnvironmentProperty(org.onap.policy.guard.Util.PROP_GUARD_CLIENT_USER, clientName); + PolicyEngine.manager.setEnvironmentProperty(org.onap.policy.guard.Util.PROP_GUARD_CLIENT_PASS, clientPassword); + PolicyEngine.manager.setEnvironmentProperty(org.onap.policy.guard.Util.PROP_GUARD_ENV, environment); + } + public static void setGuardEnvProp(String key, String value){ + PolicyEngine.manager.setEnvironmentProperty(key, value); + } + public static String getGuardProp(String propName){ + return PolicyEngine.manager.getEnvironmentProperty(propName); + } } |