2 * ============LICENSE_START=======================================================
3 * org.onap.dcaegen2.collectors.ves
4 * ================================================================================
5 * Copyright (C) 2018 - 2019 Nokia. 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=========================================================
20 package org.onap.dcae.restapi;
22 import io.vavr.control.Option;
23 import java.io.IOException;
24 import java.security.cert.X509Certificate;
25 import java.util.Arrays;
26 import java.util.Base64;
27 import java.util.stream.Collectors;
28 import javax.servlet.http.HttpServletRequest;
29 import javax.servlet.http.HttpServletResponse;
30 import org.onap.dcae.ApplicationSettings;
31 import org.onap.dcae.common.configuration.AuthMethodType;
32 import org.onap.dcae.common.configuration.SubjectMatcher;
33 import org.onap.dcaegen2.services.sdk.security.CryptPassword;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.springframework.stereotype.Component;
37 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
40 public class ApiAuthInterceptor extends HandlerInterceptorAdapter {
42 private static final Logger LOG = LoggerFactory.getLogger(ApiAuthInterceptor.class);
43 private static final String CERTIFICATE_X_509 = "javax.servlet.request.X509Certificate";
44 private static final String MESSAGE = "SubjectDN didn't match with any regexp from %s";
45 private final CryptPassword cryptPassword = new CryptPassword();
46 private final ApplicationSettings settings;
47 private Logger errorLogger;
49 public ApiAuthInterceptor(ApplicationSettings applicationSettings, Logger errorLogger) {
50 this.settings = applicationSettings;
51 this.errorLogger = errorLogger;
55 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
58 X509Certificate[] certificates = (X509Certificate[]) request.getAttribute(CERTIFICATE_X_509);
59 SubjectMatcher subjectMatcher = new SubjectMatcher(settings, certificates);
61 if(isHttpPortCalledWithAuthTurnedOn(request)){
62 if(isHealthcheckCalledFromInsideCluster(request)){
65 response.getWriter().write("Operation not permitted");
66 response.setStatus(400);
70 if(isCertSubject(subjectMatcher)){
71 LOG.debug("Cert and subjectDN is valid. Subject: " + extractSubject(certificates));
76 return validateBasicHeader(request, response);
81 private String extractSubject(X509Certificate[] certs) {
82 return Arrays.stream(certs)
83 .map(e -> e.getSubjectDN().getName())
84 .collect(Collectors.joining(","));
87 private boolean isHttpPortCalledWithAuthTurnedOn(HttpServletRequest request) {
88 return !settings.authMethod().equalsIgnoreCase(AuthMethodType.NO_AUTH.value())
89 && request.getLocalPort() == settings.httpPort();
92 private boolean isHealthcheckCalledFromInsideCluster(HttpServletRequest request) {
93 return request.getRequestURI().replaceAll("^/|/$", "").equalsIgnoreCase("healthcheck")
94 && request.getServerPort() == settings.httpPort();
97 private boolean validateBasicHeader(HttpServletRequest request, HttpServletResponse response)
99 String authorizationHeader = request.getHeader("Authorization");
100 if (authorizationHeader == null || !isAuthorized(authorizationHeader)) {
101 response.setStatus(401);
102 errorLogger.error("EVENT_RECEIPT_FAILURE: Unauthorized user");
103 response.getWriter().write(ApiException.UNAUTHORIZED_USER.toJSON().toString());
106 LOG.debug("Request is authorized by basic auth. User: " + extractUser(decodeCredentials(authorizationHeader)));
110 private boolean isCertSubject(SubjectMatcher subjectMatcher) {
111 if(subjectMatcher.isCert() && subjectMatcher.match()){
114 LOG.info(String.format(MESSAGE, settings.certSubjectMatcher()));
118 private boolean isBasicAuth() {
119 return settings.authMethod().equalsIgnoreCase(AuthMethodType.CERT_BASIC_AUTH.value());
122 private boolean isAuthorized(String authorizationHeader) {
124 String decodeCredentials = decodeCredentials(authorizationHeader);
125 String providedUser = extractUser(decodeCredentials);
126 String providedPassword = extractPassword(decodeCredentials);
127 Option<String> maybeSavedPassword = settings.validAuthorizationCredentials().get(providedUser);
128 boolean userRegistered = maybeSavedPassword.isDefined();
129 return userRegistered && cryptPassword.matches(providedPassword,maybeSavedPassword.get());
130 } catch (Exception e) {
131 LOG.warn(String.format("Could not check if user is authorized (header: '%s')), probably malformed header.",
132 authorizationHeader), e);
137 private String extractPassword(String decodeCredentials) {
138 return decodeCredentials.split(":")[1].trim();
141 private String extractUser(String decodeCredentials) {
142 return decodeCredentials.split(":")[0].trim();
145 private String decodeCredentials(String authorizationHeader) {
146 String encodedData = authorizationHeader.split(" ")[1];
147 return new String(Base64.getDecoder().decode(encodedData));