/******************************************************************************* * 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.stork2; import at.gv.egovernment.moa.id.auth.exception.AuthenticationException; import at.gv.egovernment.moa.id.auth.exception.MOAIDException; 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.config.auth.IOAAuthParameters; import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; import at.gv.egovernment.moa.id.data.IAuthData; 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.storage.AssertionStorage; import at.gv.egovernment.moa.id.util.VelocityProvider; import at.gv.egovernment.moa.id.util.client.mis.simple.MISMandate; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MiscUtil; import eu.stork.peps.auth.commons.*; import eu.stork.peps.auth.engine.STORKSAMLEngine; import eu.stork.peps.exceptions.STORKSAMLEngineException; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.runtime.RuntimeConstants; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.StringWriter; import java.net.MalformedURLException; import java.net.URL; /** * Second request step - after authentication of the user is done and moasession obtained, * process request and forward the user further to PEPS and/or other entities * * @author bsuzic */ public class AuthenticationRequest implements IAction { private VelocityEngine velocityEngine; private MOASTORKRequest moaStorkRequest = null; public SLOInformationInterface processRequest(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp, IAuthData authData) throws MOAIDException { if ((req instanceof MOASTORKRequest)) { // && ( ((MOASTORKRequest) req).getCitizenCountryCode() == null || ((MOASTORKRequest) req).getCitizenCountryCode().equals("AT") )) { this.moaStorkRequest = (MOASTORKRequest) req; Logger.debug("Entering MOASTORKRequest"); httpResp.reset(); //TODO: CHECK: req.getOAURL() should return the unique OA identifier OAAuthParameter oaParam = AuthConfigurationProvider.getInstance().getOnlineApplicationParameter(req.getOAURL()); if (oaParam == null) throw new AuthenticationException("stork.12", new Object[]{req.getOAURL()}); MOASTORKResponse moaStorkResponse = new MOASTORKResponse(); // check if it is attribute query if (moaStorkRequest.isAttrRequest()) { Logger.debug("Starting AttrQueryRequest"); moaStorkResponse.setSTORKAttrResponse(new STORKAttrQueryResponse()); } // check if we have authentication request else if (moaStorkRequest.isAuthnRequest()) { Logger.debug("Starting AuthenticationRequest"); moaStorkResponse.setSTORKAuthnResponse(new STORKAuthnResponse()); //STORKSAMLEngine engine = STORKSAMLEngine.getInstance("VIDP"); // Logger.debug("Starting generation of SAML response"); // try { // moaStorkResponse.setSTORKAuthnResponse(engine.generateSTORKAuthnResponse(moaStorkRequest.getStorkAuthnRequest(), moaStorkResponse.getStorkAuthnResponse(), httpReq.getRemoteAddr(), false)); // } catch (STORKSAMLEngineException ex) { // Logger.error("Failed to generate STORK SAML Response", ex); // throw new MOAIDException("stork.05", null); // TODO // } // Get personal attributtes from MOA/IdentityLink //build STORK attributes from local authentication information if (authData != null) moaStorkResponse.setPersonalAttributeList(populateAttributes(authData, oaParam)); } //moaStorkResponse.setCountry(moaStorkRequest.getSpCountry()); // Prepare extended attributes Logger.debug("Preparing data container"); // create fresh container DataContainer container = new DataContainer(); // - fill in the request we extracted above container.setRequest(moaStorkRequest); // - fill in the partial response created above container.setResponse(moaStorkResponse); container.setRemoteAddress(httpReq.getRemoteAddr()); Logger.debug("Data container prepared"); return (new AttributeCollector()).processRequest(container, httpReq, httpResp, authData, oaParam); } // // check if we are getting request for citizen of some other country // else if (req instanceof MOASTORKRequest) { // return handleMOAStorkRequest("VIDP", (MOASTORKRequest) req, httpReq.getRemoteAddr(), httpResp); // } // Check if we got the response from PEPS // If so then process it and forward to SP else if ((req instanceof MOASTORKResponse)) { return handleMOAStorkResponse("VIDP", (MOASTORKResponse) req, httpReq.getRemoteAddr(), httpResp); } else { Logger.error("Could not recognize request."); throw new MOAIDException("stork.15", null); } } /* Handles STORKAuthnRequeste received for citizens of other countries */ private SLOInformationInterface handleMOAStorkRequest(String instanceName, MOASTORKRequest moastorkRequest, String remoteAddr, HttpServletResponse httpResp) throws MOAIDException { STORKAuthnRequest spAuthnRequest = moastorkRequest.getStorkAuthnRequest(); STORKAuthnRequest storkAuthnRequest = null; String citizenCountryCode = spAuthnRequest.getCitizenCountryCode(); Logger.info("Got authentication request for citizen of " + citizenCountryCode); try { storkAuthnRequest = (STORKAuthnRequest) spAuthnRequest.clone(); } catch (CloneNotSupportedException e) { Logger.error("Could not clone AuthnRequest ", e); throw new MOAIDException("stork.05", null); // TODO } //TODO: in case of Single LogOut -> SLO information has to be stored // check if citizen country is configured in the system if (!(AuthConfigurationProvider.getInstance().getStorkConfig().getCpepsMap().containsKey(citizenCountryCode))) { Logger.error("Citizen country PEPS not configured in MOA instance: " + citizenCountryCode); throw new MOAIDException("stork.05", null); // TODO } // extracting basic settings and adjusting assertion consumer String issuer = null; String assertionConsumerURL = null; String publicURLPrefix = null; String destinationURL = null; try { issuer = new URL(AuthConfigurationProvider.getInstance().getPublicURLPrefix()).toString(); destinationURL = AuthConfigurationProvider.getInstance().getStorkConfig().getCPEPS(citizenCountryCode).getPepsURL().toString(); publicURLPrefix = AuthConfigurationProvider.getInstance().getPublicURLPrefix(); assertionConsumerURL = publicURLPrefix + "/stork2/SendPEPSAuthnRequest"; } catch (MalformedURLException ex) { Logger.error("Wrong PublicURLPrefix setting of MOA instance: " + AuthConfigurationProvider.getInstance().getPublicURLPrefix(), ex); throw new MOAIDException("stork.05", null); // TODO } catch (Exception ex) { Logger.error("Problem with PEPS configuration of MOA instance.", ex); throw new MOAIDException("stork.05", null); // TODO } // drop if we do not have publicprefix url configured on the instance if (publicURLPrefix == null) throw new AuthenticationException("stork.12", new String[]{"PublicURLPrefix"}); // adjusting request storkAuthnRequest.setEIDCrossBorderShare(spAuthnRequest.isEIDCrossBorderShare()); storkAuthnRequest.setEIDSectorShare(spAuthnRequest.isEIDSectorShare()); storkAuthnRequest.setEIDCrossSectorShare(spAuthnRequest.isEIDCrossSectorShare()); storkAuthnRequest.setCitizenCountryCode(spAuthnRequest.getCitizenCountryCode()); storkAuthnRequest.setIssuer(issuer); storkAuthnRequest.setAssertionConsumerServiceURL(assertionConsumerURL); storkAuthnRequest.setDestination(destinationURL); // regenerate request try { //Get SAMLEngine instance STORKSAMLEngine engine = STORKSAMLEngine.getInstance("VIDP"); Logger.debug("Starting generation of SAML request"); storkAuthnRequest = engine.generateSTORKAuthnRequest(storkAuthnRequest); //generateSAML Token Logger.info("SAML response succesfully generated!"); } catch (STORKSAMLEngineException e) { Logger.error("Failed to generate STORK SAML Response", e); throw new MOAIDException("stork.05", null); } // store original request from SP in order to be able to extract it in later iteration/response DataContainer spRequestContainer = new DataContainer(); spRequestContainer.setRequest(moastorkRequest); try { AssertionStorage.getInstance().put(storkAuthnRequest.getSamlId(), spRequestContainer); Logger.info("Storing artifactId " + storkAuthnRequest.getSamlId() + " of SP authentication request with id " + spAuthnRequest.getSamlId()); } catch (MOADatabaseException e) { e.printStackTrace(); } // preparing redirection for the client performRedirection("SAMLRequest", destinationURL, storkAuthnRequest.getTokenSaml(), httpResp); SLOInformationImpl sloInfo = new SLOInformationImpl(); sloInfo.setProtocolType(moastorkRequest.requestedModule()); return sloInfo; } /* Handles STORKAuthnResponse received from PEPS (return to SP) */ private SLOInformationInterface handleMOAStorkResponse(String instanceName, MOASTORKResponse moastorkResponse, String remoteAddr, HttpServletResponse httpResp) throws MOAIDException { STORKAuthnResponse authnResponse = null; //Get SAMLEngine instance STORKSAMLEngine engine = STORKSAMLEngine.getInstance(instanceName); try { authnResponse = engine.validateSTORKAuthnResponse(moastorkResponse.getSTORKAuthnResponseToken(), remoteAddr); } catch (STORKSAMLEngineException ex) { Logger.error("Unable to validate Stork AuthenticationResponse: " + ex.getMessage()); throw new MOAIDException("stork.15", null); // TODO } Logger.debug("Requesting artifactId " + authnResponse.getInResponseTo() + " from store."); DataContainer dataContainer = null; try { dataContainer = AssertionStorage.getInstance().get(authnResponse.getInResponseTo(), DataContainer.class); } catch (MOADatabaseException e) { Logger.error("Unable to retrieve datacontainer with reference authentication request. Database exception."); throw new MOAIDException("stork.15", null); // TODO } // setting new reference request and return url authnResponse.setInResponseTo(dataContainer.getRequest().getStorkAuthnRequest().getSamlId()); authnResponse.setAudienceRestriction(dataContainer.getRequest().getAssertionConsumerServiceURL()); //AudienceRestrictionBuilder audienceRestrictionBuilder = new AudienceRestrictionBuilder(); //AudienceRestriction audienceRestriction = audienceRestrictionBuilder.buildObject(dataContainer.getRequest().getAssertionConsumerServiceURL(), "localname", "nameprefix"); //authnResponse.getAssertions().get(0).getConditions().getAudienceRestrictions().add(audienceRestriction); Logger.debug("Starting generation of SAML response"); try { authnResponse = engine.generateSTORKAuthnResponse(dataContainer.getRequest().getStorkAuthnRequest(), authnResponse, remoteAddr, false); } catch (STORKSAMLEngineException e) { Logger.error("Failed to generate STORK SAML Response", e); throw new MOAIDException("stork.05", null); // TODO check } Logger.info("SAML response succesfully generated."); // preparing redirection for the client performRedirection("SAMLResponse", dataContainer.getRequest().getAssertionConsumerServiceURL(), authnResponse.getTokenSaml(), httpResp); return null; } /* Perform redirection of the client based on post binding */ private void performRedirection(String actionType, String assertionConsumerURL, byte[] tokenSaml, HttpServletResponse httpResp) throws MOAIDException { Logger.info("Performing redirection, using action type: " + actionType); try { VelocityEngine velocityEngine = VelocityProvider.getClassPathVelocityEngine(); Template template = velocityEngine.getTemplate("/resources/templates/stork2_postbinding_template.html"); VelocityContext context = new VelocityContext(); context.put(actionType, PEPSUtil.encodeSAMLToken(tokenSaml)); Logger.debug("Encoded " + actionType + " original: " + new String(tokenSaml)); Logger.debug("Using assertion consumer url as action: " + assertionConsumerURL); context.put("action", assertionConsumerURL); Logger.debug("Starting template merge"); StringWriter writer = new StringWriter(); Logger.debug("Doing template merge"); template.merge(context, writer); Logger.debug("Template merge done"); Logger.debug("Sending html content: " + writer.getBuffer().toString()); Logger.debug("Sending html content2 : " + new String(writer.getBuffer())); httpResp.getOutputStream().write(writer.getBuffer().toString().getBytes("UTF-8")); } catch (IOException e) { Logger.error("Velocity IO error: " + e.getMessage()); throw new MOAIDException("stork.15", null); // TODO } catch (Exception e) { Logger.error("Velocity general error: " + e.getMessage()); throw new MOAIDException("stork.15", null); // TODO } } public void generatePEPSRedirect(HttpServletResponse httpResp, DataContainer container) throws MOAIDException { MOASTORKRequest request = container.getRequest(); MOASTORKResponse response = container.getResponse(); Logger.info("generating stork response..."); try { //Get SAMLEngine instance STORKSAMLEngine engine = STORKSAMLEngine.getInstance("VIDP"); Logger.debug("Starting generation of SAML response"); if (response.isAuthnResponse()) response.setSTORKAuthnResponse(engine.generateSTORKAuthnResponse(request.getStorkAuthnRequest(), response.getStorkAuthnResponse(), container.getRemoteAddress(), false)); else response.setSTORKAttrResponse(engine.generateSTORKAttrQueryResponse(request.getStorkAttrQueryRequest(), response.getStorkAttrQueryResponse(), container.getRemoteAddress(), "", false)); //generateSAML Token Logger.info("SAML response succesfully generated!"); } catch (STORKSAMLEngineException e) { Logger.error("Failed to generate STORK SAML Response", e); throw new MOAIDException("stork.05", null); } // preparing redirection for the client try { VelocityEngine velocityEngine = VelocityProvider.getClassPathVelocityEngine(); Template template = velocityEngine.getTemplate("/resources/templates/stork2_postbinding_template.html"); VelocityContext context = new VelocityContext(); byte[] blob; if (request.isAttrRequest()) blob = response.getStorkAttrQueryResponse().getTokenSaml(); else blob = response.getStorkAuthnResponse().getTokenSaml(); context.put("SAMLResponse", PEPSUtil.encodeSAMLToken(blob)); Logger.debug("SAMLResponse original: " + new String(blob)); Logger.debug("Putting assertion consumer url as action: " + request.getAssertionConsumerServiceURL()); context.put("action", request.getAssertionConsumerServiceURL()); Logger.debug("Starting template merge"); StringWriter writer = new StringWriter(); Logger.debug("Doing template merge"); template.merge(context, writer); Logger.debug("Template merge done"); Logger.debug("Sending html content: " + writer.getBuffer().toString()); Logger.debug("Sending html content2 : " + new String(writer.getBuffer())); httpResp.getOutputStream().write(writer.getBuffer().toString().getBytes("UTF-8")); } catch (Exception e) { Logger.error("Velocity error: " + e.getMessage()); } } public boolean needAuthentication(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp) { //redirect to national PVP IDP infrastructure if special attributes are requested if (MiscUtil.isEmpty(req.getRequestedIDP()) && req instanceof MOASTORKRequest) return !STORKPVPUtilits.performAuthenticationOnNationalIDP((MOASTORKRequest) req); // // authentication is not needed if we have authentication request from SP for citizen of configured PEPS country // if (req instanceof MOASTORKRequest) { // MOASTORKRequest moastorkRequest = (MOASTORKRequest) req; // if (moastorkRequest.getStorkAuthnRequest() != null) { // String citizenCountryCode = moastorkRequest.getStorkAuthnRequest().getCitizenCountryCode(); // // check if citizen country is configured in the system // try { // if (AuthConfigurationProvider.getInstance().getStorkConfig().getCpepsMap().containsKey(citizenCountryCode)) { // return false; // } // } catch (MOAIDException e) { // Logger.error("Could not initialize AuthConfigurationProvider"); // } // } // // authentication is not required if received authentication response // } else if (req instanceof MOASTORKResponse) { // return false; // } return true; } private void iterate(NamedNodeMap attributesList) { for (int j = 0; j < attributesList.getLength(); j++) { Logger.debug("--Attribute: " + attributesList.item(j).getNodeName() + " = " + attributesList.item(j).getNodeValue()); } } // does nothing public void mandate(IAuthData authData) { if (authData.isUseMandate()) { try { MISMandate mandate = authData.getMISMandate(); String owbpk = mandate.getOWbPK(); byte[] mand = mandate.getMandate(); String profprep = mandate.getProfRep(); //String textdesc = mandate.getTextualDescriptionOfOID(); Element mndt = authData.getMandate(); iterate(mndt.getAttributes()); Logger.debug("mandate encoded: " + new String(org.bouncycastle.util.encoders.Base64.encode(mand))); } catch (Exception x) { Logger.debug("There is no mandate used in transaction"); } } } public PersonalAttributeList populateAttributes(IAuthData authData, IOAAuthParameters oaParam) { IPersonalAttributeList attrLst = moaStorkRequest.getStorkAuthnRequest().getPersonalAttributeList(); Logger.info("Found " + attrLst.size() + " personal attributes in the request."); // Define attribute list to be populated PersonalAttributeList attributeList = new PersonalAttributeList(); MOAAttributeProvider moaAttributeProvider = new MOAAttributeProvider(authData, moaStorkRequest); try { for (PersonalAttribute personalAttribute : attrLst) { try { Logger.debug("Personal attribute found in request: " + personalAttribute.getName() + " isRequired: " + personalAttribute.isRequired()); moaAttributeProvider.populateAttribute(attributeList, personalAttribute); } catch (Exception e) { Logger.error("Exception, attributes: " + e.getMessage()); } } } catch (Exception e) { Logger.error("Exception, attributes: " + e.getMessage()); } Logger.debug("AUTHBLOCK " + authData.getAuthBlock()); Logger.debug("SESSION IDENTIFIER " + authData.getCcc() + " " + oaParam.getIdentityLinkDomainIdentifier()); return attributeList; } public String getDefaultActionName() { return STORKProtocol.AUTHENTICATIONREQUEST; } private void initVelocityEngine() throws Exception { velocityEngine = new VelocityEngine(); velocityEngine.setProperty(RuntimeConstants.ENCODING_DEFAULT, "UTF-8"); velocityEngine.setProperty(RuntimeConstants.OUTPUT_ENCODING, "UTF-8"); velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath"); velocityEngine.setProperty("classpath.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); velocityEngine.init(); } }