make the dispatch feature work for core securityHandler

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2023-08-17 15:32:40 +10:00
parent db5209e97a
commit 9208629e95
5 changed files with 71 additions and 183 deletions

View File

@ -0,0 +1,21 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.security;
import org.eclipse.jetty.server.Request;
public interface ServeAs
{
Request serveAs(Request request, String path);
}

View File

@ -40,6 +40,10 @@ import org.slf4j.LoggerFactory;
/**
* FORM Authenticator.
*
* <p>This authenticator implements form authentication will use dispatchers to
* the login page if the {@link #__FORM_DISPATCH} init parameter is set to true.
* Otherwise it will redirect.</p>
*
* <p>The form authenticator redirects unauthenticated requests to a log page
* which should use a form to gather username/password from the user and send them
* to the /j_security_check URI within the context. FormAuthentication uses
@ -52,6 +56,8 @@ public class FormAuthenticator extends LoginAuthenticator
public static final String __FORM_LOGIN_PAGE = "org.eclipse.jetty.security.form_login_page";
public static final String __FORM_ERROR_PAGE = "org.eclipse.jetty.security.form_error_page";
public static final String __FORM_DISPATCH = "org.eclipse.jetty.security.dispatch";
public static final String __J_URI = "org.eclipse.jetty.security.form_URI";
public static final String __J_POST = "org.eclipse.jetty.security.form_POST";
public static final String __J_METHOD = "org.eclipse.jetty.security.form_METHOD";
@ -64,24 +70,19 @@ public class FormAuthenticator extends LoginAuthenticator
private String _formLoginPage;
private String _formLoginPath;
private boolean _alwaysSaveUri;
private boolean _dispatch;
public FormAuthenticator()
{
}
@Deprecated
public FormAuthenticator(String login, String error, boolean dispatch)
{
this(login, error);
}
public FormAuthenticator(String login, String error)
{
this();
if (login != null)
setLoginPage(login);
if (error != null)
setErrorPage(error);
_dispatch = dispatch;
}
/**
@ -112,6 +113,9 @@ public class FormAuthenticator extends LoginAuthenticator
String error = configuration.getParameter(FormAuthenticator.__FORM_ERROR_PAGE);
if (error != null)
setErrorPage(error);
String dispatch = configuration.getParameter(FormAuthenticator.__FORM_DISPATCH);
_dispatch = dispatch == null ? _dispatch : Boolean.parseBoolean(dispatch);
}
@Override
@ -361,10 +365,36 @@ public class FormAuthenticator extends LoginAuthenticator
return sendChallenge(request, response, callback);
}
protected AuthenticationState dispatch(String path, Request request, Response response, Callback callback)
{
try
{
HttpURI.Mutable newUri = HttpURI.build(request.getHttpURI()).path(path);
return new AuthenticationState.ServeAs(newUri)
{
@Override
public Request wrap(Request request)
{
org.eclipse.jetty.security.ServeAs serveAs = Request.as(request, org.eclipse.jetty.security.ServeAs.class);
if (serveAs != null)
return serveAs.serveAs(request, path);
return super.wrap(request);
}
};
}
catch (Throwable t)
{
Response.writeError(request, response, callback, t);
return AuthenticationState.SEND_FAILURE;
}
}
protected AuthenticationState sendError(Request request, Response response, Callback callback)
{
if (_formErrorPage == null)
Response.writeError(request, response, callback, HttpStatus.FORBIDDEN_403);
else if (_dispatch && getErrorPage() != null)
return dispatch(_formErrorPage, request, response, callback);
else
Response.sendRedirect(request, response, callback, encodeURL(URIUtil.addPaths(request.getContext().getContextPath(), _formErrorPage), request), true);
return AuthenticationState.SEND_FAILURE;
@ -372,8 +402,15 @@ public class FormAuthenticator extends LoginAuthenticator
protected AuthenticationState sendChallenge(Request request, Response response, Callback callback)
{
Response.sendRedirect(request, response, callback, encodeURL(URIUtil.addPaths(request.getContext().getContextPath(), _formLoginPage), request), true);
return AuthenticationState.SEND_FAILURE;
if (_dispatch)
{
return dispatch(_formLoginPage, request, response, callback);
}
else
{
Response.sendRedirect(request, response, callback, encodeURL(URIUtil.addPaths(request.getContext().getContextPath(), _formLoginPage), request), true);
return AuthenticationState.SEND_FAILURE;
}
}
public boolean isJSecurityCheck(String uri)

View File

@ -768,7 +768,7 @@ public interface Request extends Attributes, Content.Source
}
@SuppressWarnings("unchecked")
static <T extends Request.Wrapper> T as(Request request, Class<T> type)
static <T> T as(Request request, Class<T> type)
{
while (request instanceof Request.Wrapper wrapper)
{

View File

@ -32,6 +32,7 @@ import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.UriCompliance;
import org.eclipse.jetty.http.pathmap.MatchedResource;
import org.eclipse.jetty.security.ServeAs;
import org.eclipse.jetty.server.FormFields;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
@ -53,7 +54,7 @@ import org.eclipse.jetty.util.Fields;
* This class is single use only.
* </p>
*/
public class ServletContextRequest extends ContextRequest implements ServletContextHandler.ServletRequestInfo
public class ServletContextRequest extends ContextRequest implements ServletContextHandler.ServletRequestInfo, ServeAs
{
public static final String MULTIPART_CONFIG_ELEMENT = "org.eclipse.jetty.multipartConfig";
static final int INPUT_NONE = 0;
@ -117,6 +118,7 @@ public class ServletContextRequest extends ContextRequest implements ServletCont
addIdleTimeoutListener(_servletChannel.getServletRequestState()::onIdleTimeout);
}
@Override
public Request serveAs(Request request, String path)
{
MatchedResource<ServletHandler.MappedServlet> matchedResource = getServletContextHandler().getServletHandler().getMatchedServlet(path);

View File

@ -13,41 +13,8 @@
package org.eclipse.jetty.ee10.servlet.security;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;
import org.eclipse.jetty.ee10.servlet.ServletContextRequest;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.security.AuthenticationState;
import org.eclipse.jetty.security.authentication.SessionAuthentication;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.Callback;
/**
* FORM Authenticator.
*
* <p>This authenticator implements form authentication will use dispatchers to
* the login page if the {@link #__FORM_DISPATCH} init parameter is set to true.
* Otherwise it will redirect.</p>
*
* <p>The form authenticator redirects unauthenticated requests to a log page
* which should use a form to gather username/password from the user and send them
* to the /j_security_check URI within the context. FormAuthentication uses
* {@link SessionAuthentication} to wrap Authentication results so that they
* are associated with the session.</p>
*/
public class FormAuthenticator extends org.eclipse.jetty.security.authentication.FormAuthenticator
{
public static final String __FORM_DISPATCH = "org.eclipse.jetty.security.dispatch";
private boolean _dispatch;
public FormAuthenticator()
{
}
@ -55,144 +22,5 @@ public class FormAuthenticator extends org.eclipse.jetty.security.authentication
public FormAuthenticator(String login, String error, boolean dispatch)
{
super(login, error, dispatch);
_dispatch = dispatch;
}
@Override
public void setConfiguration(Configuration configuration)
{
super.setConfiguration(configuration);
String dispatch = configuration.getParameter(FormAuthenticator.__FORM_DISPATCH);
_dispatch = dispatch == null ? _dispatch : Boolean.parseBoolean(dispatch);
}
@Override
protected AuthenticationState sendError(Request request, Response response, Callback callback)
{
if (_dispatch && getErrorPage() != null)
return dispatch(getErrorPage(), request, response, callback);
else
return super.sendError(request, response, callback);
}
@Override
protected AuthenticationState sendChallenge(Request request, Response response, Callback callback)
{
if (_dispatch)
return dispatch(getLoginPage(), request, response, callback);
else
return super.sendChallenge(request, response, callback);
}
private AuthenticationState dispatch(String path, Request request, Response response, Callback callback)
{
try
{
// Currently we do not attempt to wrap the request
return new AuthenticationState.ServeAs(request.getHttpURI())
{
@Override
public Request wrap(Request request)
{
ServletContextRequest servletContextRequest = Request.as(request, ServletContextRequest.class);
if (servletContextRequest == null)
return super.wrap(request);
return servletContextRequest.serveAs(request, path);
}
};
}
catch (Throwable t)
{
Response.writeError(request, response, callback, t);
return AuthenticationState.SEND_FAILURE;
}
}
protected static class FormRequest extends HttpServletRequestWrapper
{
public FormRequest(HttpServletRequest request)
{
super(request);
}
@Override
public long getDateHeader(String name)
{
if (name.toLowerCase(Locale.ENGLISH).startsWith("if-"))
return -1;
return super.getDateHeader(name);
}
@Override
public String getHeader(String name)
{
if (name.toLowerCase(Locale.ENGLISH).startsWith("if-"))
return null;
return super.getHeader(name);
}
@Override
public Enumeration<String> getHeaderNames()
{
return Collections.enumeration(Collections.list(super.getHeaderNames()));
}
@Override
public Enumeration<String> getHeaders(String name)
{
if (name.toLowerCase(Locale.ENGLISH).startsWith("if-"))
return Collections.<String>enumeration(Collections.<String>emptyList());
return super.getHeaders(name);
}
}
protected static class FormResponse extends HttpServletResponseWrapper
{
public FormResponse(HttpServletResponse response)
{
super(response);
}
@Override
public void addDateHeader(String name, long date)
{
if (notIgnored(name))
super.addDateHeader(name, date);
}
@Override
public void addHeader(String name, String value)
{
if (notIgnored(name))
super.addHeader(name, value);
}
@Override
public void setDateHeader(String name, long date)
{
if (notIgnored(name))
super.setDateHeader(name, date);
}
@Override
public void setHeader(String name, String value)
{
if (notIgnored(name))
super.setHeader(name, value);
}
private boolean notIgnored(String name)
{
if (HttpHeader.CACHE_CONTROL.is(name) ||
HttpHeader.PRAGMA.is(name) ||
HttpHeader.ETAG.is(name) ||
HttpHeader.EXPIRES.is(name) ||
HttpHeader.LAST_MODIFIED.is(name) ||
HttpHeader.AGE.is(name))
return false;
return true;
}
}
}