/* * 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.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.joda.time.DateTime; import org.opensaml.saml2.core.Assertion; import org.opensaml.saml2.core.Attribute; import org.opensaml.saml2.core.AttributeQuery; import org.opensaml.saml2.core.Response; import org.opensaml.ws.message.encoder.MessageEncodingException; import org.opensaml.xml.security.SecurityException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import at.gv.egovernment.moa.id.auth.builder.AuthenticationDataBuilder; import at.gv.egovernment.moa.id.auth.builder.DynamicOAAuthParameterBuilder; import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; import at.gv.egovernment.moa.id.commons.api.AuthConfiguration; import at.gv.egovernment.moa.id.commons.api.IOAAuthParameters; import at.gv.egovernment.moa.id.commons.api.IRequest; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.commons.db.dao.session.InterfederationSessionStore; import at.gv.egovernment.moa.id.commons.db.ex.MOADatabaseException; import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; import at.gv.egovernment.moa.id.data.IAuthData; import at.gv.egovernment.moa.id.data.SLOInformationInterface; import at.gv.egovernment.moa.id.data.Trible; import at.gv.egovernment.moa.id.moduls.IAction; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.SoapBinding; import at.gv.egovernment.moa.id.protocols.pvp2x.builder.AuthResponseBuilder; import at.gv.egovernment.moa.id.protocols.pvp2x.builder.PVPAttributeBuilder; import at.gv.egovernment.moa.id.protocols.pvp2x.builder.assertion.PVP2AssertionBuilder; import at.gv.egovernment.moa.id.protocols.pvp2x.config.PVPConfiguration; import at.gv.egovernment.moa.id.protocols.pvp2x.messages.MOARequest; import at.gv.egovernment.moa.id.protocols.pvp2x.metadata.MOAMetadataProvider; import at.gv.egovernment.moa.id.protocols.pvp2x.signer.IDPCredentialProvider; import at.gv.egovernment.moa.id.protocols.pvp2x.utils.AssertionAttributeExtractor; import at.gv.egovernment.moa.id.storage.IAuthenticationSessionStoreage; import at.gv.egovernment.moa.logging.Logger; /** * @author tlenz * */ @Service("AttributQueryAction") public class AttributQueryAction implements IAction { @Autowired private IAuthenticationSessionStoreage authenticationSessionStorage; @Autowired private AuthenticationDataBuilder authDataBuilder; @Autowired private IDPCredentialProvider pvpCredentials; @Autowired private AuthConfiguration authConfig; @Autowired(required=true) private MOAMetadataProvider metadataProvider; private final static List DEFAULTSTORKATTRIBUTES = Arrays.asList( new String[]{PVPConstants.EID_STORK_TOKEN_NAME}); private final static List DEFAULTMANDATEATTRIBUTES = Arrays.asList( new String[]{ PVPConstants.MANDATE_FULL_MANDATE_NAME, PVPConstants.MANDATE_PROF_REP_OID_NAME}); /* (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 pendingReq, HttpServletRequest httpReq, HttpServletResponse httpResp, IAuthData authData) throws MOAIDException { if (pendingReq instanceof PVPTargetConfiguration && ((PVPTargetConfiguration) pendingReq).getRequest() instanceof MOARequest && ((MOARequest)((PVPTargetConfiguration) pendingReq).getRequest()).getSamlRequest() instanceof AttributeQuery) { //set time reference DateTime date = new DateTime(); try { //get Single Sign-On information for the Service-Provider // which sends the Attribute-Query request AuthenticationSession moaSession = authenticationSessionStorage.getInternalSSOSession(pendingReq.getInternalSSOSessionIdentifier()); if (moaSession == null) { Logger.warn("No MOASession with ID:" + pendingReq.getInternalSSOSessionIdentifier() + " FOUND."); throw new MOAIDException("auth.02", new Object[]{pendingReq.getInternalSSOSessionIdentifier()}); } InterfederationSessionStore nextIDPInformation = authenticationSessionStorage.searchInterfederatedIDPFORAttributeQueryWithSessionID(moaSession.getSessionID()); AttributeQuery attrQuery = (AttributeQuery)((MOARequest)((PVPTargetConfiguration) pendingReq).getRequest()).getSamlRequest(); //build PVP 2.1 response-attribute information for this AttributQueryRequest Trible, Date, String> responseInfo = buildResponseInformationForAttributQuery(pendingReq, moaSession, attrQuery.getAttributes(), nextIDPInformation); Logger.debug("AttributQuery return " + responseInfo.getFirst().size() + " attributes with QAA-Level:" + responseInfo.getThird() + " validTo:" + responseInfo.getSecond().toString()); //build PVP 2.1 assertion String issuerEntityID = PVPConfiguration.getInstance().getIDPSSOMetadataService( pendingReq.getAuthURL()); Assertion assertion = PVP2AssertionBuilder.buildAssertion(issuerEntityID, attrQuery, responseInfo.getFirst(), date, new DateTime(responseInfo.getSecond().getTime()), responseInfo.getThird(), authData.getSessionIndex()); //build PVP 2.1 response Response authResponse = AuthResponseBuilder.buildResponse( metadataProvider, issuerEntityID, attrQuery, date, assertion, authConfig.isPVP2AssertionEncryptionActive()); SoapBinding decoder = new SoapBinding(); decoder.encodeRespone(httpReq, httpResp, authResponse, null, null, pvpCredentials.getIDPAssertionSigningCredential()); return null; } 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); } catch (MOADatabaseException e) { Logger.error("MOASession with SessionID=" + pendingReq.getInternalSSOSessionIdentifier() + " is not found in Database", e); throw new MOAIDException("init.04", new Object[] { pendingReq.getInternalSSOSessionIdentifier() }); } } else { Logger.error("Process AttributeQueryAction but request is NOT of type AttributQuery."); throw new MOAIDException("pvp2.13", 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.ATTRIBUTEQUERY; } private Trible, Date, String> buildResponseInformationForAttributQuery(IRequest pendingReq, AuthenticationSession session, List reqAttributes, InterfederationSessionStore nextIDPInformation) throws MOAIDException { try { //mark AttributeQuery as used if it exists if ( pendingReq instanceof PVPTargetConfiguration && ((PVPTargetConfiguration) pendingReq).getRequest() instanceof MOARequest && ((PVPTargetConfiguration) pendingReq).getRequest().getInboundMessage() instanceof AttributeQuery) { authenticationSessionStorage.markOAWithAttributeQueryUsedFlag(session, pendingReq.getOAURL(), pendingReq.requestedModule()); } //build OnlineApplication dynamic from requested attributes (AttributeQuerry Request) and configuration IOAAuthParameters spConfig = DynamicOAAuthParameterBuilder.buildFromAttributeQuery(reqAttributes); //search federated IDP information for this MOASession if (nextIDPInformation != null) { Logger.info("Find active federated IDP information." + ". --> Request next IDP:" + nextIDPInformation.getIdpurlprefix() + " for authentication information."); //load configuration of next IDP IOAAuthParameters idpLoaded = authConfig.getOnlineApplicationParameter(nextIDPInformation.getIdpurlprefix()); if (idpLoaded == null || !(idpLoaded instanceof OAAuthParameter)) { Logger.warn("Configuration for federated IDP:" + nextIDPInformation.getIdpurlprefix() + "is not loadable."); throw new MOAIDException("auth.32", new Object[]{nextIDPInformation.getIdpurlprefix()}); } OAAuthParameter idp = (OAAuthParameter) idpLoaded; //check if next IDP config allows inbound messages if (!idp.isInboundSSOInterfederationAllowed()) { Logger.warn("Configuration for federated IDP:" + nextIDPInformation.getIdpurlprefix() + "disallow inbound authentication messages."); throw new MOAIDException("auth.33", new Object[]{nextIDPInformation.getIdpurlprefix()}); } //check next IDP service area policy. BusinessService IDPs can only request wbPKs if (!spConfig.getBusinessService() && !idp.isIDPPublicService()) { Logger.error("Interfederated IDP " + idp.getPublicURLPrefix() + " has a BusinessService-IDP but requests PublicService attributes."); throw new MOAIDException("auth.34", new Object[]{nextIDPInformation.getIdpurlprefix()}); } //validation complete --> start AttributeQuery Request AssertionAttributeExtractor extractor = authDataBuilder.getAuthDataFromAttributeQuery(reqAttributes, nextIDPInformation.getUserNameID(), idp); //mark attribute request as used if (nextIDPInformation.isStoreSSOInformation()) { nextIDPInformation.setAttributesRequested(true); authenticationSessionStorage.persistIdpInformation(nextIDPInformation); //moaSessionDBUtils.saveOrUpdate(nextIDPInformation); //delete federated IDP from Session } else { authenticationSessionStorage.deleteIdpInformation(nextIDPInformation); //moaSessionDBUtils.delete(nextIDPInformation); } return Trible.newInstance( extractor.getAllResponseAttributesFromFirstAttributeStatement(), extractor.getAssertionNotOnOrAfter(), extractor.getQAALevel()); } else { Logger.debug("Build authData for AttributQuery from local MOASession."); IAuthData authData = authDataBuilder.buildAuthenticationData(pendingReq, session, spConfig); //add default attributes in case of mandates or STORK is in use List attrList = addDefaultAttributes(reqAttributes, authData); //build Set of response attributes List respAttr = PVPAttributeBuilder.buildSetOfResponseAttributes(authData, attrList); return Trible.newInstance(respAttr, authData.getSsoSessionValidTo(), authData.getQAALevel()); } } catch (MOAIDException e) { throw e; } } /** * Add additional PVP Attribute-Names in respect to current MOASession. *

*
As example: if current MOASession includes mandates but mandate attributes are not requested, 
	 * this method a a minimum set of mandate attribute-names
* * @param reqAttr From Service Provider requested attributes * @param authData AuthenticationData * @return List of PVP attribute-names */ private List addDefaultAttributes(List reqAttr, IAuthData authData) { List reqAttributeNames = new ArrayList(); for (Attribute attr : reqAttr) { reqAttributeNames.add(attr.getName()); } //add default STORK attributes if it is a STORK authentication if (authData.isForeigner() && !reqAttributeNames.containsAll(DEFAULTSTORKATTRIBUTES)) { for (String el : DEFAULTSTORKATTRIBUTES) { if (!reqAttributeNames.contains(el)) reqAttributeNames.add(el); } } //add default mandate attributes if it is a authentication with mandates if (authData.isUseMandate() && !reqAttributeNames.containsAll(DEFAULTMANDATEATTRIBUTES)) { for (String el : DEFAULTMANDATEATTRIBUTES) { if (!reqAttributeNames.contains(el)) reqAttributeNames.add(el); } } return reqAttributeNames; } }