ml.alternet.security.web.tomcat.AltProtocolHandler Maven / Gradle / Ivy
package ml.alternet.security.web.tomcat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import ml.alternet.misc.Invoker;
import ml.alternet.misc.Thrower;
import ml.alternet.security.PasswordManager;
import ml.alternet.security.PasswordManagerFactory;
import ml.alternet.security.web.server.DebugLevel;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.connector.CoyoteAdapter;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.coyote.Adapter;
import org.apache.coyote.ProtocolHandler;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.IntrospectionUtils;
/**
* A protocol handler wrapper that enhance a Tomcat server for
* safe-processing of passwords.
*
* This protocol handler wrapper allow to capture the
* passwords that would be sent by the client BEFORE they are
* processed as Strings ; after capturing, the raw data are replaced
* with '*'. The captured passwords are wrapped in an encrypted form
* and are available to the user by the classes of the package
* ml.alternet.security.web.
*
* The configuration of the wrapped protocol handler remains the
* same, except for the "protocol" attribute that have to be set
* to this class name. Additional attributes are "tomcatProtocol"
* (with the replaced value of the "protocol" attribute),
* "passwordManager" (by default, it is the strong password
* manager), and "allowUnsecureTrace" (false by default).
*
* <Connector port="8080"
* tomcatProtocol="HTTP/1.1"
* protocol="ml.alternet.security.web.tomcat.EnhancedProtocolHandler"
* passwordManager="ml.alternet.security.impl.StrongPasswordManager"
* allowUnsecureTrace="false"
* connectionTimeout="20000"
* redirectPort="8443" />
*
*
* @author Philippe Poulard
*/
public class AltProtocolHandler implements ProtocolHandler, DebugLevel.Debuggable {
static final Log LOG = LogFactory.getLog(AltProtocolHandler.class);
static ThreadLocal request = new ThreadLocal<>();
private ProtocolHandler ph; // ph 7 is neutral - just joking
private DebugLevel debugLevel = new DebugLevel();
private PasswordManager pm;
private Map props = new HashMap<>(); // pending props until ph is set
@Override
public DebugLevel getDebugLevel() {
return this.debugLevel;
}
/**
* Set a password manager from its class name.
*
* @param passwordManager The name of the password manager.
*
* @see PasswordManager
*/
public void setPasswordManager(String passwordManager) {
try {
this.pm = (PasswordManager) Class.forName(passwordManager).newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
Thrower.doThrow(e);
}
}
/**
* Get the password manager.
*
* @return By default, the password manager use encryption.
*/
public PasswordManager getPasswordManager() {
if (this.pm == null) {
this.pm = PasswordManagerFactory.getStrongPasswordManager();
}
return this.pm;
}
/**
* Allow unsecure trace (false
by default).
* DO NOT ALLOW UNSECURE TRACE IN PRODUCTION ENVIRONMENT.
*
* @param value true
to allow unsecure trace,
* false
to disallow unsecure trace.
*/
public void setAllowUnsecureTrace(String value) {
if ("true".equalsIgnoreCase(value)) {
this.debugLevel.allowUnsecureTrace();
} else {
this.debugLevel.disallowUnsecureTrace();
}
}
private void setProtocolHandler(String phClass) {
try {
ph = (ProtocolHandler) Class.forName(phClass).newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
Thrower.doThrow(e);
}
}
/**
* Set a configured property.
*
* @param name The name of the property.
* @param value The value of the property.
*
* @return true
the more often.
*/
public boolean setProperty(String name, String value) {
if ("tomcatProtocol".equals(name)) {
// see Connector.setProtocol(String)
if (AprLifecycleListener.isAprAvailable()) {
if ("HTTP/1.1".equals(value)) {
setProtocolHandler("org.apache.coyote.http11.Http11AprProtocol");
} else if ("AJP/1.3".equals(value)) {
setProtocolHandler
("org.apache.coyote.ajp.AjpAprProtocol");
} else if (value != null) {
setProtocolHandler(value);
} else {
setProtocolHandler
("org.apache.coyote.http11.Http11AprProtocol");
}
} else {
if ("HTTP/1.1".equals(value)) {
setProtocolHandler
("org.apache.coyote.http11.Http11NioProtocol");
} else if ("AJP/1.3".equals(value)) {
setProtocolHandler
("org.apache.coyote.ajp.AjpNioProtocol");
} else if (value != null) {
setProtocolHandler(value);
}
}
} else if (ph == null) {
props.put(name, value);
return true;
} else {
return IntrospectionUtils.setProperty(ph, name, value);
}
if (ph != null && props != null) {
props.forEach((n, v) -> IntrospectionUtils.setProperty(ph, n, v));
props = null;
}
return true;
}
@Override
public void setAdapter(Adapter adapter) {
try {
CoyoteAdapter ca = (CoyoteAdapter) adapter;
Connector connector = Invoker.get(ca, "connector");
adapter = new AltCoyoteAdapter(connector, getDebugLevel(), this.pm);
ph.setAdapter(adapter);
} catch (NoSuchFieldException | IllegalAccessException e) {
Thrower.doThrow(e);
}
}
// delegate methods
@Override
public Adapter getAdapter() {
return ph.getAdapter();
}
@Override
public Executor getExecutor() {
return ph.getExecutor();
}
@Override
public void init() throws Exception {
ph.init();
}
@Override
public void start() throws Exception {
ph.start();
}
@Override
public void pause() throws Exception {
ph.pause();
}
@Override
public void resume() throws Exception {
ph.resume();
}
@Override
public void stop() throws Exception {
ph.stop();
}
@Override
public void destroy() throws Exception {
ph.destroy();
}
@Override
public boolean isAprRequired() {
return ph.isAprRequired();
}
@Override
public boolean isCometSupported() {
return ph.isCometSupported();
}
@Override
public boolean isCometTimeoutSupported() {
return ph.isCometTimeoutSupported();
}
@Override
public boolean isSendfileSupported() {
return ph.isSendfileSupported();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy