/**
 * 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.server.api;

import java.io.IOException;
import java.net.URLDecoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.ambari.server.security.authorization.AuthorizationHelper;

/**
 * This filter overrides usernames found in request url.
 */
public class UserNameOverrideFilter implements Filter {

  // Regex for extracting user name component from the user related api request Uris
  private final static Pattern USER_NAME_IN_URI_REGEXP = Pattern.compile("(?<pre>.*/users/)(?<username>[^/]+)(?<post>(/.*)?)");

  /**
   * Called by the web container to indicate to a filter that it is
   * being placed into service.
   *
   * <p>The servlet container calls the init
   * method exactly once after instantiating the filter. The init
   * method must complete successfully before the filter is asked to do any
   * filtering work.
   *
   * <p>The web container cannot place the filter into service if the init
   * method either
   * <ol>
   * <li>Throws a ServletException
   * <li>Does not return within a time period defined by the web container
   * </ol>
   *
   * @param filterConfig
   */
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {

  }

  /**
   * The <code>doFilter</code> method of the Filter is called by the
   * container each time a request/response pair is passed through the
   * chain due to a client request for a resource at the end of the chain.
   * The FilterChain passed in to this method allows the Filter to pass
   * on the request and response to the next entity in the chain.
   *
   * Verify if this a user related request by checking that the Uri of the request contains
   * username and resolves the username to actual ambari user name if username
   * is a login alias.
   *
   * @param request
   * @param response
   * @param chain
   */
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    if (request instanceof HttpServletRequest) {
      final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
      Matcher userNameMatcher = getUserNameMatcher(httpServletRequest.getRequestURI());

      if (userNameMatcher.find()) {
        String userNameFromUri = URLDecoder.decode(userNameMatcher.group("username"), "UTF-8");
        final String userName = AuthorizationHelper.resolveLoginAliasToUserName(userNameFromUri);

        if (!userNameFromUri.equals(userName)) {
          final String requestUriOverride = String.format("%s%s%s", userNameMatcher.group("pre"), userName, userNameMatcher.group("post"));

          request = new HttpServletRequestWrapper(httpServletRequest) {
            @Override
            public String getRequestURI() {
              return requestUriOverride;
            }
          };

        }
      }
    }

    chain.doFilter(request, response);
  }

  /**
   * Returns a {@link Matcher} created from {@link #USER_NAME_IN_URI_REGEXP} for the
   * provided requestUri.
   * @param requestUri the Uri the Matcher is created for.
   * @return the matcher
   */
  protected Matcher getUserNameMatcher(String requestUri) {
    return USER_NAME_IN_URI_REGEXP.matcher(requestUri);
  }

  /**
   * Called by the web container to indicate to a filter that it is being
   * taken out of service.
   *
   * <p>This method is only called once all threads within the filter's
   * doFilter method have exited or after a timeout period has passed.
   * After the web container calls this method, it will not call the
   * doFilter method again on this instance of the filter.
   *
   * <p>This method gives the filter an opportunity to clean up any
   * resources that are being held (for example, memory, file handles,
   * threads) and make sure that any persistent state is synchronized
   * with the filter's current state in memory.
   */
  @Override
  public void destroy() {

  }
}
