package edu.internet2.middleware.shibboleth.idp.profile.saml2;
-import javax.servlet.ServletException;
-
import org.apache.log4j.Logger;
-import org.joda.time.DateTime;
import org.opensaml.common.SAMLObjectBuilder;
-import org.opensaml.common.binding.BindingException;
import org.opensaml.saml2.core.Advice;
import org.opensaml.saml2.core.Assertion;
-import org.opensaml.saml2.core.AttributeStatement;
import org.opensaml.saml2.core.Audience;
import org.opensaml.saml2.core.AudienceRestriction;
import org.opensaml.saml2.core.Conditions;
import org.opensaml.saml2.core.Subject;
import org.opensaml.saml2.encryption.Encrypter;
import org.opensaml.saml2.metadata.provider.MetadataProvider;
-import org.opensaml.saml2.metadata.provider.MetadataProviderException;
+import org.opensaml.xml.XMLObjectBuilder;
import org.opensaml.xml.encryption.EncryptionException;
-import org.opensaml.xml.security.credential.Credential;
+import org.opensaml.xml.signature.Signature;
+import org.opensaml.xml.signature.Signer;
-import edu.internet2.middleware.shibboleth.common.attribute.AttributeRequestException;
import edu.internet2.middleware.shibboleth.common.attribute.SAML2AttributeAuthority;
import edu.internet2.middleware.shibboleth.common.attribute.provider.ShibbolethAttributeRequestContext;
import edu.internet2.middleware.shibboleth.common.attribute.provider.ShibbolethSAML2AttributeAuthority;
import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolver;
-import edu.internet2.middleware.shibboleth.common.profile.ProfileRequest;
-import edu.internet2.middleware.shibboleth.common.profile.ProfileResponse;
import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
import edu.internet2.middleware.shibboleth.common.relyingparty.saml2.AttributeQueryConfiguration;
-import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
/**
* SAML 2.0 Attribute Query profile handler.
*/
-public class AttributeQuery extends AbstractSAML2ProfileHandler {
+public abstract class AbstractAttributeQuery extends AbstractSAML2ProfileHandler {
/** Class logger. */
- private static Logger log = Logger.getLogger(AttributeQuery.class);
+ private static Logger log = Logger.getLogger(AbstractAttributeQuery.class);
/** For building response. */
private SAMLObjectBuilder<Response> responseBuilder;
/** For building advice. */
private SAMLObjectBuilder<Advice> adviceBuilder;
- /** Provider id. */
- private String providerId;
+ /** For building signature. */
+ private XMLObjectBuilder<Signature> signatureBuilder;
/** Attribute authority. */
private SAML2AttributeAuthority attributeAuthority;
- /** Attribute query configuration. */
- private AttributeQueryConfiguration config;
-
/**
* This creates a new attribute query.
*
* @param ar <code>AttributeResolver</code>
*/
- public AttributeQuery(AttributeResolver<ShibbolethAttributeRequestContext> ar) {
- // instantiate configuration
- config = new AttributeQueryConfiguration();
- providerId = config.getProfileId();
-
+ public AbstractAttributeQuery(AttributeResolver<ShibbolethAttributeRequestContext> ar) {
// instantiate attribute authority
attributeAuthority = new ShibbolethSAML2AttributeAuthority(ar);
statusBuilder = (SAMLObjectBuilder<Status>) getBuilderFactory().getBuilder(Status.DEFAULT_ELEMENT_NAME);
statusCodeBuilder = (SAMLObjectBuilder<StatusCode>) getBuilderFactory().getBuilder(
StatusCode.DEFAULT_ELEMENT_NAME);
+ issuerBuilder = (SAMLObjectBuilder<Issuer>) getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
assertionBuilder = (SAMLObjectBuilder<Assertion>) getBuilderFactory()
.getBuilder(Assertion.DEFAULT_ELEMENT_NAME);
- issuerBuilder = (SAMLObjectBuilder<Issuer>) getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
subjectBuilder = (SAMLObjectBuilder<Subject>) getBuilderFactory().getBuilder(Subject.DEFAULT_ELEMENT_NAME);
conditionsBuilder = (SAMLObjectBuilder<Conditions>) getBuilderFactory().getBuilder(
Conditions.DEFAULT_ELEMENT_NAME);
AudienceRestriction.DEFAULT_ELEMENT_NAME);
audienceBuilder = (SAMLObjectBuilder<Audience>) getBuilderFactory().getBuilder(Audience.DEFAULT_ELEMENT_NAME);
adviceBuilder = (SAMLObjectBuilder<Advice>) getBuilderFactory().getBuilder(Advice.DEFAULT_ELEMENT_NAME);
+ signatureBuilder = (XMLObjectBuilder<Signature>) getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME);
}
- /** {@inheritDoc} */
- public boolean processRequest(ProfileRequest request, ProfileResponse response) throws ServletException {
- if (log.isDebugEnabled()) {
- log.debug("begin processRequest");
- }
-
- // get message from the decoder
- org.opensaml.saml2.core.AttributeQuery message = null;
- try {
- message = (org.opensaml.saml2.core.AttributeQuery) decodeMessage(request.getMessageDecoder(), request
- .getRequest());
- } catch (BindingException e) {
- log.error("Error decoding attribute query message", e);
- throw new ServletException("Error decoding attribute query message");
- }
-
- // TODO get user data from the session
- ServiceInformation serviceInformation = null;
- String principalName = serviceInformation.getSubjectNameID().getSPProvidedID();
- String authenticationMethod = serviceInformation.getAuthenticationMethod().getAuthenticationMethod();
-
- // create attribute request for the attribute authority
- ShibbolethAttributeRequestContext requestContext = null;
- try {
- MetadataProvider metadataProvider = getRelyingPartyManager().getMetadataProvider();
- RelyingPartyConfiguration relyingPartyConfiguration = getRelyingPartyManager()
- .getRelyingPartyConfiguration(providerId);
- requestContext = new ShibbolethAttributeRequestContext(metadataProvider, relyingPartyConfiguration);
- requestContext.setPrincipalName(principalName);
- requestContext.setPrincipalAuthenticationMethod(authenticationMethod);
- requestContext.setRequest(request.getRequest());
- } catch (MetadataProviderException e) {
- log.error("Error creating ShibbolethAttributeRequestContext", e);
- throw new ServletException("Error retrieving metadata", e);
- }
-
- // resolve attributes with the attribute authority
- AttributeStatement statement = null;
- try {
- statement = attributeAuthority.performAttributeQuery(requestContext);
- } catch (AttributeRequestException e) {
- log.error("Error resolving attributes", e);
- throw new ServletException("Error resolving attributes", e);
- }
+ /**
+ * This returns the <code>RelyingPartyConfiguration</code> for the supplied provider id.
+ *
+ * @param providerId <code>String</code>
+ * @return <code>RelyingPartyConfiguration</code>
+ */
+ protected RelyingPartyConfiguration getRelyingPartyConfiguration(String providerId) {
+ return getRelyingPartyConfigurationManager().getRelyingPartyConfiguration(providerId);
+ }
- // construct attribute response
- Response samlResponse = null;
- try {
- ProfileResponseContext profileResponse = new ProfileResponseContext(request, message);
- profileResponse.setAttributeStatement(statement);
- samlResponse = buildResponse(profileResponse);
- } catch (EncryptionException e) {
- log.error("Error encrypting SAML response", e);
- throw new ServletException("Error encrypting SAML response", e);
- }
- if (log.isDebugEnabled()) {
- log.debug("built saml2 response: " + samlResponse);
- }
+ /**
+ * This returns the <code>AttributeQueryConfiguration</code> for the supplied provider id.
+ *
+ * @param providerId <code>String</code>
+ * @return <code>AttributeQueryConfiguration</code>
+ */
+ protected AttributeQueryConfiguration getAttributeQueryConfiguration(String providerId) {
+ return (AttributeQueryConfiguration) getRelyingPartyConfiguration(providerId).getProfileConfigurations().get(
+ AttributeQueryConfiguration.PROFILE_ID);
+ }
- // encode response
- try {
- encodeResponse(response.getMessageEncoder(), samlResponse);
- } catch (BindingException e) {
- log.error("Error encoding attribute query response", e);
- throw new ServletException("Error encoding attribute query response", e);
- }
+ /**
+ * This returns the <code>MetadataProvider</code> for this attribute query.
+ *
+ * @return <code>MetadataProvider</code>
+ */
+ protected MetadataProvider getMetadataProvider() {
+ return getRelyingPartyConfigurationManager().getMetadataProvider();
+ }
- return true;
+ /**
+ * This returns the <code>AttributeAuthority</code> for this attribute query.
+ *
+ * @return <code>SAML2AttributeAuthority</code>
+ */
+ protected SAML2AttributeAuthority getAttributeAuthority() {
+ return attributeAuthority;
}
/**
* This builds the response for this SAML request.
*
* @param responseContext <code>ProfileResponseContext</code>
+ * @param issuer <code>String</code>
+ * @param destination <code>String</code>
* @return <code>Response</code>
* @throws EncryptionException if an error occurs attempting to encrypt data
*/
- private Response buildResponse(ProfileResponseContext responseContext) throws EncryptionException {
+ protected Response buildResponse(ProfileResponseContext responseContext, String issuer, String destination)
+ throws EncryptionException {
+ AttributeQueryConfiguration config = getAttributeQueryConfiguration(responseContext.getProviderId());
+
/*
* required: samlp:Status, ID, Version, IssueInstant
*/
Response response = responseBuilder.buildObject();
response.setVersion(SAML_VERSION);
response.setID(getIdGenerator().generateIdentifier());
- response.setInResponseTo(responseContext.getRequest().getMessageDecoder().getSecurityPolicy().getIssuer()
- .toString());
+ response.setInResponseTo(issuer);
response.setIssueInstant(responseContext.getIssueInstant());
- response.setDestination(responseContext.getRequest().getRequest().getRemoteHost());
+ response.setDestination(destination);
- response.setIssuer(buildIssuer());
+ response.setIssuer(buildIssuer(responseContext.getProviderId()));
- // TODO get consent configuration
/*
- * if (consent != null) { response.setConsent(consent); }
+ * Will be hard coded in the future: if (consent != null) { response.setConsent(consent); }
+ *
*/
- // TODO get extension configuration
/*
- * if (extensions != null) { response.setExtensions(extensions); }
+ * No extensions currently exist, will be hardcorded in the future: if (extensions != null) {
+ * response.setExtensions(extensions); }
+ *
*/
if (config.getSignAssertions()) {
- // TODO sign assertion: Credential credential = config.getSigningCredential();
+ Signature s = buildSignature();
+ s.setSigningKey(config.getSigningCredential().getPrivateKey());
if (config.getEncryptAssertion()) {
// TODO load encryption parameters
Encrypter encrypter = null;
- response.getEncryptedAssertions().add(encrypter.encrypt(buildAssertion(responseContext)));
+ Assertion a = buildAssertion(responseContext);
+ a.setSignature(s);
+ Signer.signObject(s);
+ response.getEncryptedAssertions().add(encrypter.encrypt(a));
} else {
- response.getAssertions().add(buildAssertion(responseContext));
+ Assertion a = buildAssertion(responseContext);
+ a.setSignature(s);
+ Signer.signObject(s);
+ response.getAssertions().add(a);
}
} else {
if (config.getEncryptAssertion()) {
* @throws EncryptionException if an error occurs attempting to encrypt data
*/
private Assertion buildAssertion(ProfileResponseContext responseContext) throws EncryptionException {
+ AttributeQueryConfiguration config = getAttributeQueryConfiguration(responseContext.getProviderId());
+
/*
* required: saml:Issuer, ID, Version, IssueInstant
*/
assertion.setID(getIdGenerator().generateIdentifier());
assertion.setIssueInstant(responseContext.getIssueInstant());
assertion.setVersion(SAML_VERSION);
- assertion.setIssuer(buildIssuer());
+ assertion.setIssuer(buildIssuer(responseContext.getProviderId()));
// build subject
- assertion.setSubject(buildSubject(responseContext.getMessage().getSubject()));
+ assertion.setSubject(buildSubject(responseContext.getMessage().getSubject(), config.getEncryptNameID()));
// build conditions
- assertion.setConditions(buildConditions(responseContext.getIssueInstant()));
+ assertion.setConditions(buildConditions(responseContext));
// build advice
assertion.setAdvice(buildAdvice());
// add attribute statement
/**
* This builds the issuer response for this SAML request.
*
+ * @param providerId <code>String</code>
* @return <code>Issuer</code>
*/
- private Issuer buildIssuer() {
- RelyingPartyConfiguration relyingPartyConfiguration = getRelyingPartyManager().getRelyingPartyConfiguration(
- providerId);
+ private Issuer buildIssuer(String providerId) {
+ RelyingPartyConfiguration relyingPartyConfiguration = getRelyingPartyConfiguration(providerId);
Issuer issuer = issuerBuilder.buildObject();
- issuer.setValue(relyingPartyConfiguration.getProviderID());
+ issuer.setValue(relyingPartyConfiguration.getProviderId());
return issuer;
}
* This builds the subject for this SAML request.
*
* @param messageSubject <code>Subject</code>
+ * @param encryptNameId <code>boolean</code>
* @return <code>Subject</code>
* @throws EncryptionException if encryption of the name id fails
*/
- private Subject buildSubject(Subject messageSubject) throws EncryptionException {
+ private Subject buildSubject(Subject messageSubject, boolean encryptNameId) throws EncryptionException {
Subject subject = subjectBuilder.buildObject();
- if (config.getEncryptNameID()) {
+ if (encryptNameId) {
// TODO load encryption parameters
Encrypter encrypter = null;
subject.setEncryptedID(encrypter.encrypt(messageSubject.getNameID()));
/**
* This builds the conditions for this SAML request.
*
- * @param issueInstant <code>DateTime</code>
+ * @param responseContext <code>ProfileResponseContext</code>
* @return <code>Conditions</code>
*/
- private Conditions buildConditions(DateTime issueInstant) {
+ private Conditions buildConditions(ProfileResponseContext responseContext) {
+ AttributeQueryConfiguration config = getAttributeQueryConfiguration(responseContext.getProviderId());
+
Conditions conditions = conditionsBuilder.buildObject();
- conditions.setNotBefore(issueInstant);
- conditions.setNotOnOrAfter(issueInstant.plus(config.getAssertionLifetime()));
+ conditions.setNotBefore(responseContext.getIssueInstant());
+ conditions.setNotOnOrAfter(responseContext.getIssueInstant().plus(config.getAssertionLifetime()));
// add audience restrictions
AudienceRestriction audienceRestriction = audienceRestrictionBuilder.buildObject();
}
proxyRestriction.setProxyCount(new Integer(config.getProxyCount()));
- // TODO add additional conditions : conditions.getConditions().add(Condition);
- // TODO what about OneTimeUse?
+ /*
+ * OneTimeUse and additional conditions not supported yet
+ */
+
return conditions;
}
* @return <code>Advice</code>
*/
private Advice buildAdvice() {
+ /*
+ * Advice not supported at this time
+ */
Advice advice = adviceBuilder.buildObject();
- // TODO set advice
- // advice.getAssertionIDReferences().add();
- // advice.getAssertionURIReferences().add();
- // advice.getAssertions().add();
- // advice.getEncryptedAssertions().add();
- // advice.addNamespace(namespace);
return advice;
}
+
+ /**
+ * This builds a signature for this SAML request.
+ *
+ * @return <code>Signature</code>
+ */
+ private Signature buildSignature() {
+ Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
+ return signature;
+ }
}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * 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 edu.internet2.middleware.shibboleth.idp.profile.saml2;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+import org.opensaml.common.binding.BindingException;
+import org.opensaml.saml2.binding.HTTPSOAP11Decoder;
+import org.opensaml.saml2.binding.HTTPSOAP11Encoder;
+import org.opensaml.saml2.core.AttributeStatement;
+import org.opensaml.saml2.core.Response;
+import org.opensaml.saml2.metadata.provider.MetadataProviderException;
+import org.opensaml.xml.encryption.EncryptionException;
+
+import edu.internet2.middleware.shibboleth.common.attribute.AttributeRequestException;
+import edu.internet2.middleware.shibboleth.common.attribute.provider.ShibbolethAttributeRequestContext;
+import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolver;
+import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
+import edu.internet2.middleware.shibboleth.common.profile.ProfileRequest;
+import edu.internet2.middleware.shibboleth.common.profile.ProfileResponse;
+import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
+
+/**
+ * SAML 2.0 SOAP Attribute Query profile handler.
+ */
+public class HTTPSOAPAttributeQuery extends AbstractAttributeQuery {
+
+ /** Class logger. */
+ private static Logger log = Logger.getLogger(HTTPSOAPAttributeQuery.class);
+
+ /**
+ * This creates a new http soap attribute query.
+ *
+ * @param ar <code>AttributeResolver</code>
+ */
+ public HTTPSOAPAttributeQuery(AttributeResolver<ShibbolethAttributeRequestContext> ar) {
+ super(ar);
+ }
+
+ /** {@inheritDoc} */
+ public void processRequest(ProfileRequest<ServletRequest> request, ProfileResponse<ServletResponse> response)
+ throws ProfileException {
+ if (log.isDebugEnabled()) {
+ log.debug("begin processRequest");
+ }
+
+ // check that request/response is of proper type
+ if (!(request.getRawRequest() instanceof HttpServletRequest)) {
+ throw new ProfileException(HTTPSOAPAttributeQuery.class.getName() + " can only process requests of type "
+ + HttpServletRequest.class.getName());
+ } else if (!(response.getRawResponse() instanceof HttpServletResponse)) {
+ throw new ProfileException(HTTPSOAPAttributeQuery.class.getName() + " can only process responses of type "
+ + HttpServletResponse.class.getName());
+ }
+
+ // create decoder
+ HTTPSOAP11Decoder decoder = new HTTPSOAP11Decoder();
+ decoder.setMetadataProvider(getMetadataProvider());
+ // TODO decoder.setSecurityPolicy(policy);
+ // TODO decoder.setTrustEngine(newEngine);
+
+ // get message from the decoder
+ org.opensaml.saml2.core.AttributeQuery message = null;
+ try {
+ decoder.setRequest((HttpServletRequest) request.getRawRequest());
+ decoder.decode();
+ if (log.isDebugEnabled()) {
+ log.debug("decoded http servlet request");
+ }
+ message = (org.opensaml.saml2.core.AttributeQuery) decoder.getSAMLMessage();
+ } catch (BindingException e) {
+ log.error("Error decoding attribute query message", e);
+ throw new ProfileException("Error decoding attribute query message");
+ }
+
+ // get the provider id from the message issuer
+ String providerId = message.getIssuer().getSPProvidedID();
+
+ // TODO get user data from the session, need sessionId
+ // ?? getSessionManager().getSession(null).getServicesInformation().get(0);
+ ServiceInformation serviceInformation = null;
+ String principalName = serviceInformation.getSubjectNameID().getSPProvidedID();
+ String authenticationMethod = serviceInformation.getAuthenticationMethod().getAuthenticationMethod();
+
+ // create attribute request for the attribute authority
+ ShibbolethAttributeRequestContext requestContext = null;
+ try {
+ requestContext = new ShibbolethAttributeRequestContext(getMetadataProvider(),
+ getRelyingPartyConfiguration(providerId));
+ requestContext.setPrincipalName(principalName);
+ requestContext.setPrincipalAuthenticationMethod(authenticationMethod);
+ requestContext.setRequest(request.getRawRequest());
+ } catch (MetadataProviderException e) {
+ log.error("Error creating ShibbolethAttributeRequestContext", e);
+ throw new ProfileException("Error retrieving metadata", e);
+ }
+
+ // resolve attributes with the attribute authority
+ AttributeStatement statement = null;
+ try {
+ statement = getAttributeAuthority().performAttributeQuery(requestContext);
+ } catch (AttributeRequestException e) {
+ log.error("Error resolving attributes", e);
+ throw new ProfileException("Error resolving attributes", e);
+ }
+
+ // construct attribute response
+ Response samlResponse = null;
+ try {
+ ProfileResponseContext profileResponse = new ProfileResponseContext(request, message);
+ profileResponse.setAttributeStatement(statement);
+ samlResponse = buildResponse(profileResponse, decoder.getSecurityPolicy().getIssuer().toString(), request
+ .getRawRequest().getRemoteHost());
+ } catch (EncryptionException e) {
+ log.error("Error encrypting SAML response", e);
+ throw new ProfileException("Error encrypting SAML response", e);
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("built saml2 response: " + samlResponse);
+ }
+
+ // encode response
+ try {
+ HTTPSOAP11Encoder encoder = new HTTPSOAP11Encoder();
+ encoder.setMetadataProvider(getMetadataProvider());
+ encoder.setRelyingParty(getRelyingPartyConfiguration(providerId).getRelyingPartyId());
+ encoder.setResponse((HttpServletResponse) response.getRawResponse());
+ encoder.setSAMLMessage(samlResponse);
+ encoder.encode();
+ } catch (BindingException e) {
+ log.error("Error encoding attribute query response", e);
+ throw new ProfileException("Error encoding attribute query response", e);
+ }
+ }
+}