package water;

import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlets.ProxyServlet;
import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.security.Credential;
import water.network.SecurityUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

public class JettyProxy extends AbstractHTTPD {

  private final String _proxyTo;
  private final Credentials _credentials;

  public JettyProxy(H2O.BaseArgs args, Credentials credentials, String proxyTo) {
    super(args);
    _proxyTo = proxyTo;
    _credentials = credentials;
  }

  @Override
  protected void registerHandlers(HandlerWrapper handlerWrapper, ServletContextHandler context) {
    ServletHolder proxyServlet = new ServletHolder(Transparent.class);
    proxyServlet.setInitParameter("ProxyTo", _proxyTo);
    proxyServlet.setInitParameter("Prefix", "/");
    proxyServlet.setInitParameter("Password", _credentials._password);
    context.addServlet(proxyServlet, "/*");
    handlerWrapper.setHandler(context);
  }

  @Override
  protected RuntimeException failEx(String message) {
    return new IllegalStateException(message);
  }

  /**
   * Transparent proxy that automatically adds authentication to each request
   */
  public static class Transparent extends ProxyServlet.Transparent {
    private String _password;

    @Override
    public void init(ServletConfig config) throws ServletException {
      super.init(config);
      _password = config.getInitParameter("Password");
    }

    @Override
    protected void customizeExchange(HttpExchange exchange, HttpServletRequest request) {
      exchange.setRequestHeader("Authorization", getBasicAuth(request));
    }

    private String getBasicAuth(HttpServletRequest request) {
      // Note: we need to use the authenticated user and pass it to the H2O node
      // H2O will be responsible of authorizing the user (it will reject it if the cluster was started for another user)
      return "Basic " + B64Code.encode(request.getUserPrincipal().getName() + ":" + _password);
    }
  }

  /**
   * Representation of the User-Password pair
   */
  public static class Credentials {
    private static final int GEN_PASSWORD_LENGTH = 16;

    private final String _user;
    private final String _password;

    private Credentials(String _user, String _password) {
      this._user = _user;
      this._password = _password;
    }

    public String toHashFileEntry() {
      return _user + ": " + Credential.MD5.digest(_password) + "\n";
    }

    public String toDebugString() {
      return "Credentials[_user='" + _user + "', _password='" + _password + "']";
    }

    public static Credentials make(String user, String password) {
      return new Credentials(user, password);
    }

    public static Credentials make(String user) {
      return make(user, SecurityUtils.passwordGenerator(GEN_PASSWORD_LENGTH));
    }
  }

}

@@ -1119,24 +1119,25 @@
 ameter(%22
-Password
+BasicAuth
 %22, _cred
@@ -1144,25 +1144,29 @@
 entials.
-_password
+toBasicAuth()
 );%0A    c
@@ -1542,32 +1542,33 @@
 ate String _
-password
+basicAuth
 ;%0A%0A    @Over
@@ -1674,24 +1674,25 @@
 %0A      _
-password
+basicAuth
  = confi
@@ -1711,24 +1711,25 @@
 ameter(%22
-Password
+BasicAuth
 %22);%0A    
@@ -1888,396 +1888,18 @@
 n%22, 
-getBasicAuth(request));%0A    %7D%0A%0A    private String getBasicAuth(HttpServletRequest request) %7B%0A      // Note: we need to use the authenticated user and pass it to the H2O node%0A      // H2O will be responsible of authorizing the user (it will reject it if the cluster was started for another user)%0A      return %22Basic %22 + B64Code.encode(request.getUserPrincipal().getName() + %22:%22 + _password
+_basicAuth
 );%0A 
@@ -2207,24 +2207,24 @@
 er = _user;%0A
-
       this._
@@ -2244,32 +2244,138 @@
 assword;%0A    %7D%0A%0A
+    public String toBasicAuth() %7B%0A      return %22Basic %22 + B64Code.encode(_user + %22:%22 + _password);%0A    %7D%0A%0A
     public Strin

Revert "Fix proxy authorization (we need to pass the actual authenticated username) (#1598)"