Commit 9fc391a3 authored by michael.simon's avatar michael.simon
Browse files

Add basic redirect functionality

parent ce8545fc
/*******************************************************************************
* Copyright (c) 2014 Michael Simon.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Michael Simon - initial
******************************************************************************/
package edu.kit.scc.webreg.dao;
import edu.kit.scc.webreg.entity.SamlAuthnRequestEntity;
public interface SamlAuthnRequestDao extends BaseDao<SamlAuthnRequestEntity, Long> {
}
/*******************************************************************************
* Copyright (c) 2014 Michael Simon.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Michael Simon - initial
******************************************************************************/
package edu.kit.scc.webreg.dao.jpa;
import java.io.Serializable;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import edu.kit.scc.webreg.dao.SamlAuthnRequestDao;
import edu.kit.scc.webreg.entity.SamlAuthnRequestEntity;
@Named
@ApplicationScoped
public class JpaSamlAuthnRequestDao extends JpaBaseDao<SamlAuthnRequestEntity, Long> implements SamlAuthnRequestDao, Serializable {
private static final long serialVersionUID = 1L;
@Override
public Class<SamlAuthnRequestEntity> getEntityClass() {
return SamlAuthnRequestEntity.class;
}
}
package edu.kit.scc.webreg.entity;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Lob;
import javax.persistence.Table;
import org.hibernate.annotations.Type;
@Entity(name = "SamlAuthnRequestEntity")
@Table(name = "samlauthnrequest")
public class SamlAuthnRequestEntity extends AbstractBaseEntity {
private static final long serialVersionUID = 1L;
@Column(name = "authnrequest_data")
@Basic(fetch = FetchType.LAZY)
@Lob
@Type(type = "org.hibernate.type.TextType")
private String authnrequestData;
@Column(name = "valid_until")
private Date validUntil;
public Date getValidUntil() {
return validUntil;
}
public void setValidUntil(Date validUntil) {
this.validUntil = validUntil;
}
public String getAuthnrequestData() {
return authnrequestData;
}
public void setAuthnrequestData(String authnrequestData) {
this.authnrequestData = authnrequestData;
}
}
package edu.kit.scc.webreg.entity;
import java.util.Date;
import javax.annotation.Generated;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;
@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(SamlAssertionEntity.class)
public abstract class SamlAuthnRequestEntity_ extends edu.kit.scc.webreg.entity.AbstractBaseEntity_ {
public static volatile SingularAttribute<SamlMetadataEntity, String> authnrequestData;
public static volatile SingularAttribute<SamlMetadataEntity, Date> validUntil;
}
......@@ -18,8 +18,10 @@ import net.shibboleth.utilities.java.support.component.ComponentInitializationEx
import org.opensaml.messaging.decoder.MessageDecodingException;
import org.opensaml.saml.common.SAMLObject;
import org.opensaml.saml.saml2.binding.decoding.impl.HTTPPostDecoder;
import org.opensaml.saml.saml2.binding.decoding.impl.HTTPRedirectDeflateDecoder;
import org.opensaml.saml.saml2.binding.decoding.impl.HTTPSOAP11Decoder;
import org.opensaml.saml.saml2.core.AttributeQuery;
import org.opensaml.saml.saml2.core.AuthnRequest;
import org.opensaml.saml.saml2.core.Response;
import edu.kit.scc.webreg.exc.SamlAuthenticationException;
......@@ -59,4 +61,19 @@ public class Saml2DecoderService {
throw new SamlAuthenticationException("Not a valid SAML2 Attribute Query");
}
public AuthnRequest decodeRedirectMessage(HttpServletRequest request)
throws MessageDecodingException, SecurityException, SamlAuthenticationException, ComponentInitializationException {
HTTPRedirectDeflateDecoder decoder = new HTTPRedirectDeflateDecoder();
decoder.setHttpServletRequest(request);
decoder.initialize();
decoder.decode();
SAMLObject obj = decoder.getMessageContext().getMessage();
if (obj instanceof AuthnRequest)
return (AuthnRequest) obj;
else
throw new SamlAuthenticationException("Not a valid SAML2 Authnrequest: " + obj.getClass());
}
}
package edu.kit.scc.webreg.service.saml;
import org.opensaml.saml.saml2.core.AuthnRequest;
public interface SamlIdpService {
long registerAuthnRequest(AuthnRequest authnRequest);
AuthnRequest resumeAuthnRequest(Long authnRequestId);
}
package edu.kit.scc.webreg.service.saml;
import java.util.Date;
import javax.ejb.Stateless;
import javax.inject.Inject;
import org.opensaml.saml.saml2.core.AuthnRequest;
import edu.kit.scc.webreg.dao.SamlAuthnRequestDao;
import edu.kit.scc.webreg.entity.SamlAuthnRequestEntity;
@Stateless
public class SamlIdpServiceImpl implements SamlIdpService {
@Inject
private SamlAuthnRequestDao samlAuthnRequestDao;
@Inject
private SamlHelper samlHelper;
@Override
public long registerAuthnRequest(AuthnRequest authnRequest) {
SamlAuthnRequestEntity authnRequestEntity = samlAuthnRequestDao.createNew();
authnRequestEntity.setValidUntil(new Date(System.currentTimeMillis() + 30L * 60L * 1000L));
authnRequestEntity.setAuthnrequestData(samlHelper.prettyPrint(authnRequest));
authnRequestEntity = samlAuthnRequestDao.persist(authnRequestEntity);
return authnRequestEntity.getId();
}
@Override
public AuthnRequest resumeAuthnRequest(Long authnRequestId) {
SamlAuthnRequestEntity authnRequestEntity = samlAuthnRequestDao.findById(authnRequestId);
AuthnRequest authnRequest = samlHelper.unmarshal(authnRequestEntity.getAuthnrequestData(), AuthnRequest.class);
return authnRequest;
}
}
......@@ -21,6 +21,7 @@ import org.opensaml.saml.common.SAMLVersion;
import org.opensaml.saml.saml2.core.AuthnRequest;
import org.opensaml.saml.saml2.core.Issuer;
import org.opensaml.saml.saml2.core.NameIDPolicy;
import org.opensaml.saml.saml2.core.Response;
@Named("ssoHelper")
@ApplicationScoped
......@@ -52,5 +53,18 @@ public class SsoHelper implements Serializable {
return authnRequest;
}
public Response buildAuthnResponse(AuthnRequest authnRequest, String spEntityId) {
Response response = samlHelper.create(Response.class, Response.DEFAULT_ELEMENT_NAME);
response.setID(samlHelper.getRandomId());
response.setInResponseTo(authnRequest.getID());
response.setVersion(SAMLVersion.VERSION_20);
response.setIssueInstant(new DateTime());
Issuer issuer = samlHelper.create(Issuer.class, Issuer.DEFAULT_ELEMENT_NAME);
issuer.setValue(spEntityId);
response.setIssuer(issuer);
return response;
}
}
......@@ -15,13 +15,9 @@ import java.io.IOException;
import javax.faces.bean.ApplicationScoped;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import org.joda.time.DateTime;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.XMLObjectBuilderFactory;
......@@ -54,9 +50,10 @@ import edu.kit.scc.webreg.service.UserService;
import edu.kit.scc.webreg.service.saml.Saml2DecoderService;
import edu.kit.scc.webreg.service.saml.Saml2ResponseValidationService;
import edu.kit.scc.webreg.service.saml.SamlHelper;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
@ApplicationScoped
public class Saml2AttributeQueryServlet {
public class Saml2AttributeQueryHandler {
@Inject
private Logger logger;
......@@ -79,12 +76,9 @@ public class Saml2AttributeQueryServlet {
@Inject
private ApplicationConfig appConfig;
public void service(ServletRequest servletRequest, ServletResponse servletResponse, SamlAAConfigurationEntity aaConfig)
public void service(HttpServletRequest request, HttpServletResponse response, SamlAAConfigurationEntity aaConfig)
throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
logger.debug("Consuming SAML AttributeQuery");
try {
......
/*******************************************************************************
* Copyright (c) 2014 Michael Simon.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Michael Simon - initial
******************************************************************************/
package edu.kit.scc.webreg.sec;
import java.io.IOException;
import javax.faces.bean.ApplicationScoped;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.opensaml.messaging.decoder.MessageDecodingException;
import org.opensaml.saml.saml2.core.AuthnRequest;
import org.slf4j.Logger;
import edu.kit.scc.webreg.bootstrap.ApplicationConfig;
import edu.kit.scc.webreg.exc.SamlAuthenticationException;
import edu.kit.scc.webreg.service.saml.Saml2DecoderService;
import edu.kit.scc.webreg.service.saml.SamlHelper;
import edu.kit.scc.webreg.service.saml.SamlIdpService;
import edu.kit.scc.webreg.session.SessionManager;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
@ApplicationScoped
public class Saml2IdpRedirectHandler {
@Inject
private Logger logger;
@Inject
private SessionManager session;
@Inject
private SamlIdpService samlIdpService;
@Inject
private SamlHelper samlHelper;
@Inject
private Saml2DecoderService saml2DecoderService;
@Inject
private ApplicationConfig appConfig;
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
AuthnRequest authnRequest;
try {
authnRequest = saml2DecoderService.decodeRedirectMessage(request);
logger.debug(samlHelper.prettyPrint(authnRequest));
} catch (MessageDecodingException | SecurityException | SamlAuthenticationException
| ComponentInitializationException e) {
logger.warn("An exception occured", e);
throw new ServletException(e);
}
if (session == null || session.getIdpId() == null || session.getSpId() == null) {
logger.debug("Client session from {} not established. In order to serve client must login. Sending to login page.",
request.getRemoteAddr());
long id = samlIdpService.registerAuthnRequest(authnRequest);
session.setAuthnRequestId(id);
session.setOriginalRequestPath(request.getRequestURI() + "/response");
response.sendRedirect("/welcome/index.xhtml");
return;
}
long id = samlIdpService.registerAuthnRequest(authnRequest);
session.setAuthnRequestId(id);
response.sendRedirect(request.getRequestURI() + "/response");
}
}
/*******************************************************************************
* Copyright (c) 2014 Michael Simon.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Michael Simon - initial
******************************************************************************/
package edu.kit.scc.webreg.sec;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.faces.bean.ApplicationScoped;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.velocity.app.VelocityEngine;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.messaging.decoder.MessageDecodingException;
import org.opensaml.messaging.encoder.MessageEncodingException;
import org.opensaml.saml.common.SAMLObject;
import org.opensaml.saml.common.messaging.context.SAMLEndpointContext;
import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext;
import org.opensaml.saml.common.xml.SAMLConstants;
import org.opensaml.saml.saml2.binding.encoding.impl.HTTPPostEncoder;
import org.opensaml.saml.saml2.core.AuthnRequest;
import org.opensaml.saml.saml2.core.Response;
import org.opensaml.saml.saml2.metadata.AssertionConsumerService;
import org.slf4j.Logger;
import edu.kit.scc.webreg.bootstrap.ApplicationConfig;
import edu.kit.scc.webreg.drools.KnowledgeSessionService;
import edu.kit.scc.webreg.exc.SamlAuthenticationException;
import edu.kit.scc.webreg.service.SamlIdpMetadataService;
import edu.kit.scc.webreg.service.UserService;
import edu.kit.scc.webreg.service.saml.Saml2AssertionService;
import edu.kit.scc.webreg.service.saml.Saml2DecoderService;
import edu.kit.scc.webreg.service.saml.SamlHelper;
import edu.kit.scc.webreg.service.saml.SamlIdpService;
import edu.kit.scc.webreg.service.saml.SsoHelper;
import edu.kit.scc.webreg.session.SessionManager;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
@ApplicationScoped
public class Saml2IdpRedirectResponseHandler {
@Inject
private Logger logger;
@Inject
private SessionManager session;
@Inject
private SamlIdpService samlIdpService;
@Inject
private SamlHelper samlHelper;
@Inject
private SsoHelper ssoHelper;
@Inject
private Saml2DecoderService saml2DecoderService;
@Inject
private ApplicationConfig appConfig;
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (session.getAuthnRequestId() == null) {
logger.warn("No AuthnRequestId set in session. Cannot continue");
return;
}
AuthnRequest authnRequest = samlIdpService.resumeAuthnRequest(session.getAuthnRequestId());
logger.debug("Authn request reloaded: {}", samlHelper.prettyPrint(authnRequest));
HTTPPostEncoder postEncoder = new HTTPPostEncoder();
postEncoder.setHttpServletResponse(response);
MessageContext<SAMLObject> messageContext = new MessageContext<SAMLObject>();
Response samlResponse = ssoHelper.buildAuthnResponse(authnRequest, "https://bwidm.scc.kit.edu/saml/idp/metadata");
messageContext.setMessage(samlResponse);
SAMLPeerEntityContext entityContext = new SAMLPeerEntityContext();
//entityContext.setEntityId("http://test");
SAMLEndpointContext endpointContext = new SAMLEndpointContext();
AssertionConsumerService acs = samlHelper.create(AssertionConsumerService.class, AssertionConsumerService.DEFAULT_ELEMENT_NAME);
acs.setLocation(authnRequest.getAssertionConsumerServiceURL());
endpointContext.setEndpoint(acs);
entityContext.addSubcontext(endpointContext);
messageContext.addSubcontext(entityContext);
postEncoder.setMessageContext(messageContext);
VelocityEngine engine = new VelocityEngine();
engine.setProperty("runtime.log.logsystem.log4j.logger", "root");
engine.setProperty("resource.loader", "class");
engine.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
engine.init();
postEncoder.setVelocityEngine(engine);
logger.debug(samlHelper.prettyPrint(samlResponse));
try {
postEncoder.initialize();
postEncoder.encode();
} catch (MessageEncodingException | ComponentInitializationException e) {
logger.warn("Exception occured", e);
throw new ServletException(e);
}
}
}
......@@ -17,8 +17,6 @@ import java.util.Map;
import javax.faces.bean.ApplicationScoped;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
......@@ -76,12 +74,9 @@ public class Saml2PostHandler {
@Inject
private ApplicationConfig appConfig;
public void service(ServletRequest servletRequest, ServletResponse servletResponse, SamlSpConfigurationEntity spConfig)
public void service(HttpServletRequest request, HttpServletResponse response, SamlSpConfigurationEntity spConfig)
throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if (session == null || session.getIdpId() == null || session.getSpId() == null) {
logger.debug("Client session from {} not established. Sending client back to welcome page",
request.getRemoteAddr());
......
......@@ -21,6 +21,12 @@ public class SamlIdpDispatcherServlet implements Servlet {
@Inject
private Logger logger;
@Inject
private Saml2IdpRedirectHandler redirectHandler;
@Inject
private Saml2IdpRedirectResponseHandler redirectResponseHandler;
@Override
public void init(ServletConfig config) throws ServletException {
......@@ -40,6 +46,17 @@ public class SamlIdpDispatcherServlet implements Servlet {
logger.debug("Dispatching request context '{}' path '{}'", context, path);
if ("/saml/idp/redirect".equals(path)) {
logger.debug("Executing Redirect Handler");
redirectHandler.service(request, response);
return;
}
else if ("/saml/idp/redirect/response".equals(path)) {
logger.debug("Executing Redirect Response Handler");
redirectResponseHandler.service(request, response);
return;
}
logger.info("No matching servlet for context '{}' path '{}'", context, path);
......
......@@ -44,7 +44,7 @@ public class SamlSpDispatcherServlet implements Servlet {
private SamlAAConfigurationService aaConfigService;
@Inject
private Saml2AttributeQueryServlet attributeQueryServlet;
private Saml2AttributeQueryHandler attributeQueryServlet;
@Inject
private Saml2PostHandler postHandler;
......@@ -72,7 +72,7 @@ public class SamlSpDispatcherServlet implements Servlet {
if (spConfig != null && spConfig.getAcs() != null &&
spConfig.getAcs().endsWith(context + path)) {
logger.debug("Executing POST Handler for entity {}", spConfig.getEntityId());
postHandler.service(servletRequest, response, spConfig);
postHandler.service(request, response, spConfig);
return;
}
......@@ -81,7 +81,7 @@ public class SamlSpDispatcherServlet implements Servlet {
if (aaConfig != null && aaConfig.getAq() != null &&
aaConfig.getAq().endsWith(context + path)) {
logger.debug("Executing AttributeQuery Handler for entity {}", aaConfig.getEntityId());
attributeQueryServlet.service(servletRequest, response, aaConfig);
attributeQueryServlet.service(request, response, aaConfig);
return;
}
......
......@@ -31,6 +31,8 @@ public class SessionManager implements Serializable {
private static final long serialVersionUID = 1L;
private Long authnRequestId;
private Long userId;
private Long idpId;
......@@ -239,4 +241,12 @@ public class SessionManager implements Serializable {
public void setUnregisteredServiceCreated(Long unregisteredServiceCreated) {
this.unregisteredServiceCreated = unregisteredServiceCreated;
}
public Long getAuthnRequestId() {
return authnRequestId;
}
public void setAuthnRequestId(Long authnRequestId) {
this.authnRequestId = authnRequestId;
}
}
##
## Velocity Template for SAML 2 HTTP-POST binding
##
## Velocity context may contain the following properties
## action - String - the action URL for the form