| /** |
| * 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.hadoop.hdfsproxy; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.hadoop.fs.Path; |
| import org.apache.hadoop.security.UserGroupInformation; |
| import org.apache.hadoop.conf.Configuration; |
| |
| import javax.servlet.*; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import java.io.IOException; |
| import java.util.List; |
| import java.util.Arrays; |
| import java.util.regex.Pattern; |
| import java.util.regex.Matcher; |
| |
| public class AuthorizationFilter implements Filter { |
| public static final Log LOG = LogFactory.getLog(AuthorizationFilter.class); |
| |
| private static final Pattern HDFS_PATH_PATTERN = Pattern |
| .compile("(^hdfs://([\\w\\-]+(\\.)?)+:\\d+|^hdfs://([\\w\\-]+(\\.)?)+)"); |
| |
| /** Pattern for a filter to find out if a request is HFTP/HSFTP request */ |
| protected static final Pattern HFTP_PATTERN = Pattern |
| .compile("^(/listPaths|/data|/streamFile|/file)$"); |
| /** |
| * Pattern for a filter to find out if an HFTP/HSFTP request stores its file |
| * path in the extra path information associated with the URL; if not, the |
| * file path is stored in request parameter "filename" |
| */ |
| protected static final Pattern FILEPATH_PATTERN = Pattern |
| .compile("^(/listPaths|/data|/file)$"); |
| |
| protected String namenode; |
| |
| /** {@inheritDoc} **/ |
| public void init(FilterConfig filterConfig) throws ServletException { |
| Configuration conf = new Configuration(false); |
| conf.addResource("hdfsproxy-default.xml"); |
| conf.addResource("hdfsproxy-site.xml"); |
| namenode = conf.get("fs.default.name"); |
| } |
| |
| /** {@inheritDoc} **/ |
| @SuppressWarnings("unchecked") |
| public void doFilter(ServletRequest request, |
| ServletResponse response, |
| FilterChain chain) |
| throws IOException, ServletException { |
| |
| HttpServletResponse rsp = (HttpServletResponse) response; |
| HttpServletRequest rqst = (HttpServletRequest) request; |
| |
| String userId = getUserId(request); |
| String groups = getGroups(request); |
| List<Path> allowedPaths = getAllowedPaths(request); |
| |
| UserGroupInformation ugi = |
| UserGroupInformation.createRemoteUser(userId); |
| |
| String filePath = getPathFromRequest(rqst); |
| |
| if (filePath == null || !checkHdfsPath(filePath, allowedPaths)) { |
| String msg = "User " + userId + " (" + groups |
| + ") is not authorized to access path " + filePath; |
| LOG.warn(msg); |
| rsp.sendError(HttpServletResponse.SC_FORBIDDEN, msg); |
| return; |
| } |
| request.setAttribute("authorized.ugi", ugi); |
| |
| LOG.info("User: " + userId + "(" + groups + |
| ") Request: " + rqst.getPathInfo() + " From: " + |
| rqst.getRemoteAddr()); |
| |
| chain.doFilter(request, response); |
| } |
| |
| protected String getUserId(ServletRequest rqst) { |
| String userId = (String) rqst. |
| getAttribute("org.apache.hadoop.hdfsproxy.authorized.userID"); |
| if (userId != null) |
| userId = userId.split("[/@]")[0]; |
| return userId; |
| } |
| |
| protected String getGroups(ServletRequest request) { |
| UserGroupInformation ugi = UserGroupInformation. |
| createRemoteUser(getUserId(request)); |
| return Arrays.toString(ugi.getGroupNames()); |
| } |
| |
| @SuppressWarnings("unchecked") |
| protected List<Path> getAllowedPaths(ServletRequest request) { |
| return (List<Path>)request. |
| getAttribute("org.apache.hadoop.hdfsproxy.authorized.paths"); |
| } |
| |
| protected String getPathFromRequest(HttpServletRequest rqst) { |
| String filePath = null; |
| // check request path |
| String servletPath = rqst.getServletPath(); |
| if (HFTP_PATTERN.matcher(servletPath).matches()) { |
| // request is an HSFTP request |
| if (FILEPATH_PATTERN.matcher(servletPath).matches()) { |
| // file path as part of the URL |
| filePath = rqst.getPathInfo() != null ? rqst.getPathInfo() : "/"; |
| } else { |
| // file path is stored in "filename" parameter |
| filePath = rqst.getParameter("filename"); |
| } |
| } |
| return filePath; |
| } |
| |
| /** check that the requested path is listed in the ldap entry |
| * @param pathInfo - Path to check access |
| * @param ldapPaths - List of paths allowed access |
| * @return true if access allowed, false otherwise */ |
| public boolean checkHdfsPath(String pathInfo, |
| List<Path> ldapPaths) { |
| if (pathInfo == null || pathInfo.length() == 0) { |
| LOG.info("Can't get file path from the request"); |
| return false; |
| } |
| for (Path ldapPathVar : ldapPaths) { |
| String ldapPath = ldapPathVar.toString(); |
| if (isPathQualified(ldapPath) && |
| isPathAuthroized(ldapPath)) { |
| String allowedPath = extractPath(ldapPath); |
| if (pathInfo.startsWith(allowedPath)) |
| return true; |
| } else { |
| if (pathInfo.startsWith(ldapPath)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private String extractPath(String ldapPath) { |
| return HDFS_PATH_PATTERN.split(ldapPath)[1]; |
| } |
| |
| private boolean isPathAuthroized(String pathStr) { |
| Matcher namenodeMatcher = HDFS_PATH_PATTERN.matcher(pathStr); |
| return namenodeMatcher.find() && namenodeMatcher.group().contains(namenode); |
| } |
| |
| private boolean isPathQualified(String pathStr) { |
| if (pathStr == null || pathStr.trim().isEmpty()) { |
| return false; |
| } else { |
| return HDFS_PATH_PATTERN.matcher(pathStr).find(); |
| } |
| } |
| |
| /** {@inheritDoc} **/ |
| public void destroy() { |
| } |
| } |