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