2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 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.
18 * ============LICENSE_END=========================================================
22 package org.openecomp.policy.std;
24 import java.io.FileInputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.StringReader;
28 import java.io.UnsupportedEncodingException;
30 import java.net.URLConnection;
31 import java.nio.charset.StandardCharsets;
32 import java.nio.file.Files;
33 import java.nio.file.Path;
34 import java.nio.file.Paths;
35 import java.text.ParseException;
36 import java.text.SimpleDateFormat;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Base64;
40 import java.util.Collection;
41 import java.util.Collections;
42 import java.util.HashMap;
43 import java.util.List;
45 import java.util.Properties;
46 import java.util.UUID;
48 import javax.json.Json;
49 import javax.json.JsonObject;
50 import javax.json.JsonReader;
51 import javax.xml.parsers.DocumentBuilder;
52 import javax.xml.parsers.DocumentBuilderFactory;
54 import org.openecomp.policy.api.AttributeType;
55 import org.openecomp.policy.api.ConfigRequestParameters;
56 import org.openecomp.policy.api.DecisionRequestParameters;
57 import org.openecomp.policy.api.DecisionResponse;
58 import org.openecomp.policy.api.DeletePolicyParameters;
59 import org.openecomp.policy.api.DictionaryParameters;
60 import org.openecomp.policy.api.DictionaryResponse;
61 import org.openecomp.policy.api.EventRequestParameters;
62 import org.openecomp.policy.api.ImportParameters;
63 import org.openecomp.policy.api.MetricsRequestParameters;
64 import org.openecomp.policy.api.MetricsResponse;
65 import org.openecomp.policy.api.NotificationHandler;
66 import org.openecomp.policy.api.NotificationScheme;
67 import org.openecomp.policy.api.PDPNotification;
68 import org.openecomp.policy.api.PolicyChangeResponse;
69 import org.openecomp.policy.api.PolicyClass;
70 import org.openecomp.policy.api.PolicyConfig;
71 import org.openecomp.policy.api.PolicyConfigException;
72 import org.openecomp.policy.api.PolicyConfigType;
73 import org.openecomp.policy.api.PolicyDecisionException;
74 import org.openecomp.policy.api.PolicyEngineException;
75 import org.openecomp.policy.api.PolicyEventException;
76 import org.openecomp.policy.api.PolicyException;
77 import org.openecomp.policy.api.PolicyParameters;
78 import org.openecomp.policy.api.PolicyResponse;
79 import org.openecomp.policy.api.PolicyType;
80 import org.openecomp.policy.api.PushPolicyParameters;
81 import org.openecomp.policy.common.logging.flexlogger.FlexLogger;
82 import org.openecomp.policy.common.logging.flexlogger.Logger;
83 import org.openecomp.policy.models.APIDictionaryResponse;
84 import org.openecomp.policy.models.APIPolicyConfigResponse;
85 import org.openecomp.policy.utils.AAFPolicyClient.Environment;
86 import org.openecomp.policy.utils.PolicyUtils;
87 import org.openecomp.policy.xacml.api.XACMLErrorConstants;
88 import org.springframework.core.io.FileSystemResource;
89 import org.springframework.http.HttpEntity;
90 import org.springframework.http.HttpHeaders;
91 import org.springframework.http.HttpMethod;
92 import org.springframework.http.HttpStatus;
93 import org.springframework.http.MediaType;
94 import org.springframework.http.ResponseEntity;
95 import org.springframework.util.LinkedMultiValueMap;
96 import org.springframework.web.client.HttpClientErrorException;
97 import org.springframework.web.client.RestTemplate;
98 import org.xml.sax.InputSource;
100 import com.att.aft.dme2.internal.gson.Gson;
101 import com.att.aft.dme2.internal.gson.GsonBuilder;
102 import com.fasterxml.jackson.core.JsonProcessingException;
105 * PolicyEngine Implementation class
109 public class StdPolicyEngine {
110 private static final String ERROR_AUTH_GET_PERM = "You are not allowed to Make this Request. Please contact PolicyAdmin to give access to: ";
111 private static final String DEFAULT_NOTIFICATION = "websocket";
113 private String propertyFilePath = null;
114 private String clientEncoding = null;
115 private String contentType = null;
116 private static List<String> pdps = null;
117 private static String environment= null;
118 private static String userName = null;
119 private static String pass = null;
120 private static List<String> encoding = null;
121 private static boolean junit = false;
122 private List<String> pdpDefault = null;
123 private List<String> typeDefault = null;
124 private List<String> notificationType = new ArrayList<String>();
125 private List<String> notificationURLList = new ArrayList<String>();
126 private NotificationScheme scheme = null;
127 private NotificationHandler handler = null;
128 private AutoClientUEB uebClientThread = null;
129 private Thread registerUEBThread = null;
130 private boolean uebThread = false;
131 private AutoClientDMAAP dmaapClientThread = null;
132 private Thread registerDMAAPThread = null;
133 private boolean dmaapThread = false;
134 private String topic = null;
135 private String apiKey = null;
136 private String apiSecret = null;
138 private static final String UNIQUEID = UUID.randomUUID ().toString ();
139 private static final Logger LOGGER = FlexLogger.getLogger(StdPolicyConfig.class.getName());
142 * Taking the Property file even if it null.
144 public StdPolicyEngine(String propertyFilePath, String clientKey) throws PolicyEngineException {
145 setProperty(propertyFilePath, clientKey);
149 * Taking the Notification Constructor.
151 public StdPolicyEngine(String propertyFilePath,
152 NotificationScheme scheme,
153 NotificationHandler handler) throws PolicyEngineException {
154 setProperty(propertyFilePath, null);
155 this.scheme = scheme;
156 this.handler = handler;
157 if ((!"ueb".equals(notificationType.get(0)))||(!"dmaap".equals(notificationType.get(0)))){
158 AutoClientEnd.setAuto(scheme, handler);
160 notification(scheme, handler);
164 * Taking the Notification Constructor.
166 public StdPolicyEngine(String propertyFilePath, NotificationScheme scheme) throws PolicyEngineException {
167 setProperty(propertyFilePath, null);
168 this.scheme = scheme;
173 * sendEvent API Implementation
175 public Collection<PolicyResponse> sendEvent(Map<String, String> eventAttributes, UUID requestID) throws PolicyEventException {
176 return sendEventImpl(eventAttributes, requestID);
180 * sendEvent API Implementation for eventRequestParameters
182 public Collection<PolicyResponse> sendEvent(EventRequestParameters eventRequestParameters) throws PolicyEventException{
183 if(eventRequestParameters==null){
184 String message = XACMLErrorConstants.ERROR_DATA_ISSUE + "No event Request Parameters Given. ";
185 LOGGER.error(message);
186 throw new PolicyEventException(message);
188 return sendEventImpl(eventRequestParameters.getEventAttributes(), eventRequestParameters.getRequestID());
192 * getConfig using configRequestParameters Implementation
194 public Collection<PolicyConfig> getConfig(ConfigRequestParameters configRequestParameters) throws PolicyConfigException{
195 return getConfigImpl(configRequestParameters);
199 * listPolicies using configRequestParameters Implementation
201 public Collection<String> listConfig(ConfigRequestParameters listPolicyRequestParameters) throws PolicyConfigException{
202 return listConfigImpl(listPolicyRequestParameters);
206 * getDecision using the decision Attributes.
208 public DecisionResponse getDecision(String eCOMPComponentName, Map<String, String> decisionAttributes, UUID requestID) throws PolicyDecisionException {
209 return getDecisionImpl(eCOMPComponentName, decisionAttributes, requestID);
213 * getDecision Using decisionRequestParameters.
215 public DecisionResponse getDecision(DecisionRequestParameters decisionRequestParameters) throws PolicyDecisionException{
216 if(decisionRequestParameters==null){
217 String message = XACMLErrorConstants.ERROR_DATA_ISSUE + "No Decision Request Parameters Given. ";
218 LOGGER.error(message);
219 throw new PolicyDecisionException(message);
221 return getDecisionImpl(decisionRequestParameters.getECOMPComponentName(), decisionRequestParameters.getDecisionAttributes(), decisionRequestParameters.getRequestID());
225 * getMetrics using metricsRequestParameters
227 public MetricsResponse getMetrics(MetricsRequestParameters parameters) throws PolicyException{
228 return getMetricsImpl(parameters);
231 public MetricsResponse getMetricsImpl(MetricsRequestParameters parameters) throws PolicyException{
232 StdMetricsResponse response = new StdMetricsResponse();
233 String resource = "getMetrics";
234 String body = new String();
236 // Create the Request
238 if (parameters!=null) {
239 body = PolicyUtils.objectToJsonString(parameters);
241 } catch (JsonProcessingException e) {
242 String message = XACMLErrorConstants.ERROR_SCHEMA_INVALID + e;
243 LOGGER.error(message);
244 throw new PolicyException(message, e);
248 ResponseEntity<String> result = callNewPDP(resource, HttpMethod.GET, body, String.class);
250 response.setResponseMessage(result.getBody());
251 response.setResponseCode(result.getStatusCode().value());
252 } catch (PolicyException exception) {
253 if(exception.getCause()!=null && exception.getCause() instanceof HttpClientErrorException){
254 LOGGER.error(exception);
255 HttpClientErrorException ex = (HttpClientErrorException) exception.getCause();
256 response.setResponseCode(ex.getRawStatusCode());
257 response.setResponseMessage(exception.getMessage());
260 String message = XACMLErrorConstants.ERROR_SYSTEM_ERROR+ "Error while processing results. please check logs.";
261 LOGGER.error(message, exception);
262 throw new PolicyException(message, exception);
269 * PushPolicy using pushPolicyParameters.
271 public PolicyChangeResponse pushPolicy(PushPolicyParameters pushPolicyParameters) throws PolicyException{
272 return pushPolicyImpl(pushPolicyParameters);
275 public PolicyChangeResponse pushPolicyImpl(PushPolicyParameters pushPolicyParameters) throws PolicyException{
276 StdPolicyChangeResponse response = new StdPolicyChangeResponse();
277 String resource= "pushPolicy";
278 String body = new String();
281 body = PolicyUtils.objectToJsonString(pushPolicyParameters);
282 } catch (JsonProcessingException e) {
283 String message = XACMLErrorConstants.ERROR_SCHEMA_INVALID + e;
284 LOGGER.error(message);
285 throw new PolicyException(message, e);
289 ResponseEntity<String> result = callNewPDP(resource, HttpMethod.PUT, body, String.class);
291 response.setResponseMessage(result.getBody());
292 response.setResponseCode(result.getStatusCode().value());
293 } catch (PolicyException exception) {
294 return processException(exception);
300 * Delete a Policy using deletePolicyParameters
302 public PolicyChangeResponse deletePolicy(DeletePolicyParameters parameters) throws PolicyException {
303 return deletePolicyImpl(parameters);
306 public PolicyChangeResponse deletePolicyImpl(DeletePolicyParameters parameters) throws PolicyException {
307 StdPolicyChangeResponse response = new StdPolicyChangeResponse();
308 String resource= "deletePolicy";
309 String body = new String();
312 body = PolicyUtils.objectToJsonString(parameters);
313 } catch (JsonProcessingException e) {
314 String message = XACMLErrorConstants.ERROR_SCHEMA_INVALID + e;
315 LOGGER.error(message);
316 throw new PolicyException(message, e);
320 ResponseEntity<String> result = callNewPDP(resource, HttpMethod.DELETE, body, String.class);
322 response.setResponseMessage(result.getBody());
323 response.setResponseCode(result.getStatusCode().value());
324 } catch (PolicyException exception) {
325 return processException(exception);
331 * getDictionaryItem Using dictionaryParameters
333 public DictionaryResponse getDictionaryItem(DictionaryParameters parameters) throws PolicyException {
334 return getDictionaryItemImpl(parameters);
337 public DictionaryResponse getDictionaryItemImpl(DictionaryParameters parameters) throws PolicyException{
338 StdDictionaryResponse response = new StdDictionaryResponse();
339 String resource="getDictionaryItems";
343 body = PolicyUtils.objectToJsonString(parameters);
344 } catch (JsonProcessingException e) {
345 String message = XACMLErrorConstants.ERROR_SCHEMA_INVALID + e;
346 LOGGER.error(message);
347 throw new PolicyException(message, e);
351 ResponseEntity<APIDictionaryResponse> result = callNewPDP(resource, HttpMethod.POST, body, APIDictionaryResponse.class);
353 response = dictionaryResult(result.getBody());
354 } catch (Exception exception) {
355 if(exception.getCause().getMessage().contains("401")){
356 String message = XACMLErrorConstants.ERROR_PERMISSIONS + ERROR_AUTH_GET_PERM + resource;
357 LOGGER.error(message);
358 response.setResponseMessage(message);
359 response.setResponseCode(401);
361 }if(exception.getCause().getMessage().contains("400")){
362 String message = XACMLErrorConstants.ERROR_DATA_ISSUE + "Invalid Data is given.";
363 response.setResponseMessage(message);
364 response.setResponseCode(400);
367 String message = XACMLErrorConstants.ERROR_PERMISSIONS+ "Unable to get valid Response from PDP(s) " + pdps;
368 LOGGER.error(message, exception);
369 response.setResponseMessage(message);
370 response.setResponseCode(500);
376 @SuppressWarnings("unchecked")
377 private StdDictionaryResponse dictionaryResult(APIDictionaryResponse body) {
378 StdDictionaryResponse response = new StdDictionaryResponse();
379 response.setResponseCode(body.getResponseCode());
380 response.setResponseMessage(body.getResponseMessage());
381 response.setDictionaryData((Map<String, String>) body.getDictionaryData());
382 if(body.getDictionaryJson()!=null){
383 Gson objGson = new GsonBuilder().create();
384 String mapToJson = objGson.toJson(body.getDictionaryJson());
385 JsonReader jsonReader = Json.createReader(new StringReader(mapToJson));
386 JsonObject object = jsonReader.readObject();
388 response.setDictionaryJson(object);
394 * createDictinaryItem Using dictionaryParameters.
396 public PolicyChangeResponse createDictionaryItem(DictionaryParameters parameters) throws PolicyException{
397 return createUpdateDictionaryItemImpl(parameters, false);
401 * updateDictinaryItem Using dictionaryParameters.
403 public PolicyChangeResponse updateDictionaryItem(DictionaryParameters parameters) throws PolicyException{
404 return createUpdateDictionaryItemImpl(parameters, true);
407 public PolicyChangeResponse createUpdateDictionaryItemImpl(DictionaryParameters parameters, boolean updateFlag) throws PolicyException{
408 StdPolicyChangeResponse response = new StdPolicyChangeResponse();
409 String resource = "createDictionaryItem";
411 resource = "updateDictionaryItem";
413 String body = new String();
416 body = PolicyUtils.objectToJsonString(parameters);
417 } catch (JsonProcessingException e) {
418 String message = XACMLErrorConstants.ERROR_SCHEMA_INVALID + e;
419 LOGGER.error(message);
420 throw new PolicyException(message, e);
424 ResponseEntity<String> result = callNewPDP(resource, HttpMethod.PUT, body, String.class);
426 response.setResponseMessage(result.getBody());
427 response.setResponseCode(result.getStatusCode().value());
428 } catch (PolicyException exception) {
429 return processException(exception);
435 * PolicyEngine Import
437 public PolicyChangeResponse policyEngineImport(ImportParameters importParameters) throws PolicyException {
438 return policyEngineImportImpl(importParameters);
441 public PolicyChangeResponse policyEngineImportImpl(ImportParameters importParameters) throws PolicyException {
442 StdPolicyChangeResponse response = new StdPolicyChangeResponse();
443 String resource= "policyEngineImport";
444 LinkedMultiValueMap<String, Object> parameters = new LinkedMultiValueMap<String, Object>();
447 String body = PolicyUtils.objectToJsonString(importParameters);
448 parameters.set("importParametersJson", body);
449 parameters.set("file", new FileSystemResource(importParameters.getFilePath()));
450 } catch (Exception e) {
451 String message = XACMLErrorConstants.ERROR_SCHEMA_INVALID + e;
452 LOGGER.error(message);
453 throw new PolicyException(message, e);
455 contentType = MediaType.MULTIPART_FORM_DATA_VALUE;
458 ResponseEntity<String> result = callNewPDP(resource, HttpMethod.POST, parameters, String.class);
460 response.setResponseMessage(result.getBody());
461 response.setResponseCode(result.getStatusCode().value());
462 } catch (PolicyException exception) {
463 return processException(exception);
471 * createPolicy Using policyParameters.
473 public PolicyChangeResponse createPolicy(PolicyParameters policyParameters) throws PolicyException{
474 return createUpdatePolicyImpl(policyParameters, false);
478 * updatePolicy using policyParameters.
480 public PolicyChangeResponse updatePolicy(PolicyParameters policyParameters) throws PolicyException{
481 return createUpdatePolicyImpl(policyParameters, true);
484 public PolicyChangeResponse createUpdatePolicyImpl(PolicyParameters policyParameters, boolean updateFlag) throws PolicyException{
485 StdPolicyChangeResponse response = new StdPolicyChangeResponse();
486 String resource= "createPolicy";
488 resource="updatePolicy";
490 String body = new String();
493 body = PolicyUtils.objectToJsonString(policyParameters);
494 } catch (JsonProcessingException e) {
495 String message = XACMLErrorConstants.ERROR_SCHEMA_INVALID + e;
496 LOGGER.error(message);
497 throw new PolicyException(message, e);
501 ResponseEntity<String> result = callNewPDP(resource, HttpMethod.PUT, body, String.class);
503 response.setResponseMessage(result.getBody());
504 response.setResponseCode(result.getStatusCode().value());
505 } catch (PolicyException exception) {
506 return processException(exception);
511 private PolicyChangeResponse processException(PolicyException exception) throws PolicyException {
512 StdPolicyChangeResponse response = new StdPolicyChangeResponse();
513 if(exception.getCause()!=null && exception.getCause() instanceof HttpClientErrorException){
514 LOGGER.error(exception);
515 HttpClientErrorException ex = (HttpClientErrorException) exception.getCause();
516 response.setResponseCode(ex.getRawStatusCode());
517 response.setResponseMessage(exception.getMessage());
520 String message = XACMLErrorConstants.ERROR_SYSTEM_ERROR+ "Error while processing results. please check logs.";
521 LOGGER.error(message, exception);
522 throw new PolicyException(message, exception);
526 public DecisionResponse getDecisionImpl(String eCOMPComponentName,
527 Map<String, String> decisionAttributes,
528 UUID requestID) throws PolicyDecisionException {
529 String resource= "getDecision";
530 StdDecisionResponse response = new StdDecisionResponse();
531 String body = new String();
534 DecisionRequestParameters decisionRequestParameters = new DecisionRequestParameters();
535 decisionRequestParameters.setDecisionAttributes(decisionAttributes);
536 decisionRequestParameters.setECOMPComponentName(eCOMPComponentName);
537 decisionRequestParameters.setRequestID(requestID);
538 body = PolicyUtils.objectToJsonString(decisionRequestParameters);
539 } catch (JsonProcessingException e) {
540 String message = XACMLErrorConstants.ERROR_SCHEMA_INVALID + e;
541 LOGGER.error(message);
542 throw new PolicyDecisionException(message, e);
546 ResponseEntity<StdDecisionResponse> result = callNewPDP(resource, HttpMethod.POST, body, StdDecisionResponse.class);
548 response = result.getBody();
549 } catch (Exception exception) {
550 if(exception.getCause().getMessage().contains("401")){
551 String message = XACMLErrorConstants.ERROR_PERMISSIONS + ERROR_AUTH_GET_PERM + resource;
552 LOGGER.error(message);
553 throw new PolicyDecisionException(message, exception);
554 }if(exception.getCause().getMessage().contains("400")){
555 String message = XACMLErrorConstants.ERROR_DATA_ISSUE + "Invalid Data is given.";
556 LOGGER.error(message);
557 throw new PolicyDecisionException(message, exception);
559 String message = XACMLErrorConstants.ERROR_PERMISSIONS+ "Unable to get valid Response from PDP(s) " + pdps;
560 LOGGER.error(message, exception);
561 throw new PolicyDecisionException(message, exception);
566 public Collection<PolicyConfig> getConfigImpl(ConfigRequestParameters configRequestParameters) throws PolicyConfigException{
567 String resource= "getConfig";
568 ArrayList<PolicyConfig> response = new ArrayList<PolicyConfig>();
569 String body = new String();
572 body = PolicyUtils.objectToJsonString(configRequestParameters);
573 } catch (JsonProcessingException e) {
574 String message = XACMLErrorConstants.ERROR_SCHEMA_INVALID + e;
575 LOGGER.error(message);
576 throw new PolicyConfigException(message, e);
580 ResponseEntity<APIPolicyConfigResponse[]> result = callNewPDP(resource, HttpMethod.POST, body, APIPolicyConfigResponse[].class);
582 response = configResult(result.getBody());
583 } catch (Exception exception) {
584 if(exception.getCause().getMessage().contains("401")){
585 String message = XACMLErrorConstants.ERROR_PERMISSIONS + ERROR_AUTH_GET_PERM + resource;
586 LOGGER.error(message);
587 throw new PolicyConfigException(message, exception);
588 }if(exception.getCause().getMessage().contains("400")){
589 String message = XACMLErrorConstants.ERROR_DATA_ISSUE + "Invalid Data is given.";
590 LOGGER.error(message);
591 throw new PolicyConfigException(message, exception);
593 String message = XACMLErrorConstants.ERROR_PROCESS_FLOW+ "Unable to get valid Response from PDP(s) " + pdps;
594 LOGGER.error(message, exception);
595 throw new PolicyConfigException(message, exception);
600 private ArrayList<PolicyConfig> configResult(APIPolicyConfigResponse[] response) throws PolicyConfigException {
601 ArrayList<PolicyConfig> result = new ArrayList<PolicyConfig>();
602 if(response!=null && response.length>0){
603 for(APIPolicyConfigResponse policyConfigResponse: response){
604 StdPolicyConfig policyConfig = new StdPolicyConfig();
605 policyConfig.setConfigStatus(policyConfigResponse.getPolicyConfigMessage());
606 policyConfig.setMatchingConditions(policyConfigResponse.getMatchingConditions());
607 policyConfig.setPolicyConfigStatus(policyConfigResponse.getPolicyConfigStatus());
608 policyConfig.setPolicyName(policyConfigResponse.getPolicyName());
609 policyConfig.setPolicyType(policyConfigResponse.getType());
610 policyConfig.setPolicyVersion(policyConfigResponse.getPolicyVersion());
611 policyConfig.setResponseAttributes(policyConfigResponse.getResponseAttributes());
612 setMatches(policyConfig.getMatchingConditions());
613 if(policyConfigResponse.getType()!=null){
615 switch (policyConfigResponse.getType()) {
617 JsonReader jsonReader = Json.createReader(new StringReader(policyConfigResponse.getConfig()));
618 JsonObject object = jsonReader.readObject();
620 policyConfig.setJsonObject(object);
623 policyConfig.setOther(policyConfigResponse.getConfig());
626 Properties props = new Properties();
627 props.putAll(policyConfigResponse.getProperty());
628 policyConfig.setProperties(props);
631 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
632 DocumentBuilder builder;
633 builder = factory.newDocumentBuilder();
634 policyConfig.setDocument(builder.parse(new InputSource(new StringReader(policyConfigResponse.getConfig()))));
637 } catch (Exception e) {
638 LOGGER.error(XACMLErrorConstants.ERROR_SCHEMA_INVALID+ e);
639 throw new PolicyConfigException(XACMLErrorConstants.ERROR_SCHEMA_INVALID+ "Unable to parse the config", e);
642 result.add(policyConfig);
648 private void setMatches(Map<String, String> matchingConditions) {
649 Matches match = new Matches();
650 HashMap<String, String> configAttributes = new HashMap<String,String>();
652 for(String key: matchingConditions.keySet()){
653 if(key.equalsIgnoreCase("ECOMPName")){
654 match.setEcompName(matchingConditions.get(key));
655 }else if(key.equalsIgnoreCase("ConfigName")){
656 match.setConfigName(matchingConditions.get(key));
658 configAttributes.put(key, matchingConditions.get(key));
661 if(!configAttributes.isEmpty()){
662 match.setConfigAttributes(configAttributes);
664 MatchStore.storeMatch(match);
666 LOGGER.info("StoreMatch failed for Ecomp:"
667 + match.getEcompName() + " Config: "
668 + match.getConfigName());
673 * Generic Rest Client to call PDP services.
675 private <T> ResponseEntity<T> callNewPDP(String resource,
676 HttpMethod method, Object body, Class<T> responseType) throws PolicyException{
677 RestTemplate restTemplate = new RestTemplate();
678 HttpEntity<?> requestEntity = new HttpEntity<>(body, getHeaders());
679 ResponseEntity<T> result = null;
680 HttpClientErrorException exception = null;
682 while(pdpsCount < pdps.size()){
684 result = restTemplate.exchange(pdps.get(0)+"/api/" + resource, method, requestEntity, responseType);
685 }catch(HttpClientErrorException e){
686 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Error while connecting to " + pdps.get(0), e);
689 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Error while connecting to " + pdps.get(0), e);
690 exception = new HttpClientErrorException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage());
694 Collections.rotate(pdps, -1);
695 Collections.rotate(encoding, -1);
702 if(exception != null && exception.getStatusCode()!=null){
703 if(exception.getStatusCode().equals(HttpStatus.UNAUTHORIZED)){
704 String message = XACMLErrorConstants.ERROR_PERMISSIONS +":"+exception.getStatusCode()+":" +ERROR_AUTH_GET_PERM + resource;
705 LOGGER.error(message);
706 throw new PolicyException(message, exception);
708 if(exception.getStatusCode().equals(HttpStatus.BAD_REQUEST)){
709 String message = XACMLErrorConstants.ERROR_DATA_ISSUE + ":"+exception.getStatusCode()+":" + exception.getResponseBodyAsString();
710 LOGGER.error(message);
711 throw new PolicyException(message, exception);
713 if(exception.getStatusCode().equals(HttpStatus.NOT_FOUND)){
714 String message = XACMLErrorConstants.ERROR_PROCESS_FLOW + "Error while connecting to " + pdps + exception;
715 LOGGER.error(message);
716 throw new PolicyException(message, exception);
718 String message = XACMLErrorConstants.ERROR_PROCESS_FLOW + ":"+exception.getStatusCode()+":" + exception.getResponseBodyAsString();
719 LOGGER.error(message);
720 throw new PolicyException(message, exception);
725 private HttpHeaders getHeaders() {
726 HttpHeaders headers = new HttpHeaders();
727 headers.set("ClientAuth", "Basic " + clientEncoding);
728 headers.set("Authorization", "Basic " + encoding.get(0));
729 if(contentType!=null){
730 headers.set("Content-Type", contentType.toString());
732 headers.set("Content-Type", MediaType.APPLICATION_JSON_VALUE);
734 headers.set("Environment", environment);
738 private void setClientEncoding() {
739 Base64.Encoder encoder = Base64.getEncoder();
740 clientEncoding = encoder.encodeToString((userName+":"+pass).getBytes(StandardCharsets.UTF_8));
743 public Collection<String> listConfigImpl(ConfigRequestParameters listRequestParameters) throws PolicyConfigException{
744 Collection<String> policyList = new ArrayList<String>();
746 policyList.add("Policy Name: listConfigTest");
749 Collection<PolicyConfig> policyConfig = getConfigImpl(listRequestParameters);
750 for(PolicyConfig policy : policyConfig){
751 if(policy.getPolicyConfigMessage()!=null && policy.getPolicyConfigMessage().contains("PE300")){
752 policyList.add(policy.getPolicyConfigMessage());
754 policyList.add("Policy Name: " + policy.getPolicyName());
760 public Collection<PolicyResponse> sendEventImpl(Map<String, String> eventAttributes, UUID requestID) throws PolicyEventException {
761 String resource= "sendEvent";
762 ArrayList<PolicyResponse> response = new ArrayList<PolicyResponse>();
763 String body = new String();
766 // Long way here, can be shortened and will be done.
767 EventRequestParameters eventRequestParameters = new EventRequestParameters();
768 eventRequestParameters.setEventAttributes(eventAttributes);
769 eventRequestParameters.setRequestID(requestID);
770 body = PolicyUtils.objectToJsonString(eventRequestParameters);
771 } catch (JsonProcessingException e) {
772 String message = XACMLErrorConstants.ERROR_SCHEMA_INVALID + e;
773 LOGGER.error(message);
774 throw new PolicyEventException(message, e);
778 ResponseEntity<StdPolicyResponse[]> result = callNewPDP(resource, HttpMethod.POST, body, StdPolicyResponse[].class);
780 response = eventResult(result.getBody());
781 } catch (Exception exception) {
782 if(exception.getCause().getMessage().contains("401")){
783 String message = XACMLErrorConstants.ERROR_PERMISSIONS + ERROR_AUTH_GET_PERM + resource;
784 LOGGER.error(message);
785 throw new PolicyEventException(message, exception);
786 }if(exception.getCause().getMessage().contains("400")){
787 String message = XACMLErrorConstants.ERROR_DATA_ISSUE + "Invalid Data is given.";
788 LOGGER.error(message);
789 throw new PolicyEventException(message, exception);
791 String message = XACMLErrorConstants.ERROR_PERMISSIONS+ "Unable to get valid Response from PDP(s) " + pdps;
792 LOGGER.error(message, exception);
793 throw new PolicyEventException(message, exception);
798 private ArrayList<PolicyResponse> eventResult(StdPolicyResponse[] response) throws PolicyEventException{
799 ArrayList<PolicyResponse> eventResult = new ArrayList<PolicyResponse>();
800 if(response!=null && response.length>0){
801 for(StdPolicyResponse policyConfigResponse: response){
802 eventResult.add(policyConfigResponse);
808 private void setProperty(String propertyFilePath, String clientKey)
809 throws PolicyEngineException {
810 this.propertyFilePath = propertyFilePath;
811 if (this.propertyFilePath == null) {
812 throw new PolicyEngineException(XACMLErrorConstants.ERROR_DATA_ISSUE + "Error NO PropertyFile Path provided");
814 // Adding logic for remote Properties file.
815 Properties prop = new Properties();
816 if (propertyFilePath.startsWith("http")) {
819 configURL = new URL(propertyFilePath);
820 URLConnection connection = null;
821 connection = configURL.openConnection();
822 prop.load(connection.getInputStream());
823 } catch (IOException e) {
824 LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + e);
825 throw new PolicyEngineException(XACMLErrorConstants.ERROR_DATA_ISSUE + "Maformed property URL "+ e.getMessage());
828 Path file = Paths.get(propertyFilePath);
829 if (Files.notExists(file)) {
830 throw new PolicyEngineException(XACMLErrorConstants.ERROR_DATA_ISSUE + "File doesn't exist in the specified Path " + file.toString());
832 if (file.toString().endsWith(".properties")) {
834 prop = new Properties();
836 in = new FileInputStream(file.toFile());
838 } catch (IOException e) {
839 LOGGER.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + e);
840 throw new PolicyEngineException(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Cannot Load the Properties file", e);
843 LOGGER.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Not a .properties file " + propertyFilePath);
844 throw new PolicyEngineException(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Not a .properties file");
847 // UEB and DMAAP Settings
848 String check_type = prop.getProperty("NOTIFICATION_TYPE");
849 String serverList = prop.getProperty("NOTIFICATION_SERVERS");
850 topic = prop.getProperty("NOTIFICATION_TOPIC");
851 apiKey = prop.getProperty("UEB_API_KEY");
852 apiSecret = prop.getProperty("UEB_API_SECRET");
854 if(check_type==null) {
855 notificationType.add(DEFAULT_NOTIFICATION);
856 LOGGER.info("Properties file doesn't have the NOTIFICATION_TYPE parameter system will use defualt websockets");
858 check_type = check_type.trim();
859 if(check_type.contains(",")) {
860 typeDefault = new ArrayList<String>(Arrays.asList(prop.getProperty("NOTIFICATION_TYPE").split(",")));
861 notificationType = typeDefault;
863 notificationType = new ArrayList<String>();
864 notificationType.add(check_type);
867 if(serverList==null) {
868 notificationType.clear();
869 notificationType.add(DEFAULT_NOTIFICATION);
870 LOGGER.info("Properties file doesn't have the NOTIFICATION_SERVERS parameter system will use defualt websockets");
872 serverList = serverList.trim();
873 if(serverList.contains(",")) {
874 notificationURLList = new ArrayList<String>(Arrays.asList(serverList.split(",")));
876 notificationURLList = new ArrayList<String>();
877 notificationURLList.add(serverList);
882 topic = topic.trim();
884 LOGGER.error("Properties file doesn't have the NOTIFICATION_TOPIC parameter.");
887 // Client ID Authorization Settings.
888 String clientID = prop.getProperty("CLIENT_ID");
890 clientKey = prop.getProperty("CLIENT_KEY");
892 clientKey = PolicyUtils.decode(clientKey);
893 } catch (UnsupportedEncodingException|IllegalArgumentException e) {
894 LOGGER.error(XACMLErrorConstants.ERROR_PERMISSIONS+" Cannot Decode the given Password Proceeding with given Password!!");
897 if(clientID ==null || clientKey == null || clientID.isEmpty() || clientKey.isEmpty()){
898 LOGGER.error(XACMLErrorConstants.ERROR_PERMISSIONS+" Cannot proceed without the CLIENT_KEY and CLIENT_ID values !!");
899 throw new PolicyEngineException(XACMLErrorConstants.ERROR_PERMISSIONS+ " Cannot proceed without the CLIENT_KEY and CLIENT_ID values !!");
901 userName = clientID.trim();
902 pass = clientKey.trim();
905 environment = prop.getProperty("ENVIRONMENT", Environment.DEVL.toString());
906 if(environment.equalsIgnoreCase(Environment.TEST.toString())){
907 environment = Environment.TEST.toString();
908 }else if(environment.equalsIgnoreCase(Environment.PROD.toString())){
909 environment = Environment.PROD.toString();
911 environment = Environment.DEVL.toString();
913 // Initializing the values.
914 pdps = new ArrayList<String>();
915 encoding = new ArrayList<String>();
916 // Check the Keys for PDP_URLs
917 Collection<Object> unsorted = prop.keySet();
918 @SuppressWarnings({ "rawtypes", "unchecked" })
919 List<String> sorted = new ArrayList(unsorted);
920 Collections.sort(sorted);
921 for (String propKey : sorted) {
922 if (propKey.startsWith("PDP_URL")) {
923 String check_val = prop.getProperty(propKey);
924 if (check_val == null) {
925 throw new PolicyEngineException(XACMLErrorConstants.ERROR_DATA_ISSUE + "Properties file doesn't have the PDP_URL parameter");
927 if (check_val.contains(";")) {
928 pdpDefault = new ArrayList<String>(Arrays.asList(check_val.split("\\s*;\\s*")));
930 while (pdpCount < pdpDefault.size()) {
931 String pdpVal = pdpDefault.get(pdpCount);
932 readPDPParam(pdpVal);
936 readPDPParam(check_val);
940 if (pdps == null || pdps.isEmpty()) {
941 LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Cannot Proceed without PDP_URLs");
942 throw new PolicyEngineException(XACMLErrorConstants.ERROR_DATA_ISSUE + "Cannot Proceed without PDP_URLs");
945 // Get JUNIT property from properties file when running tests
946 String junit = prop.getProperty("JUNIT");
947 if(junit == null || junit.isEmpty()){
948 LOGGER.info("No JUNIT property provided, this will not be executed as a test.");
950 if(junit.equalsIgnoreCase("test")){
951 StdPolicyEngine.junit = true;
953 StdPolicyEngine.junit = false;
960 * Read the PDP_URL parameter
962 private void readPDPParam(String pdpVal) throws PolicyEngineException{
963 if(pdpVal.contains(",")){
964 List<String> pdpValues = new ArrayList<String>(Arrays.asList(pdpVal.split("\\s*,\\s*")));
965 if(pdpValues.size()==3){
967 pdps.add(pdpValues.get(0));
968 // 1:2 will be UserID:Password
969 String userID = pdpValues.get(1);
970 String pass = pdpValues.get(2);
971 Base64.Encoder encoder = Base64.getEncoder();
972 encoding.add(encoder.encodeToString((userID+":"+pass).getBytes(StandardCharsets.UTF_8)));
974 LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "No Credentials to send Request: " + pdpValues);
975 throw new PolicyEngineException(XACMLErrorConstants.ERROR_DATA_ISSUE + "No enough Credentials to send Request. " + pdpValues);
978 LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "PDP value is improper/missing required values: " + pdpVal);
979 throw new PolicyEngineException(XACMLErrorConstants.ERROR_DATA_ISSUE + "PDP value is improper/missing required values.");
983 * Allowing changes to the scheme and Handler.
985 public void notification(NotificationScheme scheme, NotificationHandler handler) {
986 this.scheme = scheme;
987 this.handler = handler;
988 LOGGER.debug("Scheme is : " + scheme.toString());
989 LOGGER.debug("Handler is : " + handler.getClass().getName());
991 if (notificationType.get(0).equals("ueb")){
992 if (this.uebThread) {
993 uebClientThread.setAuto(scheme, handler);
994 this.uebThread = registerUEBThread.isAlive();
996 } else if (notificationType.get(0).equals("dmaap")){
997 if (this.dmaapThread) {
998 dmaapClientThread.setAuto(scheme, handler);
999 this.dmaapThread = registerDMAAPThread.isAlive();
1002 AutoClientEnd.setAuto(scheme, handler);
1010 if (notificationType.get(0).equals("ueb") && !this.uebThread){
1011 this.uebClientThread = new AutoClientUEB(pdps.get(0), notificationURLList, apiKey, apiSecret);
1012 this.uebClientThread.setAuto(scheme, handler);
1013 this.registerUEBThread = new Thread(this.uebClientThread);
1014 this.registerUEBThread.start();
1015 this.uebThread = true;
1016 }else if (notificationType.get(0).equals("dmaap") && !this.dmaapThread){
1017 this.dmaapClientThread = new AutoClientDMAAP(notificationURLList,topic,userName,pass);
1018 this.dmaapClientThread.setAuto(scheme, handler);
1019 this.registerDMAAPThread = new Thread(this.dmaapClientThread);
1020 this.registerDMAAPThread.start();
1021 this.dmaapThread = true;
1023 if(pdps.get(0)!=null){
1024 if(AutoClientEnd.getURL()==null){
1025 AutoClientEnd.start(pdps.get(0));
1027 AutoClientEnd.stop();
1028 AutoClientEnd.start(pdps.get(0));
1036 * Gets the Notification if one exists. Used only for Manual Polling
1039 public PDPNotification getNotification(){
1040 //Check if there is proper scheme..
1041 PDPNotification notification = null;
1042 if(this.scheme.equals(NotificationScheme.MANUAL_ALL_NOTIFICATIONS) || this.scheme.equals(NotificationScheme.MANUAL_NOTIFICATIONS)) {
1043 if (notificationType.get(0).equals("ueb")){
1044 ManualClientEndUEB.start(pdps.get(0), notificationURLList, UNIQUEID);
1045 notification = ManualClientEndUEB.result(scheme);
1046 }else if (notificationType.get(0).equals("dmaap")){
1047 ManualClientEndDMAAP.start(notificationURLList, topic, UNIQUEID, userName, pass);
1048 notification = ManualClientEndDMAAP.result(scheme);
1050 ManualClientEnd.start(pdps.get(0));
1051 LOGGER.debug("manual notification requested.. : " + scheme.toString());
1052 notification = ManualClientEnd.result(scheme);
1054 if (notification == null){
1055 LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "No Notification yet..");
1058 return notification;
1066 * Setting the Scheme.
1068 public void setScheme(NotificationScheme scheme) {
1069 this.scheme = scheme;
1070 if (notificationType.get(0).equals("ueb")){
1071 AutoClientUEB.setScheme(this.scheme);
1072 if (this.scheme.equals(NotificationScheme.MANUAL_ALL_NOTIFICATIONS)){
1073 ManualClientEndUEB.createTopic(pdps.get(0), UNIQUEID, notificationURLList);
1075 }else if (notificationType.get(0).equals("dmaap")){
1076 AutoClientDMAAP.setScheme(this.scheme);
1077 if (this.scheme.equals(NotificationScheme.MANUAL_ALL_NOTIFICATIONS)){
1078 ManualClientEndDMAAP.createTopic(topic, UNIQUEID, notificationURLList, userName, pass);
1081 AutoClientEnd.setScheme(this.scheme);
1086 * Returns the Scheme
1088 public NotificationScheme getScheme() {
1093 * Returns the NotificationHandler
1095 public NotificationHandler getNotificationHandler() {
1096 return this.handler;
1100 * Stop the Notification Service if its running.
1102 public void stopNotification() {
1103 if (this.scheme != null && this.handler != null) {
1104 if (this.scheme.equals(NotificationScheme.AUTO_ALL_NOTIFICATIONS)
1106 .equals(NotificationScheme.AUTO_NOTIFICATIONS)) {
1107 LOGGER.info("Clear Notification called.. ");
1108 if (notificationType.get(0).equals("ueb")){
1109 this.uebClientThread.terminate();
1110 this.uebThread = false;
1111 }else if (notificationType.get(0).equals("dmaap")){
1112 this.dmaapClientThread.terminate();
1113 this.dmaapThread = false;
1115 AutoClientEnd.stop();
1122 * Push a policy to the PDP API implementation
1124 public String pushPolicy(String policyScope, String policyName, String policyType, String pdpGroup, UUID requestID) throws PolicyException {
1125 PushPolicyParameters pushPolicyParameters = new PushPolicyParameters();
1126 if(policyScope==null|| policyScope.trim().isEmpty()){
1127 String message = XACMLErrorConstants.ERROR_DATA_ISSUE + "No Policy Scope given.";
1128 LOGGER.error(message);
1129 throw new PolicyException(message);
1131 if(policyName==null|| policyName.trim().isEmpty()){
1132 String message = XACMLErrorConstants.ERROR_DATA_ISSUE + "No Policy Name given.";
1133 LOGGER.error(message);
1134 throw new PolicyException(message);
1136 pushPolicyParameters.setPolicyName(policyScope+"."+policyName);
1137 pushPolicyParameters.setPolicyType(policyType);
1138 pushPolicyParameters.setPdpGroup(pdpGroup);
1139 pushPolicyParameters.setRequestID(requestID);
1140 return pushPolicyImpl(pushPolicyParameters).getResponseMessage();
1143 public String createUpdateConfigPolicy(String policyName, String policyDescription, String ecompName, String configName,
1144 Map<String, String> configAttributes, String configType, String body, String policyScope, UUID requestID,
1145 String riskLevel, String riskType, String guard, String ttlDate, boolean updateFlag) throws PolicyException {
1146 return createUpdateConfigPolicyImpl(policyName, policyDescription, ecompName, configName,
1147 configAttributes, configType, body, policyScope, requestID,
1148 riskLevel, riskType, guard, ttlDate, updateFlag);
1152 * Create Config Policy API Implementation
1154 public String createUpdateConfigPolicyImpl(String policyName, String policyDescription, String ecompName, String configName,
1155 Map<String, String> configAttributes, String configType, String body, String policyScope, UUID requestID,
1156 String riskLevel, String riskType, String guard, String ttlDate, boolean updateFlag) throws PolicyException {
1157 PolicyParameters policyParameters = new PolicyParameters();
1158 policyParameters.setPolicyClass(PolicyClass.Config);
1159 policyParameters.setPolicyConfigType(PolicyConfigType.Base);
1160 if(policyScope==null|| policyScope.trim().isEmpty()){
1161 String message = XACMLErrorConstants.ERROR_DATA_ISSUE + "No Policy Scope given.";
1162 LOGGER.error(message);
1163 throw new PolicyException(message);
1165 if(policyName==null|| policyName.trim().isEmpty()){
1166 String message = XACMLErrorConstants.ERROR_DATA_ISSUE + "No Policy Name given.";
1167 LOGGER.error(message);
1168 throw new PolicyException(message);
1170 policyParameters.setPolicyName(policyScope+"."+policyName);
1171 policyParameters.setPolicyDescription(policyDescription);
1172 policyParameters.setEcompName(ecompName);
1173 policyParameters.setConfigName(configName);
1174 Map<AttributeType, Map<String, String>> attributes = new HashMap<AttributeType, Map<String, String>>();
1175 attributes.put(AttributeType.MATCHING, configAttributes);
1176 policyParameters.setAttributes(attributes);
1177 policyParameters.setConfigBodyType(PolicyType.valueOf(configType));
1178 policyParameters.setConfigBody(body);
1179 policyParameters.setRequestID(requestID);
1180 policyParameters.setRiskLevel(riskLevel);
1181 policyParameters.setRiskType(riskType);
1182 policyParameters.setGuard(Boolean.parseBoolean(guard));
1184 policyParameters.setTtlDate(new SimpleDateFormat("dd-MM-yyyy").parse(ttlDate));
1185 } catch (ParseException e) {
1186 LOGGER.warn("Error Parsing date given " + ttlDate);
1187 policyParameters.setTtlDate(null);
1189 return createUpdatePolicyImpl(policyParameters, updateFlag).getResponseMessage();
1192 public String createUpdateConfigFirewallPolicy(String policyName, JsonObject firewallJson, String policyScope, UUID requestID,
1193 String riskLevel, String riskType, String guard, String ttlDate, boolean updateFlag) throws PolicyException {
1194 return createUpdateConfigFirewallPolicyImpl(policyName, firewallJson, policyScope, requestID,
1195 riskLevel, riskType, guard, ttlDate, updateFlag);
1199 * Create Update Config Firewall Policy API implementation
1201 public String createUpdateConfigFirewallPolicyImpl(String policyName, JsonObject firewallJson, String policyScope, UUID requestID,
1202 String riskLevel, String riskType, String guard, String ttlDate, boolean updateFlag) throws PolicyException {
1203 PolicyParameters policyParameters = new PolicyParameters();
1204 policyParameters.setPolicyClass(PolicyClass.Config);
1205 policyParameters.setPolicyConfigType(PolicyConfigType.Firewall);
1206 if(policyScope==null|| policyScope.trim().isEmpty()){
1207 String message = XACMLErrorConstants.ERROR_DATA_ISSUE + "No Policy Scope given.";
1208 LOGGER.error(message);
1209 throw new PolicyException(message);
1211 if(policyName==null|| policyName.trim().isEmpty()){
1212 String message = XACMLErrorConstants.ERROR_DATA_ISSUE + "No Policy Name given.";
1213 LOGGER.error(message);
1214 throw new PolicyException(message);
1216 policyParameters.setPolicyName(policyScope+"."+policyName);
1217 policyParameters.setConfigBody(firewallJson.toString());
1218 policyParameters.setRequestID(requestID);
1219 policyParameters.setRiskLevel(riskLevel);
1220 policyParameters.setRiskType(riskType);
1221 policyParameters.setGuard(Boolean.parseBoolean(guard));
1223 policyParameters.setTtlDate(new SimpleDateFormat("dd-MM-yyyy").parse(ttlDate));
1224 } catch (NullPointerException | ParseException e) {
1225 LOGGER.warn("Error Parsing date given " + ttlDate);
1226 policyParameters.setTtlDate(null);
1228 return createUpdatePolicyImpl(policyParameters, updateFlag).getResponseMessage();
1231 public void setClientKey(String clientKey){
1232 if(clientKey!=null && !clientKey.isEmpty()){
1233 StdPolicyEngine.pass = clientKey;
1234 setClientEncoding();
1238 * Get the Environment.
1240 public static String getEnvironment() {
1244 * Rotate the PDP list upon WEBsocket Failures
1246 public static void rotatePDPList() {
1247 Collections.rotate(pdps, -1);
1248 Collections.rotate(encoding, -1);
1251 * Get the latest PDP
1253 public static String getPDPURL() {