2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.pdp.xacml.application.common;
23 import com.att.research.xacml.api.Attribute;
24 import com.att.research.xacml.api.AttributeValue;
25 import com.att.research.xacml.api.Identifier;
26 import com.att.research.xacml.api.XACML3;
27 import com.att.research.xacml.api.pip.PIPException;
28 import com.att.research.xacml.api.pip.PIPFinder;
29 import com.att.research.xacml.api.pip.PIPRequest;
30 import com.att.research.xacml.api.pip.PIPResponse;
31 import com.att.research.xacml.std.StdMutableAttribute;
32 import com.att.research.xacml.std.datatypes.DataTypes;
33 import com.att.research.xacml.std.pip.StdMutablePIPResponse;
34 import com.att.research.xacml.std.pip.StdPIPRequest;
35 import com.att.research.xacml.std.pip.StdPIPResponse;
36 import com.att.research.xacml.std.pip.engines.StdConfigurableEngine;
37 import com.google.common.base.Strings;
39 import java.math.BigInteger;
40 import java.util.Arrays;
41 import java.util.Collection;
42 import java.util.Collections;
43 import java.util.Iterator;
44 import java.util.Properties;
46 import javax.persistence.EntityManager;
47 import javax.persistence.Persistence;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
52 public class OnapOperationsHistoryPipEngine extends StdConfigurableEngine {
53 private static Logger logger = LoggerFactory.getLogger(OnapOperationsHistoryPipEngine.class);
55 private static final PIPRequest PIP_REQUEST_ACTOR = new StdPIPRequest(
56 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE,
57 ToscaDictionary.ID_RESOURCE_GUARD_ACTOR,
58 XACML3.ID_DATATYPE_STRING);
60 private static final PIPRequest PIP_REQUEST_RECIPE = new StdPIPRequest(
61 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE,
62 ToscaDictionary.ID_RESOURCE_GUARD_RECIPE,
63 XACML3.ID_DATATYPE_STRING);
65 private static final PIPRequest PIP_REQUEST_TARGET = new StdPIPRequest(
66 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE,
67 ToscaDictionary.ID_RESOURCE_GUARD_TARGETID,
68 XACML3.ID_DATATYPE_STRING);
70 private Properties properties;
72 public OnapOperationsHistoryPipEngine() {
77 public Collection<PIPRequest> attributesRequired() {
78 return Arrays.asList(PIP_REQUEST_ACTOR, PIP_REQUEST_RECIPE, PIP_REQUEST_TARGET);
82 public Collection<PIPRequest> attributesProvided() {
83 return Collections.emptyList();
87 public PIPResponse getAttributes(PIPRequest pipRequest, PIPFinder pipFinder) throws PIPException {
88 logger.debug("getAttributes requesting attribute {} of type {} for issuer {}",
89 pipRequest.getAttributeId(), pipRequest.getDataTypeId(), pipRequest.getIssuer());
91 // Determine if the issuer is correct
93 if (Strings.isNullOrEmpty(pipRequest.getIssuer())) {
94 logger.debug("issuer is null - returning empty response");
96 // We only respond to ourself as the issuer
98 return StdPIPResponse.PIP_RESPONSE_EMPTY;
100 if (! pipRequest.getIssuer().startsWith(ToscaDictionary.GUARD_ISSUER)) {
101 logger.debug("Issuer does not start with guard");
103 // We only respond to ourself as the issuer
105 return StdPIPResponse.PIP_RESPONSE_EMPTY;
108 // Parse out the issuer which denotes the time window
110 // Eg: urn:org:onapxacml:guard:historydb:tw:10:minute
112 String[] s1 = pipRequest.getIssuer().split("tw:");
113 String[] s2 = s1[1].split(":");
114 int timeWindowVal = Integer.parseInt(s2[0]);
115 String timeWindowScale = s2[1];
117 // Grab other attribute values
119 String actor = getActor(pipFinder);
120 String operation = getRecipe(pipFinder);
121 String target = getTarget(pipFinder);
122 String timeWindow = timeWindowVal + " " + timeWindowScale;
123 logger.info("Going to query DB about: actor {} operation {} target {} time window {}",
124 actor, operation, target, timeWindow);
128 if (actor == null || operation == null || target == null) {
130 // See if we have all the values
132 logger.error("missing attributes return empty");
133 return StdPIPResponse.PIP_RESPONSE_EMPTY;
136 // Ok do the database query
138 int operationCount = doDatabaseQuery(actor, operation, target, timeWindowVal, timeWindowScale);
140 // Right now return empty
142 StdMutablePIPResponse stdPipResponse = new StdMutablePIPResponse();
143 this.addIntegerAttribute(stdPipResponse,
144 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE,
145 ToscaDictionary.ID_RESOURCE_GUARD_OPERATIONCOUNT,
148 return new StdPIPResponse(stdPipResponse);
152 public void configure(String id, Properties properties) throws PIPException {
153 super.configure(id, properties);
154 logger.debug("Configuring historyDb PIP {}", properties);
155 this.properties = properties;
158 private String getActor(PIPFinder pipFinder) {
160 // Get the actor value
162 PIPResponse pipResponse = this.getAttribute(PIP_REQUEST_ACTOR, pipFinder);
163 if (pipResponse == null) {
164 logger.error("Need actor attribute which is not found");
170 return findFirstAttributeValue(pipResponse);
173 private String getRecipe(PIPFinder pipFinder) {
175 // Get the actor value
177 PIPResponse pipResponse = this.getAttribute(PIP_REQUEST_RECIPE, pipFinder);
178 if (pipResponse == null) {
179 logger.error("Need recipe attribute which is not found");
185 return findFirstAttributeValue(pipResponse);
188 private String getTarget(PIPFinder pipFinder) {
190 // Get the actor value
192 PIPResponse pipResponse = this.getAttribute(PIP_REQUEST_TARGET, pipFinder);
193 if (pipResponse == null) {
194 logger.error("Need target attribute which is not found");
200 return findFirstAttributeValue(pipResponse);
203 private PIPResponse getAttribute(PIPRequest pipRequest, PIPFinder pipFinder) {
204 PIPResponse pipResponse = null;
206 pipResponse = pipFinder.getMatchingAttributes(pipRequest, this);
207 if (pipResponse.getStatus() != null && !pipResponse.getStatus().isOk()) {
208 if (logger.isInfoEnabled()) {
209 logger.info("get attribute error retrieving {}: {}", pipRequest.getAttributeId().stringValue(),
210 pipResponse.getStatus());
214 if (pipResponse != null && pipResponse.getAttributes().isEmpty()) {
215 if (logger.isInfoEnabled()) {
216 logger.info("No value for {}", pipRequest.getAttributeId().stringValue());
220 } catch (PIPException ex) {
221 logger.error("PIPException getting subject-id attribute: " + ex.getMessage(), ex);
226 private String findFirstAttributeValue(PIPResponse pipResponse) {
227 for (Attribute attribute: pipResponse.getAttributes()) {
228 Iterator<AttributeValue<String>> iterAttributeValues = attribute.findValues(DataTypes.DT_STRING);
229 if (iterAttributeValues != null) {
230 while (iterAttributeValues.hasNext()) {
231 String value = iterAttributeValues.next().getValue();
241 private void addIntegerAttribute(StdMutablePIPResponse stdPipResponse, Identifier category,
242 Identifier attributeId, int value, PIPRequest pipRequest) {
243 AttributeValue<BigInteger> attributeValue = null;
245 attributeValue = DataTypes.DT_INTEGER.createAttributeValue(value);
246 } catch (Exception e) {
247 logger.error("Failed to convert {} to integer {}", value, e);
249 if (attributeValue != null) {
250 stdPipResponse.addAttribute(new StdMutableAttribute(category, attributeId, attributeValue,
251 pipRequest.getIssuer(), false));
255 private int doDatabaseQuery(String actor, String operation, String target, int timeWindowVal,
256 String timeWindowScale) {
257 logger.info("Querying operations history for {} {} {} {} {}",
258 actor, operation, target, timeWindowVal, timeWindowScale);
260 // Create our entity manager
265 // In case there are any overloaded properties for the JPA
267 Properties emProperties = new Properties(properties);
269 // Create the entity manager factory
271 em = Persistence.createEntityManagerFactory(
272 properties.getProperty("historydb.persistenceunit", "OperationsHistoryPU"),
273 emProperties).createEntityManager();
274 } catch (Exception e) {
275 logger.error("Persistence failed {} operations history db {}", e.getLocalizedMessage(), e);
279 // Compute the time window
281 if (! "minute".equalsIgnoreCase(timeWindowScale)
282 && ! "hour".equalsIgnoreCase(timeWindowScale)
283 && ! "day".equalsIgnoreCase(timeWindowScale)
284 && ! "week".equalsIgnoreCase(timeWindowScale)
285 && ! "month".equalsIgnoreCase(timeWindowScale)
286 && ! "year".equalsIgnoreCase(timeWindowScale)) {
290 logger.error("Unsupported time window scale value {}", timeWindowScale);
292 // Throw an exception instead?
299 Object result = null;
304 String strQuery = "select count(*) as numops from operationshistory"
305 + " where outcome<>'Failure_Guard'"
309 + " and endtime between TIMESTAMPADD("
310 + timeWindowScale.toUpperCase()
311 + ", ?, CURRENT_TIMESTAMP)"
312 + " and CURRENT_TIMESTAMP";
314 // We are expecting a single result
316 result = em.createNativeQuery(strQuery)
317 .setParameter(1, actor)
318 .setParameter(2, operation)
319 .setParameter(3, target)
320 .setParameter(4, timeWindowVal * -1)
322 } catch (Exception e) {
323 logger.error("Named query failed ", e);
326 // Check our query results
328 if (result != null) {
330 // Success let's see what JPA returned to us
332 logger.info("operations query returned {}", result);
334 // Should get back a long
336 if (result instanceof Long) {
337 return ((Long) result).intValue();
340 // We shouldn't really get this result, but just
341 // in case we'll do the dirty work of parsing the
342 // string representation of the object.
344 return Integer.parseInt(result.toString());
347 // We get here if we didn't get a result. Should
348 // we propagate back an exception?