2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019-2020 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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 * SPDX-License-Identifier: Apache-2.0
20 * ============LICENSE_END=========================================================
23 package org.onap.policy.pdp.xacml.application.common.std;
25 import com.att.research.xacml.api.Request;
26 import com.att.research.xacml.api.Response;
27 import com.att.research.xacml.api.pdp.PDPEngine;
28 import com.att.research.xacml.api.pdp.PDPEngineFactory;
29 import com.att.research.xacml.api.pdp.PDPException;
30 import com.att.research.xacml.util.FactoryException;
31 import java.io.IOException;
32 import java.nio.charset.StandardCharsets;
33 import java.nio.file.Files;
34 import java.nio.file.Path;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.List;
40 import java.util.Properties;
42 import org.apache.commons.lang3.tuple.Pair;
43 import org.onap.policy.common.endpoints.parameters.RestServerParameters;
44 import org.onap.policy.models.decisions.concepts.DecisionRequest;
45 import org.onap.policy.models.decisions.concepts.DecisionResponse;
46 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
47 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
48 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
49 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;
50 import org.onap.policy.pdp.xacml.application.common.XacmlApplicationException;
51 import org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider;
52 import org.onap.policy.pdp.xacml.application.common.XacmlPolicyUtils;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
56 public abstract class StdXacmlApplicationServiceProvider implements XacmlApplicationServiceProvider {
58 private static final Logger LOGGER = LoggerFactory.getLogger(StdXacmlApplicationServiceProvider.class);
60 protected String applicationName = "Please Override";
61 protected List<String> actions = Collections.emptyList();
62 protected List<ToscaPolicyTypeIdentifier> supportedPolicyTypes = new ArrayList<>();
64 private Path pathForData = null;
66 private RestServerParameters policyApiParameters;
67 private Properties pdpProperties = null;
68 private PDPEngine pdpEngine = null;
69 private Map<ToscaPolicy, Path> mapLoadedPolicies = new HashMap<>();
71 public StdXacmlApplicationServiceProvider() {
76 public String applicationName() {
77 return applicationName;
81 public List<String> actionDecisionsSupported() {
86 public void initialize(Path pathForData, RestServerParameters policyApiParameters)
87 throws XacmlApplicationException {
91 this.pathForData = pathForData;
92 LOGGER.info("New Path is {}", this.pathForData.toAbsolutePath());
96 this.policyApiParameters = policyApiParameters;
98 // Look for and load the properties object
101 pdpProperties = XacmlPolicyUtils.loadXacmlProperties(XacmlPolicyUtils.getPropertiesPath(pathForData));
102 LOGGER.info("{}", pdpProperties);
103 } catch (IOException e) {
104 throw new XacmlApplicationException("Failed to load " + XacmlPolicyUtils.XACML_PROPERTY_FILE, e);
109 createEngine(pdpProperties);
113 public List<ToscaPolicyTypeIdentifier> supportedPolicyTypes() {
114 return supportedPolicyTypes;
118 public boolean canSupportPolicyType(ToscaPolicyTypeIdentifier policyTypeId) {
119 throw new UnsupportedOperationException("Please override and implement canSupportPolicyType");
123 public synchronized void loadPolicy(ToscaPolicy toscaPolicy) throws XacmlApplicationException {
126 // Convert the policies first
128 Object xacmlPolicy = this.getTranslator(toscaPolicy.getType()).convertPolicy(toscaPolicy);
129 if (xacmlPolicy == null) {
130 throw new ToscaPolicyConversionException("Failed to convert policy");
133 // Create a copy of the properties object
135 Properties newProperties = this.getProperties();
137 // Construct the filename
139 Path refPath = XacmlPolicyUtils.constructUniquePolicyFilename(xacmlPolicy, this.getDataPath());
141 // Write the policy to disk
142 // Maybe check for an error
144 if (XacmlPolicyUtils.writePolicyFile(refPath, xacmlPolicy) == null) {
145 throw new ToscaPolicyConversionException("Unable to writePolicyFile");
147 if (LOGGER.isInfoEnabled()) {
148 LOGGER.info("Xacml Policy is {}{}", XacmlPolicyUtils.LINE_SEPARATOR,
149 new String(Files.readAllBytes(refPath), StandardCharsets.UTF_8));
152 // Add root policy to properties object
154 XacmlPolicyUtils.addRootPolicy(newProperties, refPath);
156 // Write the properties to disk
158 XacmlPolicyUtils.storeXacmlProperties(newProperties,
159 XacmlPolicyUtils.getPropertiesPath(this.getDataPath()));
163 this.createEngine(newProperties);
165 // Save the properties
167 this.pdpProperties = newProperties;
171 this.mapLoadedPolicies.put(toscaPolicy, refPath);
172 } catch (IOException | ToscaPolicyConversionException e) {
173 throw new XacmlApplicationException("loadPolicy failed", e);
178 public synchronized boolean unloadPolicy(ToscaPolicy toscaPolicy) throws XacmlApplicationException {
180 // Find it in our map
182 Path refPolicy = this.mapLoadedPolicies.get(toscaPolicy);
183 if (refPolicy == null) {
184 LOGGER.error("Failed to find ToscaPolicy {} in our map size {}", toscaPolicy.getMetadata(),
185 this.mapLoadedPolicies.size());
189 // Create a copy of the properties object
191 Properties newProperties = this.getProperties();
193 // Remove it from the properties
195 XacmlPolicyUtils.removeRootPolicy(newProperties, refPolicy);
197 // We can delete the file
200 Files.delete(refPolicy);
201 } catch (IOException e) {
202 LOGGER.error("Failed to delete policy {} from disk {}", toscaPolicy.getMetadata(),
203 refPolicy.toAbsolutePath(), e);
206 // Write the properties to disk
209 XacmlPolicyUtils.storeXacmlProperties(newProperties,
210 XacmlPolicyUtils.getPropertiesPath(this.getDataPath()));
211 } catch (IOException e) {
212 LOGGER.error("Failed to save the properties to disk {}", newProperties, e);
217 this.createEngine(newProperties);
219 // Save the properties
221 this.pdpProperties = newProperties;
225 if (this.mapLoadedPolicies.remove(toscaPolicy) == null) {
226 LOGGER.error("Failed to remove toscaPolicy {} from internal map size {}", toscaPolicy.getMetadata(),
227 this.mapLoadedPolicies.size());
230 // Not sure if any of the errors above warrant returning false
236 public Pair<DecisionResponse, Response> makeDecision(DecisionRequest request,
237 Map<String, String[]> requestQueryParams) {
239 // Convert to a XacmlRequest
241 Request xacmlRequest;
243 xacmlRequest = this.getTranslator().convertRequest(request);
244 } catch (ToscaPolicyConversionException e) {
245 LOGGER.error("Failed to convert request", e);
246 DecisionResponse response = new DecisionResponse();
247 response.setStatus("error");
248 response.setMessage(e.getLocalizedMessage());
249 return Pair.of(response, null);
252 // Now get a decision
254 Response xacmlResponse = this.xacmlDecision(xacmlRequest);
256 // Convert to a DecisionResponse
258 return Pair.of(this.getTranslator().convertResponse(xacmlResponse), xacmlResponse);
261 protected abstract ToscaPolicyTranslator getTranslator(String type);
263 protected ToscaPolicyTranslator getTranslator() {
264 return this.getTranslator("");
267 protected synchronized PDPEngine getEngine() {
268 return this.pdpEngine;
271 protected synchronized Properties getProperties() {
272 Properties newProperties = new Properties();
273 newProperties.putAll(pdpProperties);
274 return newProperties;
277 protected synchronized Path getDataPath() {
282 * Creates an instance of PDP engine given the Properties object.
284 protected synchronized void createEngine(Properties properties) {
286 // Now initialize the XACML PDP Engine
289 PDPEngineFactory factory = getPdpEngineFactory();
290 PDPEngine engine = factory.newEngine(properties);
291 if (engine != null) {
293 // If there is a previous engine have it shutdown.
295 this.destroyEngine();
299 this.pdpEngine = engine;
301 } catch (FactoryException e) {
302 LOGGER.error("Failed to create XACML PDP Engine", e);
306 protected synchronized void destroyEngine() {
307 if (this.pdpEngine == null) {
311 this.pdpEngine.shutdown();
312 } catch (Exception e) {
313 LOGGER.warn("Exception thrown when destroying XACML PDP engine.", e);
315 this.pdpEngine = null;
319 * Make a decision call.
321 * @param request Incoming request object
322 * @return Response object
324 protected synchronized Response xacmlDecision(Request request) {
326 // This is what we need to return
328 Response response = null;
332 long timeStart = System.currentTimeMillis();
334 response = this.pdpEngine.decide(request);
335 } catch (PDPException e) {
336 LOGGER.error("Xacml PDP Engine decide failed", e);
339 // Track the end of timing
341 long timeEnd = System.currentTimeMillis();
342 LOGGER.info("Elapsed Time: {}ms", (timeEnd - timeStart));
347 // these may be overridden by junit tests
349 protected PDPEngineFactory getPdpEngineFactory() throws FactoryException {
350 return PDPEngineFactory.newInstance();