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