/*
* Copyright 2005-2014 WSO2, Inc. (http://wso2.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wso2.carbon.identity.tools.saml.validator.processors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joda.time.DateTime;
import org.opensaml.Configuration;
import org.opensaml.common.SAMLVersion;
import org.opensaml.saml1.core.NameIdentifier;
import org.opensaml.saml2.core.Assertion;
import org.opensaml.saml2.core.Attribute;
import org.opensaml.saml2.core.AttributeStatement;
import org.opensaml.saml2.core.AttributeValue;
import org.opensaml.saml2.core.Audience;
import org.opensaml.saml2.core.AudienceRestriction;
import org.opensaml.saml2.core.AuthnContext;
import org.opensaml.saml2.core.AuthnContextClassRef;
import org.opensaml.saml2.core.AuthnStatement;
import org.opensaml.saml2.core.Conditions;
import org.opensaml.saml2.core.EncryptedAssertion;
import org.opensaml.saml2.core.NameID;
import org.opensaml.saml2.core.Response;
import org.opensaml.saml2.core.Status;
import org.opensaml.saml2.core.StatusCode;
import org.opensaml.saml2.core.StatusMessage;
import org.opensaml.saml2.core.Subject;
import org.opensaml.saml2.core.SubjectConfirmation;
import org.opensaml.saml2.core.SubjectConfirmationData;
import org.opensaml.saml2.core.impl.AssertionBuilder;
import org.opensaml.saml2.core.impl.AttributeBuilder;
import org.opensaml.saml2.core.impl.AttributeStatementBuilder;
import org.opensaml.saml2.core.impl.AudienceBuilder;
import org.opensaml.saml2.core.impl.AudienceRestrictionBuilder;
import org.opensaml.saml2.core.impl.AuthnContextBuilder;
import org.opensaml.saml2.core.impl.AuthnContextClassRefBuilder;
import org.opensaml.saml2.core.impl.AuthnStatementBuilder;
import org.opensaml.saml2.core.impl.ConditionsBuilder;
import org.opensaml.saml2.core.impl.NameIDBuilder;
import org.opensaml.saml2.core.impl.StatusBuilder;
import org.opensaml.saml2.core.impl.StatusCodeBuilder;
import org.opensaml.saml2.core.impl.StatusMessageBuilder;
import org.opensaml.saml2.core.impl.SubjectBuilder;
import org.opensaml.saml2.core.impl.SubjectConfirmationBuilder;
import org.opensaml.saml2.core.impl.SubjectConfirmationDataBuilder;
import org.opensaml.xml.encryption.EncryptionConstants;
import org.opensaml.xml.schema.XSString;
import org.opensaml.xml.schema.impl.XSStringBuilder;
import org.wso2.carbon.identity.base.IdentityException;
import org.wso2.carbon.identity.core.model.SAMLSSOServiceProviderDO;
import org.wso2.carbon.identity.sso.saml.SAMLSSOConstants;
import org.wso2.carbon.identity.sso.saml.builders.SignKeyDataHolder;
import org.wso2.carbon.identity.sso.saml.util.SAMLSSOUtil;
import org.wso2.carbon.identity.tools.saml.validator.util.SAMLValidatorUtil;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
import java.util.Iterator;
import java.util.Map;
public class SAMLResponseBuilder {
private static Log log = LogFactory.getLog(SAMLResponseBuilder.class);
/**
* Build SAML response using IdP configuration & user name
*
* @param ssoIdPConfigs
* @param userName
* @return SAML Response object
* @throws IdentityException
*/
public Response buildSAMLResponse(SAMLSSOServiceProviderDO ssoIdPConfigs, String userName)
throws IdentityException {
if (log.isDebugEnabled()) {
log.debug("Building SAML Response for the consumer '" +
ssoIdPConfigs.getAssertionConsumerUrl() + "'");
}
Response response = new org.opensaml.saml2.core.impl.ResponseBuilder().buildObject();
response.setIssuer(SAMLSSOUtil.getIssuer());
response.setID(SAMLSSOUtil.createID());
response.setDestination(ssoIdPConfigs.getAssertionConsumerUrl());
response.setStatus(buildStatus(SAMLSSOConstants.StatusCodes.SUCCESS_CODE, null));
response.setVersion(SAMLVersion.VERSION_20);
DateTime issueInstant = new DateTime();
DateTime notOnOrAfter =
new DateTime(issueInstant.getMillis() +
SAMLSSOUtil.getSAMLResponseValidityPeriod() * 60 *
1000);
response.setIssueInstant(issueInstant);
Assertion assertion = buildSAMLAssertion(ssoIdPConfigs, notOnOrAfter, userName);
if (ssoIdPConfigs.isDoEnableEncryptedAssertion()) {
String domainName = MultitenantUtils.getTenantDomain(userName);
String alias = ssoIdPConfigs.getCertAlias();
if (alias != null) {
EncryptedAssertion encryptedAssertion =
SAMLSSOUtil.setEncryptedAssertion(assertion,
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256,
alias,
domainName);
response.getEncryptedAssertions().add(encryptedAssertion);
}
} else {
response.getAssertions().add(assertion);
}
if (ssoIdPConfigs.isDoSignResponse()) {
SAMLSSOUtil.setSignature(response, ssoIdPConfigs.getSigningAlgorithmUri(), ssoIdPConfigs
.getDigestAlgorithmUri(), new SignKeyDataHolder(userName));
}
return response;
}
/**
* Build SAML assertion
*
* @param ssoIdPConfigs
* @param notOnOrAfter
* @param userName
* @return Assertion object
* @throws IdentityException
*/
private Assertion buildSAMLAssertion(SAMLSSOServiceProviderDO ssoIdPConfigs,
DateTime notOnOrAfter, String userName)
throws IdentityException {
DateTime currentTime = new DateTime();
Assertion samlAssertion = new AssertionBuilder().buildObject();
samlAssertion.setID(SAMLSSOUtil.createID());
samlAssertion.setVersion(SAMLVersion.VERSION_20);
samlAssertion.setIssuer(SAMLSSOUtil.getIssuer());
samlAssertion.setIssueInstant(currentTime);
Subject subject = new SubjectBuilder().buildObject();
NameID nameId = new NameIDBuilder().buildObject();
String claimValue = null;
if (ssoIdPConfigs.getNameIdClaimUri() != null) {
Map<String, String> claims =
SAMLValidatorUtil.getUserClaimValues(userName,
new String[]{ssoIdPConfigs.getNameIdClaimUri()},
null);
claimValue = claims.get(ssoIdPConfigs.getNameIdClaimUri());
nameId.setValue(claimValue);
}
if (claimValue == null) {
nameId.setValue(userName);
}
if (ssoIdPConfigs.getNameIDFormat() != null) {
nameId.setFormat(ssoIdPConfigs.getNameIDFormat());
} else {
nameId.setFormat(NameIdentifier.EMAIL);
}
subject.setNameID(nameId);
SubjectConfirmation subjectConfirmation = new SubjectConfirmationBuilder().buildObject();
subjectConfirmation.setMethod(SAMLSSOConstants.SUBJECT_CONFIRM_BEARER);
SubjectConfirmationData subjectConfirmationData =
new SubjectConfirmationDataBuilder().buildObject();
subjectConfirmationData.setRecipient(ssoIdPConfigs.getAssertionConsumerUrl());
subjectConfirmationData.setNotOnOrAfter(notOnOrAfter);
subjectConfirmation.setSubjectConfirmationData(subjectConfirmationData);
subject.getSubjectConfirmations().add(subjectConfirmation);
samlAssertion.setSubject(subject);
AuthnStatement authStmt = new AuthnStatementBuilder().buildObject();
authStmt.setAuthnInstant(new DateTime());
AuthnContext authContext = new AuthnContextBuilder().buildObject();
AuthnContextClassRef authCtxClassRef = new AuthnContextClassRefBuilder().buildObject();
authCtxClassRef.setAuthnContextClassRef(AuthnContext.PASSWORD_AUTHN_CTX);
authContext.setAuthnContextClassRef(authCtxClassRef);
authStmt.setAuthnContext(authContext);
samlAssertion.getAuthnStatements().add(authStmt);
Map<String, String> claims = getAttributes(ssoIdPConfigs, userName);
if (claims != null) {
samlAssertion.getAttributeStatements().add(buildAttributeStatement(claims));
}
AudienceRestriction audienceRestriction = new AudienceRestrictionBuilder().buildObject();
Audience issuerAudience = new AudienceBuilder().buildObject();
issuerAudience.setAudienceURI(ssoIdPConfigs.getIssuer());
audienceRestriction.getAudiences().add(issuerAudience);
if (ssoIdPConfigs.getRequestedAudiences() != null) {
for (String requestedAudience : ssoIdPConfigs.getRequestedAudiences()) {
Audience audience = new AudienceBuilder().buildObject();
audience.setAudienceURI(requestedAudience);
audienceRestriction.getAudiences().add(audience);
}
}
Conditions conditions = new ConditionsBuilder().buildObject();
conditions.setNotBefore(currentTime);
conditions.setNotOnOrAfter(notOnOrAfter);
conditions.getAudienceRestrictions().add(audienceRestriction);
samlAssertion.setConditions(conditions);
if (ssoIdPConfigs.isDoSignAssertions()) {
SAMLSSOUtil.setSignature(samlAssertion, ssoIdPConfigs.getSigningAlgorithmUri(), ssoIdPConfigs
.getDigestAlgorithmUri(), new SignKeyDataHolder(userName));
}
return samlAssertion;
}
/**
* Get attributes
*
* @param ssoIdPConfigs
* @param userName
* @return attributes
* @throws IdentityException
*/
private Map<String, String> getAttributes(SAMLSSOServiceProviderDO ssoIdPConfigs,
String userName) throws IdentityException {
String[] requestedClaims = ssoIdPConfigs.getRequestedClaims();
if (requestedClaims == null) {
return null;
}
return SAMLValidatorUtil.getUserClaimValues(userName, requestedClaims, null);
}
/**
* Get status
*
* @param status
* @param statMsg
* @return Status object
*/
private Status buildStatus(String status, String statMsg) {
Status stat = new StatusBuilder().buildObject();
// Set the status code
StatusCode statCode = new StatusCodeBuilder().buildObject();
statCode.setValue(status);
stat.setStatusCode(statCode);
// Set the status Message
if (statMsg != null) {
StatusMessage statMesssage = new StatusMessageBuilder().buildObject();
statMesssage.setMessage(statMsg);
stat.setStatusMessage(statMesssage);
}
return stat;
}
/**
* Build Attribute Statement
*
* @param claims
* @return AttributeStatement
*/
private AttributeStatement buildAttributeStatement(Map<String, String> claims) {
AttributeStatement attStmt = null;
if (claims != null) {
attStmt = new AttributeStatementBuilder().buildObject();
Iterator<String> ite = claims.keySet().iterator();
for (int i = 0; i < claims.size(); i++) {
Attribute attrib = new AttributeBuilder().buildObject();
String claimUri = ite.next();
attrib.setName(claimUri);
// look
// https://wiki.shibboleth.net/confluence/display/OpenSAML/OSTwoUsrManJavaAnyTypes
XSStringBuilder stringBuilder =
(XSStringBuilder) Configuration.getBuilderFactory()
.getBuilder(XSString.TYPE_NAME);
XSString stringValue =
stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME,
XSString.TYPE_NAME);
stringValue.setValue(claims.get(claimUri));
attrib.getAttributeValues().add(stringValue);
attStmt.getAttributes().add(attrib);
}
}
return attStmt;
}
}