diff options
Diffstat (limited to 'id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/SingleLogOutAction.java')
-rw-r--r-- | id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/SingleLogOutAction.java | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/SingleLogOutAction.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/SingleLogOutAction.java new file mode 100644 index 000000000..c67d10ab7 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/SingleLogOutAction.java @@ -0,0 +1,361 @@ +/* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egovernment.moa.id.protocols.pvp2x; + +import java.util.Iterator; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.opensaml.common.xml.SAMLConstants; +import org.opensaml.saml2.core.LogoutRequest; +import org.opensaml.saml2.core.LogoutResponse; +import org.opensaml.saml2.core.RequestAbstractType; +import org.opensaml.saml2.core.Status; +import org.opensaml.saml2.core.StatusCode; +import org.opensaml.saml2.metadata.SingleLogoutService; +import org.opensaml.ws.message.encoder.MessageEncodingException; +import org.opensaml.ws.soap.common.SOAPException; +import org.opensaml.xml.XMLObject; +import org.opensaml.xml.security.SecurityException; + +import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; +import at.gv.egovernment.moa.id.auth.exception.MOAIDException; +import at.gv.egovernment.moa.id.commons.db.dao.session.OASessionStore; +import at.gv.egovernment.moa.id.commons.db.ex.MOADatabaseException; +import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider; +import at.gv.egovernment.moa.id.data.IAuthData; +import at.gv.egovernment.moa.id.data.SLOInformationContainer; +import at.gv.egovernment.moa.id.data.SLOInformationImpl; +import at.gv.egovernment.moa.id.data.SLOInformationInterface; +import at.gv.egovernment.moa.id.moduls.IAction; +import at.gv.egovernment.moa.id.moduls.IRequest; +import at.gv.egovernment.moa.id.moduls.SSOManager; +import at.gv.egovernment.moa.id.protocols.pvp2x.binding.IEncoder; +import at.gv.egovernment.moa.id.protocols.pvp2x.binding.PostBinding; +import at.gv.egovernment.moa.id.protocols.pvp2x.binding.RedirectBinding; +import at.gv.egovernment.moa.id.protocols.pvp2x.builder.SingleLogOutBuilder; +import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.BindingNotSupportedException; +import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.SLOException; +import at.gv.egovernment.moa.id.protocols.pvp2x.messages.MOARequest; +import at.gv.egovernment.moa.id.protocols.pvp2x.utils.MOASAMLSOAPClient; +import at.gv.egovernment.moa.id.storage.AssertionStorage; +import at.gv.egovernment.moa.id.storage.AuthenticationSessionStoreage; +import at.gv.egovernment.moa.id.util.Random; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.MiscUtil; + +/** + * @author tlenz + * + */ +public class SingleLogOutAction implements IAction { + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.moduls.IAction#processRequest(at.gv.egovernment.moa.id.moduls.IRequest, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, at.gv.egovernment.moa.id.data.IAuthData) + */ + @Override + public SLOInformationInterface processRequest(IRequest req, + HttpServletRequest httpReq, HttpServletResponse httpResp, + IAuthData authData) throws MOAIDException { + + PVPTargetConfiguration pvpReq = (PVPTargetConfiguration) req; + + if (pvpReq.getRequest() instanceof MOARequest) { + MOARequest samlReq = (MOARequest) pvpReq.getRequest(); + if (samlReq.getSamlRequest() instanceof LogoutRequest) { + Logger.debug("Process Single LogOut request"); + LogoutRequest logOutReq = (LogoutRequest) samlReq.getSamlRequest(); + + AuthenticationSession session = + AuthenticationSessionStoreage.searchMOASessionWithNameIDandOAID( + logOutReq.getIssuer().getValue(), + logOutReq.getNameID().getValue()); + + if (session == null) { + Logger.warn("Can not find active SSO session with nameID " + + logOutReq.getNameID().getValue() + " and OA " + + logOutReq.getIssuer().getValue()); + Logger.info("Search active SSO session with SSO session cookie"); + SSOManager ssomanager = SSOManager.getInstance(); + String ssoID = ssomanager.getSSOSessionID(httpReq); + if (MiscUtil.isEmpty(ssoID)) { + Logger.warn("Can not find active Session. Single LogOut not possible!"); + SingleLogoutService sloService = SingleLogOutBuilder.getResponseSLODescriptor(pvpReq); + LogoutResponse message = SingleLogOutBuilder.buildSLOErrorResponse(sloService, pvpReq); + sendFrontChannelSLOMessage(sloService, message, httpReq, httpResp, samlReq.getRelayState()); + return null; + + } else { + String moasession = ssomanager.getMOASession(ssoID); + try { + session = AuthenticationSessionStoreage.getSession(moasession); + + } catch (MOADatabaseException e) { + Logger.warn("Can not find active Session. Single LogOut not possible!"); + SingleLogoutService sloService = SingleLogOutBuilder.getResponseSLODescriptor(pvpReq); + LogoutResponse message = SingleLogOutBuilder.buildSLOErrorResponse(sloService, pvpReq); + sendFrontChannelSLOMessage(sloService, message, httpReq, httpResp, samlReq.getRelayState()); + return null; + + } + } + } + + //store active OAs to SLOContaine + List<OASessionStore> dbOAs = AuthenticationSessionStoreage.getAllActiveOAFromMOASession(session); + SLOInformationContainer sloContainer = new SLOInformationContainer(); + sloContainer.setSloRequest(pvpReq); + sloContainer.parseActiveOAs(dbOAs, logOutReq.getIssuer().getValue()); + + //terminate MOASession + try { + AuthenticationSessionStoreage.destroySession(session.getSessionID()); + + } catch (MOADatabaseException e) { + Logger.warn("Delete MOASession FAILED."); + sloContainer.putFailedOA(AuthConfigurationProvider.getInstance().getPublicURLPrefix()); + + } + + //start service provider back channel logout process + Iterator<String> nextOAInterator = sloContainer.getNextBackChannelOA(); + while (nextOAInterator.hasNext()) { + SLOInformationImpl sloDescr = sloContainer.getBackChannelOASessionDescripten(nextOAInterator.next()); + LogoutRequest sloReq = SingleLogOutBuilder.buildSLORequestMessage(sloDescr); + + try { + List<XMLObject> soapResp = MOASAMLSOAPClient.send(sloDescr.getServiceURL(), sloReq); + + LogoutResponse sloResp = null; + for (XMLObject el : soapResp) { + if (el instanceof LogoutResponse) + sloResp = (LogoutResponse) el; + } + + if (sloResp == null) { + Logger.warn("Single LogOut for OA " + sloReq.getIssuer().getValue() + + " FAILED. NO LogOut response received."); + sloContainer.putFailedOA(sloReq.getIssuer().getValue()); + + } + + checkStatusCode(sloContainer, sloResp); + + } catch (SOAPException e) { + Logger.warn("Single LogOut for OA " + sloReq.getIssuer().getValue() + + " FAILED.", e); + sloContainer.putFailedOA(sloReq.getIssuer().getValue()); + + } catch (SecurityException e) { + Logger.warn("Single LogOut for OA " + sloReq.getIssuer().getValue() + + " FAILED.", e); + sloContainer.putFailedOA(sloReq.getIssuer().getValue()); + + } + } + + //start service provider front channel logout process + try { + doFrontChannelLogOut(sloContainer, httpReq, httpResp); + + } catch (MOADatabaseException e) { + Logger.error("MOA AssertionDatabase ERROR", e); + SingleLogoutService sloService = SingleLogOutBuilder.getResponseSLODescriptor(pvpReq); + LogoutResponse message = SingleLogOutBuilder.buildSLOErrorResponse(sloService, pvpReq); + sendFrontChannelSLOMessage(sloService, message, httpReq, httpResp, samlReq.getRelayState()); + return null; + + } + + } else if (samlReq.getSamlRequest() instanceof LogoutResponse) { + Logger.debug("Process Single LogOut response"); + LogoutResponse logOutResp = (LogoutResponse) samlReq.getSamlRequest(); + + try { + if (MiscUtil.isEmpty(samlReq.getRelayState())) { + Logger.warn("SLO Response from " + logOutResp.getIssuer().getValue() + + " has no SAML2 RelayState."); + throw new SLOException("pvp2.19", null); + + } + + SLOInformationContainer sloContainer = + AssertionStorage.getInstance().get(samlReq.getRelayState(), SLOInformationContainer.class); + checkStatusCode(sloContainer, logOutResp); + sloContainer.removeFrontChannelOA(logOutResp.getIssuer().getValue()); + doFrontChannelLogOut(sloContainer, httpReq, httpResp); + + } catch (MOADatabaseException e) { + Logger.error("MOA AssertionDatabase ERROR", e); + throw new SLOException("pvp2.19", null); + + } + + } else { + Logger.error("Process SingleLogOutAction but request is NOT of type LogoutRequest or LogoutResponse."); + throw new MOAIDException("pvp2.13", null); + + } + + } else { + Logger.error("Process SingleLogOutAction but request is NOT of type MOARequest."); + throw new MOAIDException("pvp2.13", null); + + } + + return null; + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.moduls.IAction#needAuthentication(at.gv.egovernment.moa.id.moduls.IRequest, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + */ + @Override + public boolean needAuthentication(IRequest req, HttpServletRequest httpReq, + HttpServletResponse httpResp) { + return false; + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.moduls.IAction#getDefaultActionName() + */ + @Override + public String getDefaultActionName() { + return PVP2XProtocol.SINGLELOGOUT; + } + + private void checkStatusCode(SLOInformationContainer sloContainer, LogoutResponse logOutResp) { + Status status = logOutResp.getStatus(); + if (!status.getStatusCode().equals(StatusCode.SUCCESS_URI)) { + Logger.warn("Single LogOut for OA " + logOutResp.getIssuer().getValue() + + " FAILED. (ResponseCode: " + status.getStatusCode().getValue() + + " Message: " + status.getStatusMessage().getMessage() + ")"); + sloContainer.putFailedOA(logOutResp.getIssuer().getValue()); + + } else + Logger.debug("Single LogOut for OA " + logOutResp.getIssuer().getValue() + " SUCCESS"); + + } + + private void doFrontChannelLogOut(SLOInformationContainer sloContainer, + HttpServletRequest httpReq, HttpServletResponse httpResp + ) throws MOAIDException, MOADatabaseException { + String nextOA = sloContainer.getNextFrontChannelOA(); + if (MiscUtil.isNotEmpty(nextOA)) { + SLOInformationImpl sloDescr = sloContainer.getFrontChannelOASessionDescripten(nextOA); + LogoutRequest sloReq = SingleLogOutBuilder.buildSLORequestMessage(sloDescr); + String relayState = Random.nextRandom(); + + AssertionStorage.getInstance().put(relayState, sloContainer); + + sendFrontChannelSLOMessage(sloDescr.getServiceURL(), sloDescr.getBinding(), + sloReq, httpReq, httpResp, relayState); + + } else { + //send SLO response to SLO request issuer + PVPTargetConfiguration pvpReq = sloContainer.getSloRequest(); + MOARequest samlReq = (MOARequest) pvpReq.getRequest(); + SingleLogoutService sloService = SingleLogOutBuilder.getResponseSLODescriptor(pvpReq); + LogoutResponse message = SingleLogOutBuilder.buildSLOResponseMessage(sloService, pvpReq, sloContainer.getSloFailedOAs()); + sendFrontChannelSLOMessage(sloService, message, httpReq, httpResp, samlReq.getRelayState()); + + } + } + + /** + * @param serviceURL + * @param binding + * @param sloReq + * @param httpReq + * @param httpResp + * @param relayState + */ + private void sendFrontChannelSLOMessage(String serviceURL, String bindingType, + RequestAbstractType sloReq, HttpServletRequest httpReq, + HttpServletResponse httpResp, String relayState) throws MOAIDException { + IEncoder binding = null; + if (bindingType.equals( + SAMLConstants.SAML2_REDIRECT_BINDING_URI)) { + binding = new RedirectBinding(); + + } else if (bindingType.equals( + SAMLConstants.SAML2_POST_BINDING_URI)) { + binding = new PostBinding(); + + } + + if (binding == null) { + throw new BindingNotSupportedException(bindingType); + } + + try { + binding.encodeRequest(httpReq, httpResp, sloReq, + serviceURL, relayState); + + } catch (MessageEncodingException e) { + Logger.error("Message Encoding exception", e); + throw new MOAIDException("pvp2.01", null, e); + + } catch (SecurityException e) { + Logger.error("Security exception", e); + throw new MOAIDException("pvp2.01", null, e); + + } + + } + + private void sendFrontChannelSLOMessage(SingleLogoutService consumerService, + LogoutResponse sloResp, HttpServletRequest req, HttpServletResponse resp, + String relayState) throws MOAIDException { + IEncoder binding = null; + if (consumerService.getBinding().equals( + SAMLConstants.SAML2_REDIRECT_BINDING_URI)) { + binding = new RedirectBinding(); + + } else if (consumerService.getBinding().equals( + SAMLConstants.SAML2_POST_BINDING_URI)) { + binding = new PostBinding(); + + } + + if (binding == null) { + throw new BindingNotSupportedException(consumerService.getBinding()); + } + + try { + binding.encodeRespone(req, resp, sloResp, + consumerService.getLocation(), relayState); + + } catch (MessageEncodingException e) { + Logger.error("Message Encoding exception", e); + throw new MOAIDException("pvp2.01", null, e); + + } catch (SecurityException e) { + Logger.error("Security exception", e); + throw new MOAIDException("pvp2.01", null, e); + + } + + } +} |