org.eclipse.jetty.security.AuthenticationState Maven / Gradle / Ivy
//
// ========================================================================
// 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 java.security.Principal;
import org.eclipse.jetty.http.HttpException;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.security.IdentityService.RunAsToken;
import org.eclipse.jetty.security.authentication.LoginAuthenticator;
import org.eclipse.jetty.security.internal.DeferredAuthenticationState;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.Callback;
/**
* The Authentication state of a request.
*
* The Authentication state can be one of several sub-types that
* reflects where the request is in the many different authentication
* cycles. Authentication might not yet be checked or it might be checked
* and failed, checked and deferred or succeeded.
*/
public interface AuthenticationState extends Request.AuthenticationState
{
/**
* Get the authentication state of a request
* @param request The request to query
* @return The authentication state of the request or null if none.
*/
static AuthenticationState getAuthenticationState(Request request)
{
Request.AuthenticationState state = Request.getAuthenticationState(request);
return state instanceof AuthenticationState authenticationState ? authenticationState : null;
}
/**
* Set the authentication state of a request.
* @param request The request to update
* @param authenticationState the state to set on the request.
*/
static void setAuthenticationState(Request request, AuthenticationState authenticationState)
{
Request.setAuthenticationState(request, authenticationState);
}
/**
* Get the {@link UserPrincipal} of an authenticated request. If the {@link AuthenticationState}
* is {@link Deferred}, then an attempt to validate is made and the {@link AuthenticationState}
* of the request is updated.
* @see #authenticate(Request)
* @param request The request to query
* @return The {@link UserPrincipal} of any {@link Succeeded} authentication state, potential
* after validating a {@link Deferred} state.
*/
static Principal getUserPrincipal(Request request)
{
Succeeded succeeded = authenticate(request);
if (succeeded == null)
return null;
return succeeded.getUserIdentity().getUserPrincipal();
}
/**
* Get successful authentication for a request. If the {@link AuthenticationState}
* is {@link Deferred}, then an attempt to authenticate, but without sending a challenge.
* @see Deferred#authenticate(Request)
* @see #authenticate(Request, Response, Callback) if an authentication challenge should be sent.
* @param request The request to query.
* @return A {@link Succeeded} authentiction or null
*/
static Succeeded authenticate(Request request)
{
AuthenticationState authenticationState = getAuthenticationState(request);
// resolve any Deferred authentication
if (authenticationState instanceof Deferred deferred)
authenticationState = deferred.authenticate(request);
//if authenticated, return the state
if (authenticationState instanceof Succeeded succeeded)
return succeeded;
// else null
return null;
}
/**
* Get successful authentication for a request. If the {@link AuthenticationState}
* is {@link Deferred}, then an attempt to authenticate, possibly sending a challenge.
* If authentication is not successful, then a {@link HttpStatus#FORBIDDEN_403} response is sent.
* @see Deferred#authenticate(Request, Response, Callback)
* @see #authenticate(Request) if an authentication challenge should not be sent.
* @param request The request to query.
* @param response The response to use for a challenge or error
* @param callback The collback to complete if a challenge or error is sent.
* @return A {@link Succeeded} authentication or null. If null is returned, then the callback
* will be completed.
*/
static Succeeded authenticate(Request request, Response response, Callback callback)
{
AuthenticationState authenticationState = getAuthenticationState(request);
// resolve any Deferred authentication
if (authenticationState instanceof Deferred deferred)
{
authenticationState = deferred.authenticate(request, response, callback);
if (authenticationState instanceof AuthenticationState.ResponseSent)
return null;
}
// if already authenticated, return the state
if (authenticationState instanceof Succeeded succeeded)
return succeeded;
Response.writeError(request, response, callback, HttpStatus.FORBIDDEN_403);
return null;
}
/**
* Attempt to login a request using the passed credentials.
* The current {@link AuthenticationState} must be {@link Deferred}.
* @see Deferred#login(String, String, Request, Response)
* @param request The request to query.
* @return A {@link Succeeded} authentiction or null
*/
static Succeeded login(String username, String password, Request request, Response response)
{
AuthenticationState authenticationState = getAuthenticationState(request);
// if already authenticated, throw
if (authenticationState instanceof Succeeded)
throw new HttpException.RuntimeException(HttpStatus.INTERNAL_SERVER_ERROR_500, "Already authenticated");
// Use Deferred authentication to login
if (authenticationState instanceof Deferred deferred)
{
Succeeded undeferred = deferred.login(username, password, request, response);
if (undeferred != null)
{
setAuthenticationState(request, undeferred);
return undeferred;
}
}
return null;
}
static boolean logout(Request request, Response response)
{
AuthenticationState authenticationState = getAuthenticationState(request);
//if already authenticated, return true
if (authenticationState instanceof Succeeded succeededAuthentication)
{
succeededAuthentication.logout(request, response);
return true;
}
if (authenticationState instanceof Deferred deferred)
{
deferred.logout(request, response);
return true;
}
return false;
}
/**
* A successful Authentication with User information.
*/
interface Succeeded extends AuthenticationState
{
/**
* @return The method used to authenticate the user.
*/
String getAuthenticationType();
/**
* @return The {@link UserIdentity} of the authenticated user.
*/
UserIdentity getUserIdentity();
@Override
default Principal getUserPrincipal()
{
UserIdentity user = getUserIdentity();
if (user != null)
return user.getUserPrincipal();
return null;
}
/**
* @param role The role to check.
* @return True if the user is in the passed role
*/
boolean isUserInRole(String role);
/**
* Remove any user information that may be present in the request
* such that a call to getUserPrincipal/getRemoteUser will return null.
*
* @param request the request
* @param response the response
*/
void logout(Request request, Response response);
}
/**
* Authentication Response sent state.
* Responses are sent by authenticators either to issue an
* authentication challenge or on successful authentication in
* order to redirect the user to the original URL.
*/
interface ResponseSent extends AuthenticationState
{
}
/**
* Authentication challenge sent.
*
* This convenience instance is for when an authentication challenge has been sent.
*/
AuthenticationState CHALLENGE = new ResponseSent()
{
@Override
public String toString()
{
return "CHALLENGE";
}
};
/**
* Authentication failure sent.
*
* This convenience instance is for when an authentication failure has been sent.
*/
AuthenticationState SEND_FAILURE = new ResponseSent()
{
@Override
public String toString()
{
return "FAILURE";
}
};
/**
* Authentication success sent.
*
* This convenience instance is for when an authentication success has been sent.
*/
AuthenticationState SEND_SUCCESS = new ResponseSent()
{
@Override
public String toString()
{
return "SEND_SUCCESS";
}
};
/**
* The {@link SecurityHandler} will use this to wrap the {@link Request}.
* And then will return a {@link Deferred} authentication to bypass security constraints.
*/
class ServeAs implements AuthenticationState
{
private final HttpURI _uri;
public ServeAs(HttpURI uri)
{
_uri = uri;
}
public Request wrap(Request request)
{
return Request.serveAs(request, _uri);
}
}
static Deferred defer(LoginAuthenticator loginAuthenticator)
{
return new DeferredAuthenticationState(loginAuthenticator);
}
/**
* Authentication is Deferred, either so that credentials can later be passed
* with {@link #login(String, String, Request, Response)}; or that existing
* credentials on the request may be validated with {@link #authenticate(Request)};
* or an authentication dialog can be advanced with {@link #authenticate(Request, Response, Callback)}.
*/
interface Deferred extends AuthenticationState
{
/**
* @param response the response
* @return true if this response is from a deferred call to {@link #authenticate(Request)}
*/
static boolean isDeferred(Response response)
{
return response instanceof DeferredResponse;
}
/**
* Authenticate the request using any credentials already associated with the request.
* No challenge can be sent. If the login is successful, then the
* {@link IdentityService#associate(UserIdentity, RunAsToken)} method is used and the returned
* {@link org.eclipse.jetty.security.IdentityService.Association} is made available via
* {@link #getAssociation()}.
* @see #getAssociation()
* @param request The request to authenticate
* @return A {@link Succeeded} authentication or null
*/
Succeeded authenticate(Request request);
/**
* Authenticate the request using any credentials already associated with the request or
* challenging if necessary. If the login is successful, then the
* {@link IdentityService#associate(UserIdentity, RunAsToken)} method is used and the returned
* {@link org.eclipse.jetty.security.IdentityService.Association} is made available via
* {@link #getAssociation()}.
* @see #getAssociation()
* @param request The request to authenticate
* @param response The response to use for a challenge.
* @param callback The callback to complete if a challenge is sent
* @return The next {@link AuthenticationState}, if it is {@link ResponseSent}, then the
* callback will be completed.
*/
AuthenticationState authenticate(Request request, Response response, Callback callback);
/**
* Authenticate the request with the passed credentials
* @param username The username to validate
* @param password The credential to validate
* @param request The request to authenticate
* @param response The response, which may be updated if the session ID is changed.
* @return A {@link Succeeded} authentication or null
*/
Succeeded login(String username, Object password, Request request, Response response);
/**
* Logout the authenticated user.
* @param request The authenticated request
* @param response The associated response, which may be updated to clear a session ID.
*/
void logout(Request request, Response response);
/**
* @return Any {@link org.eclipse.jetty.security.IdentityService.Association} created during
* deferred authentication.
* @see #authenticate(Request, Response, Callback)
* @see #authenticate(Request)
*/
IdentityService.Association getAssociation();
/**
* A tag interface used to identify a {@link Response} that might be passed to
* {@link Authenticator#validateRequest(Request, Response, Callback)} while
* doing deferred authentication when a challenge cannot be sent.
* @see #authenticate(Request)
*/
interface DeferredResponse extends Response
{
}
}
}