Initial OpenECOMP policy/engine commit
[policy/engine.git] / ecomp-sdk-app / src / main / java / org / openecomp / policy / elk / converter / Xacml2Elk.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ECOMP Policy Engine
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
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.openecomp.policy.elk.converter;
22
23
24 import io.searchbox.core.Update;
25
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.OutputStream;
31 import java.io.StringReader;
32 import java.io.StringWriter;
33 import java.net.MalformedURLException;
34 import java.net.URL;
35 import java.net.URLConnection;
36 import java.nio.file.FileSystems;
37 import java.nio.file.Files;
38 import java.nio.file.Path;
39 import java.nio.file.Paths;
40 import java.util.ArrayList;
41 import java.util.Collection;
42 import java.util.Enumeration;
43 import java.util.HashMap;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Properties;
47
48 import javax.xml.bind.JAXBContext;
49 import javax.xml.bind.JAXBElement;
50 import javax.xml.bind.JAXBException;
51 import javax.xml.bind.Marshaller;
52 import javax.xml.bind.Unmarshaller;
53 import javax.xml.transform.Result;
54 import javax.xml.transform.stream.StreamResult;
55
56 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressionType;
57 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType;
58 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
59 import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType;
60 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType;
61 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
62
63 import org.apache.commons.io.IOUtils;
64 import org.eclipse.persistence.jaxb.JAXBContextFactory;
65 import org.eclipse.persistence.jaxb.MarshallerProperties;
66 import org.eclipse.persistence.jaxb.json.JsonSchemaOutputResolver;
67
68 import com.att.research.xacml.api.Advice;
69 import com.att.research.xacml.api.AttributeAssignment;
70 import com.att.research.xacml.api.Identifier;
71 import com.att.research.xacml.api.Obligation;
72 import org.openecomp.policy.xacml.api.XACMLErrorConstants;
73 import com.att.research.xacml.util.XACMLPolicyScanner;
74 import com.att.research.xacml.util.XACMLProperties;
75 import com.att.research.xacml.util.XACMLPolicyScanner.CallbackResult;
76 import com.att.research.xacml.util.XACMLPolicyScanner.SimpleCallback;
77 import com.fasterxml.jackson.core.JsonProcessingException;
78 import com.fasterxml.jackson.databind.JsonNode;
79 import com.fasterxml.jackson.databind.ObjectMapper;
80 import com.fasterxml.jackson.databind.SerializationFeature;
81 import com.fasterxml.jackson.databind.node.ObjectNode;
82 import com.fasterxml.jackson.dataformat.xml.XmlMapper;
83 import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
84 import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
85 import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper;
86
87 import org.kohsuke.args4j.CmdLineException;
88 import org.kohsuke.args4j.CmdLineParser;
89 import org.kohsuke.args4j.Option;
90 import org.openecomp.policy.elk.client.ElkConnector;
91 import org.openecomp.policy.elk.client.ElkConnector.PolicyBodyType;
92 import org.openecomp.policy.rest.XACMLRestProperties;
93 import org.openecomp.policy.common.logging.flexlogger.FlexLogger; 
94 import org.openecomp.policy.common.logging.flexlogger.Logger;
95
96
97 @SuppressWarnings("unused")
98 public class Xacml2Elk {
99         public static final String URLID_ATTRIBUTE = "URLID";
100         public static final String BODY_ATTRIBUTE = "body";
101         
102         protected static final Logger logger = FlexLogger.getLogger(Xacml2Elk.class);   
103         protected static JAXBContext jaxbContext = jaxbContext();
104         
105         protected static String toConfigsWebDirectory(String policyType) 
106                   throws IllegalArgumentException {
107                 if (policyType == null || policyType.isEmpty())
108                         throw new IllegalArgumentException("Unexpected policy type: " + policyType);
109                 
110                 ElkConnector.PolicyType type = ElkConnector.PolicyType.valueOf(policyType);
111                 switch(type) {
112                 case Config:
113                         return type.name();
114                 case Action:
115                         return type.name();
116                 case Decision:
117                         return type.name();
118                 case Config_Fault:
119                 case Config_PM:
120                 case Config_FW:
121                 case Config_MS:
122                         return ElkConnector.PolicyType.Config.name();
123                 default:
124                         throw new IllegalArgumentException("Unexpected policy type: " + policyType);
125                 }
126         }
127         
128         protected synchronized static JAXBContext jaxbContext() {
129                 if (jaxbContext != null) {
130                         return jaxbContext;
131                 }
132                 
133                 try {
134                         jaxbContext = JAXBContextFactory.createContext(new Class[] {PolicyType.class}, null);
135                 } catch (JAXBException e) {
136                         logger.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + ":" + 
137                                  "JAXB Context cannot be created");
138                         return null;
139                 }
140                 
141                 return jaxbContext;
142         }
143         
144         protected static class CLIOptions {
145                 @Option(name="-t", usage="policy type", aliases={"-type", "--type"}, required=true)
146                 protected String type;
147                 
148                 @Option(name="-n", usage="policy name", aliases={"-name", "--name"}, required=true)
149                 protected String name;
150                 
151                 @Option(name="-o", usage="git repository owner", aliases={"-owner", "--owner"}, required=true)
152                 protected String owner;
153                 
154                 @Option(name="-s", usage="enclosing scope", aliases={"-scope", "--scope"}, required=true)
155                 protected String scope;
156                 
157                 @Option(name="-x", usage="xacml input file", aliases={"-xacml", "--xacml"}, required=true, metaVar="<xacml input file>")
158                 protected File xacmlFile;
159                 
160                 @Option(name="-p", usage="payload body type", aliases={"-payloadType", "--payloadType"}, required=false, metaVar="<bayload body type>", depends={"-b"})
161                 protected PolicyBodyType bodyType;
162                 
163                 @Option(name="-b", usage="payload body", aliases={"-body", "--body"}, required=false, metaVar="<bayload body type>", depends={"-p"})
164                 protected String body;
165                 
166                 @Option(name="-d", usage="elk record output directory", aliases={"-d", "--directory"}, required=true, metaVar="<elk directory>")
167                 protected File elkDirectory;
168                 
169                 @Option(name = "-h", aliases = {"-help", "--help"}, usage = "print this message")
170                 private boolean help = false;           
171         };
172         
173         class AttributeAssignmentFinderProcessor extends SimpleCallback {
174                 protected final String attributeId;
175                 protected String attributeValue = null;
176                 
177                 public AttributeAssignmentFinderProcessor(String attributeId) {
178                         this.attributeId = attributeId;
179                 }
180                                 
181                 public String getAttributeValue() {
182                         return attributeValue;
183                 }
184                 
185                 public boolean isAttributeValue() {
186                         return (this.attributeValue != null && !this.attributeValue.isEmpty());
187                 }       
188                 
189                 private boolean processAssignments(
190                                 List<AttributeAssignmentExpressionType> assignments) {
191                         if (logger.isTraceEnabled())
192                                 logger.trace("ENTER");
193                         
194                         for (AttributeAssignmentExpressionType assignment : assignments) {
195                                 if (!assignment.getAttributeId().equals(attributeId)) {
196                                         if (logger.isDebugEnabled())
197                                                 logger.debug("Ignoring: " + assignment.getAttributeId());
198                                         continue;
199                                 }
200                                 
201                                 if (logger.isDebugEnabled())
202                                         logger.debug("Found Attribute ID: " + assignment.getAttributeId());
203                                 
204                                 JAXBElement<?> jaxbExp = assignment.getExpression();
205                                 Object assignmentObject = jaxbExp.getValue();
206                                 if (assignmentObject instanceof AttributeValueType) {
207                                         AttributeValueType avt = (AttributeValueType) assignmentObject;
208                                         if (avt.getContent().size() <= 0) {
209                                                 logger.warn("Ignoring: " + assignment.getAttributeId() + ": No values");
210                                                 continue;
211                                         }
212
213                                         this.attributeValue = avt.getContent().get(0).toString();
214                                         if (logger.isInfoEnabled())
215                                                 logger.info("Found: " + this.attributeValue);
216                                         
217                                         return true;
218                                 }
219                         }
220                         return false;
221                 }       
222
223                 @Override
224                 public CallbackResult onAdvice(Object parent, AdviceExpressionType expression, Advice advice) {
225                         if (logger.isTraceEnabled())
226                                 logger.trace("ENTER");
227                         
228                         List<AttributeAssignmentExpressionType> assignments = 
229                                         expression.getAttributeAssignmentExpression();
230
231                         if (assignments != null) {
232                                 boolean found = processAssignments(assignments);
233                                 if (found)
234                                         return CallbackResult.STOP;
235                         }
236                         
237                         return super.onAdvice(parent, expression, advice);
238                 }
239                 
240                 @Override
241                 public CallbackResult onObligation(Object parent, ObligationExpressionType expression, Obligation obligation) {
242                         if (logger.isTraceEnabled())
243                                 logger.trace("ENTER");
244                         
245                         List<AttributeAssignmentExpressionType> assignments = 
246                                         expression.getAttributeAssignmentExpression();
247
248                         if (assignments != null) {
249                                 boolean found = processAssignments(assignments);
250                                 if (found)
251                                         return CallbackResult.STOP;
252                         }
253                         
254                         return super.onObligation(parent, expression, obligation);
255                         
256                 }
257         }
258         
259         final protected String type;
260         final protected String name;
261         final protected String owner;
262         final protected String scope;
263         final protected File xacmlFile;
264         final protected File elkDirectory;
265         
266         final protected JAXBElement<PolicyType> policy;
267
268         protected PolicyBodyType bodyType;
269         protected String body;
270         
271         
272         public Xacml2Elk(String type, String name, 
273                                  String owner, String scope, 
274                                  File xacmlFile, File elkDirectory) 
275                throws IllegalArgumentException {
276                 
277                 this.type = type;
278                 this.name = name;
279                 this.owner = owner;
280                 this.scope = scope;
281                 this.xacmlFile = xacmlFile;
282                 this.elkDirectory = elkDirectory;
283                 
284                 this.policy = jaxbXacml(xacmlFile);
285                 
286                 this.body = "";
287                 this.bodyType = PolicyBodyType.none;
288                 bodyFromXacml();        
289         }
290         
291         public Xacml2Elk(CLIOptions args) throws IllegalArgumentException {
292                 this.type = args.type;
293                 this.name = args.name;
294                 this.owner = args.owner;
295                 this.scope = args.scope;
296                 this.xacmlFile = args.xacmlFile;
297                 this.elkDirectory = args.elkDirectory;
298                 
299                 this.policy = jaxbXacml(xacmlFile);
300                 
301                 if (args.body == null || args.body.isEmpty()) {
302                         this.body = "";
303                         this.bodyType = PolicyBodyType.none;
304                         bodyFromXacml();
305                 } else {                
306                         this.body = args.body;
307                         this.bodyType = args.bodyType;
308                 }
309         }
310         
311         public Xacml2Elk(String type, String name, String owner,
312                         String scope, File xacmlFile, PolicyBodyType bodyType,
313                         String body, File destinationDir) 
314                 throws IllegalArgumentException {
315                 this.type = type;
316                 this.name = name;
317                 this.owner = owner;
318                 this.scope = scope;
319                 this.xacmlFile = xacmlFile;
320                 this.bodyType = bodyType;
321                 this.body = body;
322                 this.elkDirectory = destinationDir;
323                 
324                 this.policy = jaxbXacml(xacmlFile);
325         }
326         
327         public Xacml2Elk(File xacmlFile, boolean skipBody) 
328                         throws IllegalArgumentException {               
329                 this.policy = jaxbXacml(xacmlFile);
330                 PolicyType jPolicy = this.policy.getValue();
331                 
332                 this.xacmlFile = xacmlFile;
333                 
334                 this.type = ElkConnector.toPolicyType(xacmlFile.getName()).name();
335                 String fileName = xacmlFile.getName().replaceFirst(this.type + "_", "");
336                 if (fileName.indexOf(".") > 0)
337                         this.name = fileName.substring(0, fileName.lastIndexOf("."));
338                 else
339                         this.name = fileName;
340                 
341                 this.owner = "admin";
342                 this.scope = getScope(xacmlFile.getParent());
343                 this.elkDirectory = null;
344                 
345                 this.body = "";
346                 this.bodyType = PolicyBodyType.none;
347                 if (!skipBody) {
348                         bodyFromXacml();
349                 } 
350         }
351         
352         @SuppressWarnings("unchecked")
353         protected JAXBElement<PolicyType> jaxbXacml(File xacmlFile) throws IllegalArgumentException {
354                 Path xacmlPath = xacmlFile.toPath();
355                 if (!Files.isReadable(xacmlPath) || !Files.isRegularFile(xacmlPath)) {
356                         if (logger.isWarnEnabled()) {
357                                 logger.warn("Error: " + xacmlPath + " is invalid.");
358                         }
359                         throw new IllegalArgumentException("Error: " + xacmlPath + " is invalid.");
360                 }
361
362                 try {
363                         Unmarshaller u = jaxbContext.createUnmarshaller();
364                         return (JAXBElement<PolicyType>) u.unmarshal(xacmlFile);
365                 } catch (Exception e) {
366                         if (logger.isWarnEnabled()) {
367                                 logger.warn(XACMLErrorConstants.ERROR_DATA_ISSUE + " - error: " + xacmlPath + " is invalid.");
368                         }
369                         throw new IllegalArgumentException(xacmlFile.getAbsolutePath() + " does not contain valid XACML");
370                 }
371         }
372         
373         public JAXBElement<PolicyType> getPolicy() {
374                 return policy;
375         }
376         
377         protected String getScope(String xacmlDirPath) {
378                 if (logger.isTraceEnabled()) logger.trace("ENTER");             
379                 
380                 xacmlDirPath = xacmlDirPath.replaceAll("//", "/");
381                 xacmlDirPath = xacmlDirPath.replaceAll("\\\\", "/");
382                 xacmlDirPath = xacmlDirPath.replace('\\', '/');
383                 
384                 String ws = XACMLProperties.getProperty(XACMLRestProperties.PROP_ADMIN_WORKSPACE);
385                 String adminRepo = XACMLProperties.getProperty(XACMLRestProperties.PROP_ADMIN_REPOSITORY);
386                 Path wsPath = Paths.get(ws, "admin", adminRepo);
387                 File repoDir = wsPath.toFile();
388                 String repoPath = repoDir.getPath();
389                 repoPath = repoPath.replaceAll("//", "/");
390                 repoPath = repoPath.replaceAll("\\\\", "/");
391                 repoPath = repoPath.replace('\\', '/');
392                 
393                 int startIndex = xacmlDirPath.indexOf(repoPath.toString()) + repoPath.toString().length() + 1;
394                 String scope = xacmlDirPath.substring(startIndex, xacmlDirPath.length());
395                 
396                 if (logger.isInfoEnabled())
397                         logger.info("xacml-policy-path=" + xacmlDirPath + "|" + 
398                                 "repository-path=" + repoPath + "|" + 
399                                              "scope=" + scope);
400                 
401                 return scope;
402         }
403         
404         @SuppressWarnings("deprecation")
405         private boolean bodyFromXacml() {
406                 if (logger.isTraceEnabled())
407                         logger.trace("ENTER");
408                 
409                 String urlAttribute = URLID_ATTRIBUTE;
410                 try {
411                         switch (ElkConnector.toPolicyType(this.type)) {
412                         case Action:
413                                 urlAttribute = BODY_ATTRIBUTE;
414                                 break;
415                         case Decision:
416                         case none:
417                                 /* no body attached to decision policies */
418                                 if (logger.isInfoEnabled())
419                                         logger.info("No body attached for this type of policy: " + this.xacmlFile.getAbsolutePath());
420                                 return false;
421                         default:
422                                 /* a flavour of a config policy - default is fine */
423                                 break;
424                         }
425                 } catch (IllegalArgumentException iae) {
426                         if (logger.isWarnEnabled()) {
427                                 logger.warn(this.type + " cannot be converted to a valid type: " + iae.getMessage(), iae);
428                         }
429                         return false;
430                 }
431                 
432                 AttributeAssignmentFinderProcessor
433                         processor = new AttributeAssignmentFinderProcessor(urlAttribute);
434                 XACMLPolicyScanner xacmlScanner = 
435                                 new XACMLPolicyScanner(this.policy.getValue(), processor);
436                 xacmlScanner.scan();
437                 if (!processor.isAttributeValue()) {
438                         if (logger.isInfoEnabled())
439                                 logger.info(urlAttribute + " not found in " + this.xacmlFile.getAbsolutePath());
440                         return false;
441                 }
442                 
443                 String configsUrl = XACMLProperties.getProperty(XACMLRestProperties.PROP_CONFIG_URL);
444                 if (configsUrl == null || configsUrl.isEmpty() || !configsUrl.startsWith("http")) {
445                         if (logger.isWarnEnabled()) {
446                                 logger.warn(XACMLRestProperties.PROP_CONFIG_URL + " property is not set.");
447                         }
448                         configsUrl = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_URL);
449                         if (configsUrl == null || configsUrl.isEmpty() || !configsUrl.startsWith("http")) {
450                                 if (logger.isWarnEnabled()) {
451                                         logger.warn(XACMLRestProperties.PROP_PAP_URL + " property is not set.");
452                                 }
453                                 return false;
454                         } else {
455                                 configsUrl = configsUrl.replaceFirst("/pap", "");
456                         }               
457                 }
458                 
459                 if (!configsUrl.endsWith("/")) {
460                         configsUrl += "/";
461                 }
462                 
463                 String urlXacml = processor.getAttributeValue();
464                 if (logger.isDebugEnabled()) {
465                         logger.debug("configs url is " + configsUrl + "and url in xacml is " + urlXacml);
466                 }
467                 
468                 if (urlXacml.startsWith("http")) {
469                         // assuming this an unescaped url
470                 } else if (urlXacml.startsWith("$URLConfig/")) {
471                         urlXacml = urlXacml.replace("$URLConfig/", configsUrl);
472                 } else if (urlXacml.startsWith("$URL/")) {
473                         urlXacml = urlXacml.replace("$URL/", configsUrl);
474                 } else{
475                         if (logger.isWarnEnabled()) {
476                                 logger.warn("XACML url is not in the expected format: " + urlXacml);
477                         }
478                         return false;
479                 }
480                 
481                 if (urlXacml.endsWith(".properties")) {
482                         this.bodyType = PolicyBodyType.properties;
483                 } else if (urlXacml.endsWith(".json")) {
484                         this.bodyType = PolicyBodyType.json;
485                 } else if (urlXacml.endsWith(".xml")) {
486                         this.bodyType = PolicyBodyType.xml;
487                 } else if (urlXacml.endsWith(".txt")) {
488                         this.bodyType = PolicyBodyType.txt;
489                 }
490                 
491                 if (logger.isDebugEnabled()) {
492                         logger.debug("converted url from xacml is " + urlXacml + ", body-type is " + this.bodyType);
493                 }
494                 
495                 InputStream inConfigs = null;
496                 try {
497                         URL url = new URL(urlXacml);
498                         URLConnection connection = url.openConnection();
499                         inConfigs = connection.getInputStream();
500                         String encoding = connection.getContentEncoding();
501                         encoding = (encoding == null ? "UTF-8" : encoding);
502                         this.body = IOUtils.toString(inConfigs, encoding);                      
503                         if (logger.isInfoEnabled()) {
504                                 logger.info("The following document of type " + this.bodyType.toString() + 
505                                                     " has been fetched from " + urlXacml + System.lineSeparator() +
506                                                     this.body);
507                         }
508                         try {
509                                 inConfigs.close();
510                         } catch (IOException e) {
511                                 // ignore
512                                 logger.warn("Unexpected error closing stream to " + urlXacml, e);
513                         }
514                         return true;
515                 } catch (Exception e) {
516                         if (logger.isWarnEnabled()) {
517                                 logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + 
518                                                     "- XACML url is not in the expected format: " + e.getMessage() + 
519                                                     ": " + urlXacml, e);
520                         }       
521                         // continue
522                 } finally {
523                         if (inConfigs != null) {
524                                 try {
525                                         inConfigs.close();
526                                 } catch (IOException e) {
527                                         // ignore
528                                         logger.warn("Unexpected error closing stream to " + urlXacml, e);
529                                 }
530                         }
531                 }
532                 
533                 // if retrieval through PAP url was not possible, try to retrieve it from 
534                 // filesystem instead.
535                 
536                 if (this.body == null || this.body.isEmpty()) {
537                         Path webappsPath = Paths.get(XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_WEBAPPS));
538                         if (webappsPath == null) {
539                                 logger.error("Invalid Webapps Path Location property : " + 
540                                          XACMLRestProperties.PROP_PAP_WEBAPPS);
541                                 return false;
542                         }
543                         String waPath = webappsPath.toFile().getAbsolutePath();
544                         
545                         String typeDir = null;
546                         try {
547                                 typeDir = Xacml2Elk.toConfigsWebDirectory(this.type);
548                         } catch (IllegalArgumentException iae) {
549                                 if (logger.isWarnEnabled()) {
550                                         logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + 
551                                                             "- " + this.type + 
552                                                             " cannot be converted to body-directory: " + iae.getMessage(), 
553                                                             iae);
554                                 }
555                                 this.bodyType = PolicyBodyType.none;
556                                 return false;
557                         }
558                         
559                         String scopePrefix = this.scope.replace('/', '.');
560                         Path bodyPath = Paths.get(waPath, 
561                                                           typeDir, 
562                                                           scopePrefix + "." + this.type + "_" + 
563                                                           this.name + "." + this.bodyType.name());
564                         File bodyFile = bodyPath.toFile();
565                         if (Files.notExists(bodyPath)) {
566                                 if (logger.isWarnEnabled()) {
567                                         logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + 
568                                                             "The following document of type " + this.bodyType.toString() + 
569                                                             " does not exist at filesystem location " + bodyFile.getAbsolutePath());
570                                 }
571                                 this.bodyType = PolicyBodyType.none;
572                                 return false;
573                         } else {                        
574                                 if (logger.isInfoEnabled()) {
575                                         logger.info("The following document of type " + this.bodyType.toString() + 
576                                                             " will be fetched from filesystem location " + bodyFile.getAbsolutePath());
577                                 }
578                         }
579                         
580                         try {
581                                 inConfigs = new FileInputStream(bodyFile);
582                                 this.body = IOUtils.toString(inConfigs);                        
583                                 inConfigs.close();
584                                 if (logger.isInfoEnabled()) {
585                                         logger.info("The document of type " + this.bodyType.toString() + 
586                                                             " has been found at filesystem location " + bodyFile.getAbsolutePath());
587                                 }
588                         } catch (Exception e) {
589                                 if (logger.isWarnEnabled()) {
590                                         logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + 
591                                                             "- XACML Body File cannot be read: " + bodyFile.getAbsolutePath(), e);
592                                 }
593                                 this.bodyType = PolicyBodyType.none;
594                                 return false;
595                         } finally {
596                                 if (inConfigs != null) {
597                                         try {
598                                                 inConfigs.close();
599                                         } catch (IOException e) {
600                                                 // ignore
601                                                 logger.warn("Unexpected error closing stream to " + urlXacml, e);
602                                         }
603                                 } else {
604                                         return false;
605                                 }
606                         }                       
607                 } 
608                 return true;
609         }
610
611         public boolean attachJsonBody(JsonNode jPolicy) {
612                 if (this.body == null) {
613                         if (logger.isWarnEnabled()) {
614                                 logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + 
615                                                     "- JSON Body expected but none provided from " +
616                                                     this.scope + ":" + this.type + ":" + this.name);
617                         }
618                         return true;
619                 }
620                 
621                 ObjectNode jPolicyRoot = (ObjectNode) jPolicy;
622                 
623                 // verify the json is valid
624                 final ObjectMapper mapper = new ObjectMapper();
625                 JsonNode jBodyContent;
626                 try {
627                         jBodyContent = mapper.readTree(this.body);
628                 } catch (IOException e) {
629                         logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + 
630                                         "- JSON Body is invalid in " +
631                                         this.scope + ":" + this.type + ":" + this.name + ":" +
632                                                 e.getMessage() + System.lineSeparator() + this.body, e);
633                         return false;
634                 }
635                 
636                 String jBodyName = this.type + "_" + "Body";
637                 ObjectNode jBodyContainer = mapper.createObjectNode();
638                 jBodyContainer.set(jBodyName, jBodyContent);            
639                 
640                 jPolicyRoot.set("Body", jBodyContainer);
641                 
642                 if (logger.isDebugEnabled())
643                         logger.debug("Attaching JSON to " +
644                                   this.scope + ":" + 
645                                   this.type + ":" + this.name + ":" + 
646                                   jBodyName + System.lineSeparator() +
647                                   jBodyContent);
648                 
649                 return true;
650         }
651         
652         @SuppressWarnings("unchecked")
653         public boolean attachXmlBody(JsonNode jPolicy) {
654                 if (this.body == null) {
655                         if (logger.isWarnEnabled()) {
656                                 logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + 
657                                                     "- JSON Body expected but none provided from " +
658                                                     this.scope + ":" + this.type + ":" + this.name);
659                         }
660                         return true;
661                 }
662                 
663                 XmlMapper xmlMapper = new XmlMapper();
664                 xmlMapper.setConfig(xmlMapper.getSerializationConfig().withRootName(""));
665                 Map<Object, Object> map;
666                 try {
667                         map = xmlMapper.readValue(this.body, Map.class);
668                 } catch (IOException e) {
669                         logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + 
670                                 "- XML Body is invalid in " +
671                                 this.scope + ":" + this.type + ":" + this.name + ":" +
672                                         e.getMessage() + System.lineSeparator() + this.body, e);
673                         return false;
674                 }
675
676                 final ObjectMapper mapper = new ObjectMapper();
677                 String jXmlBody;
678                 try {
679                         jXmlBody = mapper.writeValueAsString(map);
680                 } catch (JsonProcessingException e) {
681                         logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + 
682                                 "- Cannot convert XML Body to JSON in " +
683                                 this.scope + ":" + this.type + ":" + this.name + ":" +
684                                         e.getMessage() + System.lineSeparator() + this.body, e);
685                         return false;
686                 }
687                 
688                 if (logger.isDebugEnabled())
689                         logger.debug("XML-to-JSON Body conversion: " + this.scope + ":" + 
690                                   this.type + ":" + this.name +jXmlBody);
691
692                 JsonNode jBodyContent;
693                 try {
694                         jBodyContent = mapper.readTree(jXmlBody);
695                 } catch (IOException e) {
696                         logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + 
697                                         "- JSON Body (converted from XML) is invalid in " +
698                                         this.scope + ":" + this.type + ":" + this.name  + ":" +
699                                                 e.getMessage() + System.lineSeparator() + jXmlBody, e);
700                         return false;
701                 }
702                 
703                 ObjectNode jPolicyRoot = (ObjectNode) jPolicy;          
704
705                 String jBodyName = this.type + "_" + "Body";
706                 ObjectNode jBodyContainer = mapper.createObjectNode();
707                 jBodyContainer.set(jBodyName, jBodyContent);            
708                 
709                 jPolicyRoot.set("Body", jBodyContainer);
710                 
711                 
712                 if (logger.isDebugEnabled())
713                         logger.debug("Attaching JSON to " +
714                                   this.scope + ":" + 
715                                   this.type + ":" + this.name + ":" + 
716                                   jBodyName + System.lineSeparator() +
717                                   jBodyContent);
718                 
719                 return true;
720         }       
721         
722         protected boolean attachPropertiesBody(JsonNode jPolicy) {
723                 if (this.body == null) {
724                         if (logger.isWarnEnabled()) {
725                                 logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + 
726                                                     "- JSON Body expected but none provided from " +
727                                                     this.scope + ":" + this.type + ":" + this.name);
728                         }
729                         return true;
730                 }
731                 
732             final Properties propBody = new Properties();
733             try {
734                         propBody.load(new StringReader(this.body));
735                 } catch (IOException e) {
736                         logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + 
737                                 "- JSON Body is invalid in " +
738                                 this.scope + ":" + this.type + ":" + this.name + ":" +
739                                 e.getMessage() + System.lineSeparator() + this.body, e);
740                         return false;
741                 }
742                 
743             if (propBody.isEmpty()) {
744                 if (logger.isInfoEnabled()) {
745                                 logger.info("Empty set of properties: " +
746                                                 this.scope + ":" + this.type + ":" + this.name +
747                                                 System.lineSeparator() + this.body);
748                 }
749                 return true;
750             }
751             
752                 
753                 final ObjectMapper mapper = new ObjectMapper();
754                 
755                 ObjectNode jPolicyRoot = (ObjectNode) jPolicy;  
756                 ObjectNode jBody = jPolicyRoot.putObject("Body");
757                 String jBodyName = this.type + "_" + "Body";
758                 ObjectNode jBodyContainer = jBody.putObject(jBodyName);
759                 
760                 // ObjectNode jBodyContainer = mapper.createObjectNode();
761                 
762                 for(String key : propBody.stringPropertyNames()) {
763                         String value = propBody.getProperty(key);
764                 if (logger.isDebugEnabled()) {
765                                 logger.debug("Attaching JSON field to " + jBodyName + " for " + 
766                                                 this.type.toString() + ":" + 
767                                                     this.scope + ":" + this.name + ":" + jBodyName + ":" +
768                                                 " <" + key +"," + value + ">");
769                 }
770                 jBodyContainer.put(key, propBody.getProperty(key));
771                 }
772
773                 return true;
774         }
775         
776         public boolean attachTextBody(JsonNode jPolicy) {
777                 if (this.body == null) {
778                         if (logger.isWarnEnabled()) {
779                                 logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + 
780                                                     "- JSON Body expected but none provided from " +
781                                                     this.scope + ":" + this.type + ":" + this.name);
782                         }
783                         return true;
784                 }
785                 
786                 final ObjectMapper mapper = new ObjectMapper();
787                 StringWriter jsonEscapedTextWriter = new StringWriter();
788                 try {
789                         mapper.writer().writeValue(jsonEscapedTextWriter, this.body);
790                 } catch (IOException e) {
791                         if (logger.isWarnEnabled()) {
792                                 logger.warn(XACMLErrorConstants.ERROR_SYSTEM_ERROR + 
793                                                     "- Text Body cannot be converted from " +
794                                                     this.scope + ":" + this.type + ":" + this.name + ":" +
795                                                     e.getMessage() + ":" + jsonEscapedTextWriter , e);
796                         }
797                         return false;
798                 }
799                 String jTextBody = jsonEscapedTextWriter.toString();
800                 
801                 if (logger.isDebugEnabled())
802                         logger.debug("XML 2JSON Body conversion: " + this.scope + ":" + 
803                                   this.type + ":" + this.name + ":" + jTextBody);
804                 
805                 ObjectNode jPolicyRoot = (ObjectNode) jPolicy;          
806                 
807                 String jBodyName = this.type + "_" + "Body";    
808                 ObjectNode jBodyContainer = mapper.createObjectNode();
809                 jBodyContainer.put(jBodyName, jTextBody);               
810                 
811                 jPolicyRoot.set("Body", jBodyContainer);
812                 
813                 if (logger.isDebugEnabled())
814                         logger.debug("Attaching JSON to " +
815                                   this.scope + ":" + 
816                                   this.type + ":" + this.name + ":" + 
817                                   jBodyName + ":" +
818                                   jTextBody);
819
820                 return true;
821         }               
822         
823         protected boolean attachBody(JsonNode jPolicy) {
824                 if (logger.isTraceEnabled()) logger.trace("ENTER");
825                 
826                 if (this.bodyType == PolicyBodyType.none) {
827                         if (logger.isInfoEnabled())
828                                 logger.info("No body to attach for policy " + 
829                                                 this.scope + "/" + this.type +  "_" + this.name);
830                         
831                         return true;
832                 }
833                 
834                 if (this.body == null || this.body.isEmpty()) {
835                         if (logger.isWarnEnabled())
836                                 logger.warn("No body to attach for policy " + 
837                                         this.bodyType + this.type + this.scope + this.name);
838                         
839                         return true;
840                 }
841                 
842                 switch (this.bodyType) {
843                 case json:
844                         return attachJsonBody(jPolicy);
845                 case properties:
846                         return attachPropertiesBody(jPolicy);
847                 case xml:
848                         return attachXmlBody(jPolicy);
849                 case txt:
850                         return attachTextBody(jPolicy);                 
851                 case none:
852                 default:
853                         if (logger.isWarnEnabled())
854                                 logger.warn("Unexpected body type: " +  this.bodyType + 
855                                         this.bodyType + this.type + this.scope + this.name);
856                         return false;
857                 }
858         }
859         
860         public ElkRecord record() throws JAXBException, JsonProcessingException, 
861                                          IOException, IllegalArgumentException {
862                 if (logger.isTraceEnabled()) logger.trace("ENTER");
863                 
864                 Marshaller m = jaxbContext.createMarshaller();          
865                 m.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
866                 m.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, true);
867                 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
868                 m.setProperty(MarshallerProperties.JSON_REDUCE_ANY_ARRAYS, true);
869                 m.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, false);
870                 
871                 StringWriter policyStringWriter = new StringWriter();
872                 m.marshal(policy, policyStringWriter);
873                 
874                 // add metadata to elk record
875                 
876                 final ObjectMapper mapper = new ObjectMapper();
877                 JsonNode jRoot = mapper.readTree(policyStringWriter.toString());
878                 JsonNode jPolicy = jRoot.path("Policy");
879                 if (jPolicy.isMissingNode()) {
880                         logger.warn("Aborting: Policy root node is missing.");
881                         throw new IllegalArgumentException("Missing policy root node");
882                 }
883                 
884                 ((ObjectNode) jPolicy).put("PolicyType", this.type.toString());
885                 ((ObjectNode) jPolicy).put("PolicyName", this.name);            
886                 ((ObjectNode) jPolicy).put("Owner", this.owner);
887                 ((ObjectNode) jPolicy).put("Scope", this.scope);
888                 
889                 JsonNode jPolicyId = jPolicy.path("PolicyId");
890                 if (jPolicyId.isMissingNode()) {
891                         logger.warn("Aborting: Policy ID node is missing.");
892                         throw new IllegalArgumentException("Missing policy id");
893                 }       
894                 
895                 if (!jPolicyId.isTextual() || !jPolicyId.isValueNode()) {
896                         logger.warn("Aborting: Policy ID invalid.");
897                         throw new IllegalArgumentException("Invalid policy id");
898                 }
899                 
900                 String xacmlPolicyId = jPolicyId.asText();
901                 String policyId = xacmlPolicyId.substring(xacmlPolicyId.lastIndexOf(":")+1);
902                 
903                 boolean success = attachBody(jPolicy);
904                 
905                 mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
906                 mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
907                 mapper.configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false);
908                 mapper.configure(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED, true);                
909
910                 String recordText = mapper.writeValueAsString(jRoot);
911                 if (logger.isDebugEnabled()) {
912                         logger.debug("ELK Record: " + System.lineSeparator() + recordText);
913                 }
914                 
915                 ElkRecord elkRecord = new ElkRecord(policyId, recordText, jRoot, success);
916                 return elkRecord;
917         }
918         
919         public void store(String policyId, String record) throws IOException {
920                 if (logger.isTraceEnabled()) logger.trace("ENTER");
921                 
922                 if (this.elkDirectory != null) {
923                         Files.createDirectories(this.elkDirectory.toPath());;
924                         Path elkPolicyFile = Paths.get(this.elkDirectory.getPath(), policyId + ".json");
925                         
926                         if (logger.isDebugEnabled()) {
927                                 logger.info("Output: " + elkPolicyFile.toAbsolutePath().toString());
928                                 logger.info("---------------------------------------------------");
929                         }
930                         
931                         Files.write(elkPolicyFile, record.getBytes());
932                 }               
933         }
934         
935         public static void main(String args[]) 
936                         throws JAXBException, IOException, CmdLineException, IllegalStateException {
937                 
938                 CLIOptions cliOptions = new CLIOptions();
939                 CmdLineParser cliParser= new CmdLineParser(cliOptions);
940                 
941                 try {
942                         cliParser.parseArgument(args);
943                 } catch (CmdLineException e) {
944                         System.err.println("Usage: Xacml2elk");
945                         cliParser.printUsage(System.err);
946                         throw e;
947                 }
948                 
949                 System.out.println("---------------------------------------------------");
950                 System.out.println("Converting " + cliOptions.xacmlFile.getName() + " to ELK format");
951                 System.out.println("Metadata=" + "[type:" + cliOptions.type + 
952                                            "|name:" + cliOptions.name + 
953                                            "|owner:" + cliOptions.owner + 
954                                            "|scope:" + cliOptions.scope + "]");
955                 
956                 // generate json from jaxb input file
957                 
958                 Path xacmlPath = cliOptions.xacmlFile.toPath();
959                 if (!Files.isReadable(xacmlPath) || !Files.isRegularFile(xacmlPath)) {
960                         System.out.println("Error: " + xacmlPath + " is invalid.");
961                         throw new IllegalArgumentException("Error: " + xacmlPath + " is invalid.");
962                 }
963                 
964                 
965                 Xacml2Elk convertor = new Xacml2Elk(cliOptions);
966                 ElkRecord elkRecord = convertor.record();
967                 System.out.println(elkRecord.record);
968                 
969                 Path elkOutDir = cliOptions.elkDirectory.toPath();
970                 if (!Files.isReadable(elkOutDir) || !Files.isDirectory(elkOutDir) || 
971                         !Files.isWritable(elkOutDir) || !Files.isExecutable(elkOutDir)) {
972                         System.out.println("Error: " + elkOutDir.getFileName() + " is invalid.");
973                         throw new IllegalArgumentException("Error: " + elkOutDir.getFileName() + " is invalid.");
974                 }
975                 
976                 convertor.store(elkRecord.policyId, elkRecord.record);
977         }
978 }