Initial OpenECOMP policy/engine commit
[policy/engine.git] / ECOMP-XACML / src / main / java / org / openecomp / policy / xacml / std / pap / StdEngine.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ECOMP-XACML
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 package org.openecomp.policy.xacml.std.pap;
21
22 import java.io.FileInputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.nio.file.FileVisitResult;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.nio.file.Paths;
30 import java.nio.file.SimpleFileVisitor;
31 import java.nio.file.attribute.BasicFileAttributes;
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.Enumeration;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Properties;
38 import java.util.Set;
39 import java.util.TreeSet;
40
41 import org.apache.commons.logging.Log;
42 import org.apache.commons.logging.LogFactory;
43 import org.openecomp.policy.xacml.api.pap.EcompPDP;
44 import org.openecomp.policy.xacml.api.pap.EcompPDPGroup;
45 import org.openecomp.policy.xacml.api.pap.PAPPolicyEngine;
46 import org.openecomp.policy.common.logging.eelf.MessageCodes;
47 import org.openecomp.policy.common.logging.eelf.PolicyLogger;
48 import com.att.research.xacml.api.pap.PAPException;
49 import com.att.research.xacml.api.pap.PDP;
50 import com.att.research.xacml.api.pap.PDPGroup;
51 import com.att.research.xacml.api.pap.PDPPolicy;
52 import com.att.research.xacml.api.pap.PDPStatus;
53 import com.att.research.xacml.util.XACMLProperties;
54 import com.google.common.base.Joiner;
55 import com.google.common.base.Splitter;
56 import com.google.common.collect.Sets;
57
58 /**
59  * This is a simple PAP engine that uses some property files and a simple directory
60  * structure in the file system to manage a policy repository and set of PDP nodes.
61  * 
62  *
63  */
64 public class StdEngine extends StdPDPItemSetChangeNotifier implements PAPPolicyEngine {
65         private static Log      logger  = LogFactory.getLog(StdEngine.class);
66
67         public static String    PROP_PAP_REPO = "xacml.pap.pdps";
68         public static String    PROP_PAP_GROUPS = "xacml.pap.groups";
69         public static String    PROP_PAP_GROUPS_DEFAULT = "xacml.pap.groups.default";
70         public static String    PROP_PAP_GROUPS_DEFAULT_NAME = "default";
71         //this value will be accessed from XacmlPapServlet so that we know if a default group did not exist
72         //and was just added. This way, we can add the new group to the database.
73         public boolean wasDefaultGroupJustAdded = false;
74
75     protected final Path repository;
76         protected Set<StdPDPGroup> groups;
77                 
78         public StdEngine() throws PAPException, IOException {
79                 //
80                 // Get the location in the file system of our repository
81                 //
82                 this.repository = Paths.get(XACMLProperties.getProperty(PROP_PAP_REPO));
83                 //
84                 // Initialize
85                 //
86                 this.intialize();
87         }
88         
89         public StdEngine(Properties properties) throws PAPException, IOException {
90                 //
91                 // Get the location in the file system of our repository
92                 //
93                 this.repository = Paths.get(properties.getProperty(PROP_PAP_REPO));
94                 //
95                 // Initialize
96                 //
97                 this.intialize();
98         }
99         
100         public StdEngine(Path repository) throws PAPException, IOException {
101                 //
102                 // Save our location
103                 //
104                 this.repository = repository;
105                 //
106                 // Initialize
107                 //
108                 this.intialize();
109         }
110         
111         private void intialize() throws PAPException, IOException {
112                 //
113                 // Sanity check the repository path
114                 //
115                 if (this.repository == null) {
116                         throw new PAPException ("No repository specified.");
117                 }
118                 if (Files.notExists(this.repository)) {
119                         Files.createDirectory(repository);
120                 }
121                 if (Files.isDirectory(this.repository) == false) {
122                         throw new PAPException ("Repository is NOT a directory: " + this.repository.toAbsolutePath());
123                 }
124                 if (Files.isWritable(this.repository) == false) {
125                         throw new PAPException ("Repository is NOT writable: " + this.repository.toAbsolutePath());
126                 }
127                 //
128                 // Load our groups
129                 //
130                 this.loadGroups();
131         }
132         
133         private void loadGroups() throws PAPException {
134                 //
135                 // Create a properties object
136                 //
137                 Properties properties = new Properties();
138                 Path file = Paths.get(this.repository.toString(), XACMLProperties.XACML_PROPERTIES_NAME);
139                 try {
140                         //
141                         // Load the properties
142                         //
143                         try (InputStream is = new FileInputStream(file.toFile())) {
144                                 properties.load(is);
145                         }
146
147                         //
148                         // Parse it
149                         //
150                         this.groups = this.readProperties(this.repository, properties);
151                 } catch (IOException e) {
152                         //TODO:EELF Cleanup - Remove logger
153                         //logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Failed to load " + file.toAbsolutePath().toString());
154                         PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "StdEngine", "Failed to load properties file");
155                         this.groups = new HashSet<StdPDPGroup>();
156                 }
157                 //
158                 // Initialize the default group
159                 //
160                 PDPGroup defaultGroup = this.initializeDefaultGroup(file, properties);
161                 logger.info("Default group is: " + defaultGroup.getId() + "=" + defaultGroup.getName());
162         }
163         
164         private PDPGroup initializeDefaultGroup(Path file, Properties properties) throws PAPException {
165                 wasDefaultGroupJustAdded = false;
166                 //
167                 // Make sure we have the default group
168                 //
169                 PDPGroup group = this.getDefaultGroup();
170                 if (group != null) {
171                         return group;
172                 }
173                 //
174                 // We don't have the default group, create it
175                 //
176                 String defaultId = properties.getProperty(PROP_PAP_GROUPS_DEFAULT, PROP_PAP_GROUPS_DEFAULT_NAME);
177                 if(defaultId == null){
178                         defaultId = PROP_PAP_GROUPS_DEFAULT_NAME;
179                 }
180                 if(defaultId.equals("")){
181                         defaultId = PROP_PAP_GROUPS_DEFAULT_NAME;
182                 }
183                 //we're going to check one more time in case the PROP_PAP_GROUPS_DEFAULT_NAME doesn't exist
184                 if(defaultId == null){
185                         defaultId = "default";
186                 }
187                 if(defaultId.equals("")){
188                         defaultId = "default";
189                 }
190                 logger.warn("Default group does NOT exist, creating " + defaultId);
191                 Path defaultPath = Paths.get(this.repository.toString(), defaultId);
192                 try {
193                         //
194                         // Does it exist?
195                         //
196                         if (Files.notExists(defaultPath)) {
197                                 //
198                                 // Create its directory
199                                 //
200                                 Files.createDirectory(defaultPath);
201                                 //
202                                 // Create property files
203                                 //
204                                 {
205                                         Properties props = new Properties();
206                                         props.setProperty(XACMLProperties.PROP_REFERENCEDPOLICIES, "");
207                                         props.setProperty(XACMLProperties.PROP_ROOTPOLICIES, "");
208                                         Path policyPath = Paths.get(defaultPath.toAbsolutePath().toString(), "xacml.policy.properties");
209                                         Files.createFile(policyPath);
210                                         try (OutputStream os = Files.newOutputStream(policyPath)) {
211                                                 props.store(os, "");
212                                         } catch (IOException e) {
213                                                 //TODO:EELF Cleanup - Remove logger
214                                                 //logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Failed to write default policy properties", e);
215                                                 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "StdEngine", "Failed to write default policy properties");
216                                         }
217                                 }
218                                 {
219                                         Properties props = new Properties();
220                                         props = setPIPProperties(props);
221                                         Path pipPath = Paths.get(defaultPath.toAbsolutePath().toString(), "xacml.pip.properties");
222                                         Files.createFile(pipPath);
223                                         try (OutputStream os = Files.newOutputStream(pipPath)) {
224                                                 props.store(os, "");
225                                         } catch (IOException e) {
226                                                 //TODO:EELF Cleanup - Remove logger
227                                                 //logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Failed to write default pip properties", e);
228                                                 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "StdEngine", "Failed to write default pip properties");
229                                         }                                       
230                                 }
231                         }
232                         //
233                         // Create the default group
234                         //
235                         StdPDPGroup newDefault = new StdPDPGroup(defaultId, true, "default", "The default group where new PDP's are put.", defaultPath);
236                         //
237                         // Add it to our list
238                         //
239                         this.groups.add(newDefault);
240                         //
241                         // Save our properties out since we have
242                         // a new default group.
243                         //
244                         StdEngine.setGroupProperties(newDefault, properties);
245                         //
246                         // Save it to disk
247                         //
248                         try {
249                                 try (OutputStream os = Files.newOutputStream(file)) {
250                                         properties.store(os, "");
251                                 }
252                         } catch (IOException e) {
253                                 //TODO:EELF Cleanup - Remove logger
254                                 //logger.error("Failed to save properties with new default group information.", e);
255                                 PolicyLogger.error(MessageCodes.EXCEPTION_ERROR, e, "StdEngine", "Failed to save properties with new default group information.");
256                         }
257                         //
258                         // Return it
259                         //
260                         wasDefaultGroupJustAdded = true;
261                         return newDefault;
262                 } catch (IOException e) {
263                         //TODO:EELF Cleanup - Remove logger
264                         //logger.error("Failed to create default group: " + defaultId, e);
265                         PolicyLogger.error(MessageCodes.EXCEPTION_ERROR, e, "StdEngine", "Failed to create default group");
266                         throw new PAPException("Failed to create default group");
267                 }
268         }
269         
270
271     
272     
273         @Override
274         public EcompPDPGroup getDefaultGroup() throws PAPException
275         {
276                 for (EcompPDPGroup group : this.groups) {
277                         if (group.isDefaultGroup()) {
278                                 return group;
279                         }
280                 }
281                 //
282                 // Default group doesn't exist
283                 //
284                 return null;
285         }
286
287         /*@Override
288         public void     SetDefaultGroup(PDPGroup group) throws PAPException {
289                 
290                 boolean changesMade = false;
291                 for (PDPGroup aGroup : groups) {
292                         if (aGroup.getId().equals(group.getId())) {
293                                 if ( ! aGroup.isDefaultGroup()) {
294 //TODO - since the original code checked for type we do also.
295                                         if (aGroup instanceof StdPDPGroup) {
296                                                 ((StdPDPGroup) aGroup).setDefault(true);
297                                                 changesMade = true;
298                                         } else {
299                                                 throw new IllegalArgumentException("Group in groups of unknown type '" + aGroup.getClass().getName() + "'");
300                                         }
301                                 }
302                         } else {
303                                 // not the new default group
304                                 if (aGroup.isDefaultGroup()) {
305 //TODO - since the original code checked for type we do also.
306                                         if (aGroup instanceof StdPDPGroup) {
307                                                 ((StdPDPGroup) aGroup).setDefault(false);
308                                                 changesMade = true;
309                                         } else {
310                                                 throw new IllegalArgumentException("Group in groups of unknown type '" + aGroup.getClass().getName() + "'");
311                                         }
312                                 }
313                         }
314                 }
315                 if (changesMade) {
316                         this.doSave();
317                 }
318                 
319                 return; 
320         }*/
321
322         /*@Override
323         public Set<PDPGroup> getPDPGroups() throws PAPException {
324                 final Set<PDPGroup> grps = new HashSet<PDPGroup>();
325                 for (PDPGroup g : this.groups) {
326                         grps.add(g);
327                 }
328                 return Collections.unmodifiableSet(grps);
329         }*/
330         
331         @Override
332         public EcompPDPGroup getGroup(String id) throws PAPException {
333                 for (EcompPDPGroup g: this.groups) {
334                         if (g.getId().equals(id)) {
335                                 return g;
336                         }
337                 }
338                 return null;
339         }
340
341         @Override
342         public void     newGroup(String name, String description) throws PAPException, NullPointerException
343         {       
344                 //
345                 // Null check
346                 //
347                 if (name == null) {
348                         throw new NullPointerException();
349                 }
350                 //
351                 // Do we already have this group?
352                 //
353                 for (PDPGroup group : this.groups) {
354                         if (group.getName().equals(name)) {
355                                 throw new PAPException("Group with this name=" + name + " already exists.");
356                         }
357                 }
358                 
359                 
360                 // create an Id that can be used as a file name and a properties file key.
361                 // Ids must not contain \/:*?"<>|=,;
362                 // The ID must also be unique within the current set of PDPGroups.
363                 String id = createNewPDPGroupId(name);
364                 
365                 
366                 //
367                 // Construct the directory path
368                 //
369                 Path groupPath = Paths.get(this.repository.toString(), id);
370                 //
371                 // If it exists already
372                 //
373                 if (Files.exists(groupPath)) {
374                         logger.warn("addGroup " + id + " directory exists" + groupPath.toString());
375                 } else {
376                         try {
377                                 //
378                                 // Create the directory
379                                 //
380                                 Files.createDirectory(groupPath);
381                         } catch (IOException e) {
382                                 //TODO:EELF Cleanup - Remove logger
383                                 //logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Failed to create " + groupPath);
384                                 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "StdEngine", "Failed to create " + groupPath);
385                                 throw new PAPException("Failed to create " + id);
386                         }
387                 }
388                 //
389                 // Create the Policies
390                 //
391
392                 Path policyProperties = Paths.get(groupPath.toString(), "xacml.policy.properties");
393                 if (Files.exists(policyProperties)) {
394                         logger.warn("addGroup " + id + " file exists: " + policyProperties.toString());
395                 } else {
396                         Properties props = new Properties();
397                         props.setProperty(XACMLProperties.PROP_REFERENCEDPOLICIES, "");
398                         props.setProperty(XACMLProperties.PROP_ROOTPOLICIES, "");
399                         try {
400                                 Files.createFile(policyProperties);
401                                 try (OutputStream os = Files.newOutputStream(policyProperties)) {
402                                         props.store(os, "");
403                                 }
404                         } catch (IOException e) {
405                                 //TODO:EELF Cleanup - Remove logger
406                                 //logger.error("Failed to create " + policyProperties);
407                                 PolicyLogger.error(MessageCodes.EXCEPTION_ERROR, e, "StdEngine", "Failed to create " + policyProperties);
408                                 throw new PAPException("Failed to create " + id);
409                         }
410                 }
411                 //
412                 // Create the PIP config
413                 //
414                 Path pipProperties = Paths.get(groupPath.toString(), "xacml.pip.properties");
415                 if (Files.exists(pipProperties)) {
416                         logger.warn("addGroup " + id + " file exists: " + pipProperties.toString());
417                 } else {
418                         try {
419                                 Properties props = new Properties();
420                                 props = setPIPProperties(props);
421                                 Files.createFile(pipProperties);
422                                 try (OutputStream os = Files.newOutputStream(pipProperties)) {
423                                         props.store(os, "");
424                                 }
425                         } catch (IOException e) {
426                                 //TODO:EELF Cleanup - Remove logger
427                                 //logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Failed to create " + pipProperties);
428                                 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "StdEngine", "Failed to create " + pipProperties);
429                                 throw new PAPException("Failed to create " + id);
430                         }       
431
432                 }
433                 //
434                 // Ok now add it
435                 //
436                 StdPDPGroup newGroup = new StdPDPGroup(id, name, description, groupPath);
437                 if (this.groups.add(newGroup)) {
438                         // save the new group in our properties and notify any listeners of the change
439                         groupChanged(newGroup);
440                 }
441
442         }
443         
444
445         
446         
447         /**
448          * Helper to create a new Group ID.
449          * Use the Name field to create the Id.
450          * The Name is expected to not be null; if it is then this method throws an exception.
451          * The name is supposed to be unique within the current set of groups,
452          * so creating the ID based on the name will create a unique string.
453          * 
454          * @param name
455          * @return
456          */
457         private String createNewPDPGroupId(String name) {
458                 String id = name;
459                 // replace "bad" characters with sequences that will be ok for file names and properties keys.
460                 id = id.replace(" ", "_sp_");
461                 id = id.replace("\t", "_tab_");
462                 id = id.replace("\\", "_bksl_");
463                 id = id.replace("/", "_sl_");
464                 id = id.replace(":", "_col_");
465                 id = id.replace("*", "_ast_");
466                 id = id.replace("?", "_q_");
467                 id = id.replace("\"", "_quo_");
468                 id = id.replace("<", "_lt_");
469                 id = id.replace(">", "_gt_");
470                 id = id.replace("|", "_bar_");
471                 id = id.replace("=", "_eq_");
472                 id = id.replace(",", "_com_");
473                 id = id.replace(";", "_scom_");
474
475                 return id;
476         }
477         
478         
479         @Override
480         public EcompPDP getPDP(String pdpId) throws PAPException {
481                 for (EcompPDPGroup group : this.groups) {
482                         for (EcompPDP pdp : group.getEcompPdps()) {
483                                 if (pdp.getId().equals(pdpId)) {
484                                         return pdp;
485                                 }
486                         }
487                 }
488                 return null;
489         }
490
491         
492         @Override
493         public void movePDP(EcompPDP pdp, EcompPDPGroup newGroup) throws PAPException {
494                 if (newGroup == null) {
495                         throw new NullPointerException("You must specify which group the PDP will belong to.");
496                 }
497                 PDPGroup currentGroup = this.getPDPGroup(pdp);
498                 if (currentGroup == null) {
499                         throw new PAPException("PDP must already belong to a group.");
500                 }
501                 if (currentGroup.equals(newGroup)) {
502                         logger.warn("Already in that group.");
503                         return;
504                 }
505                 if (currentGroup instanceof StdPDPGroup && newGroup instanceof StdPDPGroup) {
506                         if (((StdPDPGroup) currentGroup).removePDP(pdp)) {
507                                 boolean result = ((StdPDPGroup) newGroup).addPDP(pdp);
508                                 if (result) {
509                                         //
510                                         // Save the configuration
511                                         //
512                                         this.doSave();
513                                 } else {
514                                         //TODO:EELF Cleanup - Remove logger
515                                         //logger.error("Failed to add to new group, putting back into original group.");
516                                         PolicyLogger.error("Failed to add to new group, putting back into original group.");
517                                         if (((StdPDPGroup) currentGroup).removePDP(pdp) == false) {
518                                                 //TODO:EELF Cleanup - Remove logger
519                                                 //logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Failed to put PDP back into original group.");
520                                                 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE + "Failed to put PDP back into original group.");
521                                         }
522                                 }
523                         }
524                 } else {
525                         String message = "Unknown PDP group class: " + newGroup.getClass().getCanonicalName() + " and " + currentGroup.getClass().getCanonicalName();
526                         logger.warn(message);
527                         throw new PAPException(message);
528                 }
529         }
530
531         
532         @Override
533         public void     updatePDP(EcompPDP pdp) throws PAPException {
534                 PDP currentPDP = this.getPDP(pdp.getId());
535                 if (currentPDP == null) {
536                         String message = "Unknown PDP id '" + pdp.getId() + "'";
537                         logger.warn(message);
538                         throw new PAPException(message);
539                 }
540                 
541                 // the only things that the user can change are name and description
542                 currentPDP.setDescription(pdp.getDescription());
543                 currentPDP.setName(pdp.getName());
544                 if (currentPDP instanceof EcompPDP && pdp instanceof EcompPDP) {
545                         ((EcompPDP)currentPDP).setJmxPort(((EcompPDP)pdp).getJmxPort());
546                 }
547                 this.doSave();
548         }
549         
550         @Override
551         public void removePDP(EcompPDP pdp) throws PAPException {
552                 PDPGroup group = this.getPDPGroup(pdp);
553                 if (group == null) {
554                         throw new NullPointerException();
555                 }
556                 if (group instanceof StdPDPGroup) {
557                         boolean result = ((StdPDPGroup) group).removePDP(pdp);
558                         if (result) {
559                                 this.doSave();
560                         }
561                         return;
562                 }
563                 String message = "Unknown PDP group class: " + group.getClass().getCanonicalName();
564                 logger.warn(message);
565                 throw new PAPException(message);
566         }
567
568         
569         @Override
570         /**
571          * Should never be called - Detailed status is held on the PDP, not the PAP
572          */
573         public PDPStatus getStatus(EcompPDP pdp) throws PAPException {
574                 return getPDP(pdp.getId()).getStatus();
575         }
576         
577         @Override
578         public void publishPolicy(String id, String name, boolean isRoot, InputStream policy, EcompPDPGroup group) throws PAPException {
579                 if (group == null) {
580                         throw new NullPointerException();
581                 }
582                 if (group instanceof StdPDPGroup && this.groups.contains(group)) {
583                         ((StdPDPGroup) group).publishPolicy(id, name, isRoot, policy);
584                         return;
585                 }
586                 logger.warn("unknown PDP Group: " + group);
587                 throw new PAPException("Unknown PDP Group: " + group.getId());
588         }
589
590         // Currently not used on the PAP side.  This is done by ((StdPDPGroup) group).copyPolicyToFile
591         @Override
592         public void copyPolicy(PDPPolicy policy, EcompPDPGroup group)
593                         throws PAPException {
594         }
595         
596         
597         @Override
598         public void removePolicy(PDPPolicy policy, EcompPDPGroup group) throws PAPException {
599                 if (group == null) {
600                         throw new NullPointerException();
601                 }
602                 if (group instanceof StdPDPGroup && this.groups.contains(group)) {
603                         ((StdPDPGroup) group).removePolicy(policy);
604                         return;
605                 }
606                 logger.warn("unknown PDP Group: " + group);
607                 throw new PAPException("Unknown PDP Group: " + group.getId());
608         }
609
610         
611         //
612         // HELPER methods
613         //
614         
615         private Set<StdPDPGroup>        readProperties(Path repository, Properties properties) throws PAPException {
616                 Set<StdPDPGroup> groups = new HashSet<StdPDPGroup>();
617                 //
618                 // See if there is a groups property
619                 //
620                 String groupList = properties.getProperty(PROP_PAP_GROUPS, "");
621                 if (groupList == null) {
622                         logger.warn("null group list " + PROP_PAP_GROUPS);
623                         groupList = "";
624                 }
625                 if (logger.isDebugEnabled()) {
626                         logger.debug("group list: " + groupList);
627                 }
628                 //
629                 // Iterate the groups, converting to a set ensures we have unique groups.
630                 //
631                 for (String id : Splitter.on(',').trimResults().omitEmptyStrings().split(groupList)) {
632                         //
633                         // Add our Group Object
634                         //
635                         StdPDPGroup g = new StdPDPGroup(id.trim(), 
636                                                                 id.equals(properties.getProperty(PROP_PAP_GROUPS_DEFAULT, PROP_PAP_GROUPS_DEFAULT_NAME)), 
637                                                                 properties,
638                                                                 Paths.get(repository.toString(), id));
639
640                         //
641                         // Add it in
642                         //
643                         groups.add(g);
644                 }
645                 //
646                 // Dump what we got
647                 //
648                 if (logger.isDebugEnabled()) {
649                         logger.debug("PDP Group List: " + groups.toString());
650                 }
651                 return groups;
652         }
653         
654         private void saveConfiguration() throws PAPException, IOException {
655                 //
656                 // Create our properties object
657                 //
658                 Properties properties = new Properties() {
659                         private static final long serialVersionUID = 1L;
660                         // For Debugging it is helpful for the file to be in a sorted order,
661                         // any by returning the keys in the natural Alpha order for strings we get close enough.
662                         // TreeSet is sorted, and this just overrides the normal Properties method to get the keys.
663                         @Override
664                     public synchronized Enumeration<Object> keys() {
665                         return Collections.enumeration(new TreeSet<Object>(super.keySet()));
666                     }
667             };
668                 //
669                 // Iterate our groups
670                 //
671                 List<String> ids = new ArrayList<String>();
672                 for (PDPGroup group : this.groups) {
673                         ids.add(group.getId());
674                         properties.setProperty(group.getId() + ".name", (group.getName() == null ? "" : group.getName()));
675                         properties.setProperty(group.getId() + ".description", (group.getDescription() == null ? "" : group.getDescription()));
676                         //
677                         // Iterate its PDPs
678                         //
679                         List<String> pdps = new ArrayList<String>();
680                         for (PDP pdp : group.getPdps()) {
681                                 pdps.add(pdp.getId());
682                                 properties.setProperty(pdp.getId() + ".name", (pdp.getName() == null ? "" : pdp.getName()));
683                                 properties.setProperty(pdp.getId() + ".description", (pdp.getDescription() == null ? "" : pdp.getDescription()));
684                                 if (pdp instanceof EcompPDP) {
685                                         properties.setProperty(pdp.getId() + ".jmxport", (((EcompPDP)pdp).getJmxPort()==0 ? "" : ((EcompPDP)pdp).getJmxPort()).toString());
686                                 }
687                         }
688                         String pdpList = "";
689                         if (pdps.size() == 1) {
690                                 pdpList = pdps.get(0);
691                         } else if (pdps.size() > 1) {
692                                 pdpList = Joiner.on(',').skipNulls().join(pdps);
693                         }
694                         if (logger.isDebugEnabled()) {
695                                 logger.debug("Group " + group.getId() + " PDPS: " + pdpList);
696                         }
697                         properties.setProperty(group.getId() + ".pdps", pdpList);
698                 }
699                 if (ids.isEmpty()) {
700                         throw new PAPException("Inconsistency - we have NO groups. We should have at least one.");
701                 }
702                 String groupList = "";
703                 if (ids.size() == 1) {
704                         groupList = ids.get(0);
705                 } else if (ids.size() > 1){
706                         groupList = Joiner.on(',').skipNulls().join(ids);
707                 }
708                 logger.info("New Group List: " + groupList);
709                 
710                 properties.setProperty(PROP_PAP_GROUPS, groupList);
711                 //
712                 // Get the default group
713                 //
714                 PDPGroup defaultGroup = this.getDefaultGroup();
715                 if (defaultGroup == null) {
716                         throw new PAPException("Invalid state - no default group.");
717                 }
718                 properties.setProperty(PROP_PAP_GROUPS_DEFAULT, defaultGroup.getId());
719                 //
720                 // Now we can save the file
721                 //
722                 Path file = Paths.get(this.repository.toString(), "xacml.properties");
723                 try (OutputStream os = Files.newOutputStream(file)) {
724                         properties.store(os, "");
725                 }
726         }
727         
728         public static void      removeGroupProperties(String id, Properties properties) {
729                 for (Object key : properties.keySet()) {
730                         if (key.toString().startsWith(id + ".")) {
731                                 properties.remove(key);
732                         }
733                 }
734         }
735         
736         public static void setGroupProperties(PDPGroup group, Properties properties) {
737                 //
738                 // make sure its in the list of groups
739                 //
740                 Iterable<String> groups = Splitter.on(',').trimResults().omitEmptyStrings().split( properties.getProperty(PROP_PAP_GROUPS, ""));
741                 boolean inList = false;
742                 for (String g : groups) {
743                         if (g.equals(group.getId())) {
744                                 inList = true;
745                         }
746                 }
747                 if (inList == false) {
748                         Set<String> grps = Sets.newHashSet(groups);
749                         grps.add(group.getId());
750                         String newGroupList = "";;
751                         if (grps.size() == 1) {
752                                 newGroupList = grps.iterator().next();
753                         } else if (grps.size() > 1) {
754                                 newGroupList = Joiner.on(',').skipNulls().join(grps);
755                         }
756                         logger.info("New Group List: " + newGroupList);
757                         properties.setProperty(PROP_PAP_GROUPS, newGroupList);
758                 }
759                 //
760                 // Set its properties
761                 //
762                 properties.setProperty(group.getId() + ".name", group.getName());
763                 properties.setProperty(group.getId() + ".description", group.getDescription());
764                 //
765                 // Set its PDP list
766                 //
767                 if (group.getPdps().size() > 0) {
768                         String pdpList = "";
769                         if (group.getPdps().size() == 1) {
770                                 pdpList = group.getPdps().iterator().next().getId();
771                         } else if (group.getPdps().size() > 1) {
772                                 Set<String> ids = new HashSet<String>();
773                                 for (PDP pdp : group.getPdps()) {
774                                         ids.add(pdp.getId());
775                                 }
776                                 pdpList = Joiner.on(',').skipNulls().join(ids);
777                         }
778                         properties.setProperty(group.getId() + ".pdps", pdpList);
779                 } else {
780                         properties.setProperty(group.getId() + ".pdps", "");
781                 }
782         }
783
784         
785         public void changed() {
786                 if (logger.isDebugEnabled()) {
787                         logger.debug("changed");
788                 }
789                 this.doSave();
790                 this.fireChanged();
791         }
792
793         public void groupChanged(EcompPDPGroup group) {
794                 if (logger.isDebugEnabled()) {
795                         logger.debug("groupChanged: " + group);
796                 }
797                 this.doSave();
798                 this.firePDPGroupChanged(group);
799         }
800
801
802         public void pdpChanged(EcompPDP pdp) {
803                 if (logger.isDebugEnabled()) {
804                         logger.debug("pdpChanged: " + pdp);
805                 }
806                 this.doSave();
807                 this.firePDPChanged(pdp);
808         }
809
810         private void doSave() {
811                 try {
812                         //
813                         // Save the configuration
814                         //
815                         this.saveConfiguration();
816                 } catch (IOException e) {
817                         //TODO:EELF Cleanup - Remove logger
818                         //logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to save configuration", e);
819                         PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e, "StdEngine", "Failed to save configuration");
820                 } catch (PAPException e) {
821                         //TODO:EELF Cleanup - Remove logger
822                         //logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to save configuration", e);
823                         PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e, "StdEngine", "Failed to save configuration");
824                 }               
825         }
826         
827         // TODO: Adding Default PIP engine(s) while Loading initially. We don't want 
828         //                      Programmer intervention with the PIP engines. 
829         private Properties setPIPProperties(Properties props){
830                 props.setProperty(XACMLProperties.PROP_PIP_ENGINES, "AAF");
831                 props.setProperty("AAF.name", "AAFEngine");
832                 props.setProperty("AAF.description", "AAFEngine to communicate with AAF to take decisions");
833                 props.setProperty("AAF.classname","org.openecomp.policy.xacml.std.pip.engines.aaf.AAFEngine");
834                 return props;
835         }
836
837         
838         @Override
839         public Set<EcompPDPGroup> getEcompPDPGroups() throws PAPException {
840                 final Set<EcompPDPGroup> grps = new HashSet<EcompPDPGroup>();
841                 for (EcompPDPGroup g : this.groups) {
842                         grps.add(g);
843                 }
844                 return Collections.unmodifiableSet(grps);
845         }
846
847         @Override
848         public EcompPDPGroup getPDPGroup(EcompPDP pdp) throws PAPException {
849                 for (EcompPDPGroup group : this.groups) {
850                         if (group.getPdps().contains(pdp)) {
851                                 return group;
852                         }
853                 }
854                 return null;
855         }
856
857         @Override
858         public void SetDefaultGroup(EcompPDPGroup group) throws PAPException {
859                 boolean changesMade = false;
860                 for (EcompPDPGroup aGroup : groups) {
861                         if (aGroup.getId().equals(group.getId())) {
862                                 if ( ! aGroup.isDefaultGroup()) {
863 //TODO - since the original code checked for type we do also.
864                                         if (aGroup instanceof StdPDPGroup) {
865                                                 ((StdPDPGroup) aGroup).setDefault(true);
866                                                 changesMade = true;
867                                         } else {
868                                                 throw new IllegalArgumentException("Group in groups of unknown type '" + aGroup.getClass().getName() + "'");
869                                         }
870                                 }
871                         } else {
872                                 // not the new default group
873                                 if (aGroup.isDefaultGroup()) {
874 //TODO - since the original code checked for type we do also.
875                                         if (aGroup instanceof StdPDPGroup) {
876                                                 ((StdPDPGroup) aGroup).setDefault(false);
877                                                 changesMade = true;
878                                         } else {
879                                                 throw new IllegalArgumentException("Group in groups of unknown type '" + aGroup.getClass().getName() + "'");
880                                         }
881                                 }
882                         }
883                 }
884                 if (changesMade) {
885                         this.doSave();
886                 }
887                 
888                 return; 
889                 
890         }
891
892         @Override
893         public void newPDP(String id, EcompPDPGroup group, String name, String description, int jmxport)
894                         throws PAPException, NullPointerException {
895                 if (group == null) {
896                         throw new PAPException("You must specify which group the PDP will belong to.");
897                 }
898                 if (this.groups.contains(group) == false) {
899                         throw new PAPException("Unknown group, not in our list.");
900                 }
901                 for (EcompPDP p : group.getEcompPdps()) {
902                         if (p.getId().equals(id)) {
903                                 throw new PAPException("A PDP with this ID exists.");
904                         }
905                 }
906                 if (group instanceof StdPDPGroup) {
907                         StdPDP pdp = new StdPDP(id, name, description, jmxport);
908                         if (((StdPDPGroup) group).addPDP(pdp)) {
909                                 //
910                                 // Save the properties and notify any listeners
911                                 //
912                                 pdpChanged(pdp);
913                                 return;
914                         }
915                 }
916                 return;
917                 
918         }
919
920         @Override
921         public void updateGroup(EcompPDPGroup group) throws PAPException {
922                 if (group == null || group.getId() == null) {
923                         throw new PAPException("Group or id is null");
924                 }
925                 if (group.getName() == null || group.getName().trim().length() == 0) {
926                         throw new PAPException("New name for group cannot be null or blank");
927                 }
928                 StdPDPGroup existingGroup = (StdPDPGroup)getGroup(group.getId());
929                 if (existingGroup == null) {
930                         throw new PAPException("Update found no existing group with id '" + group.getId() + "'");
931                 }
932                 
933                 
934                 // We do dramatically different things when the Name changes
935                 // because the Name is essentially the identity of the group (as the User knows it) so when the Identity changes we have to change the group ID.
936                 if (group.getName().equals(existingGroup.getName())) {
937
938                         // update the disk
939                         try {
940                                 ((StdPDPGroup)group).saveGroupConfiguration();
941                         } catch (IOException e) {
942                                 throw new PAPException("Unable to save new configuration for '" + group.getName() + "': " + e.getMessage());
943                         }
944                         // update the group in the set by simply replacing the old instance with the new one
945                         this.groups.remove(existingGroup);
946                         this.groups.add((StdPDPGroup)group);
947                         
948                 } else {
949                         // the name/identity of the group has changed
950                         // generate the new id
951                         String newId = createNewPDPGroupId(group.getName());
952                         
953                         // make sure no other group uses the new id
954                         for (EcompPDPGroup g : groups) {
955                                 if (g.getId().equals(newId)) {
956                                         throw new PAPException("Replacement name maps to ID '" + newId + "' which is already in use");
957                                 }
958                         }
959                         ((StdPDPGroup)group).setId(newId);
960
961                         // rename the existing directory to the new id
962                         Path oldPath = existingGroup.getDirectory();
963                         Path newPath = Paths.get(oldPath.getParent().toString(), newId);
964                         ((StdPDPGroup)group).setDirectory(newPath);
965                         
966                         try {
967                                 boolean success = oldPath.toFile().renameTo(newPath.toFile());
968                                 if ( ! success) {
969                                         throw new PAPException("Unable to rename directory; reason unknown");
970                                 }
971                         } catch (Exception e) {
972                                 //TODO:EELF Cleanup - Remove logger
973                                 //logger.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Move '" + oldPath + "' to '" + newPath + "': " + e.getMessage(), e);
974                                 PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e, "StdEngine", "Unable to rename directory");
975                                 throw new PAPException("Unable to move directory from '" + oldPath + "' to '" + newPath + "': " + e.getMessage());
976                         }
977                         // update the disk
978                         try {
979                                 ((StdPDPGroup)group).saveGroupConfiguration();
980                         } catch (IOException e) {
981                                 throw new PAPException("Unable to save new configuration for '" + group.getName() + "': " + e.getMessage());
982                         }
983                         
984                         // save the new group into the Set
985                         groups.remove(existingGroup);
986                         groups.add((StdPDPGroup)group);
987                         
988                 }
989                 
990                 // perhaps only the group changed, but if the name/id changed it may look to a listener like more than one group
991                 changed();
992
993                 
994         }
995
996         @Override
997         public void removeGroup(EcompPDPGroup group, EcompPDPGroup newGroup) throws PAPException, NullPointerException {
998                 if (group == null) {
999                         throw new NullPointerException();
1000                 }
1001                 //
1002                 // Does this group exist?
1003                 //
1004                 if (this.groups.contains(group) == false) {
1005                         //TODO:EELF Cleanup - Remove logger
1006                         //logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "This group doesn't exist.");
1007                         PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE + "This group doesn't exist.");
1008                         throw new PAPException("The group '" + group.getId() + "' does not exist");
1009                 }
1010                 //
1011                 // Is it the default group?
1012                 //
1013                 if (group.isDefaultGroup()) {
1014                         throw new PAPException("You cannot delete the default group.");
1015                 }
1016                 Set<EcompPDP> pdps = group.getEcompPdps();
1017                 //
1018                 // Are there PDPs? If so, then we need a target group
1019                 //
1020                 if (pdps.isEmpty() == false && newGroup == null) {
1021                         throw new NullPointerException("Group targeted for deletion has PDPs, you must provide a new group for them.");
1022                 }
1023                 //
1024                 // Move the PDPs
1025                 //
1026                 if (pdps.isEmpty() == false) {
1027                         if (! (newGroup instanceof StdPDPGroup)) {
1028                                 throw new PAPException("Unexpected class for newGroup: " + newGroup.getClass().getCanonicalName());
1029                         }
1030                         // The movePDP function will modify the set of PDPs in the group.
1031                         // To avoid concurrent modification exceptions we need to duplicate the list before calling that function.
1032                         List<EcompPDP> pdpList = new ArrayList<EcompPDP>();
1033                         for (EcompPDP pdp : pdps) {
1034                                 pdpList.add(pdp);
1035                         }
1036                         // now we can use the PDPs from the list without having ConcurrentAccessExceptions
1037                         for (EcompPDP pdp : pdpList) {
1038                                 this.movePDP(pdp, newGroup);
1039                         }
1040                 }
1041                 //
1042                 // remove the directory for the group
1043                 //
1044                 String id = group.getId();
1045                 Path groupPath = Paths.get(this.repository.toString(), id);
1046                 //
1047                 // If it exists already
1048                 //
1049                 if ( ! Files.exists(groupPath)) {
1050                         logger.warn("removeGroup " + id + " directory does not exist" + groupPath.toString());
1051                 } else {
1052                         try {
1053                                 Files.walkFileTree(groupPath, new SimpleFileVisitor<Path>() {
1054
1055                                         @Override
1056                                         public FileVisitResult visitFile(Path file,
1057                                                         BasicFileAttributes attrs) throws IOException {
1058                                                 Files.delete(file);
1059                                                 return super.visitFile(file, attrs);
1060                                         }
1061                                         
1062                                 });
1063                                 //
1064                                 // delete the directory
1065                                 //
1066                                 Files.delete(groupPath);
1067                         } catch (IOException e) {
1068                                 //TODO:EELF Cleanup - Remove logger
1069                                 //logger.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Failed to delete " + groupPath + ": " +e);
1070                                 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "StdEngine", "Failed to delete " + groupPath);
1071                                 throw new PAPException("Failed to delete " + id);
1072                         }
1073                 }
1074                 
1075                 // remove the group from the set of all groups
1076                 groups.remove(group);
1077                 
1078                 //
1079                 // Save changes
1080                 //
1081                 changed();
1082                 this.doSave();
1083                 return;
1084                 
1085         }
1086
1087 }