package at.gv.egovernment.moa.id.protocols.saml1;
import java.io.IOException;
import java.util.Date;
import java.util.List;
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;
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 AuthenticationData getSaml1AuthenticationData(String samlArtifact)
throws AuthenticationException {
try {
new SAMLArtifactParser(samlArtifact).parseAssertionHandle();
} catch (ParseException ex) {
throw new AuthenticationException("1205", new Object[] {
samlArtifact, ex.toString() });
}
AuthenticationData authData = null;
synchronized (authenticationDataStore) {
// System.out.println("assertionHandle: " + assertionHandle);
try {
authData = authenticationDataStore
.get(samlArtifact, AuthenticationData.class);
} catch (MOADatabaseException e) {
Logger.error("Assertion not found for SAML Artifact: " + samlArtifact);
throw new AuthenticationException("1206", new Object[] { samlArtifact });
}
}
authenticationDataStore.remove(samlArtifact);
long now = new Date().getTime();
if (now - authData.getTimestamp().getTime() > authDataTimeOut)
throw new AuthenticationException("1207", new Object[] { 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(AuthenticationSession session,
OAAuthParameter oaParam,
AuthenticationData authData)
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 = session.getEncodedSignerCertificate();
if (signerCertificate != null) {
signerCertificateBase64 = Base64Utils
.encode(signerCertificate);
} else {
Logger.info("\"provideCertificate\" is \"true\", but no signer certificate available");
}
}
//set prPersion
boolean provideStammzahl = saml1parameter.isProvideStammzahl();
String prPerson = new PersonDataBuilder().build(authData.getIdentityLink(),
provideStammzahl);
//set Authblock
String authBlock = saml1parameter.isProvideAUTHBlock() ? session
.getAuthBlock() : "";
//set IdentityLink for assortion
String ilAssertion = "";
if (saml1parameter.isProvideIdentityLink()) {
if (oaParam.getBusinessService()) {
IdentityLinkReSigner identitylinkresigner = IdentityLinkReSigner.getInstance();
Element resignedilAssertion = identitylinkresigner.resignIdentityLink(authData.getIdentityLink()
.getSamlAssertion());
ilAssertion = DOMUtils.serializeNode(resignedilAssertion);
} else {
ilAssertion = authData.getIdentityLink().getSerializedSamlAssertion();
if (!saml1parameter.isProvideStammzahl())
ilAssertion = StringUtils.replaceAll(ilAssertion, authData.getIdentityLink()
.getIdentificationValue(), "");
}
}
String samlAssertion;
if (session.getUseMandate()) {
List oaAttributes = session.getExtendedSAMLAttributesOA();
if (saml1parameter.isProvideFullMandatorData()) {
try {
ExtendedSAMLAttribute[] extendedSAMLAttributes = addExtendedSamlAttributes(
session.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(session, oaParam, authData);
samlAssertion = new AuthenticationDataAssertionBuilder().buildMandate(
authData,
prPerson,
mandateDate,
authBlock,
ilAssertion,
session.getBkuURL(),
signerCertificateBase64,
oaParam.getBusinessService(),
oaAttributes,
useCondition,
conditionLength);
} else {
samlAssertion = new AuthenticationDataAssertionBuilder().build(
authData,
prPerson,
authBlock,
ilAssertion,
session.getBkuURL(),
signerCertificateBase64,
oaParam.getBusinessService(),
session.getExtendedSAMLAttributesOA(),
useCondition,
conditionLength);
}
authData.setSamlAssertion(samlAssertion);
String samlArtifact = new SAMLArtifactBuilder().build(
session.getAuthURL(), Random.nextRandom(),
saml1parameter.getSourceID());
storeAuthenticationData(samlArtifact, authData);
Logger.info("Anmeldedaten zu MOASession " + session.getSessionID()
+ " angelegt, SAML Artifakt " + samlArtifact);
return samlArtifact;
} catch (Throwable ex) {
throw new BuildException("builder.00", new Object[] {
"AuthenticationData", ex.toString() }, ex);
}
}
private String generateMandateDate(AuthenticationSession session,
OAAuthParameter oaParam, AuthenticationData authData
) throws AuthenticationException, BuildException,
ParseException, ConfigurationException, ServiceException,
ValidateException {
if (session == null)
throw new AuthenticationException("auth.10", new Object[] {
REQ_VERIFY_AUTH_BLOCK, PARAM_SESSIONID });
IdentityLink tempIdentityLink = null;
Element mandate = session.getMandate();
if (session.getUseMandate()) {
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()) {
oatargetType = AuthenticationSession.REGISTERANDORDNR_PREFIX_+session.getDomainIdentifier();
} 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,
AuthenticationData authData) 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, authData);
}
} catch (AuthenticationException ex) {
throw ex;
} catch (Throwable ex) {
throw new AuthenticationException("auth.06",
new Object[] { samlArtifact });
}
}
}