cf24874f38c67ba738eb5c78636f7b6b8c020303
[sdc.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2019 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 package org.openecomp.sdc.securityutil.filters;
21
22 import java.io.IOException;
23 import java.util.Arrays;
24 import java.util.List;
25 import java.util.UUID;
26 import java.util.concurrent.TimeUnit;
27 import java.util.stream.Collectors;
28 import javax.servlet.Filter;
29 import javax.servlet.FilterChain;
30 import javax.servlet.FilterConfig;
31 import javax.servlet.ServletException;
32 import javax.servlet.ServletRequest;
33 import javax.servlet.ServletResponse;
34 import javax.servlet.http.Cookie;
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37 import org.apache.commons.lang.StringUtils;
38 import org.onap.logging.ref.slf4j.ONAPLogConstants;
39 import org.openecomp.sdc.securityutil.AuthenticationCookieUtils;
40 import org.openecomp.sdc.securityutil.CipherUtilException;
41 import org.openecomp.sdc.securityutil.ISessionValidationFilterConfiguration;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44 import org.slf4j.MDC;
45
46 public abstract class SessionValidationFilter implements Filter {
47
48     private static final Logger log = LoggerFactory.getLogger(SessionValidationFilter.class.getName());
49     private static final String REQUEST_ID = ONAPLogConstants.MDCs.REQUEST_ID;
50     private static final String ONAP_REQUEST_ID_HEADER = ONAPLogConstants.Headers.REQUEST_ID;
51     private static final String REQUEST_ID_HEADER = "X-RequestID";
52     private static final String TRANSACTION_ID_HEADER = "X-TransactionId";
53     private static final String ECOMP_REQUEST_ID_HEADER = "X-ECOMP-RequestID";
54     private static final String PARTNER_NAME = ONAPLogConstants.MDCs.PARTNER_NAME;
55     private static final String USER_ID_HEADER = "USER_ID";
56     private static final String ONAP_PARTNER_NAME_HEADER = ONAPLogConstants.Headers.PARTNER_NAME;
57     private static final String USER_AGENT_HEADER = "User-Agent";
58     private static final String UNKNOWN = "UNKNOWN";
59     private ISessionValidationFilterConfiguration filterConfiguration;
60     private List<String> excludedUrls;
61
62     public static void fillMDCFromHeaders(HttpServletRequest httpServletRequest) {
63         fillRequestIdFromHeader(httpServletRequest);
64         fillPartnerNameFromHeader(httpServletRequest);
65     }
66
67     private static void fillRequestIdFromHeader(HttpServletRequest httpServletRequest) {
68         if (MDC.get(REQUEST_ID) == null) {
69             if (StringUtils.isNotEmpty(httpServletRequest.getHeader(ONAP_REQUEST_ID_HEADER))) {
70                 MDC.put(REQUEST_ID, httpServletRequest.getHeader(ONAP_REQUEST_ID_HEADER));
71             } else if (StringUtils.isNotEmpty(httpServletRequest.getHeader(REQUEST_ID_HEADER))) {
72                 MDC.put(REQUEST_ID, httpServletRequest.getHeader(REQUEST_ID_HEADER));
73             } else if (StringUtils.isNotEmpty(httpServletRequest.getHeader(TRANSACTION_ID_HEADER))) {
74                 MDC.put(REQUEST_ID, httpServletRequest.getHeader(TRANSACTION_ID_HEADER));
75             } else if (StringUtils.isNotEmpty(httpServletRequest.getHeader(ECOMP_REQUEST_ID_HEADER))) {
76                 MDC.put(REQUEST_ID, httpServletRequest.getHeader(ECOMP_REQUEST_ID_HEADER));
77             } else {
78                 MDC.put(REQUEST_ID, UUID.randomUUID().toString());
79             }
80         }
81     }
82
83     private static void fillPartnerNameFromHeader(HttpServletRequest httpServletRequest) {
84         if (MDC.get(PARTNER_NAME) == null) {
85             if (StringUtils.isNotEmpty(httpServletRequest.getHeader(USER_ID_HEADER))) {
86                 MDC.put(PARTNER_NAME, httpServletRequest.getHeader(USER_ID_HEADER));
87             } else if (StringUtils.isNotEmpty(httpServletRequest.getHeader(ONAP_PARTNER_NAME_HEADER))) {
88                 MDC.put(PARTNER_NAME, httpServletRequest.getHeader(ONAP_PARTNER_NAME_HEADER));
89             } else if (StringUtils.isNotEmpty(httpServletRequest.getHeader(USER_AGENT_HEADER))) {
90                 MDC.put(PARTNER_NAME, httpServletRequest.getHeader(USER_AGENT_HEADER));
91             } else {
92                 MDC.put(PARTNER_NAME, UNKNOWN);
93             }
94         }
95     }
96
97     public abstract ISessionValidationFilterConfiguration getFilterConfiguration();
98
99     protected abstract Cookie addRoleToCookie(Cookie updatedCookie);
100
101     protected abstract boolean isRoleValid(Cookie cookie);
102
103     @Override
104     public final void init(FilterConfig filterConfig) throws ServletException {
105         filterConfiguration = getFilterConfiguration();
106         excludedUrls = filterConfiguration.getExcludedUrls();
107     }
108
109     @Override
110     public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
111         throws IOException, ServletException {
112         final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
113         final HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
114         long startTime = System.nanoTime();
115         fillMDCFromHeaders(httpRequest);
116         log.debug("SessionValidationFilter: Validation started, received request with URL {}", httpRequest.getRequestURL());
117         // request preprocessing
118         boolean isContinueProcessing = preProcessingRequest(servletRequest, servletResponse, filterChain, httpRequest, httpResponse, startTime);
119         List<Cookie> cookies = null;
120         Cookie extractedCookie = null;
121         // request processing
122         if (isContinueProcessing) {
123             cookies = extractAuthenticationCookies(httpRequest.getCookies());
124             extractedCookie = cookies.get(0);
125             isContinueProcessing = processRequest(httpRequest, httpResponse, extractedCookie);
126         }
127         // response processing
128         if (isContinueProcessing) {
129             log.debug("SessionValidationFilter: Cookie from request {} is valid, passing request to session extension ...",
130                 httpRequest.getRequestURL());
131             Cookie updatedCookie = processResponse(extractedCookie);
132             cleanResponceFromLeftoverCookies(httpResponse, cookies);
133             log.debug("SessionValidationFilter: request {} passed all validations, passing request to endpoint ...", httpRequest.getRequestURL());
134             httpResponse.addCookie(updatedCookie);
135             long durationSec = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
136             long durationMil = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
137             log.debug("SessionValidationFilter: Validation ended, running time for URL {} is: {} seconds {} miliseconds", httpRequest.getPathInfo(),
138                 durationSec, durationMil);
139             filterChain.doFilter(servletRequest, httpResponse);
140         }
141     }
142
143     private boolean preProcessingRequest(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain,
144                                          HttpServletRequest httpRequest, HttpServletResponse httpResponse, long startTime)
145         throws IOException, ServletException {
146         boolean isPreProcessingSucceeded = true;
147         if (isUrlFromWhiteList(httpRequest)) {
148             log.debug("SessionValidationFilter: URL {} excluded from access validation , passing request to endpoint ... ",
149                 httpRequest.getRequestURL());
150             long durationSec = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
151             long durationMil = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
152             log.debug("SessionValidationFilter: Validation ended, running time for URL {} is: {} seconds {} miliseconds", httpRequest.getPathInfo(),
153                 durationSec, durationMil);
154             filterChain.doFilter(servletRequest, servletResponse);
155             isPreProcessingSucceeded = false;
156         } else if (!isCookiePresent(httpRequest.getCookies())) {
157             //redirect to portal app
158             log.debug("SessionValidationFilter: Cookie from request {} is not valid, redirecting request to portal", httpRequest.getRequestURL());
159             httpResponse.sendRedirect(filterConfiguration.getRedirectURL());
160             isPreProcessingSucceeded = false;
161         }
162         return isPreProcessingSucceeded;
163     }
164
165     private boolean processRequest(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Cookie cookie) throws IOException {
166         boolean isProcessSuccessful = true;
167         try {
168             if (AuthenticationCookieUtils.isSessionExpired(cookie, filterConfiguration)) {
169                 //redirect to portal app
170                 log.debug("SessionValidationFilter: Session is expired, redirecting request {} to portal", httpRequest.getRequestURL());
171                 httpResponse.sendRedirect(filterConfiguration.getRedirectURL());
172                 isProcessSuccessful = false;
173             }
174         } catch (CipherUtilException e) {
175             log.error("SessionValidationFilter: Cookie decryption error : {}", e.getMessage());
176             log.debug("SessionValidationFilter: Cookie decryption error : {}", e.getMessage(), e);
177             isProcessSuccessful = false;
178         }
179         if (!isRoleValid(cookie)) {
180             //redirect to portal app
181             log.debug("SessionValidationFilter: Role is not valid, redirecting request {} to portal", httpRequest.getRequestURL());
182             httpResponse.sendRedirect(filterConfiguration.getRedirectURL());
183             isProcessSuccessful = false;
184         }
185         return isProcessSuccessful;
186     }
187
188     private Cookie processResponse(Cookie cookie) throws IOException, ServletException {
189         Cookie updatedCookie;
190         try {
191             updatedCookie = AuthenticationCookieUtils.updateSessionTime(cookie, filterConfiguration);
192         } catch (CipherUtilException e) {
193             log.error("SessionValidationFilter: Cookie cipher error ...");
194             log.debug("SessionValidationFilter: Cookie cipher error : {}", e.getMessage(), e);
195             throw new ServletException(e.getMessage());
196         }
197         updatedCookie = addRoleToCookie(updatedCookie);
198         return updatedCookie;
199     }
200
201     private boolean isCookiePresent(Cookie[] cookies) {
202         if (cookies == null) {
203             return false;
204         }
205         String actualCookieName = filterConfiguration.getCookieName();
206         boolean isPresent = Arrays.stream(cookies).anyMatch(c -> isCookieNameMatch(actualCookieName, c));
207         if (!isPresent) {
208             log.error("SessionValidationFilter: Session Validation Cookie missing ...");
209             return false;
210         }
211         return true;
212     }
213
214     private List<Cookie> extractAuthenticationCookies(Cookie[] cookies) {
215         String actualCookieName = filterConfiguration.getCookieName();
216         log.debug("SessionValidationFilter: Extracting authentication cookies, {} cookies in request", cookies.length);
217         List<Cookie> authenticationCookies = Arrays.stream(cookies).filter(c -> isCookieNameMatch(actualCookieName, c)).collect(Collectors.toList());
218         log.debug("SessionValidationFilter: Extracted {} authentication cookies from request", authenticationCookies.size());
219         if (authenticationCookies.size() > 1) {
220             authenticationCookies.forEach(cookie -> log
221                 .debug("SessionValidationFilter: Multiple cookies found cookie name, {} cookie value {}", cookie.getName(), cookie.getValue()));
222         }
223         return authenticationCookies;
224     }
225
226     // use contains for matching due issue with ecomp portal ( change cookie name, add prefix ), temp solution
227     private boolean isCookieNameMatch(String actualCookieName, Cookie c) {
228         return c.getName().contains(actualCookieName);
229     }
230
231     private boolean isUrlFromWhiteList(HttpServletRequest httpRequest) {
232         if (httpRequest.getPathInfo() == null) {
233             final String servletPath = httpRequest.getServletPath().toLowerCase();
234             log.debug("SessionValidationFilter: pathInfo is null, trying to check by servlet path white list validation -> ServletPath: {} ",
235                 servletPath);
236             return excludedUrls.stream().anyMatch(e -> servletPath.matches(e));
237         }
238         String pathInfo = httpRequest.getPathInfo().toLowerCase();
239         log.debug("SessionValidationFilter: white list validation ->  PathInfo: {} ", pathInfo);
240         return excludedUrls.stream().anyMatch(e -> pathInfo.matches(e));
241     }
242
243     private void cleanResponceFromLeftoverCookies(HttpServletResponse httpResponse, List<Cookie> cookiesList) {
244         for (Cookie cookie : cookiesList) {
245             Cookie cleanCookie = AuthenticationCookieUtils.createUpdatedCookie(cookie, null, filterConfiguration);
246             cleanCookie.setMaxAge(0);
247             log.debug("SessionValidationFilter Cleaning Cookie cookie name: {} added to responce", cleanCookie.getName());
248             httpResponse.addCookie(cleanCookie);
249         }
250     }
251
252     @Override
253     public void destroy() {
254     }
255 }