020694185b87d24930076e339a9b8eb4384ba973
[aai/rest-client.git] / src / main / java / org / openecomp / restclient / rest / RestClientBuilder.java
1 /**\r
2  * ============LICENSE_START=======================================================\r
3  * RestClient\r
4  * ================================================================================\r
5  * Copyright © 2017 AT&T Intellectual Property.\r
6  * Copyright © 2017 Amdocs\r
7  * All rights reserved.\r
8  * ================================================================================\r
9  * Licensed under the Apache License, Version 2.0 (the "License");\r
10  * you may not use this file except in compliance with the License.\r
11  * You may obtain a copy of the License at\r
12  *\r
13  *    http://www.apache.org/licenses/LICENSE-2.0\r
14  *\r
15  * Unless required by applicable law or agreed to in writing, software\r
16  * distributed under the License is distributed on an "AS IS" BASIS,\r
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
18  * See the License for the specific language governing permissions and\r
19  * limitations under the License.\r
20  * ============LICENSE_END=========================================================\r
21  *\r
22  * ECOMP and OpenECOMP are trademarks\r
23  * and service marks of AT&T Intellectual Property.\r
24  */\r
25 package org.openecomp.restclient.rest;\r
26 \r
27 import java.io.FileInputStream;\r
28 import java.security.KeyStore;\r
29 import java.security.cert.X509Certificate;\r
30 \r
31 import javax.net.ssl.HostnameVerifier;\r
32 import javax.net.ssl.KeyManagerFactory;\r
33 import javax.net.ssl.SSLContext;\r
34 import javax.net.ssl.SSLSession;\r
35 import javax.net.ssl.TrustManager;\r
36 import javax.net.ssl.X509TrustManager;\r
37 \r
38 import org.openecomp.restclient.enums.RestAuthenticationMode;\r
39 \r
40 import com.sun.jersey.api.client.Client;\r
41 import com.sun.jersey.api.client.config.ClientConfig;\r
42 import com.sun.jersey.api.client.config.DefaultClientConfig;\r
43 import com.sun.jersey.client.urlconnection.HTTPSProperties;\r
44 \r
45 /**\r
46  * This is a generic REST Client builder with flexible security validation. Sometimes it's nice to\r
47  * be able to disable server chain cert validation and hostname validation to work-around lab\r
48  * issues, but at the same time be able to provide complete validation with client cert + hostname +\r
49  * server cert chain validation. I used the ModelLoader REST client as a base and merged in the TSUI\r
50  * client I wrote which also validates the server hostname and server certificate chain.\r
51  */\r
52 public class RestClientBuilder {\r
53 \r
54   public static final boolean DEFAULT_VALIDATE_SERVER_HOST = false;\r
55   public static final boolean DEFAULT_VALIDATE_CERT_CHAIN = false;\r
56   public static final String DEFAULT_CLIENT_CERT_FILENAME = null;\r
57   public static final String DEFAULT_CERT_PASSWORD = null;\r
58   public static final String DEFAULT_TRUST_STORE_FILENAME = null;\r
59   public static final int DEFAULT_CONNECT_TIMEOUT_MS = 60000;\r
60   public static final int DEFAULT_READ_TIMEOUT_MS = 60000;\r
61   public static final RestAuthenticationMode DEFAULT_AUTH_MODE = RestAuthenticationMode.SSL_CERT;\r
62   public static final String DEFAULT_BASIC_AUTH_USERNAME = "";\r
63   public static final String DEFAULT_BASIC_AUTH_PASSWORD = "";\r
64 \r
65   private static final String SSL_PROTOCOL = "TLS";\r
66   private static final String KEYSTORE_ALGORITHM = "SunX509";\r
67   private static final String KEYSTORE_TYPE = "PKCS12";\r
68   private static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore";\r
69 \r
70   private boolean validateServerHostname;\r
71   private boolean validateServerCertChain;\r
72   private String clientCertFileName;\r
73   private String clientCertPassword;\r
74   private String truststoreFilename;\r
75   private int connectTimeoutInMs;\r
76   private int readTimeoutInMs;\r
77   private RestAuthenticationMode authenticationMode;\r
78   private String basicAuthUsername;\r
79   private String basicAuthPassword;\r
80 \r
81   /**\r
82    * Rest Client Builder.\r
83    */\r
84   public RestClientBuilder() {\r
85     validateServerHostname = DEFAULT_VALIDATE_SERVER_HOST;\r
86     validateServerCertChain = DEFAULT_VALIDATE_CERT_CHAIN;\r
87     clientCertFileName = DEFAULT_CLIENT_CERT_FILENAME;\r
88     clientCertPassword = DEFAULT_CERT_PASSWORD;\r
89     truststoreFilename = DEFAULT_TRUST_STORE_FILENAME;\r
90     connectTimeoutInMs = DEFAULT_CONNECT_TIMEOUT_MS;\r
91     readTimeoutInMs = DEFAULT_READ_TIMEOUT_MS;\r
92     authenticationMode = DEFAULT_AUTH_MODE;\r
93     basicAuthUsername = DEFAULT_BASIC_AUTH_USERNAME;\r
94     basicAuthPassword = DEFAULT_BASIC_AUTH_PASSWORD;\r
95   }\r
96 \r
97   public boolean isValidateServerHostname() {\r
98     return validateServerHostname;\r
99   }\r
100 \r
101   public void setValidateServerHostname(boolean validateServerHostname) {\r
102     this.validateServerHostname = validateServerHostname;\r
103   }\r
104 \r
105   public boolean isValidateServerCertChain() {\r
106     return validateServerCertChain;\r
107   }\r
108 \r
109   public void setValidateServerCertChain(boolean validateServerCertChain) {\r
110     this.validateServerCertChain = validateServerCertChain;\r
111   }\r
112 \r
113   public String getClientCertFileName() {\r
114     return clientCertFileName;\r
115   }\r
116 \r
117   public void setClientCertFileName(String clientCertFileName) {\r
118     this.clientCertFileName = clientCertFileName;\r
119   }\r
120 \r
121   public String getClientCertPassword() {\r
122     return clientCertPassword;\r
123   }\r
124 \r
125   public void setClientCertPassword(String clientCertPassword) {\r
126     this.clientCertPassword = clientCertPassword;\r
127   }\r
128 \r
129   public String getTruststoreFilename() {\r
130     return truststoreFilename;\r
131   }\r
132 \r
133   public void setTruststoreFilename(String truststoreFilename) {\r
134     this.truststoreFilename = truststoreFilename;\r
135   }\r
136 \r
137   public int getConnectTimeoutInMs() {\r
138     return connectTimeoutInMs;\r
139   }\r
140 \r
141   public void setConnectTimeoutInMs(int connectTimeoutInMs) {\r
142     this.connectTimeoutInMs = connectTimeoutInMs;\r
143   }\r
144 \r
145   public int getReadTimeoutInMs() {\r
146     return readTimeoutInMs;\r
147   }\r
148 \r
149   public void setReadTimeoutInMs(int readTimeoutInMs) {\r
150     this.readTimeoutInMs = readTimeoutInMs;\r
151   }\r
152 \r
153 \r
154 \r
155   public RestAuthenticationMode getAuthenticationMode() {\r
156     return authenticationMode;\r
157   }\r
158 \r
159   public void setAuthenticationMode(RestAuthenticationMode authenticationMode) {\r
160     this.authenticationMode = authenticationMode;\r
161   }\r
162 \r
163   public String getBasicAuthUsername() {\r
164     return basicAuthUsername;\r
165   }\r
166 \r
167   public void setBasicAuthUsername(String basicAuthUsername) {\r
168     this.basicAuthUsername = basicAuthUsername;\r
169   }\r
170 \r
171   public String getBasicAuthPassword() {\r
172     return basicAuthPassword;\r
173   }\r
174 \r
175   public void setBasicAuthPassword(String basicAuthPassword) {\r
176     this.basicAuthPassword = basicAuthPassword;\r
177   }\r
178 \r
179   /**\r
180    * Returns Client configured for SSL\r
181    */\r
182   public Client getClient() throws Exception {\r
183 \r
184     switch (authenticationMode) {\r
185       case SSL_BASIC:\r
186       case SSL_CERT:\r
187         return getClient(true);\r
188 \r
189       default:\r
190         // return basic non-authenticating HTTP client\r
191         return getClient(false);\r
192     }\r
193 \r
194   }\r
195 \r
196   protected void setupSecureSocketLayerClientConfig(ClientConfig clientConfig) throws Exception {\r
197     // Check to see if we need to perform proper validation of\r
198     // the certificate chains.\r
199     TrustManager[] trustAllCerts = null;\r
200     if (validateServerCertChain) {\r
201       if (truststoreFilename != null) {\r
202         System.setProperty(TRUST_STORE_PROPERTY, truststoreFilename);\r
203       } else {\r
204         throw new IllegalArgumentException("Trust store filename must be set!");\r
205       }\r
206 \r
207     } else {\r
208 \r
209       // We aren't validating certificates, so create a trust manager that does\r
210       // not validate certificate chains.\r
211       trustAllCerts = new TrustManager[] {new X509TrustManager() {\r
212         public X509Certificate[] getAcceptedIssuers() {\r
213           return null;\r
214         }\r
215 \r
216         public void checkClientTrusted(X509Certificate[] certs, String authType) {}\r
217 \r
218         public void checkServerTrusted(X509Certificate[] certs, String authType) {}\r
219       }};\r
220     }\r
221 \r
222     // Set up the SSL context, keystore, etc. to use for our connection\r
223     // to the AAI.\r
224     SSLContext ctx = SSLContext.getInstance(SSL_PROTOCOL);\r
225     KeyManagerFactory kmf = KeyManagerFactory.getInstance(KEYSTORE_ALGORITHM);\r
226     KeyStore ks = KeyStore.getInstance(KEYSTORE_TYPE);\r
227 \r
228     char[] pwd = null;\r
229     if (clientCertPassword != null) {\r
230       pwd = clientCertPassword.toCharArray();\r
231     }\r
232 \r
233     if (clientCertFileName != null) {\r
234       FileInputStream fin = new FileInputStream(clientCertFileName);\r
235 \r
236       // Load the keystore and initialize the key manager factory.\r
237       ks.load(fin, pwd);\r
238       kmf.init(ks, pwd);\r
239 \r
240       ctx.init(kmf.getKeyManagers(), trustAllCerts, null);\r
241     } else {\r
242       ctx.init(null, trustAllCerts, null);\r
243     }\r
244 \r
245     // Are we performing validation of the server host name?\r
246     if (validateServerHostname) {\r
247       clientConfig.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,\r
248           new HTTPSProperties(null, ctx));\r
249 \r
250     } else {\r
251       clientConfig.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,\r
252           new HTTPSProperties(new HostnameVerifier() {\r
253             @Override\r
254             public boolean verify(String str, SSLSession sslSession) {\r
255               return true;\r
256             }\r
257           }, ctx));\r
258     }\r
259   }\r
260 \r
261 \r
262   /**\r
263    * Returns client instance\r
264    * \r
265    * @param useSsl - used to configure the client with an ssl-context or just plain http\r
266    */\r
267   protected Client getClient(boolean useSsl) throws Exception {\r
268 \r
269     ClientConfig clientConfig = new DefaultClientConfig();\r
270 \r
271     if (useSsl) {\r
272       setupSecureSocketLayerClientConfig(clientConfig);\r
273     }\r
274 \r
275     // Finally, create and initialize our client...\r
276     Client client = null;\r
277     client = Client.create(clientConfig);\r
278     client.setConnectTimeout(connectTimeoutInMs);\r
279     client.setReadTimeout(readTimeoutInMs);\r
280 \r
281     // ...and return it to the caller.\r
282     return client;\r
283   }\r
284 \r
285   public String getBasicAuthenticationCredentials() {\r
286 \r
287     String usernameAndPassword = getBasicAuthUsername() + ":" + getBasicAuthPassword();\r
288     return "Basic " + java.util.Base64.getEncoder().encodeToString(usernameAndPassword.getBytes());\r
289   }\r
290 \r
291   /* \r
292    * Added a little bit of logic to obfuscate passwords that could be logged out\r
293    * (non-Javadoc)\r
294    * @see java.lang.Object#toString()\r
295    */\r
296   @Override\r
297   public String toString() {\r
298     return "RestClientBuilder [validateServerHostname=" + validateServerHostname\r
299         + ", validateServerCertChain=" + validateServerCertChain + ", "\r
300         + (clientCertFileName != null ? "clientCertFileName=" + clientCertFileName + ", " : "")\r
301         + (clientCertPassword != null\r
302             ? "clientCertPassword="\r
303                 + java.util.Base64.getEncoder().encodeToString(clientCertPassword.getBytes()) + ", "\r
304             : "")\r
305         + (truststoreFilename != null ? "truststoreFilename=" + truststoreFilename + ", " : "")\r
306         + "connectTimeoutInMs=" + connectTimeoutInMs + ", readTimeoutInMs=" + readTimeoutInMs + ", "\r
307         + (authenticationMode != null ? "authenticationMode=" + authenticationMode + ", " : "")\r
308         + (basicAuthUsername != null ? "basicAuthUsername=" + basicAuthUsername + ", " : "")\r
309         + (basicAuthPassword != null ? "basicAuthPassword="\r
310             + java.util.Base64.getEncoder().encodeToString(basicAuthPassword.getBytes()) : "")\r
311         + "]";\r
312   }\r
313 \r
314 }\r