/**
* Copyright 2015 StreamSets Inc.
*
* Licensed under 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 com.streamsets.datacollector.http;
import com.streamsets.datacollector.main.RuntimeInfo;
import com.streamsets.datacollector.util.Configuration;
import com.streamsets.lib.security.http.CORSConstants;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.security.ServerAuthException;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
import org.eclipse.jetty.security.authentication.LoginAuthenticator;
import org.eclipse.jetty.security.authentication.SessionAuthentication;
import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.B64Code;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class ProxyAuthenticator extends LoginAuthenticator {
private final RuntimeInfo runtimeInfo;
private final LoginAuthenticator authenticator;
private final Configuration conf;
public final static String AUTH_TOKEN = "auth_token";
public final static String AUTH_USER = "auth_user";
private final static String TOKEN_AUTHENTICATION_USER_NAME = "admin";
public ProxyAuthenticator(LoginAuthenticator authenticator, RuntimeInfo runtimeInfo, Configuration conf) {
this.authenticator = authenticator;
this.runtimeInfo = runtimeInfo;
this.conf = conf;
}
@Override
public void prepareRequest(ServletRequest request) {
super.prepareRequest(request);
authenticator.prepareRequest(request);
}
@Override
public UserIdentity login(String username, Object password, ServletRequest request) {
return authenticator.login(username, password, request);
}
@Override
public void setConfiguration(AuthConfiguration configuration) {
super.setConfiguration(configuration);
authenticator.setConfiguration(configuration);
}
@Override
public LoginService getLoginService() {
return authenticator.getLoginService();
}
@Override
protected HttpSession renewSession(HttpServletRequest request, HttpServletResponse response) {
return super.renewSession(request, response);
}
@Override
public String getAuthMethod() {
return authenticator.getAuthMethod();
}
@Override
public boolean secureResponse(
ServletRequest request,
ServletResponse response,
boolean mandatory,
Authentication.User validatedUser
) throws ServerAuthException {
return authenticator.secureResponse(request, response, mandatory, validatedUser);
}
@Override
public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory)
throws ServerAuthException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
HttpSession session = request.getSession(true);
Authentication authentication = (Authentication) session.getAttribute(SessionAuthentication.__J_AUTHENTICATED);
// handle CORS Options
if ("OPTIONS".equals(request.getMethod())) {
response.setHeader("Access-Control-Allow-Origin", conf.get(CORSConstants.HTTP_ACCESS_CONTROL_ALLOW_ORIGIN,
CORSConstants.HTTP_ACCESS_CONTROL_ALLOW_ORIGIN_DEFAULT));
response.setHeader("Access-Control-Allow-Headers", conf.get(CORSConstants.HTTP_ACCESS_CONTROL_ALLOW_HEADERS,
CORSConstants.HTTP_ACCESS_CONTROL_ALLOW_HEADERS_DEFAULT));
response.setHeader("Access-Control-Allow-Methods", conf.get(CORSConstants.HTTP_ACCESS_CONTROL_ALLOW_METHODS,
CORSConstants.HTTP_ACCESS_CONTROL_ALLOW_METHODS_DEFAULT));
return Authentication.SEND_SUCCESS;
}
if (authentication == null) {
String authToken = request.getHeader(AUTH_TOKEN);
String authUser = request.getHeader(AUTH_USER);
if (authToken == null) {
authToken = request.getParameter(AUTH_TOKEN);
authUser = request.getParameter(AUTH_USER);
if (authUser == null) {
authUser = TOKEN_AUTHENTICATION_USER_NAME;
}
}
if (authToken != null && runtimeInfo.isValidAuthenticationToken(authToken)) {
SdcHashLoginService loginService = (SdcHashLoginService) getLoginService();
UserIdentity userIdentity = loginService.getUserIdentity(authUser);
Authentication cached = new SessionAuthentication(getAuthMethod(), userIdentity, null);
session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached);
}
if (this.authenticator instanceof FormAuthenticator) {
String credentials = request.getHeader(HttpHeader.AUTHORIZATION.asString());
if (credentials != null) {
//Support User name and password as part of Authorization Header for Form & Digest Authenticator
int space = credentials.indexOf(' ');
if (space > 0) {
String method=credentials.substring(0,space);
if ("basic".equalsIgnoreCase(method)) {
credentials = credentials.substring(space+1);
credentials = B64Code.decode(credentials, StandardCharsets.ISO_8859_1);
int i = credentials.indexOf(':');
if (i > 0) {
String username = credentials.substring(0,i);
String password = credentials.substring(i+1);
UserIdentity userIdentity = login(username, password, request);
if (userIdentity != null) {
Authentication cached = new SessionAuthentication(getAuthMethod(), userIdentity, null);
session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached);
} else {
try {
response.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(),
"basic realm=\"" + _loginService.getName() + '"');
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return Authentication.SEND_CONTINUE;
} catch (IOException e) {
throw new ServerAuthException(e);
}
}
}
}
}
} else {
//Handle redirecting to home page instead of REST API page & setting reverse proxy base path
String pathInfo = request.getPathInfo();
if ("/j_security_check".equals(pathInfo)) {
String basePath = request.getParameter("basePath");
if (basePath == null || basePath.trim().length() == 0) {
basePath = "/";
}
String redirectURL = (String)session.getAttribute(FormAuthenticator.__J_URI);
if ((redirectURL != null && (redirectURL.contains("rest/v1/") || redirectURL.contains("jmx"))) ||
!basePath.equals("/")) {
session.setAttribute(FormAuthenticator.__J_URI, basePath);
}
}
}
}
}
return authenticator.validateRequest(req, res, mandatory);
}
}