From 7b2379b0581ae09769029861b9bc351e95fed799 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Fri, 24 Nov 2006 23:24:36 +0000 Subject: [PATCH] SAML 1.x SOAP client. --- saml/Makefile.am | 4 +- saml/saml.vcproj | 8 +++ saml/saml1/binding/SAML1SOAPClient.h | 90 ++++++++++++++++++++++++ saml/saml1/binding/impl/SAML1SOAPClient.cpp | 105 ++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 saml/saml1/binding/SAML1SOAPClient.h create mode 100644 saml/saml1/binding/impl/SAML1SOAPClient.cpp diff --git a/saml/Makefile.am b/saml/Makefile.am index a779fe7..5b628cb 100644 --- a/saml/Makefile.am +++ b/saml/Makefile.am @@ -72,7 +72,8 @@ saml1bindinclude_HEADERS = \ saml1/binding/SAML1POSTDecoder.h \ saml1/binding/SAML1POSTEncoder.h \ saml1/binding/SAML1SOAPDecoder.h \ - saml1/binding/SAML1SOAPEncoder.h + saml1/binding/SAML1SOAPEncoder.h \ + saml1/binding/SAML1SOAPClient.h saml2coreinclude_HEADERS = \ saml2/core/Assertions.h \ @@ -128,6 +129,7 @@ libsaml_la_SOURCES = \ saml1/binding/impl/SAML1POSTEncoder.cpp \ saml1/binding/impl/SAML1SOAPDecoder.cpp \ saml1/binding/impl/SAML1SOAPEncoder.cpp \ + saml1/binding/impl/SAML1SOAPClient.cpp \ saml2/core/impl/Assertions20Impl.cpp \ saml2/core/impl/Assertions20SchemaValidators.cpp \ saml2/core/impl/Protocols20Impl.cpp \ diff --git a/saml/saml.vcproj b/saml/saml.vcproj index 4682f2b..c4664d6 100644 --- a/saml/saml.vcproj +++ b/saml/saml.vcproj @@ -245,6 +245,10 @@ > + + @@ -612,6 +616,10 @@ > + + diff --git a/saml/saml1/binding/SAML1SOAPClient.h b/saml/saml1/binding/SAML1SOAPClient.h new file mode 100644 index 0000000..3c28e65 --- /dev/null +++ b/saml/saml1/binding/SAML1SOAPClient.h @@ -0,0 +1,90 @@ +/* + * Copyright 2001-2006 Internet2 + * + * 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. + */ + +/** + * @file saml/saml1/binding/SAML1SOAPClient.h + * + * Specialized SOAPClient for SAML 1.x SOAP binding. + */ + +#ifndef __saml1_soap11client_h__ +#define __saml1_soap11client_h__ + +#include + +namespace opensaml { + namespace saml1p { + + class SAML_API Request; + class SAML_API Response; + class SAML_API Status; + + /** + * Specialized SOAPClient for SAML 1.x SOAP binding. + */ + class SAML_API SAML1SOAPClient : opensaml::SOAPClient + { + public: + /** + * Creates a SOAP client instance with a particular SecurityPolicy. + * + * @param policy reference to SecurityPolicy to apply + */ + SAML1SOAPClient(SecurityPolicy& policy) : opensaml::SOAPClient(policy), m_correlate(NULL) {} + + virtual ~SAML1SOAPClient() { + XMLString::release(&m_correlate); + } + + /** + * Specialized method for sending SAML 1.x requests. The SOAP layer will be + * constructed automatically. + * + *

The request will be freed by the client object if the method succeeds. + * + * @param request SAML request to send + * @param peer peer to send message to, expressed in metadata terms + * @param endpoint URL of endpoint to recieve message + */ + virtual void sendSAML(Request* request, const saml2md::RoleDescriptor& peer, const char* endpoint); + + /** + * Specialized method for receiving SAML 1.x responses. The SOAP layer will be + * evaluated automatically, and the attached policy will be applied to the Response. + * + *

The caller is responsible for freeing the Response. + * + * @return SAML 1.x Response, after SecurityPolicy has been applied + */ + virtual Response* receiveSAML(); + + protected: + /** + * Handling of SAML errors. + * + * @param status SAML Status received by client + * @return true iff the error should be treated as a fatal error + */ + virtual bool handleError(const Status& status); + + private: + XMLCh* m_correlate; + }; + + }; +}; + +#endif /* __saml1_soap11client_h__ */ diff --git a/saml/saml1/binding/impl/SAML1SOAPClient.cpp b/saml/saml1/binding/impl/SAML1SOAPClient.cpp new file mode 100644 index 0000000..7808510 --- /dev/null +++ b/saml/saml1/binding/impl/SAML1SOAPClient.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2001-2006 Internet2 + * + * 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. + */ + +/** + * SAML1SOAPClient.cpp + * + * Specialized SOAPClient for SAML 1.x SOAP binding. + */ + +#include "internal.h" +#include "exceptions.h" +#include "saml1/binding/SAML1SOAPClient.h" +#include "saml1/core/Protocols.h" +#include "saml2/metadata/Metadata.h" + +#include +#include + +using namespace opensaml::saml1p; +using namespace opensaml::saml2md; +using namespace opensaml; +using namespace soap11; +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +void SAML1SOAPClient::sendSAML(Request* request, const RoleDescriptor& peer, const char* endpoint) +{ + Envelope* env = EnvelopeBuilder::buildEnvelope(); + Body* body = BodyBuilder::buildBody(); + env->setBody(body); + body->getXMLObjects().push_back(request); + try { + send(env, peer, endpoint); + m_correlate = XMLString::replicate(request->getRequestID()); + delete env; + } + catch (XMLToolingException&) { + // A bit weird...we have to "revert" things so that the request is isolated + // so the caller can free it. + request->getParent()->detach(); + request->detach(); + throw; + } +} + +Response* SAML1SOAPClient::receiveSAML() +{ + auto_ptr env(receive()); + if (env.get()) { + Body* body = env->getBody(); + if (body && body->hasChildren()) { + // Check for SAML Response. + Response* response = dynamic_cast(body->getXMLObjects().front()); + if (response) { + + // Check InResponseTo. + if (m_correlate && !XMLString::equals(m_correlate, response->getInResponseTo())) + throw BindingException("InResponseTo attribute did not correlate with the Request ID."); + + // Check Status. + Status* status = response->getStatus(); + if (status) { + const QName* code = status->getStatusCode() ? status->getStatusCode()->getValue() : NULL; + if (code && *code != StatusCode::SUCCESS && handleError(*status)) + throw BindingException("SAML Response contained an error."); + } + + m_policy.evaluate(*response); + env.release(); + body->detach(); // frees Envelope + response->detach(); // frees Body + return response; + } + } + + throw BindingException("SOAP Envelope did not contain a SAML Response or a Fault."); + } + return NULL; +} + +bool SAML1SOAPClient::handleError(const Status& status) +{ + const QName* code = status.getStatusCode() ? status.getStatusCode()->getValue() : NULL; + auto_ptr_char str((status.getStatusMessage() ? status.getStatusMessage()->getMessage() : NULL)); + Category::getInstance(SAML_LOGCAT".SOAPClient").error( + "SOAP client detected a SAML error: (%s) (%s)", + (code ? code->toString().c_str() : "no code"), + (str.get() ? str.get() : "no message") + ); + return true; +} -- 2.1.4