/******************************************************************************* * 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.auth.builder; import java.io.StringWriter; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.xml.bind.DatatypeConverter; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Element; import org.w3c.dom.Node; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.impl.utils.DOMUtils; import at.gv.egiz.eaaf.core.impl.utils.Random; import at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttributeImpl; import at.gv.egovernment.moa.id.auth.exception.BuildException; import at.gv.egovernment.moa.id.auth.exception.ParseException; import at.gv.egovernment.moa.id.commons.MOAIDAuthConstants; import at.gv.egovernment.moa.id.commons.api.IOAAuthParameters; import at.gv.egovernment.moa.id.commons.api.data.ExtendedSAMLAttribute; import at.gv.egovernment.moa.id.commons.api.data.IAuthenticationSession; import at.gv.egovernment.moa.id.commons.api.exceptions.ConfigurationException; import at.gv.egovernment.moa.id.config.TargetToSectorNameMapper; import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.Constants; import at.gv.egovernment.moa.util.MiscUtil; import at.gv.egovernment.moa.util.StringUtils; /** * Builder for the authentication block <saml:Assertion> * to be included in a <CreateXMLSignatureResponse>. * * @author Paul Ivancsics */ public class AuthenticationBlockAssertionBuilder extends AuthenticationAssertionBuilder implements Constants { /** template for the Auth-Block */ private static String AUTH_BLOCK = "" + NL + " " + NL + " " + NL + " {3}" + NL + " " + NL + "{4}" + " " + NL + " {5}" + NL + " " + NL + " " + NL + " {6}" + NL + " " + NL + "{7}" + "{8}" + "{9}" + " " + NL + ""; private static String GESCHAEFTS_BEREICH_ATTRIBUTE = " " + NL + " {0}" + NL + " " + NL; private static String WBPK_ATTRIBUTE = " " + NL + " " + NL + " " + NL + " {0}" + NL + " {1}" + NL + " " + NL + " " + NL + " " + NL; private static String SPECIAL_TEXT_ATTRIBUTE = " " + NL + " {0}" + NL + " " + NL; private static String AUTHBLOCKTOKKEN_ATTRIBUTE = " " + NL + " {0}" + NL + " " + NL; private static String PR_IDENTIFICATION_ATTRIBUTE = " " + NL + " {0}" + NL + " {1}" + NL + " " + NL; /** * The number of SAML attributes included in this AUTH-Block (without the extended SAML attributes). */ public static final int NUM_OF_SAML_ATTRIBUTES = 5; public static final int NUM_OF_SAML_ATTRIBUTES_SSO = 4; public static final String bPKwbPKNSDECLARATION = " xmlns:pr=\"" + PD_NS_URI + "\""; public static final String AUTHBLOCK_TEXT_PATTERN_NAME = "#NAME#"; public static final String AUTHBLOCK_TEXT_PATTERN_BIRTHDAY = "#BIRTHDAY#"; public static final String AUTHBLOCK_TEXT_PATTERN_DATE = "#DATE#"; public static final String AUTHBLOCK_TEXT_PATTERN_TIME = "#TIME#"; public static final String PENDING_REQ_AUTHBLOCK_TEXT_KEY = "specialAuthBlockTextKeyValueMap"; /** * Constructor for AuthenticationBlockAssertionBuilder. */ public AuthenticationBlockAssertionBuilder() { super(); } public static Map generateSpezialAuthBlockPatternMap(IRequest pendingReq, String issuer, String gebDat, String issueInstant) { Map result = new HashMap(); //convert issueInstant Calendar datetime = DatatypeConverter.parseDateTime(issueInstant); SimpleDateFormat dateformat = new SimpleDateFormat("dd.MM.yyyy"); SimpleDateFormat timeformat = new SimpleDateFormat("HH:mm:ss"); //set default values result.put(AUTHBLOCK_TEXT_PATTERN_NAME, issuer); result.put(AUTHBLOCK_TEXT_PATTERN_BIRTHDAY, gebDat); result.put(AUTHBLOCK_TEXT_PATTERN_DATE, dateformat.format(datetime.getTime())); result.put(AUTHBLOCK_TEXT_PATTERN_TIME, timeformat.format(datetime.getTime())); //set other values from pendingReq if exists Map processSpecificElements = pendingReq.getRawData(PENDING_REQ_AUTHBLOCK_TEXT_KEY, Map.class); if (processSpecificElements != null && !processSpecificElements.isEmpty()) { Logger.debug("Find process-specific patterns for 'special AuthBlock-Text'. Start processing ..."); Iterator mapIterator = processSpecificElements.entrySet().iterator(); while (mapIterator.hasNext()) { Object objEl = mapIterator.next(); if (objEl instanceof Entry) { try { @SuppressWarnings("unchecked") Entry el = (Entry) objEl; Logger.trace(" Add pattern-> Key: " + el.getKey() + " Value:" + el.getValue()); if (result.containsKey(el.getKey())) Logger.warn(" Can not add pattern: " + el.getKey() + " , because it already exists."); else result.put(el.getKey(), el.getValue()); } catch (Exception e) { Logger.warn("A pendingReq. specific 'special AuthBlock-Text' element has a suspect type. Ignore it!", e); } } } } return result; } /** * * @param issuer * @param issueInstant * @param authURL * @param sectorSpecificUniqueId * @param sectorSpecificUniqueIdType * @param gebDat * @param oaURL * @param spTargetAreaFriendlyName * @param extendedSAMLAttributes * @param session * @param oaParam * @return * @throws BuildException * @throws ConfigurationException */ public String buildAuthBlock( String issuer, String issueInstant, String authURL, String sectorSpecificUniqueId, String sectorSpecificUniqueIdType, String gebDat, String oaURL, String spTargetAreaFriendlyName, List extendedSAMLAttributes, IAuthenticationSession session, IOAAuthParameters oaParam, Map specialAuthBlockTextPatterns) throws BuildException, ConfigurationException { //initialize state session.setSAMLAttributeGebeORwbpk(true); String usedwbPKbPKNamespaceDeclaration = org.apache.commons.lang3.StringUtils.EMPTY; String publicSectorIdOrwbPK = org.apache.commons.lang3.StringUtils.EMPTY; if (MiscUtil.isEmpty(sectorSpecificUniqueIdType) && MiscUtil.isEmpty(sectorSpecificUniqueId) ) { //bPK or wbPK is not provided --> SAML attribute is not needed session.setSAMLAttributeGebeORwbpk(false); Logger.trace("No bPK or TargetIdentifier --> do not set bPK or Target into AuthBlock"); } else if (!sectorSpecificUniqueIdType.startsWith(MOAIDAuthConstants.PREFIX_CDID)) { //service provider has not an sector Id from Austrian public-domain --> build AuthBlock like a wbPK if (!Constants.URN_PREFIX_HPI.equals(sectorSpecificUniqueIdType)) { //Only add wbPKs to AUTH-Block. HPIs can be added to the AUTH-Block by the corresponding Validator publicSectorIdOrwbPK = MessageFormat.format(WBPK_ATTRIBUTE, new Object[] {sectorSpecificUniqueId, sectorSpecificUniqueIdType}); usedwbPKbPKNamespaceDeclaration = bPKwbPKNSDECLARATION; //adding type of wbPK domain identifier ExtendedSAMLAttribute idLinkDomainIdentifierTypeAttribute = new ExtendedSAMLAttributeImpl("IdentityLinkDomainIdentifierType", spTargetAreaFriendlyName, Constants.MOA_NS_URI, ExtendedSAMLAttribute.ADD_TO_AUTHBLOCK_ONLY); extendedSAMLAttributes.add(idLinkDomainIdentifierTypeAttribute); } else { // We do not have a wbPK, therefore no SAML-Attribute is provided session.setSAMLAttributeGebeORwbpk(false); } } else { // OA is a govermental application //convert sector identifier into friendly name and add it to AuthBlock String sectorName = TargetToSectorNameMapper.getSectorNameViaTarget(sectorSpecificUniqueIdType); if (StringUtils.isEmpty(sectorName)) { if (spTargetAreaFriendlyName != null) sectorName = spTargetAreaFriendlyName; } publicSectorIdOrwbPK = MessageFormat.format(GESCHAEFTS_BEREICH_ATTRIBUTE, new Object[] {sectorSpecificUniqueIdType.substring(MOAIDAuthConstants.PREFIX_CDID.length()) + " (" + sectorName + ")" }); //add bPK to AuthBlock if it is not empty if (MiscUtil.isNotEmpty(sectorSpecificUniqueId)) { Element bpkSamlValueElement; try { bpkSamlValueElement = DOMUtils.parseDocument(MessageFormat.format(PR_IDENTIFICATION_ATTRIBUTE, new Object[] { sectorSpecificUniqueId, Constants.URN_PREFIX_BPK }), false, null, null).getDocumentElement(); } catch (Exception e) { Logger.error("Error on building AUTH-Block: " + e.getMessage()); throw new BuildException("builder.00", new Object[] { "AUTH-Block", e.toString()}); } ExtendedSAMLAttribute bpkAttribute = new ExtendedSAMLAttributeImpl("bPK", bpkSamlValueElement, Constants.MOA_NS_URI, ExtendedSAMLAttribute.ADD_TO_AUTHBLOCK_ONLY); extendedSAMLAttributes.add(bpkAttribute); } usedwbPKbPKNamespaceDeclaration = bPKwbPKNSDECLARATION; } //check if mandates should be used if (session.isMandateUsed()) { //generate mandate reference value String mandateReferenceValue = Random.nextProcessReferenceValue(); session.setMandateReferenceValue(mandateReferenceValue); ExtendedSAMLAttribute mandateReferenceValueAttribute = new ExtendedSAMLAttributeImpl("mandateReferenceValue", mandateReferenceValue, Constants.MOA_NS_URI, ExtendedSAMLAttribute.ADD_TO_AUTHBLOCK); extendedSAMLAttributes.add(mandateReferenceValueAttribute); } //adding friendly name of OA String oaFriendlyName = StringUtils.isEmpty(oaParam.getFriendlyName()) ? "" : oaParam.getFriendlyName(); ExtendedSAMLAttribute oaFriendlyNameAttribute = new ExtendedSAMLAttributeImpl("oaFriendlyName", oaFriendlyName, Constants.MOA_NS_URI, ExtendedSAMLAttribute.ADD_TO_AUTHBLOCK_ONLY); extendedSAMLAttributes.add(oaFriendlyNameAttribute); //generate special AuthBlock text String text = ""; if (MiscUtil.isNotEmpty(oaParam.getAditionalAuthBlockText())) { Logger.debug("Use addional AuthBlock Text from OA=" + oaParam.getPublicURLPrefix()); text = oaParam.getAditionalAuthBlockText(); } String specialText = MessageFormat.format(SPECIAL_TEXT_ATTRIBUTE, new Object[] { generateSpecialText(text, specialAuthBlockTextPatterns) }); //generate unique AuthBlock tokken String uniquetokken = Random.nextProcessReferenceValue(); session.setAuthBlockTokken(uniquetokken); String assertion; try { assertion = MessageFormat.format( AUTH_BLOCK, new Object[] { usedwbPKbPKNamespaceDeclaration, issuer, issueInstant, authURL, publicSectorIdOrwbPK, oaURL, gebDat, specialText, MessageFormat.format(AUTHBLOCKTOKKEN_ATTRIBUTE, new Object[] {uniquetokken}), buildExtendedSAMLAttributes(extendedSAMLAttributes)}); } catch (ParseException e) { Logger.error("Error on building AUTH-Block: " + e.getMessage()); throw new BuildException("builder.00", new Object[] { "AUTH-Block", e.toString()}); } return assertion; } public static String generateSpecialText(String inputtext, Map specialAuthBlockTextPatterns) { Iterator> it = specialAuthBlockTextPatterns.entrySet().iterator(); String text = inputtext; while (it.hasNext()) { Entry el = it.next(); text = text.replaceAll(el.getKey(), el.getValue()); } return text; } public static String xmlToString(Node node) { try { Source source = new DOMSource(node); StringWriter stringWriter = new StringWriter(); Result result = new StreamResult(stringWriter); TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(); transformer.transform(source, result); return stringWriter.getBuffer().toString(); } catch (TransformerConfigurationException e) { e.printStackTrace(); } catch (TransformerException e) { e.printStackTrace(); } return null; } public String buildAuthBlockSSO( String issuer, String issueInstant, String authURL, String oaURL, String gebDat, List extendedSAMLAttributes, IAuthenticationSession session, IOAAuthParameters oaParam, Map specialAuthBlockTextPatterns) throws BuildException { session.setSAMLAttributeGebeORwbpk(true); String gebeORwbpk = ""; String wbpkNSDeclaration = ""; //add mandate reference-value if mandates are used if (session.isMandateUsed()) { String mandateReferenceValue = Random.nextProcessReferenceValue(); session.setMandateReferenceValue(mandateReferenceValue); ExtendedSAMLAttribute mandateReferenceValueAttribute = new ExtendedSAMLAttributeImpl("mandateReferenceValue", mandateReferenceValue, Constants.MOA_NS_URI, ExtendedSAMLAttribute.ADD_TO_AUTHBLOCK); extendedSAMLAttributes.add(mandateReferenceValueAttribute); } //adding friendly name of OA String friendlyname; try { friendlyname = AuthConfigurationProviderFactory.getInstance().getSSOFriendlyName(); ExtendedSAMLAttribute oaFriendlyNameAttribute = new ExtendedSAMLAttributeImpl("oaFriendlyName", friendlyname, Constants.MOA_NS_URI, ExtendedSAMLAttribute.ADD_TO_AUTHBLOCK_ONLY); extendedSAMLAttributes.add(oaFriendlyNameAttribute); //generate special AuthBlock text String text = AuthConfigurationProviderFactory.getInstance().getSSOSpecialText(); if (MiscUtil.isEmpty(text)) text=""; String specialText = MessageFormat.format(SPECIAL_TEXT_ATTRIBUTE, new Object[] { generateSpecialText(text, specialAuthBlockTextPatterns) }); //generate unique AuthBlock tokken String uniquetokken = Random.nextProcessReferenceValue(); session.setAuthBlockTokken(uniquetokken); String assertion = MessageFormat.format( AUTH_BLOCK, new Object[] { wbpkNSDeclaration, issuer, issueInstant, authURL, gebeORwbpk, oaURL, gebDat, specialText, MessageFormat.format(AUTHBLOCKTOKKEN_ATTRIBUTE, new Object[] { uniquetokken }), buildExtendedSAMLAttributes(extendedSAMLAttributes)}); return assertion; } catch (ParseException e) { Logger.error("Error on building AUTH-Block: " + e.getMessage()); throw new BuildException("builder.00", new Object[] { "AUTH-Block", e.toString()}); } catch (ConfigurationException e) { Logger.error("Error on building AUTH-Block: " + e.getMessage()); throw new BuildException("builder.00", new Object[] { "AUTH-Block", e.toString()}); } } }