/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.ambari.logsearch.web.filters; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.apache.ambari.logsearch.common.PropertiesHelper; import org.apache.commons.collections.iterators.IteratorEnumeration; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler; import org.apache.hadoop.security.authentication.server.PseudoAuthenticationHandler; import org.apache.hadoop.security.authentication.util.KerberosName; import org.springframework.security.web.authentication.WebAuthenticationDetails; public class LogsearchKRBAuthenticationFilter extends LogsearchKrbFilter { private static final Logger logger = LoggerFactory.getLogger(LogsearchKRBAuthenticationFilter.class); private static final String NAME_RULES = "hadoop.security.auth_to_local"; private static final String TOKEN_VALID = "logsearch.admin.kerberos.token.valid.seconds"; private static final String COOKIE_DOMAIN = "logsearch.admin.kerberos.cookie.domain"; private static final String COOKIE_PATH = "logsearch.admin.kerberos.cookie.path"; private static final String PRINCIPAL = "logsearch.spnego.kerberos.principal"; private static final String KEYTAB = "logsearch.spnego.kerberos.keytab"; private static final String HOST_NAME = "logsearch.spnego.kerberos.host"; private static final String KERBEROS_ENABLED = "logsearch.spnego.kerberos.enabled"; private static final String NAME_RULES_PARAM = "kerberos.name.rules"; private static final String TOKEN_VALID_PARAM = "token.validity"; private static final String COOKIE_DOMAIN_PARAM = "cookie.domain"; private static final String COOKIE_PATH_PARAM = "cookie.path"; private static final String PRINCIPAL_PARAM = "kerberos.principal"; private static final String KEYTAB_PARAM = "kerberos.keytab"; private static final String AUTH_TYPE = "type"; private static final String AUTH_COOKIE_NAME = "hadoop.auth"; private static final String DEFAULT_USER_ROLE = "ROLE_USER"; private static final NoServletContext NO_SERVLET_CONTEXT = new NoServletContext(); private static final Pattern usernamePattern = Pattern.compile("(?<=u=)(.*?)(?=&)|(?<=u=)(.*)"); private String authType = PseudoAuthenticationHandler.TYPE; private static boolean spnegoEnable = false; public LogsearchKRBAuthenticationFilter() { try { isSpnegoEnable(); init(null); } catch (ServletException e) { logger.error("Error while initializing Filter : " + e.getMessage()); } } @Override public void init(FilterConfig conf) throws ServletException { final FilterConfig globalConf = conf; String hostName = PropertiesHelper.getProperty(HOST_NAME, "localhost"); final Map<String, String> params = new HashMap<String, String>(); if (spnegoEnable) { authType = KerberosAuthenticationHandler.TYPE; } params.put(AUTH_TYPE,authType); params.put(NAME_RULES_PARAM,PropertiesHelper.getProperty(NAME_RULES, "DEFAULT")); params.put(TOKEN_VALID_PARAM, PropertiesHelper.getProperty(TOKEN_VALID, "30")); params.put(COOKIE_DOMAIN_PARAM, PropertiesHelper.getProperty(COOKIE_DOMAIN, hostName)); params.put(COOKIE_PATH_PARAM, PropertiesHelper.getProperty(COOKIE_PATH, "/")); params.put(PRINCIPAL_PARAM,PropertiesHelper.getProperty(PRINCIPAL,"")); params.put(KEYTAB_PARAM,PropertiesHelper.getProperty(KEYTAB,"")); FilterConfig myConf = new FilterConfig() { @Override public ServletContext getServletContext() { if (globalConf != null) { return globalConf.getServletContext(); } else { return NO_SERVLET_CONTEXT; } } @SuppressWarnings("unchecked") @Override public Enumeration<String> getInitParameterNames() { return new IteratorEnumeration(params.keySet().iterator()); } @Override public String getInitParameter(String param) { return params.get(param); } @Override public String getFilterName() { return "KerberosFilter"; } }; super.init(myConf); } @Override protected void doFilter(FilterChain filterChain, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { logger.debug("LogsearchKRBAuthenticationFilter private filter"); String userName = getUsernameFromResponse(response); if (StringUtils.isNotEmpty(userName)) { Authentication existingAuth = SecurityContextHolder.getContext() .getAuthentication(); if (existingAuth == null || !existingAuth.isAuthenticated()) { // --------------------------- To Create Logsearch Session-------------------------------------- // if we get the userName from the token then log into Logsearch using the same user final List<GrantedAuthority> grantedAuths = new ArrayList<>(); grantedAuths.add(new SimpleGrantedAuthority(DEFAULT_USER_ROLE)); final UserDetails principal = new User(userName, "", grantedAuths); final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken( principal, "", grantedAuths); WebAuthenticationDetails webDetails = new WebAuthenticationDetails( request); ((AbstractAuthenticationToken) finalAuthentication) .setDetails(webDetails); Authentication authentication = this .authenticate(finalAuthentication); authentication = getGrantedAuthority(authentication); SecurityContextHolder.getContext().setAuthentication(authentication); request.getSession(true).setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext()); request.setAttribute("spnegoEnabled", true); logger.info("Logged into Logsearch as = " + userName); filterChain.doFilter(request, response); } else { try { super.doFilter(filterChain, request, response); } catch (Exception e) { logger.error("Error LogsearchKRBAuthenticationFilter : " + e.getMessage()); } } } else { filterChain.doFilter(request, response); } } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; logger.debug("LogsearchKRBAuthenticationFilter public filter path >>>>" +httpRequest.getPathInfo()); SecurityContextImpl securityContextImpl=(SecurityContextImpl) httpRequest.getSession(true).getAttribute("SPRING_SECURITY_CONTEXT"); Authentication existingAuth = null; if(securityContextImpl!=null){ existingAuth= securityContextImpl.getAuthentication(); } if (!isLoginRequest(httpRequest) && spnegoEnable && (existingAuth == null || !existingAuth.isAuthenticated())) { KerberosName.setRules(PropertiesHelper.getProperty(NAME_RULES, "DEFAULT")); String userName = getUsernameFromRequest(httpRequest); if ((existingAuth == null || !existingAuth.isAuthenticated()) && (StringUtils.isNotEmpty(userName))) { // --------------------------- To Create Logsearch Session-------------------------------------- // if we get the userName from the token then log into logsearch using the same user final List<GrantedAuthority> grantedAuths = new ArrayList<>(); grantedAuths.add(new SimpleGrantedAuthority(DEFAULT_USER_ROLE)); final UserDetails principal = new User(userName, "", grantedAuths); final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken( principal, "", grantedAuths); WebAuthenticationDetails webDetails = new WebAuthenticationDetails( httpRequest); ((AbstractAuthenticationToken) finalAuthentication) .setDetails(webDetails); Authentication authentication = this .authenticate(finalAuthentication); authentication = getGrantedAuthority(authentication); SecurityContextHolder.getContext().setAuthentication(authentication); request.setAttribute("spnegoEnabled", true); logger.info("Logged into Logsearch as = " + userName); }else { try { super.doFilter(request, response, filterChain); } catch (Exception e) { logger.error("Error LogsearchKRBAuthenticationFilter : " + e.getMessage()); } } } else { filterChain.doFilter(request, response); } } private void isSpnegoEnable() { spnegoEnable = PropertiesHelper.getBooleanProperty(KERBEROS_ENABLED, false); if (spnegoEnable) { spnegoEnable = false; String keytab = PropertiesHelper.getProperty(KEYTAB); String principal = PropertiesHelper.getProperty(PRINCIPAL); String hostname = PropertiesHelper.getProperty(HOST_NAME); if (StringUtils.isNotEmpty(keytab) && StringUtils.isNotEmpty(principal) && StringUtils.isNotEmpty(hostname)) { spnegoEnable = true; } } } private Authentication getGrantedAuthority(Authentication authentication) { UsernamePasswordAuthenticationToken result = null; if (authentication != null && authentication.isAuthenticated()) { final List<GrantedAuthority> grantedAuths = getAuthorities(); final UserDetails userDetails = new User(authentication.getName() .toString(), authentication.getCredentials().toString(), grantedAuths); result = new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials(), grantedAuths); result.setDetails(authentication.getDetails()); return result; } return authentication; } private List<GrantedAuthority> getAuthorities() { final List<GrantedAuthority> grantedAuths = new ArrayList<>(); grantedAuths.add(new SimpleGrantedAuthority(DEFAULT_USER_ROLE)); return grantedAuths; } private Authentication authenticate(Authentication authentication) throws AuthenticationException { String username = authentication.getName(); String password = (String) authentication.getCredentials(); username = StringEscapeUtils.unescapeHtml(username); if (StringUtils.isEmpty(username)) { throw new BadCredentialsException("Username can't be null or empty."); } org.apache.ambari.logsearch.web.model.User user = new org.apache.ambari.logsearch.web.model.User(); user.setUsername(username); authentication = new UsernamePasswordAuthenticationToken(username, password, getAuthorities()); return authentication; } private String getUsernameFromRequest(HttpServletRequest httpRequest) { String userName = null; Cookie[] cookie = httpRequest.getCookies(); if (cookie != null) { for (Cookie c : cookie) { if (c.getName().equalsIgnoreCase(AUTH_COOKIE_NAME)) { String cookieStr = c.getName() + "=" + c.getValue(); Matcher m = usernamePattern.matcher(cookieStr); if (m.find()) { userName = m.group(1); } } } } logger.debug("kerberos username from request >>>>>>>>" + userName); return userName; } private String getUsernameFromResponse(HttpServletResponse response) { String userName = null; boolean checkCookie = response.containsHeader("Set-Cookie"); if (checkCookie) { Collection<String> cookiesCollection = response.getHeaders("Set-Cookie"); if (cookiesCollection != null) { Iterator<String> iterator = cookiesCollection.iterator(); while (iterator.hasNext()) { String cookie = iterator.next(); if (StringUtils.isNotEmpty(cookie)) { if (cookie.toLowerCase().startsWith(AUTH_COOKIE_NAME.toLowerCase())) { Matcher m = usernamePattern.matcher(cookie); if (m.find()) { userName = m.group(1); } } } if (StringUtils.isNotEmpty(userName)) { break; } } } } logger.debug("kerberos username from response >>>>>>>>" + userName); return userName; } private boolean isLoginRequest(HttpServletRequest httpServletRequest) { boolean isLoginRequest = false; if ("POST".equalsIgnoreCase(httpServletRequest.getMethod())) { String url = httpServletRequest.getRequestURI().toString(); if ("/login".equalsIgnoreCase(url)) { isLoginRequest = true; } } return isLoginRequest; } }