001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.http; 019 020import org.apache.hadoop.conf.Configuration; 021import org.apache.hadoop.security.authorize.AuthorizationException; 022import org.apache.hadoop.security.authorize.ProxyUsers; 023import org.apache.hadoop.security.UserGroupInformation; 024import org.apache.hadoop.security.authentication.server.AuthenticationFilter; 025import org.apache.hadoop.util.HttpExceptionUtils; 026import org.apache.hadoop.util.StringUtils; 027import org.apache.yetus.audience.InterfaceAudience; 028import org.apache.yetus.audience.InterfaceStability; 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031 032import java.io.IOException; 033import java.security.Principal; 034import java.util.ArrayList; 035import java.util.Enumeration; 036import java.util.HashMap; 037import java.util.Iterator; 038import java.util.List; 039import java.util.Map; 040import javax.servlet.FilterChain; 041import javax.servlet.FilterConfig; 042import javax.servlet.ServletException; 043import javax.servlet.http.HttpServletRequest; 044import javax.servlet.http.HttpServletRequestWrapper; 045import javax.servlet.http.HttpServletResponse; 046 047/** 048 * This file has been copied directly (changing only the package name and and the ASF license 049 * text format, and adding the Yetus annotations) from Hadoop, as the Hadoop version that HBase 050 * depends on doesn't have it yet 051 * (as of 2020 Apr 24, there is no Hadoop release that has it either). 052 * 053 * Hadoop version: 054 * unreleased, master branch commit 4ea6c2f457496461afc63f38ef4cef3ab0efce49 055 * 056 * Haddop path: 057 * hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/authentication/ 058 * server/ProxyUserAuthenticationFilter.java 059 * 060 * AuthenticationFilter which adds support to perform operations 061 * using end user instead of proxy user. Fetches the end user from 062 * doAs Query Parameter. 063 */ 064@InterfaceAudience.Private 065@InterfaceStability.Evolving 066public class ProxyUserAuthenticationFilter extends AuthenticationFilter { 067 068 private static final Logger LOG = LoggerFactory.getLogger( 069 ProxyUserAuthenticationFilter.class); 070 071 private static final String DO_AS = "doas"; 072 public static final String PROXYUSER_PREFIX = "proxyuser"; 073 074 @Override 075 public void init(FilterConfig filterConfig) throws ServletException { 076 Configuration conf = getProxyuserConfiguration(filterConfig); 077 ProxyUsers.refreshSuperUserGroupsConfiguration(conf, PROXYUSER_PREFIX); 078 super.init(filterConfig); 079 } 080 081 @Override 082 protected void doFilter(FilterChain filterChain, HttpServletRequest request, 083 HttpServletResponse response) throws IOException, ServletException { 084 final HttpServletRequest lowerCaseRequest = toLowerCase(request); 085 String doAsUser = lowerCaseRequest.getParameter(DO_AS); 086 087 if (doAsUser != null && !doAsUser.equals(request.getRemoteUser())) { 088 LOG.debug("doAsUser = {}, RemoteUser = {} , RemoteAddress = {} ", 089 doAsUser, request.getRemoteUser(), request.getRemoteAddr()); 090 UserGroupInformation requestUgi = (request.getUserPrincipal() != null) ? 091 UserGroupInformation.createRemoteUser(request.getRemoteUser()) 092 : null; 093 if (requestUgi != null) { 094 requestUgi = UserGroupInformation.createProxyUser(doAsUser, 095 requestUgi); 096 try { 097 ProxyUsers.authorize(requestUgi, request.getRemoteAddr()); 098 099 final UserGroupInformation ugiF = requestUgi; 100 request = new HttpServletRequestWrapper(request) { 101 @Override 102 public String getRemoteUser() { 103 return ugiF.getShortUserName(); 104 } 105 106 @Override 107 public Principal getUserPrincipal() { 108 return new Principal() { 109 @Override 110 public String getName() { 111 return ugiF.getUserName(); 112 } 113 }; 114 } 115 }; 116 LOG.debug("Proxy user Authentication successful"); 117 } catch (AuthorizationException ex) { 118 HttpExceptionUtils.createServletExceptionResponse(response, 119 HttpServletResponse.SC_FORBIDDEN, ex); 120 LOG.warn("Proxy user Authentication exception", ex); 121 return; 122 } 123 } 124 } 125 super.doFilter(filterChain, request, response); 126 } 127 128 protected Configuration getProxyuserConfiguration(FilterConfig filterConfig) 129 throws ServletException { 130 Configuration conf = new Configuration(false); 131 Enumeration<?> names = filterConfig.getInitParameterNames(); 132 while (names.hasMoreElements()) { 133 String name = (String) names.nextElement(); 134 if (name.startsWith(PROXYUSER_PREFIX + ".")) { 135 String value = filterConfig.getInitParameter(name); 136 conf.set(name, value); 137 } 138 } 139 return conf; 140 } 141 142 static boolean containsUpperCase(final Iterable<String> strings) { 143 for(String s : strings) { 144 for(int i = 0; i < s.length(); i++) { 145 if (Character.isUpperCase(s.charAt(i))) { 146 return true; 147 } 148 } 149 } 150 return false; 151 } 152 153 public static HttpServletRequest toLowerCase( 154 final HttpServletRequest request) { 155 @SuppressWarnings("unchecked") 156 final Map<String, String[]> original = (Map<String, String[]>) 157 request.getParameterMap(); 158 if (!containsUpperCase(original.keySet())) { 159 return request; 160 } 161 162 final Map<String, List<String>> m = new HashMap<String, List<String>>(); 163 for (Map.Entry<String, String[]> entry : original.entrySet()) { 164 final String key = StringUtils.toLowerCase(entry.getKey()); 165 List<String> strings = m.get(key); 166 if (strings == null) { 167 strings = new ArrayList<String>(); 168 m.put(key, strings); 169 } 170 for (String v : entry.getValue()) { 171 strings.add(v); 172 } 173 } 174 175 return new HttpServletRequestWrapper(request) { 176 private Map<String, String[]> parameters = null; 177 178 @Override 179 public Map<String, String[]> getParameterMap() { 180 if (parameters == null) { 181 parameters = new HashMap<String, String[]>(); 182 for (Map.Entry<String, List<String>> entry : m.entrySet()) { 183 final List<String> a = entry.getValue(); 184 parameters.put(entry.getKey(), a.toArray(new String[a.size()])); 185 } 186 } 187 return parameters; 188 } 189 190 @Override 191 public String getParameter(String name) { 192 final List<String> a = m.get(name); 193 return a == null ? null : a.get(0); 194 } 195 196 @Override 197 public String[] getParameterValues(String name) { 198 return getParameterMap().get(name); 199 } 200 201 @Override 202 public Enumeration<String> getParameterNames() { 203 final Iterator<String> i = m.keySet().iterator(); 204 return new Enumeration<String>() { 205 @Override 206 public boolean hasMoreElements() { 207 return i.hasNext(); 208 } 209 210 @Override 211 public String nextElement() { 212 return i.next(); 213 } 214 }; 215 } 216 }; 217 } 218 219}