Remove certOnly and basicAuth from authentication methods
[dcaegen2/collectors/ves.git] / src / main / java / org / onap / dcae / restapi / ApiAuthInterceptor.java
1 /*-
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
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 package org.onap.dcae.restapi;
21
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;
38
39 @Component
40 public class ApiAuthInterceptor extends HandlerInterceptorAdapter {
41
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;
48
49     public ApiAuthInterceptor(ApplicationSettings applicationSettings, Logger errorLogger) {
50         this.settings = applicationSettings;
51         this.errorLogger = errorLogger;
52     }
53
54     @Override
55     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
56         throws IOException {
57
58         X509Certificate[] certificates = (X509Certificate[]) request.getAttribute(CERTIFICATE_X_509);
59         SubjectMatcher subjectMatcher = new SubjectMatcher(settings, certificates);
60
61         if(isHttpPortCalledWithAuthTurnedOn(request)){
62             if(isHealthcheckCalledFromInsideCluster(request)){
63                 return true;
64             }
65             response.getWriter().write("Operation not permitted");
66             response.setStatus(400);
67             return false;
68         }
69
70         if(isCertSubject(subjectMatcher)){
71             LOG.debug("Cert and subjectDN is valid. Subject: " + extractSubject(certificates));
72             return true;
73         }
74
75         if (isBasicAuth()) {
76             return validateBasicHeader(request, response);
77         }
78         return true;
79     }
80
81     private String extractSubject(X509Certificate[] certs) {
82         return Arrays.stream(certs)
83             .map(e -> e.getSubjectDN().getName())
84             .collect(Collectors.joining(","));
85     }
86
87     private boolean isHttpPortCalledWithAuthTurnedOn(HttpServletRequest request) {
88         return !settings.authMethod().equalsIgnoreCase(AuthMethodType.NO_AUTH.value())
89                 && request.getLocalPort() == settings.httpPort();
90     }
91
92     private boolean isHealthcheckCalledFromInsideCluster(HttpServletRequest request) {
93         return request.getRequestURI().replaceAll("^/|/$", "").equalsIgnoreCase("healthcheck")
94                 && request.getServerPort() == settings.httpPort();
95     }
96
97     private boolean validateBasicHeader(HttpServletRequest request, HttpServletResponse response)
98         throws IOException {
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());
104             return false;
105         }
106         LOG.debug("Request is authorized by basic auth. User: " + extractUser(decodeCredentials(authorizationHeader)));
107         return true;
108     }
109
110     private boolean isCertSubject(SubjectMatcher subjectMatcher) {
111         if(subjectMatcher.isCert() && subjectMatcher.match()){
112             return true;
113         }
114         LOG.info(String.format(MESSAGE, settings.certSubjectMatcher()));
115         return false;
116     }
117
118     private boolean isBasicAuth() {
119         return settings.authMethod().equalsIgnoreCase(AuthMethodType.CERT_BASIC_AUTH.value());
120     }
121
122     private boolean isAuthorized(String authorizationHeader) {
123         try  {
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);
133             return false;
134         }
135     }
136
137     private String extractPassword(String decodeCredentials) {
138         return decodeCredentials.split(":")[1].trim();
139     }
140
141     private String extractUser(String decodeCredentials) {
142         return decodeCredentials.split(":")[0].trim();
143     }
144
145     private String decodeCredentials(String authorizationHeader) {
146         String encodedData = authorizationHeader.split(" ")[1];
147         return new String(Base64.getDecoder().decode(encodedData));
148     }
149 }