/*******************************************************************************
* 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.saml1;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import at.gv.egovernment.moa.id.auth.AuthenticationServer;
import at.gv.egovernment.moa.id.auth.builder.AuthenticationDataAssertionBuilder;
import at.gv.egovernment.moa.id.auth.builder.BPKBuilder;
import at.gv.egovernment.moa.id.auth.builder.PersonDataBuilder;
import at.gv.egovernment.moa.id.auth.builder.SAMLArtifactBuilder;
import at.gv.egovernment.moa.id.auth.data.AuthenticationSession;
import at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute;
import at.gv.egovernment.moa.id.auth.data.IdentityLink;
import at.gv.egovernment.moa.id.auth.exception.AuthenticationException;
import at.gv.egovernment.moa.id.auth.exception.BuildException;
import at.gv.egovernment.moa.id.auth.exception.ParseException;
import at.gv.egovernment.moa.id.auth.exception.ServiceException;
import at.gv.egovernment.moa.id.auth.exception.ValidateException;
import at.gv.egovernment.moa.id.auth.parser.SAMLArtifactParser;
import at.gv.egovernment.moa.id.auth.validator.parep.ParepUtils;
import at.gv.egovernment.moa.id.commons.db.dao.config.OASAML1;
import at.gv.egovernment.moa.id.commons.db.ex.MOADatabaseException;
import at.gv.egovernment.moa.id.config.ConfigurationException;
import at.gv.egovernment.moa.id.config.auth.OAAuthParameter;
import at.gv.egovernment.moa.id.data.AuthenticationData;
import at.gv.egovernment.moa.id.moduls.IRequest;
import at.gv.egovernment.moa.id.storage.AssertionStorage;
//import at.gv.egovernment.moa.id.util.IdentityLinkReSigner;
import at.gv.egovernment.moa.id.util.Random;
import at.gv.egovernment.moa.logging.Logger;
import at.gv.egovernment.moa.util.Base64Utils;
import at.gv.egovernment.moa.util.Constants;
import at.gv.egovernment.moa.util.DOMUtils;
import at.gv.egovernment.moa.util.StringUtils;
import at.gv.util.xsd.persondata.IdentificationType;
import at.gv.util.xsd.persondata.IdentificationType.Value;
import at.gv.util.xsd.persondata.PersonNameType;
import at.gv.util.xsd.persondata.PersonNameType.FamilyName;
import at.gv.util.xsd.persondata.PhysicalPersonType;
public class SAML1AuthenticationServer extends AuthenticationServer {
private static SAML1AuthenticationServer instance;
public static SAML1AuthenticationServer getInstace() {
if (instance == null)
instance = new SAML1AuthenticationServer();
return instance;
}
private static AssertionStorage authenticationDataStore = AssertionStorage.getInstance();
/**
* time out in milliseconds used by {@link cleanup} for authentication data
* store
*/
private static final long authDataTimeOut = 2 * 60 * 1000; // default 2 minutes
public Throwable getErrorResponse(String samlArtifact) throws AuthenticationException {
try {
new SAMLArtifactParser(samlArtifact).parseAssertionHandle();
} catch (ParseException ex) {
throw new AuthenticationException("1205", new Object[] {
samlArtifact, ex.toString() });
}
Throwable error = null;
synchronized (authenticationDataStore) {
try {
error = authenticationDataStore
.get(samlArtifact, Throwable.class);
authenticationDataStore.remove(samlArtifact);
} catch (MOADatabaseException e) {
Logger.error("Assertion not found for SAML Artifact: " + samlArtifact);
throw new AuthenticationException("1206", new Object[] { samlArtifact });
}
}
return error;
}
/**
* Retrieves AuthenticationData
indexed by the SAML artifact.
* The AuthenticationData
is deleted from the store upon end of
* this call.
*
* @return AuthenticationData
*/
public String getSaml1AuthenticationData(String samlArtifact)
throws AuthenticationException {
try {
new SAMLArtifactParser(samlArtifact).parseAssertionHandle();
} catch (ParseException ex) {
throw new AuthenticationException("1205", new Object[] {
samlArtifact, ex.toString() });
}
String authData = null;
synchronized (authenticationDataStore) {
// System.out.println("assertionHandle: " + assertionHandle);
try {
authData = authenticationDataStore
.get(samlArtifact, String.class, authDataTimeOut);
} catch (MOADatabaseException e) {
Logger.error("Assertion not found for SAML Artifact: " + samlArtifact);
throw new AuthenticationException("1206", new Object[] { samlArtifact });
}
}
authenticationDataStore.remove(samlArtifact);
Logger.debug("Assertion delivered for SAML Artifact: " + samlArtifact);
return authData;
}
public String BuildErrorAssertion(Throwable error, IRequest protocolRequest)
throws BuildException, MOADatabaseException {
String samlArtifact = new SAMLArtifactBuilder().build(
protocolRequest.getOAURL(), protocolRequest.getRequestID(),
null);
authenticationDataStore.put(samlArtifact, error);
return samlArtifact;
}
public String BuildSAMLArtifact(OAAuthParameter oaParam,
SAML1AuthenticationData authData, String sourceID)
throws ConfigurationException, BuildException, AuthenticationException {
//Load SAML1 Parameter from OA config
OASAML1 saml1parameter = oaParam.getSAML1Parameter();
boolean useCondition = saml1parameter.isUseCondition();
int conditionLength = saml1parameter.getConditionLength().intValue();
try {
//set BASE64 encoded signer certificate
String signerCertificateBase64 = "";
if (saml1parameter.isProvideCertificate()) {
byte[] signerCertificate = authData.getSignerCertificate();
if (signerCertificate != null) {
signerCertificateBase64 = Base64Utils
.encode(signerCertificate);
} else {
Logger.info("\"provideCertificate\" is \"true\", but no signer certificate available");
}
}
//set prPersion
boolean provideStammzahl = saml1parameter.isProvideStammzahl()
|| oaParam.getBusinessService();
String prPerson = "";
String ilAssertion = "";
if (authData.getIdentityLink() != null) {
prPerson = new PersonDataBuilder().build(authData.getIdentityLink(),
provideStammzahl);
//set IdentityLink for assortion
if (saml1parameter.isProvideIdentityLink()) {
ilAssertion = authData.getIdentityLink().getSerializedSamlAssertion();
if (!provideStammzahl)
ilAssertion = StringUtils.replaceAll(ilAssertion, authData.getIdentityLink()
.getIdentificationValue(), "");
}
} else {
Logger.info("No IdentityLink available! Build attribute 'PersonDate' from givenname, familyname and dateofbirth. ");
PhysicalPersonType person = new PhysicalPersonType();
PersonNameType name = new PersonNameType();
person.setName(name);
FamilyName familyName = new FamilyName();
name.getFamilyName().add(familyName );
IdentificationType id = new IdentificationType();
person.getIdentification().add(id );
Value value = new Value();
id.setValue(value );
id.setType(Constants.URN_PREFIX_BASEID);
value.setValue("");
familyName.setValue(authData.getFamilyName());
familyName.setPrimary("undefined");
name.getGivenName().add(authData.getGivenName());
person.setDateOfBirth(authData.getFormatedDateOfBirth());
JAXBContext jc = JAXBContext.newInstance("at.gv.util.xsd.persondata");
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
// m.setProperty("com.sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapper() {
// public String getPreferredPrefix(String arg0, String arg1, boolean arg2) {
// if (Constants.PD_NS_URI.equals(arg0))
// return Constants.PD_PREFIX;
// else
// return arg1;
// }
// });
ByteArrayOutputStream stream = new ByteArrayOutputStream();
m.marshal(
new JAXBElement(new QName(Constants.PD_NS_URI,"Person"), PhysicalPersonType.class, person),
stream);
prPerson = StringUtils.removeXMLDeclaration(new String(stream.toByteArray(), "UTF-8"));
stream.close();
}
//set Authblock
String authBlock = "";
if (authData.getAuthBlock() != null) {
authBlock = saml1parameter.isProvideAUTHBlock() ? authData.getAuthBlock() : "";
} else {
Logger.info("\"provideAuthBlock\" is \"true\", but no authblock available");
}
String samlAssertion;
if (authData.isUseMandate()) {
List oaAttributes = authData.getExtendedSAMLAttributesOA();
//only provide full mandate if it is included.
//In case of federation only a short mandate could be include
if (saml1parameter.isProvideFullMandatorData()
&& authData.getMISMandate().isFullMandateIncluded()) {
try {
ExtendedSAMLAttribute[] extendedSAMLAttributes = addExtendedSamlAttributes(
authData.getMISMandate(), oaParam.getBusinessService(),
saml1parameter.isProvideStammzahl());
if (extendedSAMLAttributes != null) {
String identifier = "MISService";
String friendlyName ="MISService";
int length = extendedSAMLAttributes.length;
for (int i = 0; i < length; i++) {
ExtendedSAMLAttribute samlAttribute = extendedSAMLAttributes[i];
Object value = verifySAMLAttribute(samlAttribute, i, identifier,
friendlyName);
if ((value instanceof String) || (value instanceof Element)) {
switch (samlAttribute.getAddToAUTHBlock()) {
case ExtendedSAMLAttribute.ADD_TO_AUTHBLOCK:
replaceExtendedSAMLAttribute(oaAttributes, samlAttribute);
break;
case ExtendedSAMLAttribute.NOT_ADD_TO_AUTHBLOCK:
replaceExtendedSAMLAttribute(oaAttributes, samlAttribute);
break;
case ExtendedSAMLAttribute.ADD_TO_AUTHBLOCK_ONLY:
break;
default:
Logger
.info("Invalid return value from method \"getAddToAUTHBlock()\" ("
+ samlAttribute.getAddToAUTHBlock()
+ ") in SAML attribute number "
+ (i + 1)
+ " for infobox " + identifier);
throw new ValidateException("validator.47", new Object[] {
friendlyName, String.valueOf((i + 1)) });
}
} else {
Logger
.info("The type of SAML-Attribute number "
+ (i + 1)
+ " returned from "
+ identifier
+ "-infobox validator is not valid. Must be either \"java.Lang.String\""
+ " or \"org.w3c.dom.Element\"");
throw new ValidateException("validator.46", new Object[] {
identifier, String.valueOf((i + 1)) });
}
}
}
} catch (SAXException e) {
throw new AuthenticationException("auth.16",
new Object[] { GET_MIS_SESSIONID }, e);
} catch (IOException e) {
throw new AuthenticationException("auth.16",
new Object[] { GET_MIS_SESSIONID }, e);
} catch (ParserConfigurationException e) {
throw new AuthenticationException("auth.16",
new Object[] { GET_MIS_SESSIONID }, e);
} catch (TransformerException e) {
throw new AuthenticationException("auth.16",
new Object[] { GET_MIS_SESSIONID }, e);
}
}
String mandateDate = generateMandateDate(oaParam, authData);
samlAssertion = new AuthenticationDataAssertionBuilder().buildMandate(
authData,
prPerson,
mandateDate,
authBlock,
ilAssertion,
authData.getBkuURL(),
signerCertificateBase64,
oaParam.getBusinessService(),
oaAttributes,
useCondition,
conditionLength);
} else {
samlAssertion = new AuthenticationDataAssertionBuilder().build(
authData,
prPerson,
authBlock,
ilAssertion,
authData.getBkuURL(),
signerCertificateBase64,
oaParam.getBusinessService(),
authData.getExtendedSAMLAttributesOA(),
useCondition,
conditionLength);
}
//authData.setSamlAssertion(samlAssertion);
String samlArtifact = new SAMLArtifactBuilder().build(
authData.getIssuer(), Random.nextRandom(),
sourceID);
storeAuthenticationData(samlArtifact, samlAssertion);
Logger.info("Anmeldedaten angelegt, SAML Artifakt " + samlArtifact);
return samlArtifact;
} catch (Throwable ex) {
throw new BuildException("builder.00", new Object[] {
"AuthenticationData", ex.toString() }, ex);
}
}
private String generateMandateDate(OAAuthParameter oaParam, AuthenticationData authData
) throws AuthenticationException, BuildException,
ParseException, ConfigurationException, ServiceException,
ValidateException {
if (authData == null)
throw new AuthenticationException("auth.10", new Object[] {
REQ_VERIFY_AUTH_BLOCK, PARAM_SESSIONID });
IdentityLink tempIdentityLink = null;
Element mandate = authData.getMandate();
if (authData.isUseMandate()) {
tempIdentityLink = new IdentityLink();
Element mandator = ParepUtils.extractMandator(mandate);
String dateOfBirth = "";
Element prPerson = null;
String familyName = "";
String givenName = "";
String identificationType = "";
String identificationValue = "";
if (mandator != null) {
boolean physical = ParepUtils.isPhysicalPerson(mandator);
if (physical) {
familyName = ParepUtils.extractText(mandator,
"descendant-or-self::pr:Name/pr:FamilyName/text()");
givenName = ParepUtils.extractText(mandator,
"descendant-or-self::pr:Name/pr:GivenName/text()");
dateOfBirth = ParepUtils
.extractMandatorDateOfBirth(mandator);
} else {
familyName = ParepUtils.extractMandatorFullName(mandator);
}
identificationType = ParepUtils.getIdentification(mandator,
"Type");
identificationValue = ParepUtils.extractMandatorWbpk(mandator);
prPerson = ParepUtils.extractPrPersonOfMandate(mandate);
if (physical
&& oaParam.getBusinessService()
&& identificationType != null
&& Constants.URN_PREFIX_BASEID
.equals(identificationType)) {
// now we calculate the wbPK and do so if we got it from the
// BKU
//load IdentityLinkDomainType from OAParam
String type = oaParam.getIdentityLinkDomainIdentifier();
if (type.startsWith(Constants.URN_PREFIX_WBPK + "+"))
identificationType = type;
else
identificationType = Constants.URN_PREFIX_WBPK + "+"
+ type;
identificationValue = new BPKBuilder().buildWBPK(
identificationValue, identificationType);
ParepUtils
.HideStammZahlen(prPerson, true, null, null, true);
}
tempIdentityLink.setDateOfBirth(dateOfBirth);
tempIdentityLink.setFamilyName(familyName);
tempIdentityLink.setGivenName(givenName);
tempIdentityLink.setIdentificationType(identificationType);
tempIdentityLink.setIdentificationValue(identificationValue);
tempIdentityLink.setPrPerson(prPerson);
try {
tempIdentityLink.setSamlAssertion(authData.getIdentityLink()
.getSamlAssertion());
} catch (Exception e) {
throw new ValidateException("validator.64", null);
}
}
}
Element mandatePerson = tempIdentityLink.getPrPerson();
String mandateData = null;
try {
boolean provideStammzahl = oaParam.getSAML1Parameter().isProvideStammzahl();
String oatargetType;
if(oaParam.getBusinessService()) {
if (oaParam.getIdentityLinkDomainIdentifier().startsWith(AuthenticationSession.REGISTERANDORDNR_PREFIX_))
oatargetType = oaParam.getIdentityLinkDomainIdentifier();
else
oatargetType = AuthenticationSession.REGISTERANDORDNR_PREFIX_+oaParam.getIdentityLinkDomainIdentifier();
} else {
oatargetType = AuthenticationSession.TARGET_PREFIX_ + oaParam.getTarget();
}
Element prIdentification = (Element) mandatePerson
.getElementsByTagNameNS(Constants.PD_NS_URI,
"Identification").item(0);
if (!oatargetType.equals(tempIdentityLink.getIdentificationType())) {
String isPrPerson = mandatePerson.getAttribute("xsi:type");
if (!StringUtils.isEmpty(isPrPerson)) {
if (isPrPerson.equalsIgnoreCase("pr:PhysicalPerson")) {
String baseid = getBaseId(mandatePerson);
Element identificationBpK = createIdentificationBPK(mandatePerson,
baseid, oaParam.getTarget());
if (!provideStammzahl) {
prIdentification.getFirstChild().setTextContent("");
}
mandatePerson.insertBefore(identificationBpK,
prIdentification);
}
}
} else {
// Element identificationBpK = mandatePerson.getOwnerDocument()
// .createElementNS(Constants.PD_NS_URI, "Identification");
// Element valueBpK = mandatePerson.getOwnerDocument().createElementNS(
// Constants.PD_NS_URI, "Value");
//
// valueBpK.appendChild(mandatePerson.getOwnerDocument().createTextNode(
// tempIdentityLink.getIdentificationValue()));
// Element typeBpK = mandatePerson.getOwnerDocument().createElementNS(
// Constants.PD_NS_URI, "Type");
// typeBpK.appendChild(mandatePerson.getOwnerDocument().createTextNode(
// "urn:publicid:gv.at:cdid+bpk"));
// identificationBpK.appendChild(valueBpK);
// identificationBpK.appendChild(typeBpK);
//
// mandatePerson.insertBefore(identificationBpK, prIdentification);
}
mandateData = DOMUtils.serializeNode(mandatePerson);
} catch (TransformerException e1) {
throw new AuthenticationException("auth.16",
new Object[] { GET_MIS_SESSIONID });
} catch (IOException e1) {
throw new AuthenticationException("auth.16",
new Object[] { GET_MIS_SESSIONID });
}
return mandateData;
}
/**
* Stores authentication data indexed by the assertion handle contained in
* the given saml artifact.
*
* @param samlArtifact
* SAML artifact
* @param authData
* authentication data
* @throws AuthenticationException
* when SAML artifact is invalid
*/
private void storeAuthenticationData(String samlArtifact,
String samlAssertion) throws AuthenticationException {
try {
SAMLArtifactParser parser = new SAMLArtifactParser(samlArtifact);
// check type code 0x0001
byte[] typeCode = parser.parseTypeCode();
if (typeCode[0] != 0 || typeCode[1] != 1)
throw new AuthenticationException("auth.06",
new Object[] { samlArtifact });
parser.parseAssertionHandle();
synchronized (authenticationDataStore) {
Logger.debug("Assertion stored for SAML Artifact: "
+ samlArtifact);
authenticationDataStore.put(samlArtifact, samlAssertion);
}
} catch (AuthenticationException ex) {
throw ex;
} catch (Throwable ex) {
throw new AuthenticationException("auth.06",
new Object[] { samlArtifact });
}
}
}