Provide support for properties encryption
[ccsdk/sli/adaptors.git] / aai-service / provider / src / main / java / org / onap / ccsdk / sli / adaptors / aai / AAIServiceProvider.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * openECOMP : SDN-C
4  * ================================================================================
5  * Copyright (C) 2017 - 2018 AT&T Intellectual Property. All rights
6  *                         reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.ccsdk.sli.adaptors.aai;
23
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.IOException;
27 import java.lang.reflect.Method;
28 import java.util.Optional;
29 import java.util.Properties;
30 import java.util.Vector;
31
32 import org.onap.ccsdk.sli.core.utils.JREFileResolver;
33 import org.onap.ccsdk.sli.core.utils.KarafRootFileResolver;
34 import org.onap.ccsdk.sli.core.utils.PropertiesFileResolver;
35 import org.onap.ccsdk.sli.core.utils.common.BundleContextFileResolver;
36 import org.onap.ccsdk.sli.core.utils.common.CoreDefaultFileResolver;
37 import org.onap.ccsdk.sli.core.utils.common.SdncConfigEnvVarFileResolver;
38 import org.osgi.framework.BundleContext;
39 import org.osgi.framework.FrameworkUtil;
40 import org.osgi.framework.ServiceReference;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * Responsible for determining the properties file to use and instantiating the <code>DBResourceManager</code>
46  * Service.  The priority for properties file resolution is as follows:
47  *
48  * <ol>
49  *     <li>A directory identified by the system environment variable <code>SDNC_CONFIG_DIR</code></li>
50  *     <li>The default directory <code>DEFAULT_DBLIB_PROP_DIR</code></li>
51  *     <li>A directory identified by the JRE argument <code>dblib.properties</code></li>
52  *     <li>A <code>dblib.properties</code> file located in the karaf root directory</li>
53  * </ol>
54  *
55  * Encryption Support
56  * <ol>
57  *    <li>Uses ecryption provided by <code>AAAEncryptionService</code></li>
58  *    <li>AAA Configuration file is <code>aaa-cert-config.xml</code></li>
59  * </ol>
60  *
61  */
62 public class AAIServiceProvider implements UtilsProvider {
63
64     private static final Logger LOG = LoggerFactory.getLogger(AAIServiceProvider.class);
65
66     /**
67      * The name of the properties file for database configuration
68      */
69     private static final String AAISEERVICE_PROP_FILE_NAME = "aaiclient.properties";
70
71     /**
72      * The name of the pwd key
73      */
74     private static final String AAICLIENT_PROPERTY_NAME = "org.onap.ccsdk.sli.adaptors.aai.client.psswd";
75
76     /**
77      * A prioritized list of strategies for resolving dblib properties files.
78      */
79     private Vector<PropertiesFileResolver> dblibPropertiesFileResolvers = new Vector<>();
80
81     /**
82      * The configuration properties for the db connection.
83      */
84     private Properties properties;
85
86     /**
87      * Set up the prioritized list of strategies for resolving dblib properties files.
88      */
89     public AAIServiceProvider() {
90         dblibPropertiesFileResolvers.add(new SdncConfigEnvVarFileResolver(
91                 "Using property file (1) from environment variable"
92             ));
93         dblibPropertiesFileResolvers.add(new JREFileResolver(
94             "Using property file (2) from JRE argument", AAIServiceProvider.class
95         ));
96         dblibPropertiesFileResolvers.add(new BundleContextFileResolver(
97             "Using property file (3) from JRE argument", AAIServiceProvider.class
98         ));
99         dblibPropertiesFileResolvers.add(new KarafRootFileResolver(
100             "Using property file (4) from karaf root", this
101         ));
102         dblibPropertiesFileResolvers.add(new CoreDefaultFileResolver(
103             "Using property file (5) from default directory"
104         ));
105
106         // determines properties file as according to the priority described in the class header comment
107         final File propertiesFile = determinePropertiesFile();
108         if (propertiesFile != null) {
109             try(FileInputStream fileInputStream = new FileInputStream(propertiesFile)) {
110                 properties = new Properties();
111                 properties.load(fileInputStream);
112
113                 if(properties.containsKey(AAICLIENT_PROPERTY_NAME)) {
114                     String sensitive = properties.getProperty(AAICLIENT_PROPERTY_NAME);
115                     if(sensitive != null && sensitive.startsWith("ENC:")) {
116                         try {
117                             sensitive = sensitive.substring(4);
118                             String postsense = decrypt(sensitive);
119                             properties.setProperty(AAICLIENT_PROPERTY_NAME, postsense);
120                         } catch(Exception exc) {
121                             LOG.error("Failed to translate property", exc);
122                         }
123                     }
124                 }
125             } catch (final IOException e) {
126                 LOG.error("Failed to load properties for file: {}", propertiesFile.toString(),
127                         new AAIServiceException("Failed to load properties for file: "
128                                 + propertiesFile.toString(), e));
129             }
130         }
131     }
132
133     /**
134      *
135      * @param value
136      * @return decrypted string if successful or the original value if unsuccessful
137      */
138     private String decrypt(String value) {
139         try {
140             BundleContext bctx = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
141
142             ServiceReference sref = bctx.getServiceReference("org.opendaylight.aaa.encrypt.AAAEncryptionService");
143             if(sref == null) {
144                 LOG.warn("Could not acquire service reference for 'org.opendaylight.aaa.encrypt.AAAEncryptionService'");
145                 return value;
146             }
147             Object encrSvc = bctx.getService(sref);
148             if(encrSvc == null) {
149                 LOG.warn("Could not access service for 'org.opendaylight.aaa.encrypt.AAAEncryptionService'");
150                 return value;
151             }
152
153             Method gs2Method = encrSvc.getClass().getMethod("decrypt", new Class[] { "".getClass() });
154             Object unmasked = gs2Method.invoke(encrSvc, new Object[] { value });
155             return unmasked.toString();
156
157         } catch (Exception exc) {
158             LOG.error("Failure", exc);
159             return value;
160         }
161     }
162
163     /**
164      * Extract db config properties.
165      *
166      * @return the db config properties
167      */
168     public Properties getProperties() {
169         return properties;
170     }
171
172     /**
173      * Reports the method chosen for properties resolution to the <code>Logger</code>.
174      *
175      * @param message Some user friendly message
176      * @param fileOptional The file location of the chosen properties file
177      * @return the file location of the chosen properties file
178      */
179     private static File reportSuccess(final String message, final Optional<File> fileOptional) {
180         if(fileOptional.isPresent()) {
181             final File file = fileOptional.get();
182             LOG.info("{} {}", message, file.getPath());
183             return file;
184         }
185         return null;
186     }
187
188     /**
189      * Reports fatal errors.  This is the case in which no properties file could be found.
190      *
191      * @param message An appropriate fatal error message
192      * @param dblibConfigurationException An exception describing what went wrong during resolution
193      */
194     private static void reportFailure(final String message,
195                                       final AAIServiceException dblibConfigurationException) {
196
197         LOG.error("{}", message, dblibConfigurationException);
198     }
199
200     /**
201      * Determines the dblib properties file to use based on the following priority:
202      * <ol>
203      *     <li>A directory identified by the system environment variable <code>SDNC_CONFIG_DIR</code></li>
204      *     <li>The default directory <code>DEFAULT_DBLIB_PROP_DIR</code></li>
205      *     <li>A directory identified by the JRE argument <code>dblib.properties</code></li>
206      *     <li>A <code>dblib.properties</code> file located in the karaf root directory</li>
207      * </ol>
208      */
209     File determinePropertiesFile() {
210
211         for (final PropertiesFileResolver dblibPropertiesFileResolver : dblibPropertiesFileResolvers) {
212             final Optional<File> fileOptional = dblibPropertiesFileResolver.resolveFile(AAISEERVICE_PROP_FILE_NAME);
213             if (fileOptional.isPresent()) {
214                 return reportSuccess(dblibPropertiesFileResolver.getSuccessfulResolutionMessage(), fileOptional);
215             }
216         }
217
218         reportFailure("Missing configuration properties resource(3)",
219                 new AAIServiceException("Missing configuration properties resource(3): "
220                         + AAISEERVICE_PROP_FILE_NAME));
221         return null;
222     }
223 }