/* * Copyright (c) 2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you 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.sso.saml.builders.assertion; import org.apache.commons.lang.StringUtils; 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.NameID; 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.SubjectBuilder; import org.opensaml.saml2.core.impl.SubjectConfirmationBuilder; import org.opensaml.saml2.core.impl.SubjectConfirmationDataBuilder; 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.util.IdentityCoreConstants; import org.wso2.carbon.identity.sso.saml.SAMLSSOConstants; import org.wso2.carbon.identity.sso.saml.builders.SignKeyDataHolder; import org.wso2.carbon.identity.sso.saml.dto.SAMLSSOAuthnReqDTO; import org.wso2.carbon.identity.sso.saml.util.SAMLSSOUtil; import java.util.Iterator; import java.util.Map; import java.util.StringTokenizer; public class DefaultSAMLAssertionBuilder implements SAMLAssertionBuilder { private static Log log = LogFactory.getLog(DefaultSAMLAssertionBuilder.class); private String userAttributeSeparator = IdentityCoreConstants.MULTI_ATTRIBUTE_SEPARATOR_DEFAULT; @Override public void init() throws IdentityException { //Overridden method, no need to implement the body } @Override public Assertion buildAssertion(SAMLSSOAuthnReqDTO authReqDTO, DateTime notOnOrAfter, String sessionId) throws IdentityException { try { 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(); nameId.setValue(authReqDTO.getUser().getAuthenticatedSubjectIdentifier()); if (authReqDTO.getNameIDFormat() != null) { nameId.setFormat(authReqDTO.getNameIDFormat()); } else { nameId.setFormat(NameIdentifier.EMAIL); } subject.setNameID(nameId); SubjectConfirmation subjectConfirmation = new SubjectConfirmationBuilder() .buildObject(); subjectConfirmation.setMethod(SAMLSSOConstants.SUBJECT_CONFIRM_BEARER); SubjectConfirmationData scData = new SubjectConfirmationDataBuilder().buildObject(); scData.setRecipient(authReqDTO.getAssertionConsumerURL()); scData.setNotOnOrAfter(notOnOrAfter); if (!authReqDTO.isIdPInitSSOEnabled()) { scData.setInResponseTo(authReqDTO.getId()); } subjectConfirmation.setSubjectConfirmationData(scData); subject.getSubjectConfirmations().add(subjectConfirmation); if (authReqDTO.getRequestedRecipients() != null && authReqDTO.getRequestedRecipients().length > 0) { for (String recipient : authReqDTO.getRequestedRecipients()) { subjectConfirmation = new SubjectConfirmationBuilder() .buildObject(); subjectConfirmation.setMethod(SAMLSSOConstants.SUBJECT_CONFIRM_BEARER); scData = new SubjectConfirmationDataBuilder().buildObject(); scData.setRecipient(recipient); scData.setNotOnOrAfter(notOnOrAfter); if (!authReqDTO.isIdPInitSSOEnabled()) { scData.setInResponseTo(authReqDTO.getId()); } subjectConfirmation.setSubjectConfirmationData(scData); 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); if (authReqDTO.isDoSingleLogout()) { authStmt.setSessionIndex(sessionId); } samlAssertion.getAuthnStatements().add(authStmt); /* * If <AttributeConsumingServiceIndex> element is in the <AuthnRequest> and according to * the spec 2.0 the subject MUST be in the assertion */ Map<String, String> claims = SAMLSSOUtil.getAttributes(authReqDTO); if (claims != null && !claims.isEmpty()) { AttributeStatement attrStmt = buildAttributeStatement(claims); if (attrStmt != null) { samlAssertion.getAttributeStatements().add(attrStmt); } } AudienceRestriction audienceRestriction = new AudienceRestrictionBuilder() .buildObject(); Audience issuerAudience = new AudienceBuilder().buildObject(); issuerAudience.setAudienceURI(authReqDTO.getIssuerWithDomain()); audienceRestriction.getAudiences().add(issuerAudience); if (authReqDTO.getRequestedAudiences() != null) { for (String requestedAudience : authReqDTO.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 (authReqDTO.getDoSignAssertions()) { SAMLSSOUtil.setSignature(samlAssertion, authReqDTO.getSigningAlgorithmUri(), authReqDTO .getDigestAlgorithmUri(), new SignKeyDataHolder(authReqDTO.getUser() .getAuthenticatedSubjectIdentifier())); } return samlAssertion; } catch (Exception e) { log.error("Error when reading claim values for generating SAML Response", e); throw IdentityException.error( "Error when reading claim values for generating SAML Response", e); } } private AttributeStatement buildAttributeStatement(Map<String, String> claims) { String claimSeparator = claims.get(IdentityCoreConstants.MULTI_ATTRIBUTE_SEPARATOR); if (StringUtils.isNotBlank(claimSeparator)) { userAttributeSeparator = claimSeparator; } claims.remove(IdentityCoreConstants.MULTI_ATTRIBUTE_SEPARATOR); AttributeStatement attStmt = new AttributeStatementBuilder().buildObject(); Iterator<Map.Entry<String, String>> iterator = claims.entrySet().iterator(); boolean atLeastOneNotEmpty = false; for (int i = 0; i < claims.size(); i++) { Map.Entry<String, String> claimEntry = iterator.next(); String claimUri = claimEntry.getKey(); String claimValue = claimEntry.getValue(); if (claimUri != null && !claimUri.trim().isEmpty() && claimValue != null && !claimValue.trim().isEmpty()) { atLeastOneNotEmpty = true; Attribute attribute = new AttributeBuilder().buildObject(); attribute.setName(claimUri); //setting NAMEFORMAT attribute value to basic attribute profile attribute.setNameFormat(SAMLSSOConstants.NAME_FORMAT_BASIC); // look // https://wiki.shibboleth.net/confluence/display/OpenSAML/OSTwoUsrManJavaAnyTypes XSStringBuilder stringBuilder = (XSStringBuilder) Configuration.getBuilderFactory(). getBuilder(XSString.TYPE_NAME); XSString stringValue; //Need to check if the claim has multiple values if (userAttributeSeparator != null && claimValue.contains(userAttributeSeparator)) { StringTokenizer st = new StringTokenizer(claimValue, userAttributeSeparator); while (st.hasMoreElements()) { String attValue = st.nextElement().toString(); if (attValue != null && attValue.trim().length() > 0) { stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME); stringValue.setValue(attValue); attribute.getAttributeValues().add(stringValue); } } } else { stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME); stringValue.setValue(claimValue); attribute.getAttributeValues().add(stringValue); } attStmt.getAttributes().add(attribute); } } if (atLeastOneNotEmpty) { return attStmt; } else { return null; } } }