e708043cee9ffe5068f81cde0e546d2e8f30a9b7
[dmaap/dbcapi.git] / src / main / java / org / onap / dmaap / dbcapi / service / ApiService.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * org.onap.dmaap
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.dmaap.dbcapi.service;
22
23 import static com.att.eelf.configuration.Configuration.MDC_BEGIN_TIMESTAMP;
24 import static com.att.eelf.configuration.Configuration.MDC_ELAPSED_TIME;
25 import static com.att.eelf.configuration.Configuration.MDC_END_TIMESTAMP;
26 import static com.att.eelf.configuration.Configuration.MDC_KEY_REQUEST_ID;
27 import static com.att.eelf.configuration.Configuration.MDC_PARTNER_NAME;
28 import static com.att.eelf.configuration.Configuration.MDC_RESPONSE_CODE;
29 import static com.att.eelf.configuration.Configuration.MDC_RESPONSE_DESC;
30 import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME;
31 import static com.att.eelf.configuration.Configuration.MDC_STATUS_CODE;
32
33 import java.text.SimpleDateFormat;
34 import java.util.Date;
35 import java.util.TimeZone;
36 import java.util.regex.Matcher;
37 import java.util.regex.Pattern;
38
39 import javax.ws.rs.core.Response;
40 import javax.ws.rs.core.Response.Status;
41 import javax.xml.bind.DatatypeConverter;
42
43 import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
44 import org.onap.dmaap.dbcapi.authentication.ApiPolicy;
45 import org.onap.dmaap.dbcapi.authentication.AuthenticationErrorException;
46 import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
47 import org.onap.dmaap.dbcapi.model.ApiError;
48 import org.onap.dmaap.dbcapi.model.Dmaap;
49 import org.onap.dmaap.dbcapi.resources.RequiredFieldException;
50 import org.onap.dmaap.dbcapi.util.DmaapConfig;
51 import org.onap.dmaap.dbcapi.util.RandomString;
52 import org.slf4j.MDC;
53
54 public class ApiService extends BaseLoggingClass {
55         private class StopWatch {
56                 private long clock = 0;
57                 private long elapsed = 0;
58                 
59
60                 
61                 public StopWatch() {
62                         clock = 0;
63                         elapsed = 0;
64                 }
65                 
66                 public void reset() {
67                         clock = System.currentTimeMillis();
68                         elapsed = 0;
69                 }
70                 public void stop() {
71                         Long stopTime = System.currentTimeMillis();
72                         elapsed +=  stopTime - clock;
73                         clock = 0;
74                         MDC.put( MDC_END_TIMESTAMP, isoFormatter.format(new Date(stopTime)));
75                         MDC.put( MDC_ELAPSED_TIME, String.valueOf(elapsed));
76                 }
77                 public void start() {
78                         if ( clock != 0 ) {
79                                 //not stopped
80                                 return;
81                         }
82                         clock = System.currentTimeMillis();     
83                         MDC.put( MDC_BEGIN_TIMESTAMP, isoFormatter.format(new Date(clock)));
84                 }
85                 private long getElapsed() {
86                         return elapsed;
87                 }
88         }
89
90          private String apiNamespace;
91          private boolean usePE;
92          private String uri;
93          private String uriPath;
94          private String method;
95          private String authorization;
96          private String requestId;
97         private ApiError err;
98         private StopWatch stopwatch;
99         private ApiPolicy apiPolicy;
100         
101         public static final String ISO_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
102     public final static TimeZone utc = TimeZone.getTimeZone("UTC");
103     public final static SimpleDateFormat isoFormatter = new SimpleDateFormat(ISO_FORMAT);
104         
105     static {
106         isoFormatter.setTimeZone(utc);
107     }   
108         public ApiService() {
109
110                 stopwatch = new StopWatch();
111                 stopwatch.start();
112                 err = new ApiError();
113                 requestId = (new RandomString(10)).nextString();
114                 
115                 if (apiNamespace == null) {
116                         DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
117                         usePE = "true".equalsIgnoreCase(p.getProperty("UsePE", "false"));
118                         apiNamespace = p.getProperty("ApiNamespace", "org.openecomp.dmaapBC.api");
119                 }
120                 apiPolicy = new ApiPolicy();
121                 logger.info( "usePE=" + usePE + " apiNamespace=" + apiNamespace);       
122         }
123
124         public ApiService setAuth( String auth ) {
125                 this.authorization = auth;
126                 logger.info( "setAuth:  authorization={} ",  authorization);
127                 return this;
128         }
129         private void setServiceName(){
130                 String svcRequest = new String( this.method + " " + this.uriPath );
131         MDC.put(MDC_SERVICE_NAME, svcRequest );
132         }
133         public ApiService setHttpMethod( String httpMethod ) {
134                 this.method = httpMethod;
135                 logger.info( "setHttpMethod: method={} ", method);
136                 setServiceName();
137                 return this;
138         }
139         public ApiService setUriPath( String uriPath ) {
140                 this.uriPath = uriPath;
141                 this.uri = setUriFromPath( uriPath );
142                 logger.info( "setUriPath: uriPath={} uri={}", uriPath, uri);
143                 setServiceName();
144                 return this;
145         }
146         private String setUriFromPath( String uriPath ) {
147                 int ch = uriPath.indexOf("/");
148                 if ( ch > 0 ) {
149                         return( (String) uriPath.subSequence(0, ch ) );
150                 } else {
151                         return uriPath;
152                 }
153         }       
154         
155         public ApiError getErr() {
156                 return err;
157         }
158
159
160         public void setErr(ApiError err) {
161                 this.err = err;
162         }
163
164
165         // test for presence of a required field
166         public void required( String name, Object val, String expr ) throws RequiredFieldException {
167                 if ( val == null  ) {
168                         err.setCode(Status.BAD_REQUEST.getStatusCode());
169                         err.setMessage("missing required field");
170                         err.setFields( name );  
171                         throw new RequiredFieldException();
172                 }
173                 if ( expr != null && ! expr.isEmpty() ) {
174                         Pattern pattern = Pattern.compile(expr);
175                         Matcher matcher = pattern.matcher((CharSequence) val);
176                         if ( ! matcher.find() ) {
177                                 err.setCode(Status.BAD_REQUEST.getStatusCode());
178                                 err.setMessage( "value '" + val + "' violates regexp check '" + expr + "'");
179                                 err.setFields( name );
180                                 throw new RequiredFieldException();
181                         }
182                 }
183         }
184         
185         // utility to serialize ApiErr object
186         public String toString() {
187                 return String.format( "code=%d msg=%s fields=%s", err.getCode(), err.getMessage(), err.getFields() );
188         }
189
190
191         public void setCode(int statusCode) {
192                 err.setCode(statusCode);        
193         }
194
195
196         public void setMessage(String string) {
197                 err.setMessage(string);
198         }
199
200
201         public void setFields(String string) {
202                 err.setFields(string);
203         }
204
205         private Response  buildResponse( Object obj ) {
206                 stopwatch.stop();
207                 MDC.put( MDC_RESPONSE_CODE, String.valueOf(err.getCode()) );
208                 
209                 auditLogger.auditEvent( "" );
210                 return Response.status( err.getCode())
211                                 .entity(obj)
212                                 .build();
213         }
214         private Response  buildSuccessResponse(Object d) {
215                 MDC.put( MDC_STATUS_CODE,  "COMPLETE");
216                 MDC.put( MDC_RESPONSE_DESC, "");
217                 return buildResponse( d );
218         }
219         private Response  buildErrResponse() {
220         
221                 MDC.put( MDC_STATUS_CODE,  "ERROR");
222                 MDC.put( MDC_RESPONSE_DESC, err.getMessage());
223                 
224                 return buildResponse(getErr());
225         }
226         public Response success( Object d ) {
227                 err.setCode(Status.OK.getStatusCode());
228                 return buildSuccessResponse(d);
229                                 
230         }
231         public Response success( int code, Object d ) {
232                 err.setCode(code);
233                 return buildSuccessResponse(d);
234         }
235
236         public Response unauthorized( String msg ) {
237                 err.setCode(Status.UNAUTHORIZED.getStatusCode());
238                 err.setFields( "Authorization");
239                 err.setMessage( msg );
240                 return buildErrResponse();
241         }
242         public Response unauthorized() {
243                 err.setCode(Status.UNAUTHORIZED.getStatusCode());
244                 err.setFields( "Authorization");
245                 err.setMessage( "User credentials in HTTP Header field Authorization are not authorized for the requested action");
246                 return buildErrResponse();
247         }
248         public Response unavailable() {
249                 err.setCode(Status.SERVICE_UNAVAILABLE.getStatusCode());
250                 err.setMessage( "Request is unavailable due to unexpected condition");
251                 return buildErrResponse();
252         }
253         public Response notFound() {
254                 err.setCode(Status.NOT_FOUND.getStatusCode());
255                 err.setMessage( "Requested object not found");
256                 return buildErrResponse();
257         }
258         public Response error() {
259                 return buildErrResponse();
260         }
261         
262         public void checkAuthorization( String auth, String uriPath, String httpMethod ) throws AuthenticationErrorException, Exception {
263                 authorization = auth;
264                 setUriFromPath( uriPath );
265                 method = httpMethod;
266                 
267                 checkAuthorization();
268         }
269
270         
271         public void checkAuthorization() throws AuthenticationErrorException, Exception {
272
273                 MDC.put(MDC_KEY_REQUEST_ID, requestId); 
274         
275                 logger.info("request: uri={} method={} auth={}", uri, method, authorization );
276
277                 if ( uri == null || uri.isEmpty()) {
278                         String errmsg = "No URI value provided ";
279                         err.setMessage(errmsg);
280                         logger.info( errmsg );
281                         throw new AuthenticationErrorException( );                      
282                 }
283                 if ( method == null || method.isEmpty()) {
284                         String errmsg = "No method value provided ";
285                         err.setMessage(errmsg);
286                         logger.info( errmsg );
287                         throw new AuthenticationErrorException( );                      
288                 }
289                 DmaapService dmaapService = new DmaapService();
290                 Dmaap dmaap = dmaapService.getDmaap();
291                 String env =            dmaap.getDmaapName();
292                 
293                 // special case during bootstrap of app when DMaaP environment may not be set.
294                 // this allows us to authorize certain APIs used for initialization during this window.
295                 if ( env == null || env.isEmpty() ) {
296                         env = "boot";
297                 }
298                 if ( ! usePE ) return;  // skip authorization if not enabled
299                 if ( authorization == null || authorization.isEmpty()) {
300                         String errmsg = "No basic authorization value provided ";
301                         err.setMessage(errmsg);
302                         logger.info( errmsg );
303                         throw new AuthenticationErrorException( );
304                 }
305                 String credentials = authorization.substring("Basic".length()).trim();
306         byte[] decoded = DatatypeConverter.parseBase64Binary(credentials);
307         String decodedString = new String(decoded);
308         String[] actualCredentials = decodedString.split(":");
309         String ID = actualCredentials[0];
310         String Password = actualCredentials[1];
311         MDC.put(MDC_PARTNER_NAME, ID);
312                 try {
313                         
314                         DmaapPerm p = new DmaapPerm( apiNamespace + "." + uri, env, method );
315                         apiPolicy.check( ID, Password, p);
316                 } catch ( AuthenticationErrorException ae ) {
317                         String errmsg =  "User " + ID + " failed authentication/authorization for " + apiNamespace + "." + uriPath + " " + env + " " + method;
318                         logger.info( errmsg );
319                         err.setMessage(errmsg);
320                         throw ae;
321
322                 } 
323                 
324
325         }
326         public String getRequestId() {
327                 return requestId;
328         }
329         public ApiService setRequestId(String requestId) {
330                 if ( requestId == null || requestId.isEmpty()) {        
331                         this.requestId = (new RandomString(10)).nextString();
332                         logger.warn( "X-ECOMP-RequestID not set in HTTP Header.  Setting RequestId value to: " + this.requestId );
333                 } else {
334                         this.requestId = requestId;
335                 }
336                 MDC.put(MDC_KEY_REQUEST_ID, this.requestId); 
337                 return this;
338         }
339 }