Commit 92d951fd authored by michael.simon's avatar michael.simon
Browse files

Merge branch 'evaluate-logout' into branch-2.7

parents 89152851 ce41a1a8
...@@ -12,15 +12,17 @@ package edu.kit.scc.webreg.dao; ...@@ -12,15 +12,17 @@ package edu.kit.scc.webreg.dao;
import java.util.List; import java.util.List;
import edu.kit.scc.webreg.entity.UserEntity;
import edu.kit.scc.webreg.entity.UserLoginInfoEntity; import edu.kit.scc.webreg.entity.UserLoginInfoEntity;
import edu.kit.scc.webreg.entity.UserLoginMethod; import edu.kit.scc.webreg.entity.UserLoginMethod;
public interface UserLoginInfoDao extends BaseDao<UserLoginInfoEntity, Long> { public interface UserLoginInfoDao extends BaseDao<UserLoginInfoEntity, Long> {
List<UserLoginInfoEntity> findByUser(Long userId); List<UserLoginInfoEntity> findByUser(Long userId);
List<UserLoginInfoEntity> findByIdentity(Long identityId);
List<UserLoginInfoEntity> findByRegistry(Long registryId); List<UserLoginInfoEntity> findByRegistry(Long registryId);
List<UserLoginInfoEntity> findByUserList(List<UserEntity> userList);
UserLoginInfoEntity findLastByRegistryAndMethod(Long registryId, UserLoginMethod method); UserLoginInfoEntity findLastByRegistryAndMethod(Long registryId, UserLoginMethod method);
void deleteLoginInfo(long millis); void deleteLoginInfo(long millis);
......
...@@ -18,6 +18,7 @@ import javax.inject.Named; ...@@ -18,6 +18,7 @@ import javax.inject.Named;
import javax.persistence.Query; import javax.persistence.Query;
import edu.kit.scc.webreg.dao.UserLoginInfoDao; import edu.kit.scc.webreg.dao.UserLoginInfoDao;
import edu.kit.scc.webreg.entity.UserEntity;
import edu.kit.scc.webreg.entity.UserLoginInfoEntity; import edu.kit.scc.webreg.entity.UserLoginInfoEntity;
import edu.kit.scc.webreg.entity.UserLoginMethod; import edu.kit.scc.webreg.entity.UserLoginMethod;
...@@ -32,6 +33,20 @@ public class JpaUserLoginInfoDao extends JpaBaseDao<UserLoginInfoEntity, Long> i ...@@ -32,6 +33,20 @@ public class JpaUserLoginInfoDao extends JpaBaseDao<UserLoginInfoEntity, Long> i
.setParameter("userId", userId).getResultList(); .setParameter("userId", userId).getResultList();
} }
@Override
@SuppressWarnings("unchecked")
public List<UserLoginInfoEntity> findByUserList(List<UserEntity> userList) {
return em.createQuery("select e from UserLoginInfoEntity e where e.user in :userList")
.setParameter("userList", userList).getResultList();
}
@Override
@SuppressWarnings("unchecked")
public List<UserLoginInfoEntity> findByIdentity(Long identityId) {
return em.createQuery("select e from UserLoginInfoEntity e where e.identity.id = :identityId")
.setParameter("identityId", identityId).getResultList();
}
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public List<UserLoginInfoEntity> findByRegistry(Long registryId) { public List<UserLoginInfoEntity> findByRegistry(Long registryId) {
......
...@@ -10,9 +10,14 @@ ...@@ -10,9 +10,14 @@
******************************************************************************/ ******************************************************************************/
package edu.kit.scc.webreg.service; package edu.kit.scc.webreg.service;
import java.util.List;
import edu.kit.scc.webreg.entity.UserEntity;
import edu.kit.scc.webreg.entity.UserLoginInfoEntity; import edu.kit.scc.webreg.entity.UserLoginInfoEntity;
public interface UserLoginInfoService extends BaseService<UserLoginInfoEntity, Long> { public interface UserLoginInfoService extends BaseService<UserLoginInfoEntity, Long> {
List<UserLoginInfoEntity> findByIdentity(Long identityId);
List<UserLoginInfoEntity> findByUserList(List<UserEntity> userList);
void deleteLoginInfo(long millis); void deleteLoginInfo(long millis);
} }
package edu.kit.scc.webreg.service.impl; package edu.kit.scc.webreg.service.impl;
import java.util.List;
import javax.ejb.Stateless; import javax.ejb.Stateless;
import javax.inject.Inject; import javax.inject.Inject;
...@@ -7,6 +9,7 @@ import org.slf4j.Logger; ...@@ -7,6 +9,7 @@ import org.slf4j.Logger;
import edu.kit.scc.webreg.dao.BaseDao; import edu.kit.scc.webreg.dao.BaseDao;
import edu.kit.scc.webreg.dao.UserLoginInfoDao; import edu.kit.scc.webreg.dao.UserLoginInfoDao;
import edu.kit.scc.webreg.entity.UserEntity;
import edu.kit.scc.webreg.entity.UserLoginInfoEntity; import edu.kit.scc.webreg.entity.UserLoginInfoEntity;
import edu.kit.scc.webreg.service.UserLoginInfoService; import edu.kit.scc.webreg.service.UserLoginInfoService;
...@@ -21,6 +24,16 @@ public class UserLoginInfoServiceImpl extends BaseServiceImpl<UserLoginInfoEntit ...@@ -21,6 +24,16 @@ public class UserLoginInfoServiceImpl extends BaseServiceImpl<UserLoginInfoEntit
@Inject @Inject
private UserLoginInfoDao dao; private UserLoginInfoDao dao;
@Override
public List<UserLoginInfoEntity> findByIdentity(Long identityId) {
return dao.findByIdentity(identityId);
}
@Override
public List<UserLoginInfoEntity> findByUserList(List<UserEntity> userList) {
return dao.findByUserList(userList);
}
@Override @Override
public void deleteLoginInfo(long millis) { public void deleteLoginInfo(long millis) {
Long start = System.currentTimeMillis(); Long start = System.currentTimeMillis();
......
...@@ -245,6 +245,7 @@ public class OidcClientCallbackServiceImpl implements OidcClientCallbackService ...@@ -245,6 +245,7 @@ public class OidcClientCallbackServiceImpl implements OidcClientCallbackService
session.setLoginTime(Instant.now()); session.setLoginTime(Instant.now());
session.setTheme(user.getTheme()); session.setTheme(user.getTheme());
session.setLocale(user.getLocale()); session.setLocale(user.getLocale());
session.getLoggedInUserList().add(user.getId());
UserLoginInfoEntity loginInfo = userLoginInfoDao.createNew(); UserLoginInfoEntity loginInfo = userLoginInfoDao.createNew();
loginInfo.setUser(user); loginInfo.setUser(user);
......
...@@ -50,6 +50,7 @@ import org.opensaml.saml.saml2.metadata.Extensions; ...@@ -50,6 +50,7 @@ import org.opensaml.saml.saml2.metadata.Extensions;
import org.opensaml.saml.saml2.metadata.IDPSSODescriptor; import org.opensaml.saml.saml2.metadata.IDPSSODescriptor;
import org.opensaml.saml.saml2.metadata.OrganizationDisplayName; import org.opensaml.saml.saml2.metadata.OrganizationDisplayName;
import org.opensaml.saml.saml2.metadata.SPSSODescriptor; import org.opensaml.saml.saml2.metadata.SPSSODescriptor;
import org.opensaml.saml.saml2.metadata.SingleLogoutService;
import org.opensaml.saml.saml2.metadata.SingleSignOnService; import org.opensaml.saml.saml2.metadata.SingleSignOnService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.w3c.dom.Document; import org.w3c.dom.Document;
...@@ -373,6 +374,19 @@ public class MetadataHelper implements Serializable { ...@@ -373,6 +374,19 @@ public class MetadataHelper implements Serializable {
return null; return null;
} }
public SingleLogoutService getSLO(EntityDescriptor entityDesc, String binding) {
IDPSSODescriptor idpSsoDesc = entityDesc.getIDPSSODescriptor(SAMLConstants.SAML20P_NS);
if (idpSsoDesc != null) {
List<SingleLogoutService> slos = idpSsoDesc.getSingleLogoutServices();
for (SingleLogoutService slo : slos) {
if (slo.getBinding().equals(binding)) {
return slo;
}
}
}
return null;
}
public AttributeService getAttributeService(EntityDescriptor entityDesc) { public AttributeService getAttributeService(EntityDescriptor entityDesc) {
AttributeAuthorityDescriptor idpAtrDesc = entityDesc.getAttributeAuthorityDescriptor(SAMLConstants.SAML20P_NS); AttributeAuthorityDescriptor idpAtrDesc = entityDesc.getAttributeAuthorityDescriptor(SAMLConstants.SAML20P_NS);
if (idpAtrDesc != null) { if (idpAtrDesc != null) {
......
package edu.kit.scc.webreg.service.saml;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import edu.kit.scc.webreg.entity.SamlSpConfigurationEntity;
public interface SamlSpLogoutService {
void redirectLogout(HttpServletRequest request, HttpServletResponse response, Long userId)
throws Exception;
void consumeRedirectLogout(HttpServletRequest request, HttpServletResponse response, SamlSpConfigurationEntity spConfig)
throws Exception;
}
package edu.kit.scc.webreg.service.saml;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.joda.time.DateTime;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.saml.common.SAMLObject;
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.SAMLPeerEntityContext;
import org.opensaml.saml.common.xml.SAMLConstants;
import org.opensaml.saml.criterion.RoleDescriptorCriterion;
import org.opensaml.saml.saml2.binding.decoding.impl.HTTPPostDecoder;
import org.opensaml.saml.saml2.binding.decoding.impl.HTTPRedirectDeflateDecoder;
import org.opensaml.saml.saml2.binding.encoding.impl.HTTPRedirectDeflateEncoder;
import org.opensaml.saml.saml2.core.Issuer;
import org.opensaml.saml.saml2.core.LogoutRequest;
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.SingleLogoutService;
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 edu.kit.scc.webreg.bootstrap.ApplicationConfig;
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.entity.SamlIdpMetadataEntity;
import edu.kit.scc.webreg.entity.SamlSpConfigurationEntity;
import edu.kit.scc.webreg.entity.SamlUserEntity;
import edu.kit.scc.webreg.entity.UserEntity;
import edu.kit.scc.webreg.service.saml.exc.SamlAuthenticationException;
import edu.kit.scc.webreg.service.saml.exc.SamlInvalidPostException;
import edu.kit.scc.webreg.session.SessionManager;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
@Stateless
public class SamlSpLogoutServiceImpl implements SamlSpLogoutService {
@Inject
private Logger logger;
@Inject
private SamlIdpMetadataDao idpDao;
@Inject
private SamlSpConfigurationDao spConfigDao;
@Inject
private SamlUserDao userDao;
@Inject
private SamlHelper samlHelper;
@Inject
private MetadataHelper metadataHelper;
@Inject
private CryptoHelper cryptoHelper;
@Inject
private ApplicationConfig appConfig;
@Inject
private SessionManager session;
@Override
public void consumeRedirectLogout(HttpServletRequest request, HttpServletResponse response, SamlSpConfigurationEntity spConfig)
throws Exception {
HTTPRedirectDeflateDecoder decoder = new HTTPRedirectDeflateDecoder();
decoder.setHttpServletRequest(request);
decoder.initialize();
decoder.decode();
SAMLObject obj = decoder.getMessageContext().getMessage();
logger.debug("Message decoded: {}", obj);
}
@Override
public void redirectLogout(HttpServletRequest request, HttpServletResponse response, Long userId)
throws Exception {
logger.debug("Init SAML redirect logout for {}", userId);
if (! session.getLoggedInUserList().contains(userId)) {
logger.warn("SAML logout for userId {}, not in logged in list", userId);
return;
}
UserEntity tempUser = userDao.findById(userId);
if (! (tempUser instanceof SamlUserEntity)) {
logger.warn("SAML logout for userId {}: User is not SAML Type, but {}", tempUser.getClass().getName());
return;
}
SamlUserEntity user = (SamlUserEntity) tempUser;
SamlIdpMetadataEntity idpEntity = user.getIdp();
SamlSpConfigurationEntity spEntity = spConfigDao.findByHostname(request.getLocalName());
EntityDescriptor entityDesc = samlHelper.unmarshal(
idpEntity.getEntityDescriptor(), EntityDescriptor.class);
SingleLogoutService slo = metadataHelper.getSLO(entityDesc, SAMLConstants.SAML2_REDIRECT_BINDING_URI);
LogoutRequest logoutRequest = samlHelper.create(LogoutRequest.class, LogoutRequest.DEFAULT_ELEMENT_NAME);
logoutRequest.setID(samlHelper.getRandomId());
logoutRequest.setVersion(SAMLVersion.VERSION_20);
logoutRequest.setIssueInstant(new DateTime());
logoutRequest.setDestination(slo.getLocation());
NameID nameId = samlHelper.create(NameID.class, NameID.DEFAULT_ELEMENT_NAME);
nameId.setFormat(NameID.PERSISTENT);
nameId.setValue(user.getPersistentId());
logoutRequest.setNameID(nameId);
Issuer issuer = samlHelper.create(Issuer.class, Issuer.DEFAULT_ELEMENT_NAME);
issuer.setValue(user.getPersistentSpId());
logoutRequest.setIssuer(issuer);
logger.debug("Logout Request: {}", samlHelper.prettyPrint(logoutRequest));
MessageContext<SAMLObject> messageContext = new MessageContext<SAMLObject>();
messageContext.setMessage(logoutRequest);
SAMLPeerEntityContext entityContext = new SAMLPeerEntityContext();
entityContext.setEntityId(idpEntity.getEntityId());
SAMLEndpointContext endpointContext = new SAMLEndpointContext();
endpointContext.setEndpoint(slo);
entityContext.addSubcontext(endpointContext);
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();
encoder.setHttpServletResponse(response);
encoder.setMessageContext(messageContext);
encoder.initialize();
encoder.prepareContext();
encoder.encode();
}
}
...@@ -185,6 +185,7 @@ public class SamlSpPostServiceImpl implements SamlSpPostService { ...@@ -185,6 +185,7 @@ public class SamlSpPostServiceImpl implements SamlSpPostService {
session.setLoginTime(Instant.now()); session.setLoginTime(Instant.now());
session.setTheme(user.getTheme()); session.setTheme(user.getTheme());
session.setLocale(user.getLocale()); session.setLocale(user.getLocale());
session.getLoggedInUserList().add(user.getId());
UserLoginInfoEntity loginInfo = userLoginInfoDao.createNew(); UserLoginInfoEntity loginInfo = userLoginInfoDao.createNew();
loginInfo.setUser(user); loginInfo.setUser(user);
......
/*******************************************************************************
* 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.bean;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.context.FacesContext;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.slf4j.Logger;
import edu.kit.scc.webreg.entity.UserEntity;
import edu.kit.scc.webreg.entity.identity.IdentityEntity;
import edu.kit.scc.webreg.service.UserService;
import edu.kit.scc.webreg.service.identity.IdentityService;
import edu.kit.scc.webreg.session.SessionManager;
@Named
@ViewScoped
public class LogoutBean implements Serializable {
private static final long serialVersionUID = 1L;
private IdentityEntity identity;
@Inject
private Logger logger;
@Inject
private IdentityService identityService;
@Inject
private UserService userService;
@Inject
private SessionManager sessionManager;
private List<UserEntity> userList;
private List<UserEntity> userLoginList;
public void preRenderView(ComponentSystemEvent ev) {
}
public void startLocalLogout() {
try {
FacesContext.getCurrentInstance().getExternalContext().redirect("/logout/local");
} catch (IOException e) {
logger.warn("Redirect failed", e);
}
}
public void startLogout() {
try {
FacesContext.getCurrentInstance().getExternalContext().redirect("/logout/saml?user_id=1009");
} catch (IOException e) {
logger.warn("Redirect failed", e);
}
}
public IdentityEntity getIdentity() {
if (identity == null) {
identity = identityService.findById(sessionManager.getIdentityId());
}
return identity;
}
public List<UserEntity> getUserList() {
if (userList == null) {
userList = userService.findByIdentity(getIdentity());
}
return userList;
}
public List<UserEntity> getUserLoginList() {
if (userLoginList == null) {
userLoginList = userService.findByMultipleId(new ArrayList<Long>(sessionManager.getLoggedInUserList()));
}
return userLoginList;
}
}
...@@ -100,7 +100,12 @@ public class UserPropertiesBean implements Serializable { ...@@ -100,7 +100,12 @@ public class UserPropertiesBean implements Serializable {
public UserEntity getUser() { public UserEntity getUser() {
if (user == null) { if (user == null) {
user = userService.findByIdWithStore(getUserList().get(0).getId()); if (sessionManager.getLoggedInUserList().size() > 0) {
user = userService.findByIdWithStore(sessionManager.getLoggedInUserList().iterator().next());
}
else {
user = userService.findByIdWithStore(getUserList().get(0).getId());
}
} }
return user; return user;
} }
......
...@@ -26,6 +26,8 @@ import javax.servlet.http.HttpSession; ...@@ -26,6 +26,8 @@ import javax.servlet.http.HttpSession;
import org.slf4j.Logger; import org.slf4j.Logger;
import edu.kit.scc.webreg.service.saml.SamlSpLogoutService;
import edu.kit.scc.webreg.session.SessionManager;
import edu.kit.scc.webreg.util.ViewIds; import edu.kit.scc.webreg.util.ViewIds;
@Named @Named
...@@ -35,6 +37,9 @@ public class LogoutServlet implements Servlet { ...@@ -35,6 +37,9 @@ public class LogoutServlet implements Servlet {
@Inject @Inject
private Logger logger; private Logger logger;
@Inject
private SamlSpLogoutService samlSpLogoutService;
@Override @Override
public void init(ServletConfig config) throws ServletException { public void init(ServletConfig config) throws ServletException {
...@@ -53,12 +58,14 @@ public class LogoutServlet implements Servlet { ...@@ -53,12 +58,14 @@ public class LogoutServlet implements Servlet {
logger.debug("logout request context '{}' path '{}'", context, path); logger.debug("logout request context '{}' path '{}'", context, path);
HttpSession session = request.getSession(false);
if(session != null) {
session.invalidate();
}
String redirect = request.getParameter("redirect"); String redirect = request.getParameter("redirect");
if (redirect != null && (! redirect.equals(""))) { if (redirect == null) redirect = "";
if (path.startsWith("/logout/local")) {
HttpSession session = request.getSession(false);
if(session != null) {
session.invalidate();
}
if (redirect.equalsIgnoreCase("delete")) if (redirect.equalsIgnoreCase("delete"))