Format ONAP-XACML and add JUnit
[policy/engine.git] / ONAP-XACML / src / main / java / org / onap / policy / xacml / std / pip / engines / OperationHistoryEngine.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP-XACML
4  * ================================================================================
5  * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.xacml.std.pip.engines;
22
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.XACML;
27 import com.att.research.xacml.api.XACML3;
28 import com.att.research.xacml.api.pip.PIPException;
29 import com.att.research.xacml.api.pip.PIPFinder;
30 import com.att.research.xacml.api.pip.PIPRequest;
31 import com.att.research.xacml.api.pip.PIPResponse;
32 import com.att.research.xacml.std.IdentifierImpl;
33 import com.att.research.xacml.std.StdMutableAttribute;
34 import com.att.research.xacml.std.datatypes.DataTypes;
35 import com.att.research.xacml.std.pip.StdMutablePIPResponse;
36 import com.att.research.xacml.std.pip.StdPIPRequest;
37 import com.att.research.xacml.std.pip.StdPIPResponse;
38 import com.att.research.xacml.std.pip.engines.StdConfigurableEngine;
39 import com.att.research.xacml.util.XACMLProperties;
40
41 import java.math.BigInteger;
42 import java.util.ArrayList;
43 import java.util.Collection;
44 import java.util.HashSet;
45 import java.util.Iterator;
46 import java.util.Properties;
47 import java.util.Set;
48
49 import javax.persistence.EntityManager;
50 import javax.persistence.Persistence;
51 import javax.persistence.Query;
52
53 import org.eclipse.persistence.config.PersistenceUnitProperties;
54 import org.onap.policy.common.logging.flexlogger.FlexLogger;
55 import org.onap.policy.common.logging.flexlogger.Logger;
56
57 /**
58  * PIP Engine for Implementing {@link com.att.research.xacml.std.pip.engines.ConfigurableEngine} interface to provide
59  * attribute retrieval from Operation History Table.
60  *
61  * @version $Revision$
62  */
63 public class OperationHistoryEngine extends StdConfigurableEngine {
64     public static final String DEFAULT_DESCRIPTION = "PIP for retrieving Operations History from DB";
65     public static final String DEFAULT_ISSUER = "org:onap:xacml:guard:historydb";
66
67     private static final Logger LOGGER = FlexLogger.getLogger(OperationHistoryEngine.class);
68
69     private static final PIPRequest PIP_REQUEST_ACTOR = new StdPIPRequest(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE,
70             new IdentifierImpl("actor"), XACML.ID_DATATYPE_STRING);
71
72     private static final PIPRequest PIP_REQUEST_RECIPE = new StdPIPRequest(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE,
73             new IdentifierImpl("recipe"), XACML.ID_DATATYPE_STRING);
74
75     private static final PIPRequest PIP_REQUEST_TARGET = new StdPIPRequest(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE,
76             new IdentifierImpl("target"), XACML.ID_DATATYPE_STRING);
77
78     public OperationHistoryEngine() {
79         super();
80     }
81
82     private void addIntegerAttribute(StdMutablePIPResponse stdPipResponse, Identifier category, Identifier attributeId,
83             int value, PIPRequest pipRequest) {
84         AttributeValue<BigInteger> attributeValue = null;
85         try {
86             attributeValue = DataTypes.DT_INTEGER.createAttributeValue(value);
87         } catch (Exception ex) {
88             LOGGER.error("Failed to convert " + value + " to an AttributeValue<Boolean>", ex);
89         }
90         if (attributeValue != null) {
91             stdPipResponse.addAttribute(new StdMutableAttribute(category, attributeId, attributeValue,
92                     pipRequest.getIssuer()/* this.getIssuer() */, false));
93         }
94     }
95
96     @Override
97     public Collection<PIPRequest> attributesRequired() {
98         return new ArrayList<>();
99     }
100
101     @Override
102     public Collection<PIPRequest> attributesProvided() {
103         return new ArrayList<>();
104     }
105
106     @Override
107     public PIPResponse getAttributes(PIPRequest pipRequest, PIPFinder pipFinder) throws PIPException {
108         LOGGER.info("Entering FeqLimiter PIP");
109         /*
110          * First check to see if the issuer is set and then match it
111          */
112         String string;
113         if ((string = pipRequest.getIssuer()) == null) {
114             LOGGER.info("FeqLimiter PIP - No issuer in the request!");
115             return StdPIPResponse.PIP_RESPONSE_EMPTY;
116         } else {
117             // Notice, we are checking here for the base issuer prefix.
118             if (!string.contains(this.getIssuer())) {
119                 LOGGER.debug("Requested issuer '" + string + "' does not match "
120                         + (this.getIssuer() == null ? "null" : "'" + this.getIssuer() + "'"));
121                 LOGGER.info("FeqLimiter PIP - Issuer " + string + " does not match with: " + this.getIssuer());
122                 return StdPIPResponse.PIP_RESPONSE_EMPTY;
123             }
124         }
125         String[] s1 = string.split("tw:");
126         String[] s2 = s1[1].split(":");
127         int timeWindowVal = Integer.parseInt(s2[0]);// number [of minutes, hours, days...]
128         String timeWindowScale = s2[1];// e.g., minute, hour, day, week, month, year
129         String actor = getActor(pipFinder).iterator().next();
130         String operation = getRecipe(pipFinder).iterator().next();
131         String target = getTarget(pipFinder).iterator().next();
132         String timeWindow = timeWindowVal + " " + timeWindowScale;
133         LOGGER.info("Going to query DB about: " + actor + " " + operation + " " + target + " " + timeWindow);
134         int countFromDB = getCountFromDB(actor, operation, target, timeWindowVal, timeWindowScale);
135         StdMutablePIPResponse stdPipResponse = new StdMutablePIPResponse();
136         this.addIntegerAttribute(stdPipResponse, XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE, new IdentifierImpl("count"),
137                 countFromDB, pipRequest);
138         return new StdPIPResponse(stdPipResponse);
139     }
140
141     @Override
142     public void configure(String id, Properties properties) throws PIPException {
143         super.configure(id, properties);
144         if (this.getDescription() == null) {
145             this.setDescription(DEFAULT_DESCRIPTION);
146         }
147         if (this.getIssuer() == null) {
148             this.setIssuer(DEFAULT_ISSUER);
149         }
150     }
151
152     private PIPResponse getAttribute(PIPRequest pipRequest, PIPFinder pipFinder) {
153         PIPResponse pipResponse = null;
154         try {
155             pipResponse = pipFinder.getMatchingAttributes(pipRequest, this);
156             if (pipResponse.getStatus() != null && !pipResponse.getStatus().isOk()) {
157                 LOGGER.info("Error retrieving " + pipRequest.getAttributeId().stringValue() + ": "
158                         + pipResponse.getStatus().toString());
159                 pipResponse = null;
160             }
161             if (pipResponse != null && pipResponse.getAttributes().isEmpty()) {
162                 LOGGER.info("No value for " + pipRequest.getAttributeId().stringValue());
163                 pipResponse = null;
164             }
165         } catch (PIPException ex) {
166             LOGGER.error("PIPException getting subject-id attribute: " + ex.getMessage(), ex);
167         }
168         return pipResponse;
169     }
170
171     private Set<String> getActor(PIPFinder pipFinder) {
172         return getUuids(this.getAttribute(PIP_REQUEST_ACTOR, pipFinder));
173     }
174
175     private Set<String> getRecipe(PIPFinder pipFinder) {
176         return getUuids(this.getAttribute(PIP_REQUEST_RECIPE, pipFinder));
177     }
178
179     private Set<String> getTarget(PIPFinder pipFinder) {
180         return getUuids(this.getAttribute(PIP_REQUEST_TARGET, pipFinder));
181     }
182
183     private Set<String> getUuids(PIPResponse pipResponseAttUid) {
184         /*
185          * Get the UID from either the subject id or the attuid property
186          */
187         if (pipResponseAttUid == null) {
188             return new HashSet<>();
189         }
190         /*
191          * Iterate over all of the returned results and do the LDAP requests
192          */
193         Set<String> setAttUids = new HashSet<>();
194         for (Attribute attributeAttUid : pipResponseAttUid.getAttributes()) {
195             Iterator<AttributeValue<String>> iterAttributeValues = attributeAttUid.findValues(DataTypes.DT_STRING);
196             if (iterAttributeValues != null) {
197                 while (iterAttributeValues.hasNext()) {
198                     String attuid = iterAttributeValues.next().getValue();
199                     if (attuid != null) {
200                         setAttUids.add(attuid);
201                     }
202                 }
203             }
204         }
205         return setAttUids;
206     }
207
208     private static int getCountFromDB(String actor, String operation, String target, int timeWindow, String timeUnits) {
209         EntityManager em;
210         try {
211             Properties properties = XACMLProperties.getProperties();
212             properties.setProperty(PersistenceUnitProperties.ECLIPSELINK_PERSISTENCE_XML,
213                     "META-INF/operationHistoryPU.xml");
214             em = Persistence.createEntityManagerFactory("OperationsHistoryPU", properties).createEntityManager();
215         } catch (Exception e) {
216             LOGGER.error(
217                     "Test thread got Exception " + e.getLocalizedMessage() + " Can't connect to Operations History DB.",
218                     e);
219             return -1;
220         }
221         // Preventing SQL injection
222         if (!validTimeUnits(timeUnits)) {
223             LOGGER.error("given PIP timeUnits is not valid. " + timeUnits);
224             em.close();
225             return -1;
226         }
227         String sql = "select count(*) as count from operationshistory10 where outcome<>'Failure_Guard' and actor=?"
228                 + " and operation=?" + " and target=?" + " and endtime between date_sub(now(),interval ? " + timeUnits
229                 + ") and now()";
230         Query nq = em.createNativeQuery(sql);
231         nq.setParameter(1, actor);
232         nq.setParameter(2, operation);
233         nq.setParameter(3, target);
234         nq.setParameter(4, timeWindow);
235         int ret = ((Number) nq.getSingleResult()).intValue();
236         LOGGER.info("###########************** History count: " + ret);
237         em.close();
238         return ret;
239     }
240
241     // Validating Time Units to prevent SQL Injection.
242     private static boolean validTimeUnits(String timeUnits) {
243         return ("minute".equalsIgnoreCase(timeUnits) || "hour".equalsIgnoreCase(timeUnits)
244                 || "day".equalsIgnoreCase(timeUnits) || "week".equalsIgnoreCase(timeUnits)
245                 || "month".equalsIgnoreCase(timeUnits) || "year".equalsIgnoreCase(timeUnits)) ? true : false;
246     }
247 }