CollectClientConnectionsFilter.java revision 1df4f51adf614210ca4a9b9728327090ec5ea264
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2013-2015 ForgeRock AS
*/
/** Servlet {@link Filter} that collects information about client connections. */
{
/** HTTP Header sent by the client with HTTP basic authentication. */
/** The tracer object for the debug logger. */
/** The connection handler that created this servlet filter. */
private final HTTPConnectionHandler connectionHandler;
/**
* Configures how to perform the search for the username prior to
* authentication.
*/
private final HTTPAuthenticationConfig authConfig;
/**
* Constructs a new instance of this class.
*
* @param connectionHandler
* the connection handler that accepted this connection
* @param authenticationConfig
* configures how to perform the search for the username prior to
* authentication
*/
public CollectClientConnectionsFilter(
{
this.connectionHandler = connectionHandler;
this.authConfig = authenticationConfig;
}
public Promise<Response, NeverThrowsException> filter(Context context, Request request, Handler next)
{
final HTTPClientConnection clientConnection = new HTTPClientConnection(this.connectionHandler, context, request);
if (connectionHandler.keepStats())
{
}
try
{
{
return resourceExceptionToPromise(ResourceException.getException(ResourceException.INTERNAL_ERROR));
}
// Logs the connect after all the possible disconnect reasons have been checked.
{
return Adapters.newRootConnection()
.thenAsync(doBindAfterSearch(context, request, next, userName, password, clientConnection, connection),
}
else if (this.connectionHandler.acceptUnauthenticatedRequests())
{
// Use unauthenticated user
}
else
{
return authenticationFailure(clientConnection);
}
}
catch (Exception e)
{
return asErrorResponse(e, clientConnection);
}
}
private boolean canProcessRequest(final HTTPClientConnection connection) throws UnknownHostException
{
// Check to see if the core server rejected the connection (e.g. already too many connections established).
{
return false;
}
// Check to see if the client is on the denied list. If so, then reject it immediately.
if (!deniedClients.isEmpty()
{
return false;
}
// Check to see if there is an allowed list and if there is whether the client is on that list.
// If not, then reject the connection.
if (!allowedClients.isEmpty()
{
ERR_CONNHANDLER_DISALLOWED_CLIENT.get(connection.getClientHostPort(), connection.getServerHostPort()));
return false;
}
return true;
}
{
// Use configured rights to find the user DN
return Requests.newSearchRequest(
}
final Context context, final Request request, final Handler next, final String userName, final String password,
{
{
{
{
return authenticationFailure(clientConnection);
}
final BindRequest bindRequest =
}
};
}
{
{
{
try
{
}
catch (Exception e)
{
return asErrorResponse(e, clientConnection);
}
}
};
}
final Context context, final Request request, final Handler next, final Connection connection) throws Exception
{
// Send the request further down the filter chain or pass to servlet
}
private AsyncFunction<? super LdapException, Response, NeverThrowsException> returnErrorAfterFailedSearch(
{
{
{
{
// Avoid information leak:
// do not hint to the user that it is the username that is invalid
return authenticationFailure(clientConnection);
}
else
{
}
}
};
}
{
{
{
return asErrorResponse(e, clientConnection);
}
};
}
private Promise<Response, NeverThrowsException> authenticationFailure(final HTTPClientConnection clientConnection)
{
return asErrorResponse(ResourceException.getException(401, "Invalid Credentials"), clientConnection,
DisconnectReason.INVALID_CREDENTIALS, false);
}
{
}
{
try
{
if (logError)
{
clientConnection.getClientHostPort(), clientConnection.getServerHostPort(), getExceptionMessage(ex));
}
}
finally
{
}
return resourceExceptionToPromise(ex);
}
{
{
}
}
/**
* Extracts the username and password from the request using one of the
* enabled authentication mechanism: HTTP Basic authentication or HTTP Custom
* headers. If no username and password can be obtained, then send back an
* HTTP basic authentication challenge if HTTP basic authentication is
* enabled.
*
* @param request
* the request where to extract the username and password from
* null otherwise
* @throws ResourceException
* if any error occur
*/
{
// TODO Use session to reduce hits with search + bind?
// Use proxied authorization control for session.
// Security: How can we remove the password held in the request headers?
{
{
}
}
{
if (httpBasicAuthHeader != null)
{
if (userCredentials != null)
{
return userCredentials;
}
}
}
return null;
}
/**
* Parses username and password from the authentication header used in HTTP
* basic authentication.
*
* @param authHeader
* the authentication header obtained from the request
* @return an array containing the username at index 0 and the password at
* index 1, or null if the header cannot be parsed successfully
* @throws ResourceException
* if the base64 password cannot be decoded
*/
{
if (authHeader != null
{
// We received authentication info
// Example received header:
// "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
try
{
// Example usage of base64:
// Base64("Aladdin:open sesame") = "QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
{
return split;
}
}
catch (ParseException e)
{
throw Rest2LDAP.asResourceException(e);
}
}
return null;
}
public void close() throws IOException {}
}