/*******************************************************************************
* 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.IOAAuthParameters;
import at.gv.egiz.eaaf.core.api.IRequest;
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.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.DOMUtils;
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
* @version $Id$
*/
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.getGenericData(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()});
}
}
}