2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.dmaap.dbcapi.service;
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;
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;
39 import javax.ws.rs.core.Response;
40 import javax.ws.rs.core.Response.Status;
41 import javax.xml.bind.DatatypeConverter;
43 import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
44 import org.onap.dmaap.dbcapi.aaf.authentication.ApiPolicy;
45 import org.onap.dmaap.dbcapi.aaf.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;
54 public class ApiService extends BaseLoggingClass {
55 private class StopWatch {
56 private long clock = 0;
57 private long elapsed = 0;
67 clock = System.currentTimeMillis();
71 Long stopTime = System.currentTimeMillis();
72 elapsed += stopTime - clock;
74 MDC.put( MDC_END_TIMESTAMP, isoFormatter.format(new Date(stopTime)));
75 MDC.put( MDC_ELAPSED_TIME, String.valueOf(elapsed));
82 clock = System.currentTimeMillis();
83 MDC.put( MDC_BEGIN_TIMESTAMP, isoFormatter.format(new Date(clock)));
85 private long getElapsed() {
90 private String apiNamespace;
91 private boolean usePE;
93 private String uriPath;
94 private String method;
95 private String authorization;
96 private String requestId;
98 private StopWatch stopwatch;
99 private ApiPolicy apiPolicy;
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);
106 isoFormatter.setTimeZone(utc);
108 public ApiService() {
110 stopwatch = new StopWatch();
112 err = new ApiError();
113 requestId = (new RandomString(10)).nextString();
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");
120 apiPolicy = new ApiPolicy();
121 logger.info( "usePE=" + usePE + " apiNamespace=" + apiNamespace);
124 public ApiService setAuth( String auth ) {
125 this.authorization = auth;
126 logger.info( "setAuth: authorization={} ", authorization);
129 private void setServiceName(){
130 String svcRequest = new String( this.method + " " + this.uriPath );
131 MDC.put(MDC_SERVICE_NAME, svcRequest );
133 public ApiService setHttpMethod( String httpMethod ) {
134 this.method = httpMethod;
135 logger.info( "setHttpMethod: method={} ", method);
139 public ApiService setUriPath( String uriPath ) {
140 this.uriPath = uriPath;
141 this.uri = setUriFromPath( uriPath );
142 logger.info( "setUriPath: uriPath={} uri={}", uriPath, uri);
146 private String setUriFromPath( String uriPath ) {
147 int ch = uriPath.indexOf("/");
149 return( (String) uriPath.subSequence(0, ch ) );
155 public ApiError getErr() {
160 public void setErr(ApiError err) {
165 // test for presence of a required field
166 public void required( String name, Object val, String expr ) throws RequiredFieldException {
168 err.setCode(Status.BAD_REQUEST.getStatusCode());
169 err.setMessage("missing required field");
170 err.setFields( name );
171 throw new RequiredFieldException();
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();
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() );
191 public void setCode(int statusCode) {
192 err.setCode(statusCode);
196 public void setMessage(String string) {
197 err.setMessage(string);
201 public void setFields(String string) {
202 err.setFields(string);
205 private Response buildResponse( Object obj ) {
207 MDC.put( MDC_RESPONSE_CODE, String.valueOf(err.getCode()) );
209 auditLogger.auditEvent( "" );
210 return Response.status( err.getCode())
214 private Response buildSuccessResponse(Object d) {
215 MDC.put( MDC_STATUS_CODE, "COMPLETE");
216 MDC.put( MDC_RESPONSE_DESC, "");
217 return buildResponse( d );
219 private Response buildErrResponse() {
221 MDC.put( MDC_STATUS_CODE, "ERROR");
222 MDC.put( MDC_RESPONSE_DESC, err.getMessage());
224 return buildResponse(getErr());
226 public Response success( Object d ) {
227 err.setCode(Status.OK.getStatusCode());
228 return buildSuccessResponse(d);
231 public Response success( int code, Object d ) {
233 return buildSuccessResponse(d);
236 public Response unauthorized( String msg ) {
237 err.setCode(Status.UNAUTHORIZED.getStatusCode());
238 err.setFields( "Authorization");
239 err.setMessage( msg );
240 return buildErrResponse();
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();
248 public Response unavailable() {
249 err.setCode(Status.SERVICE_UNAVAILABLE.getStatusCode());
250 err.setMessage( "Request is unavailable due to unexpected condition");
251 return buildErrResponse();
253 public Response notFound() {
254 err.setCode(Status.NOT_FOUND.getStatusCode());
255 err.setMessage( "Requested object not found");
256 return buildErrResponse();
258 public Response error() {
259 return buildErrResponse();
262 public void checkAuthorization( String auth, String uriPath, String httpMethod ) throws AuthenticationErrorException, Exception {
263 authorization = auth;
264 setUriFromPath( uriPath );
267 checkAuthorization();
271 public void checkAuthorization() throws AuthenticationErrorException, Exception {
273 MDC.put(MDC_KEY_REQUEST_ID, requestId);
275 logger.info("request: uri={} method={} auth={}", uri, method, authorization );
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( );
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( );
289 DmaapService dmaapService = new DmaapService();
290 Dmaap dmaap = dmaapService.getDmaap();
291 String env = dmaap.getDmaapName();
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() ) {
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( );
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);
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);
326 public String getRequestId() {
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 );
334 this.requestId = requestId;
336 MDC.put(MDC_KEY_REQUEST_ID, this.requestId);