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.lib;
019
020import static org.apache.hadoop.hbase.http.ServerConfigurationKeys.DEFAULT_HBASE_HTTP_STATIC_USER;
021import static org.apache.hadoop.hbase.http.ServerConfigurationKeys.HBASE_HTTP_STATIC_USER;
022
023import java.io.IOException;
024import java.security.Principal;
025import java.util.HashMap;
026
027import javax.servlet.Filter;
028import javax.servlet.FilterChain;
029import javax.servlet.FilterConfig;
030import javax.servlet.ServletException;
031import javax.servlet.ServletRequest;
032import javax.servlet.ServletResponse;
033import javax.servlet.http.HttpServletRequest;
034import javax.servlet.http.HttpServletRequestWrapper;
035
036import org.apache.hadoop.conf.Configuration;
037import org.apache.hadoop.hbase.HBaseInterfaceAudience;
038import org.apache.hadoop.hbase.http.FilterContainer;
039import org.apache.hadoop.hbase.http.FilterInitializer;
040import org.apache.yetus.audience.InterfaceAudience;
041import org.slf4j.Logger;
042import org.slf4j.LoggerFactory;
043
044/**
045 * Provides a servlet filter that pretends to authenticate a fake user (Dr.Who)
046 * so that the web UI is usable for a secure cluster without authentication.
047 */
048@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
049public class StaticUserWebFilter extends FilterInitializer {
050  static final String DEPRECATED_UGI_KEY = "dfs.web.ugi";
051
052  private static final Logger LOG = LoggerFactory.getLogger(StaticUserWebFilter.class);
053
054  static class User implements Principal {
055    private final String name;
056    public User(String name) {
057      this.name = name;
058    }
059    @Override
060    public String getName() {
061      return name;
062    }
063    @Override
064    public int hashCode() {
065      return name.hashCode();
066    }
067    @Override
068    public boolean equals(Object other) {
069      if (other == this) {
070        return true;
071      } else if (other == null || other.getClass() != getClass()) {
072        return false;
073      }
074      return ((User) other).name.equals(name);
075    }
076    @Override
077    public String toString() {
078      return name;
079    }
080  }
081
082  @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
083  public static class StaticUserFilter implements Filter {
084    private User user;
085    private String username;
086
087    @Override
088    public void destroy() {
089      // NOTHING
090    }
091
092    @Override
093    public void doFilter(ServletRequest request, ServletResponse response,
094                         FilterChain chain
095                         ) throws IOException, ServletException {
096      HttpServletRequest httpRequest = (HttpServletRequest) request;
097      // if the user is already authenticated, don't override it
098      if (httpRequest.getRemoteUser() != null) {
099        chain.doFilter(request, response);
100      } else {
101        HttpServletRequestWrapper wrapper =
102            new HttpServletRequestWrapper(httpRequest) {
103          @Override
104          public Principal getUserPrincipal() {
105            return user;
106          }
107          @Override
108          public String getRemoteUser() {
109            return username;
110          }
111        };
112        chain.doFilter(wrapper, response);
113      }
114    }
115
116    @Override
117    public void init(FilterConfig conf) throws ServletException {
118      this.username = conf.getInitParameter(HBASE_HTTP_STATIC_USER);
119      this.user = new User(username);
120    }
121
122  }
123
124  @Override
125  public void initFilter(FilterContainer container, Configuration conf) {
126    HashMap<String, String> options = new HashMap<>();
127
128    String username = getUsernameFromConf(conf);
129    options.put(HBASE_HTTP_STATIC_USER, username);
130
131    container.addFilter("static_user_filter",
132                        StaticUserFilter.class.getName(),
133                        options);
134  }
135
136  /**
137   * Retrieve the static username from the configuration.
138   */
139  static String getUsernameFromConf(Configuration conf) {
140    String oldStyleUgi = conf.get(DEPRECATED_UGI_KEY);
141    if (oldStyleUgi != null) {
142      // We can't use the normal configuration deprecation mechanism here
143      // since we need to split out the username from the configured UGI.
144      LOG.warn(DEPRECATED_UGI_KEY + " should not be used. Instead, use " +
145          HBASE_HTTP_STATIC_USER + ".");
146      String[] parts = oldStyleUgi.split(",");
147      return parts[0];
148    } else {
149      return conf.get(HBASE_HTTP_STATIC_USER,
150        DEFAULT_HBASE_HTTP_STATIC_USER);
151    }
152  }
153
154}