/*
* eID Applet Project.
* Copyright (C) 2008-2012 FedICT.
* Copyright (C) 2014 e-Contract.be BVBA.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version
* 3.0 as published by the Free Software Foundation.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, see
* http://www.gnu.org/licenses/.
*/
package be.fedict.eid.applet.service;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import be.fedict.eid.applet.service.impl.ServiceLocator;
import be.fedict.eid.applet.service.impl.handler.AuthSignResponseMessageHandler;
import be.fedict.eid.applet.service.impl.handler.AuthenticationDataMessageHandler;
import be.fedict.eid.applet.service.impl.handler.ClientEnvironmentMessageHandler;
import be.fedict.eid.applet.service.impl.handler.ContinueInsecureMessageHandler;
import be.fedict.eid.applet.service.impl.handler.FileDigestsDataMessageHandler;
import be.fedict.eid.applet.service.impl.handler.HandlesMessage;
import be.fedict.eid.applet.service.impl.handler.HelloMessageHandler;
import be.fedict.eid.applet.service.impl.handler.IdentityDataMessageHandler;
import be.fedict.eid.applet.service.impl.handler.InitParam;
import be.fedict.eid.applet.service.impl.handler.MessageHandler;
import be.fedict.eid.applet.service.impl.handler.SignCertificatesDataMessageHandler;
import be.fedict.eid.applet.service.impl.handler.SignatureDataMessageHandler;
import be.fedict.eid.applet.shared.AbstractProtocolMessage;
/**
* The eID applet service Servlet. This servlet should be used by web
* applications for secure communication between the Java EE servlet container
* and the eID applet. This servlet will push attributes within the HTTP session
* after a successful identification of the browser using via the eID applet.
*
* <p>
* The attribute that is pushed within the HTTP session per default is:
* <code>eid.identity</code> of type {@link Identity}.
* </p>
*
* <p>
* The address on the eID card can also be requested by setting the optional
* <code>IncludeAddress</code> <code>init-param</code> to <code>true</code>. The
* corresponding HTTP session attribute is called <code>eid.address</code> and
* is of type {@link Address}.
* </p>
*
* <p>
* The photo on the eID card can also be requested by setting the optional
* <code>IncludePhoto</code> <code>init-param</code> to <code>true</code>. The
* corresponding HTTP session attribute is called <code>eid.photo</code>.
* </p>
*
* <p>
* More information on all available init-param configuration parameters is
* available in the eID Applet developer's guide.
* </p>
*
* @author Frank Cornelis
*/
public class AppletServiceServlet extends AbstractAppletServiceServlet {
private static final long serialVersionUID = 1L;
private static final Log LOG = LogFactory.getLog(AppletServiceServlet.class);
private static final Class<? extends MessageHandler<?>>[] MESSAGE_HANDLER_CLASSES = new Class[] {
IdentityDataMessageHandler.class, HelloMessageHandler.class, ClientEnvironmentMessageHandler.class,
AuthenticationDataMessageHandler.class, SignatureDataMessageHandler.class,
FileDigestsDataMessageHandler.class, ContinueInsecureMessageHandler.class,
SignCertificatesDataMessageHandler.class, AuthSignResponseMessageHandler.class };
private Map<Class<?>, MessageHandler<?>> messageHandlers;
public AppletServiceServlet() {
super();
LOG.debug("constructor");
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
LOG.debug("init");
this.messageHandlers = new HashMap<Class<?>, MessageHandler<?>>();
for (Class<? extends MessageHandler<?>> messageHandlerClass : MESSAGE_HANDLER_CLASSES) {
HandlesMessage handlesMessageAnnotation = messageHandlerClass.getAnnotation(HandlesMessage.class);
if (null == handlesMessageAnnotation) {
throw new ServletException("missing meta-data on message handler: " + messageHandlerClass.getName());
}
Class<? extends AbstractProtocolMessage> protocolMessageClass = handlesMessageAnnotation.value();
MessageHandler<?> messageHandler;
try {
messageHandler = messageHandlerClass.newInstance();
} catch (Exception e) {
throw new ServletException("cannot create message handler instance");
}
this.messageHandlers.put(protocolMessageClass, messageHandler);
}
Collection<MessageHandler<?>> messageHandlers = this.messageHandlers.values();
for (MessageHandler<?> messageHandler : messageHandlers) {
try {
injectInitParams(config, messageHandler);
} catch (Exception e) {
throw new ServletException("error injecting init-param into message handler field: " + e.getMessage(),
e);
}
messageHandler.init(config);
}
}
public static void injectInitParams(ServletConfig config, MessageHandler<?> messageHandler)
throws ServletException, IllegalArgumentException, IllegalAccessException {
Class<?> messageHandlerClass = messageHandler.getClass();
Field[] fields = messageHandlerClass.getDeclaredFields();
for (Field field : fields) {
InitParam initParamAnnotation = field.getAnnotation(InitParam.class);
if (null == initParamAnnotation) {
continue;
}
String initParamName = initParamAnnotation.value();
Class<?> fieldType = field.getType();
field.setAccessible(true);
if (ServiceLocator.class.equals(fieldType)) {
/*
* We always inject a service locator.
*/
ServiceLocator<Object> fieldValue = new ServiceLocator<Object>(initParamName, config);
field.set(messageHandler, fieldValue);
continue;
}
String initParamValue = config.getInitParameter(initParamName);
if (initParamAnnotation.required() && null == initParamValue) {
throw new ServletException("missing required init-param: " + initParamName + " for message handler:"
+ messageHandlerClass.getName());
}
if (null == initParamValue) {
continue;
}
if (Boolean.TYPE.equals(fieldType)) {
LOG.debug("injecting boolean: " + initParamValue);
Boolean fieldValue = Boolean.parseBoolean(initParamValue);
field.set(messageHandler, fieldValue);
continue;
}
if (String.class.equals(fieldType)) {
field.set(messageHandler, initParamValue);
continue;
}
if (InetAddress.class.equals(fieldType)) {
InetAddress inetAddress;
try {
inetAddress = InetAddress.getByName(initParamValue);
} catch (UnknownHostException e) {
throw new ServletException("unknown host: " + initParamValue);
}
field.set(messageHandler, inetAddress);
continue;
}
if (Long.class.equals(fieldType)) {
Long fieldValue = Long.parseLong(initParamValue);
field.set(messageHandler, fieldValue);
continue;
}
throw new ServletException("unsupported init-param field type: " + fieldType.getName());
}
}
@Override
protected <T> MessageHandler<T> getMessageHandler(Class<T> messageClass) {
return (MessageHandler<T>) this.messageHandlers.get(messageClass);
}
}