Commit d9b721d6 authored by michael.simon's avatar michael.simon
Browse files

Refactor SAML SP dispatcher Mechanism

parent 43116b69
package edu.kit.scc.webreg.service.saml; package edu.kit.scc.webreg.service.saml;
import java.time.Instant; import java.io.IOException;
import java.util.Date; import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.ejb.Stateless; import javax.ejb.Stateless;
import javax.inject.Inject; import javax.inject.Inject;
...@@ -14,39 +15,38 @@ import org.joda.time.DateTime; ...@@ -14,39 +15,38 @@ import org.joda.time.DateTime;
import org.opensaml.messaging.context.MessageContext; import org.opensaml.messaging.context.MessageContext;
import org.opensaml.saml.common.SAMLObject; import org.opensaml.saml.common.SAMLObject;
import org.opensaml.saml.common.SAMLVersion; import org.opensaml.saml.common.SAMLVersion;
import org.opensaml.saml.common.messaging.SAMLMessageSecuritySupport;
import org.opensaml.saml.common.messaging.context.SAMLEndpointContext; import org.opensaml.saml.common.messaging.context.SAMLEndpointContext;
import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext; import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext;
import org.opensaml.saml.common.xml.SAMLConstants; import org.opensaml.saml.common.xml.SAMLConstants;
import org.opensaml.saml.criterion.RoleDescriptorCriterion;
import org.opensaml.saml.saml2.binding.encoding.impl.HTTPRedirectDeflateEncoder; import org.opensaml.saml.saml2.binding.encoding.impl.HTTPRedirectDeflateEncoder;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.AuthnRequest;
import org.opensaml.saml.saml2.core.Issuer; import org.opensaml.saml.saml2.core.Issuer;
import org.opensaml.saml.saml2.core.LogoutRequest; import org.opensaml.saml.saml2.core.LogoutRequest;
import org.opensaml.saml.saml2.core.NameID; import org.opensaml.saml.saml2.core.NameID;
import org.opensaml.saml.saml2.core.Response;
import org.opensaml.saml.saml2.metadata.EntityDescriptor; import org.opensaml.saml.saml2.metadata.EntityDescriptor;
import org.opensaml.saml.saml2.metadata.SingleLogoutService; import org.opensaml.saml.saml2.metadata.SingleLogoutService;
import org.opensaml.saml.saml2.metadata.SingleSignOnService; import org.opensaml.saml.security.impl.SAMLMetadataSignatureSigningParametersResolver;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.x509.BasicX509Credential;
import org.opensaml.xmlsec.SignatureSigningParameters;
import org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;
import org.opensaml.xmlsec.context.SecurityParametersContext;
import org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion;
import org.opensaml.xmlsec.impl.BasicSignatureSigningConfiguration;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.MDC;
import edu.kit.scc.webreg.bootstrap.ApplicationConfig; import edu.kit.scc.webreg.bootstrap.ApplicationConfig;
import edu.kit.scc.webreg.dao.SamlIdpMetadataDao; import edu.kit.scc.webreg.dao.SamlIdpMetadataDao;
import edu.kit.scc.webreg.dao.SamlSpConfigurationDao;
import edu.kit.scc.webreg.dao.SamlUserDao; import edu.kit.scc.webreg.dao.SamlUserDao;
import edu.kit.scc.webreg.dao.UserLoginInfoDao;
import edu.kit.scc.webreg.drools.impl.KnowledgeSessionSingleton;
import edu.kit.scc.webreg.entity.SamlIdpMetadataEntity; import edu.kit.scc.webreg.entity.SamlIdpMetadataEntity;
import edu.kit.scc.webreg.entity.SamlIdpMetadataEntityStatus;
import edu.kit.scc.webreg.entity.SamlSpConfigurationEntity; import edu.kit.scc.webreg.entity.SamlSpConfigurationEntity;
import edu.kit.scc.webreg.entity.SamlUserEntity; import edu.kit.scc.webreg.entity.SamlUserEntity;
import edu.kit.scc.webreg.entity.UserEntity; import edu.kit.scc.webreg.entity.UserEntity;
import edu.kit.scc.webreg.entity.UserLoginInfoEntity;
import edu.kit.scc.webreg.entity.UserLoginInfoStatus;
import edu.kit.scc.webreg.entity.UserLoginMethod;
import edu.kit.scc.webreg.exc.UserUpdateException;
import edu.kit.scc.webreg.service.impl.UserUpdater;
import edu.kit.scc.webreg.service.saml.exc.SamlAuthenticationException; import edu.kit.scc.webreg.service.saml.exc.SamlAuthenticationException;
import edu.kit.scc.webreg.session.SessionManager; import edu.kit.scc.webreg.session.SessionManager;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
@Stateless @Stateless
public class SamlSpLogoutServiceImpl implements SamlSpLogoutService { public class SamlSpLogoutServiceImpl implements SamlSpLogoutService {
...@@ -57,6 +57,9 @@ public class SamlSpLogoutServiceImpl implements SamlSpLogoutService { ...@@ -57,6 +57,9 @@ public class SamlSpLogoutServiceImpl implements SamlSpLogoutService {
@Inject @Inject
private SamlIdpMetadataDao idpDao; private SamlIdpMetadataDao idpDao;
@Inject
private SamlSpConfigurationDao spConfigDao;
@Inject @Inject
private SamlUserDao userDao; private SamlUserDao userDao;
...@@ -66,6 +69,9 @@ public class SamlSpLogoutServiceImpl implements SamlSpLogoutService { ...@@ -66,6 +69,9 @@ public class SamlSpLogoutServiceImpl implements SamlSpLogoutService {
@Inject @Inject
private MetadataHelper metadataHelper; private MetadataHelper metadataHelper;
@Inject
private CryptoHelper cryptoHelper;
@Inject @Inject
private ApplicationConfig appConfig; private ApplicationConfig appConfig;
...@@ -91,6 +97,7 @@ public class SamlSpLogoutServiceImpl implements SamlSpLogoutService { ...@@ -91,6 +97,7 @@ public class SamlSpLogoutServiceImpl implements SamlSpLogoutService {
SamlUserEntity user = (SamlUserEntity) tempUser; SamlUserEntity user = (SamlUserEntity) tempUser;
SamlIdpMetadataEntity idpEntity = user.getIdp(); SamlIdpMetadataEntity idpEntity = user.getIdp();
SamlSpConfigurationEntity spEntity = spConfigDao.findByHostname(request.getLocalName());
EntityDescriptor entityDesc = samlHelper.unmarshal( EntityDescriptor entityDesc = samlHelper.unmarshal(
idpEntity.getEntityDescriptor(), EntityDescriptor.class); idpEntity.getEntityDescriptor(), EntityDescriptor.class);
...@@ -100,6 +107,7 @@ public class SamlSpLogoutServiceImpl implements SamlSpLogoutService { ...@@ -100,6 +107,7 @@ public class SamlSpLogoutServiceImpl implements SamlSpLogoutService {
logoutRequest.setID(samlHelper.getRandomId()); logoutRequest.setID(samlHelper.getRandomId());
logoutRequest.setVersion(SAMLVersion.VERSION_20); logoutRequest.setVersion(SAMLVersion.VERSION_20);
logoutRequest.setIssueInstant(new DateTime()); logoutRequest.setIssueInstant(new DateTime());
logoutRequest.setDestination(slo.getLocation());
NameID nameId = samlHelper.create(NameID.class, NameID.DEFAULT_ELEMENT_NAME); NameID nameId = samlHelper.create(NameID.class, NameID.DEFAULT_ELEMENT_NAME);
nameId.setFormat(NameID.PERSISTENT); nameId.setFormat(NameID.PERSISTENT);
...@@ -112,10 +120,6 @@ public class SamlSpLogoutServiceImpl implements SamlSpLogoutService { ...@@ -112,10 +120,6 @@ public class SamlSpLogoutServiceImpl implements SamlSpLogoutService {
logger.debug("Logout Request: {}", samlHelper.prettyPrint(logoutRequest)); logger.debug("Logout Request: {}", samlHelper.prettyPrint(logoutRequest));
/**
* Need to sign logout message. Doesn't get accepted unsigned.
*/
MessageContext<SAMLObject> messageContext = new MessageContext<SAMLObject>(); MessageContext<SAMLObject> messageContext = new MessageContext<SAMLObject>();
messageContext.setMessage(logoutRequest); messageContext.setMessage(logoutRequest);
SAMLPeerEntityContext entityContext = new SAMLPeerEntityContext(); SAMLPeerEntityContext entityContext = new SAMLPeerEntityContext();
...@@ -124,6 +128,38 @@ public class SamlSpLogoutServiceImpl implements SamlSpLogoutService { ...@@ -124,6 +128,38 @@ public class SamlSpLogoutServiceImpl implements SamlSpLogoutService {
endpointContext.setEndpoint(slo); endpointContext.setEndpoint(slo);
entityContext.addSubcontext(endpointContext); entityContext.addSubcontext(endpointContext);
messageContext.addSubcontext(entityContext); messageContext.addSubcontext(entityContext);
/**
* Need to sign logout message. Doesn't get accepted unsigned.
*/
PrivateKey privateKey;
X509Certificate publicKey;
try {
privateKey = cryptoHelper.getPrivateKey(spEntity.getPrivateKey());
publicKey = cryptoHelper.getCertificate(spEntity.getCertificate());
} catch (IOException e) {
throw new SamlAuthenticationException("Private key is not set up properly", e);
}
BasicX509Credential credential = new BasicX509Credential(publicKey, privateKey);
List<Credential> credentialList = new ArrayList<Credential>();
credentialList.add(credential);
BasicSignatureSigningConfiguration ssConfig = DefaultSecurityConfigurationBootstrap.buildDefaultSignatureSigningConfiguration();
ssConfig.setSigningCredentials(credentialList);
CriteriaSet criteriaSet = new CriteriaSet();
criteriaSet.add(new SignatureSigningConfigurationCriterion(ssConfig));
criteriaSet.add(new RoleDescriptorCriterion(entityDesc.getIDPSSODescriptor(SAMLConstants.SAML20P_NS)));
SAMLMetadataSignatureSigningParametersResolver smsspr = new SAMLMetadataSignatureSigningParametersResolver();
SignatureSigningParameters ssp = smsspr.resolveSingle(criteriaSet);
logger.debug("Resolved algo {} for signing", ssp.getSignatureAlgorithm());
SecurityParametersContext securityContext = new SecurityParametersContext();
securityContext.setSignatureSigningParameters(ssp);
messageContext.addSubcontext(securityContext);
SAMLMessageSecuritySupport.signMessage(messageContext);
HTTPRedirectDeflateEncoder encoder = new HTTPRedirectDeflateEncoder(); HTTPRedirectDeflateEncoder encoder = new HTTPRedirectDeflateEncoder();
encoder.setHttpServletResponse(response); encoder.setHttpServletResponse(response);
......
...@@ -65,7 +65,7 @@ public class LogoutBean implements Serializable { ...@@ -65,7 +65,7 @@ public class LogoutBean implements Serializable {
public void startLogout() { public void startLogout() {
try { try {
FacesContext.getCurrentInstance().getExternalContext().redirect("/logout/all"); FacesContext.getCurrentInstance().getExternalContext().redirect("/logout/saml?user_id=1009");
} catch (IOException e) { } catch (IOException e) {
logger.warn("Redirect failed", e); logger.warn("Redirect failed", e);
} }
......
...@@ -12,9 +12,14 @@ package edu.kit.scc.webreg.sec; ...@@ -12,9 +12,14 @@ package edu.kit.scc.webreg.sec;
import java.io.IOException; import java.io.IOException;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
...@@ -43,6 +48,7 @@ import edu.kit.scc.webreg.bootstrap.ApplicationConfig; ...@@ -43,6 +48,7 @@ import edu.kit.scc.webreg.bootstrap.ApplicationConfig;
import edu.kit.scc.webreg.entity.SamlAAConfigurationEntity; import edu.kit.scc.webreg.entity.SamlAAConfigurationEntity;
import edu.kit.scc.webreg.entity.SamlSpMetadataEntity; import edu.kit.scc.webreg.entity.SamlSpMetadataEntity;
import edu.kit.scc.webreg.entity.UserEntity; import edu.kit.scc.webreg.entity.UserEntity;
import edu.kit.scc.webreg.service.SamlAAConfigurationService;
import edu.kit.scc.webreg.service.SamlSpMetadataService; import edu.kit.scc.webreg.service.SamlSpMetadataService;
import edu.kit.scc.webreg.service.UserService; import edu.kit.scc.webreg.service.UserService;
import edu.kit.scc.webreg.service.saml.Saml2DecoderService; import edu.kit.scc.webreg.service.saml.Saml2DecoderService;
...@@ -52,8 +58,9 @@ import edu.kit.scc.webreg.service.saml.SsoHelper; ...@@ -52,8 +58,9 @@ import edu.kit.scc.webreg.service.saml.SsoHelper;
import edu.kit.scc.webreg.service.saml.exc.SamlAuthenticationException; import edu.kit.scc.webreg.service.saml.exc.SamlAuthenticationException;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException; import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
@ApplicationScoped @Named
public class Saml2AttributeQueryHandler { @WebServlet(urlPatterns = {"/Shibboleth.sso/SAML2/AttributeQuery", "/saml/sp/attribute-query"})
public class Saml2AttributeQueryHandler implements Servlet {
@Inject @Inject
private Logger logger; private Logger logger;
...@@ -70,6 +77,9 @@ public class Saml2AttributeQueryHandler { ...@@ -70,6 +77,9 @@ public class Saml2AttributeQueryHandler {
@Inject @Inject
private SamlSpMetadataService spMetadataService; private SamlSpMetadataService spMetadataService;
@Inject
private SamlAAConfigurationService aaConfigService;
@Inject @Inject
private UserService userService; private UserService userService;
...@@ -79,7 +89,33 @@ public class Saml2AttributeQueryHandler { ...@@ -79,7 +89,33 @@ public class Saml2AttributeQueryHandler {
@Inject @Inject
private ApplicationConfig appConfig; private ApplicationConfig appConfig;
public void service(HttpServletRequest request, HttpServletResponse response, SamlAAConfigurationEntity aaConfig) @Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse)
throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String context = request.getServletContext().getContextPath();
String path = request.getRequestURI().substring(
context.length());
logger.debug("Dispatching request context '{}' path '{}'", context, path);
SamlAAConfigurationEntity aaConfig = aaConfigService.findByHostname(request.getServerName());
if (aaConfig != null && aaConfig.getAq() != null &&
aaConfig.getAq().endsWith(context + path)) {
logger.debug("Executing AttributeQuery Handler for entity {}", aaConfig.getEntityId());
service(request, response, aaConfig);
return;
}
logger.info("No matching servlet for context '{}' path '{}'", context, path);
}
private void service(HttpServletRequest request, HttpServletResponse response, SamlAAConfigurationEntity aaConfig)
throws ServletException, IOException { throws ServletException, IOException {
logger.debug("Consuming SAML AttributeQuery"); logger.debug("Consuming SAML AttributeQuery");
...@@ -204,4 +240,23 @@ public class Saml2AttributeQueryHandler { ...@@ -204,4 +240,23 @@ public class Saml2AttributeQueryHandler {
return attribute; return attribute;
} }
@Override
public void init(ServletConfig config) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
} }
...@@ -12,20 +12,27 @@ package edu.kit.scc.webreg.sec; ...@@ -12,20 +12,27 @@ package edu.kit.scc.webreg.sec;
import java.io.IOException; import java.io.IOException;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger; import org.slf4j.Logger;
import edu.kit.scc.webreg.entity.SamlSpConfigurationEntity; import edu.kit.scc.webreg.entity.SamlSpConfigurationEntity;
import edu.kit.scc.webreg.service.SamlSpConfigurationService;
import edu.kit.scc.webreg.service.saml.SamlSpPostService; import edu.kit.scc.webreg.service.saml.SamlSpPostService;
import edu.kit.scc.webreg.session.SessionManager; import edu.kit.scc.webreg.session.SessionManager;
@ApplicationScoped @Named
public class Saml2PostHandler { @WebServlet(urlPatterns = {"/Shibboleth.sso/SAML2/POST", "/saml/sp/post"})
public class Saml2PostHandler implements Servlet {
@Inject @Inject
private Logger logger; private Logger logger;
...@@ -33,10 +40,34 @@ public class Saml2PostHandler { ...@@ -33,10 +40,34 @@ public class Saml2PostHandler {
@Inject @Inject
private SessionManager session; private SessionManager session;
@Inject
private SamlSpConfigurationService spConfigService;
@Inject @Inject
private SamlSpPostService spPostService; private SamlSpPostService spPostService;
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse)
throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String context = request.getServletContext().getContextPath();
String path = request.getRequestURI().substring(
context.length());
logger.debug("Dispatching request context '{}' path '{}'", context, path);
SamlSpConfigurationEntity spConfig = spConfigService.findByHostname(request.getServerName());
if (spConfig != null) {
logger.debug("Executing POST Handler for entity {}", spConfig.getEntityId());
service(request, response, spConfig);
}
}
public void service(HttpServletRequest request, HttpServletResponse response, SamlSpConfigurationEntity spConfig) private void service(HttpServletRequest request, HttpServletResponse response, SamlSpConfigurationEntity spConfig)
throws ServletException, IOException { throws ServletException, IOException {
if (session == null || session.getIdpId() == null || session.getSpId() == null) { if (session == null || session.getIdpId() == null || session.getSpId() == null) {
...@@ -78,4 +109,23 @@ public class Saml2PostHandler { ...@@ -78,4 +109,23 @@ public class Saml2PostHandler {
} }
} }
} }
@Override
public void init(ServletConfig config) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
} }
/*******************************************************************************
* 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.inject.Inject;
import javax.inject.Named;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import edu.kit.scc.webreg.entity.SamlAAConfigurationEntity;
import edu.kit.scc.webreg.entity.SamlSpConfigurationEntity;
import edu.kit.scc.webreg.service.SamlAAConfigurationService;
import edu.kit.scc.webreg.service.SamlSpConfigurationService;
@Named
@WebServlet(urlPatterns = {"/Shibboleth.sso/*", "/saml/sp/*"})
public class SamlSpDispatcherServlet implements Servlet {
@Inject
private Logger logger;
@Inject
private SamlSpConfigurationService spConfigService;
@Inject
private SamlAAConfigurationService aaConfigService;
@Inject
private Saml2AttributeQueryHandler attributeQueryServlet;
@Inject
private Saml2PostHandler postHandler;
@Override
public void init(ServletConfig config) throws ServletException {
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse)
throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String context = request.getServletContext().getContextPath();
String path = request.getRequestURI().substring(
context.length());
logger.debug("Dispatching request context '{}' path '{}'", context, path);
SamlSpConfigurationEntity spConfig = spConfigService.findByHostname(request.getServerName());
if (spConfig != null && spConfig.getAcs() != null &&
spConfig.getAcs().endsWith(context + path)) {
logger.debug("Executing POST Handler for entity {}", spConfig.getEntityId());
postHandler.service(request, response, spConfig);
return;
}
SamlAAConfigurationEntity aaConfig = aaConfigService.findByHostname(request.getServerName());
if (aaConfig != null && aaConfig.getAq() != null &&
aaConfig.getAq().endsWith(context + path)) {
logger.debug("Executing AttributeQuery Handler for entity {}", aaConfig.getEntityId());
attributeQueryServlet.service(request, response, aaConfig);
return;
}
logger.info("No matching servlet for context '{}' path '{}'", context, path);
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
<p:dataTable value="#{logoutBean.userLoginList}" var="userLogin"> <p:dataTable value="#{logoutBean.userLoginList}" var="userLogin">
<p:column>#{userLogin.id}</p:column> <p:column>#{userLogin.id}</p:column>
<p:column><a href="/logout/saml?user_id=#{userLogin.id}">Logout</a></p:column>
</p:dataTable> </p:dataTable>
<p:panel id="logoutPanel"> <p:panel id="logoutPanel">
......
Markdown is supported
0% or .