import javax.xml.namespace.QName;
-import org.apache.log4j.Logger;
import org.joda.time.DateTime;
+import org.opensaml.Configuration;
import org.opensaml.common.SAMLObject;
import org.opensaml.common.SAMLObjectBuilder;
import org.opensaml.common.SAMLVersion;
-import org.opensaml.common.impl.SAMLObjectContentReference;
-import org.opensaml.log.Level;
+import org.opensaml.common.binding.encoding.SAMLMessageEncoder;
import org.opensaml.saml1.core.Assertion;
import org.opensaml.saml1.core.AttributeQuery;
import org.opensaml.saml1.core.AttributeStatement;
import org.opensaml.saml2.metadata.RoleDescriptor;
import org.opensaml.saml2.metadata.SPSSODescriptor;
import org.opensaml.saml2.metadata.SSODescriptor;
+import org.opensaml.ws.message.encoder.MessageEncodingException;
import org.opensaml.xml.XMLObjectBuilder;
+import org.opensaml.xml.io.Marshaller;
+import org.opensaml.xml.io.MarshallingException;
+import org.opensaml.xml.security.SecurityException;
+import org.opensaml.xml.security.SecurityHelper;
import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.signature.Signature;
+import org.opensaml.xml.signature.SignatureException;
import org.opensaml.xml.signature.Signer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import edu.internet2.middleware.shibboleth.common.attribute.AttributeRequestException;
import edu.internet2.middleware.shibboleth.common.attribute.BaseAttribute;
import edu.internet2.middleware.shibboleth.common.attribute.encoding.AttributeEncodingException;
import edu.internet2.middleware.shibboleth.common.attribute.encoding.SAML1NameIdentifierEncoder;
import edu.internet2.middleware.shibboleth.common.attribute.provider.SAML1AttributeAuthority;
-import edu.internet2.middleware.shibboleth.common.log.AuditLogEntry;
import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
+import edu.internet2.middleware.shibboleth.common.profile.provider.BaseSAMLProfileRequestContext;
+import edu.internet2.middleware.shibboleth.common.relyingparty.provider.CryptoOperationRequirementLevel;
import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml1.AbstractSAML1ProfileConfiguration;
import edu.internet2.middleware.shibboleth.idp.profile.AbstractSAMLProfileHandler;
+import edu.internet2.middleware.shibboleth.idp.session.Session;
/** Common implementation details for profile handlers. */
public abstract class AbstractSAML1ProfileHandler extends AbstractSAMLProfileHandler {
public static final SAMLVersion SAML_VERSION = SAMLVersion.VERSION_11;
/** Class logger. */
- private static Logger log = Logger.getLogger(AbstractSAML1ProfileHandler.class);
+ private static Logger log = LoggerFactory.getLogger(AbstractSAML1ProfileHandler.class);
/** Builder of Response objects. */
private SAMLObjectBuilder<Response> responseBuilder;
signatureBuilder = (XMLObjectBuilder<Signature>) getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME);
}
+ /** {@inheritDoc} */
+ protected void populateRequestContext(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
+ BaseSAML1ProfileRequestContext saml1Request = (BaseSAML1ProfileRequestContext) requestContext;
+ try {
+ super.populateRequestContext(requestContext);
+ } catch (ProfileException e) {
+ if (saml1Request.getFailureStatus() == null) {
+ saml1Request.setFailureStatus(buildStatus(StatusCode.REQUESTER, null, e.getMessage()));
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Populates the request context with the information about the user.
+ *
+ * This method requires the the following request context properties to be populated: inbound message transport,
+ * relying party ID
+ *
+ * This methods populates the following request context properties: user's session, user's principal name, and
+ * service authentication method
+ *
+ * @param requestContext current request context
+ */
+ protected void populateUserInformation(BaseSAMLProfileRequestContext requestContext) {
+ Session userSession = getUserSession(requestContext.getInboundMessageTransport());
+ if (userSession == null) {
+ NameIdentifier subject = (NameIdentifier) requestContext.getSubjectNameIdentifier();
+ if (subject != null && subject.getNameIdentifier() != null) {
+ userSession = getUserSession(subject.getNameIdentifier());
+ }
+ }
+
+ if (userSession != null) {
+ requestContext.setUserSession(userSession);
+ requestContext.setPrincipalName(userSession.getPrincipalName());
+ requestContext.setPrincipalAuthenticationMethod(userSession.getServicesInformation().get(
+ requestContext.getPeerEntityId()).getAuthenticationMethod().getAuthenticationMethod());
+ }
+ }
+
/**
* Checks that the SAML major version for a request is 1.
*
// create the assertion and add the attribute statement
Assertion assertion = buildAssertion(requestContext, issueInstant);
- if (statements != null) {
+ if (statements != null && !statements.isEmpty()) {
assertion.getStatements().addAll(statements);
}
assertion.setID(getIdGenerator().generateIdentifier());
assertion.setIssueInstant(issueInstant);
assertion.setVersion(SAMLVersion.VERSION_11);
- assertion.setIssuer(requestContext.getAssertingPartyEntityId());
+ assertion.setIssuer(requestContext.getLocalEntityId());
Conditions conditions = buildConditions(requestContext, issueInstant);
assertion.setConditions(conditions);
*/
protected NameIdentifier buildNameId(BaseSAML1ProfileRequestContext<?, ?, ?> requestContext)
throws ProfileException {
- if (log.isDebugEnabled()) {
- log.debug("Building assertion NameIdentifier to relying party " + requestContext.getRelyingPartyEntityId()
- + " for principal " + requestContext.getPrincipalName());
- }
- Map<String, BaseAttribute> principalAttributes = requestContext.getPrincipalAttributes();
- List<String> supportedNameFormats = getNameFormats(requestContext);
-
- if (log.isDebugEnabled()) {
- log.debug("Supported name formats: " + supportedNameFormats);
+ log.debug("Building assertion NameIdentifier to relying party {} for principal {}", requestContext
+ .getInboundMessageIssuer(), requestContext.getPrincipalName());
+ Map<String, BaseAttribute> principalAttributes = requestContext.getAttributes();
+ if (principalAttributes == null || principalAttributes.isEmpty()) {
+ log.error("No attributes for principal {}, unable to construct of NameID", requestContext
+ .getPrincipalName());
+ requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER, null,
+ "Unable to construct NameIdentifier"));
+ throw new ProfileException("No principal attributes support NameIdentifier construction");
}
- if (principalAttributes == null || supportedNameFormats == null) {
- log.error("No attributes for principal " + requestContext.getPrincipalName()
- + " support constructions of NameIdentifier");
+ List<String> supportedNameFormats = getNameFormats(requestContext);
+ if (supportedNameFormats == null || supportedNameFormats.isEmpty()) {
+ log.error("No common NameID formats supported by SP {} and IdP", requestContext.getInboundMessageIssuer());
requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER, null,
"Unable to construct NameIdentifier"));
throw new ProfileException("No principal attributes support NameIdentifier construction");
}
+ log.debug("Supported name formats: {}", supportedNameFormats);
try {
SAML1NameIdentifierEncoder nameIdEncoder;
if (encoder instanceof SAML1NameIdentifierEncoder) {
nameIdEncoder = (SAML1NameIdentifierEncoder) encoder;
if (supportedNameFormats.contains(nameIdEncoder.getNameFormat())) {
- if (log.isDebugEnabled()) {
- log.debug("Using attribute " + attribute.getId() + " suppoting name format "
- + nameIdEncoder.getNameFormat()
- + " to create the NameIdentifier for principal "
- + requestContext.getPrincipalName());
- }
+ log
+ .debug(
+ "Using attribute {} suppoting name format {} to create the NameIdentifier for principal",
+ attribute.getId(), nameIdEncoder.getNameFormat());
return nameIdEncoder.encode(attribute);
}
}
}
requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER, null,
"Unable to construct NameIdentifier"));
- throw new ProfileException("No principal attribute supported encoding into the a supported name ID format.");
+ log.error("No principal attribute supports an encoding into a supported name ID format.");
+ throw new ProfileException("No principal attribute supports an encoding into a supported name ID format.");
} catch (AttributeEncodingException e) {
log.error("Unable to construct NameIdentifier", e);
requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER, null,
*
* @return list of formats that may be used with the relying party
*
- * @throws ProfileException thrown if there is a problem determing the NameIdentifier format to use
+ * @throws ProfileException thrown if there is a problem determining the NameIdentifier format to use
*/
protected List<String> getNameFormats(BaseSAML1ProfileRequestContext<?, ?, ?> requestContext)
throws ProfileException {
ArrayList<String> nameFormats = new ArrayList<String>();
- RoleDescriptor assertingPartyRole = requestContext.getAssertingPartyRoleMetadata();
- List<String> assertingPartySupportedFormats = getEntitySupportedFormats(assertingPartyRole);
-
- if (nameFormats.isEmpty()) {
- RoleDescriptor relyingPartyRole = requestContext.getRelyingPartyRoleMetadata();
+ RoleDescriptor relyingPartyRole = requestContext.getPeerEntityRoleMetadata();
+ if (relyingPartyRole != null) {
List<String> relyingPartySupportedFormats = getEntitySupportedFormats(relyingPartyRole);
-
- assertingPartySupportedFormats.retainAll(relyingPartySupportedFormats);
- nameFormats.addAll(assertingPartySupportedFormats);
+ if (relyingPartySupportedFormats != null && !relyingPartySupportedFormats.isEmpty()) {
+ nameFormats.addAll(relyingPartySupportedFormats);
+
+ RoleDescriptor assertingPartyRole = requestContext.getLocalEntityRoleMetadata();
+ if (assertingPartyRole != null) {
+ List<String> assertingPartySupportedFormats = getEntitySupportedFormats(assertingPartyRole);
+ if (assertingPartySupportedFormats != null && !assertingPartySupportedFormats.isEmpty()) {
+ nameFormats.retainAll(assertingPartySupportedFormats);
+ }
+ }
+ }
}
+
if (nameFormats.isEmpty()) {
- nameFormats.add("urn:oasis:names:tc:SAML:1.0:nameid-format:unspecified");
+ nameFormats.add("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified");
}
return nameFormats;
SAML1AttributeAuthority attributeAuthority = profileConfiguration.getAttributeAuthority();
try {
- if (log.isDebugEnabled()) {
- log.debug("Resolving attributes for principal " + requestContext.getPrincipalName()
- + " of SAML request from relying party " + requestContext.getRelyingPartyEntityId());
- }
+ log.debug("Resolving attributes for principal {} of SAML request from relying party {}", requestContext
+ .getPrincipalName(), requestContext.getInboundMessageIssuer());
Map<String, BaseAttribute> principalAttributes = attributeAuthority.getAttributes(requestContext);
- requestContext.setPrincipalAttributes(principalAttributes);
+ requestContext.setAttributes(principalAttributes);
} catch (AttributeRequestException e) {
log.error("Error resolving attributes for SAML request from relying party "
- + requestContext.getRelyingPartyEntityId(), e);
+ + requestContext.getInboundMessageIssuer(), e);
requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER, null, "Error resolving attributes"));
throw new ProfileException("Error resolving attributes for SAML request from relying party "
- + requestContext.getRelyingPartyEntityId(), e);
+ + requestContext.getInboundMessageIssuer(), e);
}
}
protected AttributeStatement buildAttributeStatement(BaseSAML1ProfileRequestContext<?, ?, ?> requestContext,
String subjectConfMethod) throws ProfileException {
- if (log.isDebugEnabled()) {
- log.debug("Creating attribute statement in response to SAML request from relying party "
- + requestContext.getRelyingPartyEntityId());
- }
-
+ log.debug("Creating attribute statement in response to SAML request from relying party {}", requestContext
+ .getInboundMessageIssuer());
AbstractSAML1ProfileConfiguration profileConfiguration = requestContext.getProfileConfiguration();
SAML1AttributeAuthority attributeAuthority = profileConfiguration.getAttributeAuthority();
AttributeStatement statment;
if (requestContext.getInboundSAMLMessage() instanceof AttributeQuery) {
statment = attributeAuthority.buildAttributeStatement((AttributeQuery) requestContext
- .getInboundSAMLMessage(), requestContext.getPrincipalAttributes().values());
+ .getInboundSAMLMessage(), requestContext.getAttributes().values());
} else {
- statment = attributeAuthority.buildAttributeStatement(null, requestContext.getPrincipalAttributes()
- .values());
+ statment = attributeAuthority.buildAttributeStatement(null, requestContext.getAttributes().values());
}
- Subject statementSubject = buildSubject(requestContext, subjectConfMethod);
- statment.setSubject(statementSubject);
+ if (statment != null) {
+ Subject statementSubject = buildSubject(requestContext, subjectConfMethod);
+ statment.setSubject(statementSubject);
+ }
return statment;
} catch (AttributeRequestException e) {
AbstractSAML1ProfileConfiguration profileConfiguration = requestContext.getProfileConfiguration();
SAML1AttributeAuthority attributeAuthority = profileConfiguration.getAttributeAuthority();
- if (log.isDebugEnabled()) {
- log.debug("Resolving principal name for subject of SAML request from relying party "
- + requestContext.getRelyingPartyEntityId());
- }
+ log.debug("Resolving principal name for subject of SAML request from relying party {}", requestContext
+ .getInboundMessageIssuer());
try {
String principal = attributeAuthority.getPrincipal(requestContext);
requestContext.setPrincipalName(principal);
} catch (AttributeRequestException e) {
log.error("Error resolving attributes for SAML request from relying party "
- + requestContext.getRelyingPartyEntityId(), e);
+ + requestContext.getInboundMessageIssuer(), e);
requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER, StatusCode.REQUEST_DENIED,
"Error resolving principal"));
throw new ProfileException("Error resolving attributes for SAML request from relying party "
- + requestContext.getRelyingPartyEntityId(), e);
+ + requestContext.getInboundMessageIssuer(), e);
}
}
*/
protected void signAssertion(BaseSAML1ProfileRequestContext<?, ?, ?> requestContext, Assertion assertion)
throws ProfileException {
- if (log.isDebugEnabled()) {
- log.debug("Determining if SAML assertion to relying party " + requestContext.getRelyingPartyEntityId()
- + " should be signed");
- }
+ log.debug("Determining if SAML assertion to relying party {} should be signed", requestContext
+ .getInboundMessageIssuer());
boolean signAssertion = false;
- RoleDescriptor relyingPartyRole = requestContext.getRelyingPartyRoleMetadata();
+ RoleDescriptor relyingPartyRole = requestContext.getPeerEntityRoleMetadata();
+ SAMLMessageEncoder encoder = getMessageEncoders().get(requestContext.getPeerEntityEndpoint().getBinding());
AbstractSAML1ProfileConfiguration profileConfig = requestContext.getProfileConfiguration();
- if (relyingPartyRole instanceof SPSSODescriptor) {
+ try {
+ if (profileConfig.getSignAssertions() == CryptoOperationRequirementLevel.always
+ || (profileConfig.getSignAssertions() == CryptoOperationRequirementLevel.conditional && !encoder
+ .providesMessageIntegrity(requestContext))) {
+ signAssertion = true;
+ log.debug("IdP relying party configuration {} indicates to sign assertions: {}", requestContext
+ .getRelyingPartyConfiguration().getRelyingPartyId(), signAssertion);
+ }
+ } catch (MessageEncodingException e) {
+ log.error("Unable to determine if outbound encoding {} can provide integrity", encoder.getBindingURI());
+ throw new ProfileException("Unable to determine if outbound message should be signed");
+ }
+
+ if (!signAssertion && relyingPartyRole instanceof SPSSODescriptor) {
SPSSODescriptor ssoDescriptor = (SPSSODescriptor) relyingPartyRole;
if (ssoDescriptor.getWantAssertionsSigned() != null) {
signAssertion = ssoDescriptor.getWantAssertionsSigned().booleanValue();
- if (log.isDebugEnabled()) {
- log.debug("Entity metadata for relying party " + requestContext.getRelyingPartyEntityId()
- + " indicates to sign assertions: " + signAssertion);
- }
+ log.debug("Entity metadata for relying party {} indicates to sign assertions: {}", requestContext
+ .getInboundMessageIssuer(), signAssertion);
}
- } else if (profileConfig.getSignAssertions()) {
- signAssertion = true;
- log.debug("IdP relying party configuration "
- + requestContext.getRelyingPartyConfiguration().getRelyingPartyId()
- + " indicates to sign assertions: " + signAssertion);
}
if (!signAssertion) {
return;
}
- if (log.isDebugEnabled()) {
- log.debug("Determining signing credntial for assertion to relying party "
- + requestContext.getRelyingPartyEntityId());
- }
+ log.debug("Determining signing credntial for assertion to relying party {}", requestContext
+ .getInboundMessageIssuer());
Credential signatureCredential = profileConfig.getSigningCredential();
if (signatureCredential == null) {
signatureCredential = requestContext.getRelyingPartyConfiguration().getDefaultSigningCredential();
+ " or it's SAML2 attribute query profile configuration");
}
- if (log.isDebugEnabled()) {
- log.debug("Signing assertion to relying party " + requestContext.getRelyingPartyEntityId());
- }
- SAMLObjectContentReference contentRef = new SAMLObjectContentReference(assertion);
+ log.debug("Signing assertion to relying party {}", requestContext.getInboundMessageIssuer());
Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
- signature.getContentReferences().add(contentRef);
- assertion.setSignature(signature);
- Signer.signObject(signature);
- }
+ signature.setSigningCredential(signatureCredential);
+ try {
+ // TODO pull SecurityConfiguration from SAMLMessageContext? needs to be added
+ // TODO how to pull what keyInfoGenName to use?
+ SecurityHelper.prepareSignatureParams(signature, signatureCredential, null, null);
+ } catch (SecurityException e) {
+ throw new ProfileException("Error preparing signature for signing", e);
+ }
- /**
- * Writes an aduit log entry indicating the successful response to the attribute request.
- *
- * @param context current request context
- */
- protected void writeAuditLogEntry(BaseSAML1ProfileRequestContext<?, ?, ?> context) {
- AuditLogEntry auditLogEntry = new AuditLogEntry();
- auditLogEntry.setMessageProfile(getProfileId());
- auditLogEntry.setPrincipalAuthenticationMethod(context.getPrincipalAuthenticationMethod());
- auditLogEntry.setPrincipalName(context.getPrincipalName());
- auditLogEntry.setAssertingPartyId(context.getAssertingPartyEntityId());
- auditLogEntry.setRelyingPartyId(context.getRelyingPartyEntityId());
- auditLogEntry.setRequestBinding(getMessageDecoder().getBindingURI());
- auditLogEntry.setRequestId(null);
- auditLogEntry.setResponseBinding(getMessageEncoder().getBindingURI());
- auditLogEntry.setResponseId(context.getOutboundSAMLMessageId());
- if (context.getReleasedPrincipalAttributeIds() != null) {
- auditLogEntry.getReleasedAttributes().addAll(context.getReleasedPrincipalAttributeIds());
+ assertion.setSignature(signature);
+
+ Marshaller assertionMarshaller = Configuration.getMarshallerFactory().getMarshaller(assertion);
+ try {
+ assertionMarshaller.marshall(assertion);
+ Signer.signObject(signature);
+ } catch (MarshallingException e) {
+ log.error("Unable to marshall assertion for signing", e);
+ throw new ProfileException("Unable to marshall assertion for signing", e);
+ } catch (SignatureException e) {
+ log.error("Unable to sign assertion", e);
+ throw new ProfileException("Unable to sign assertion", e);
}
- getAduitLog().log(Level.CRITICAL, auditLogEntry);
}
}
\ No newline at end of file