Annotate exceptions with status information.
authorScott Cantor <cantor.2@osu.edu>
Tue, 10 Jul 2007 22:22:16 +0000 (22:22 +0000)
committerScott Cantor <cantor.2@osu.edu>
Tue, 10 Jul 2007 22:22:16 +0000 (22:22 +0000)
saml/SAMLConfig.cpp
saml/exceptions.h
saml/saml1/binding/impl/SAML1SOAPClient.cpp
saml/saml2/binding/impl/SAML2SOAPClient.cpp

index 49869c2..88aa180 100644 (file)
@@ -217,59 +217,24 @@ string SAMLInternalConfig::hashSHA1(const char* s, bool toHex)
     throw XMLSecurityException("Unable to generate SHA-1 hash.");
 }
 
+using namespace saml2p;
 using namespace saml2md;
 
-void opensaml::annotateException(XMLToolingException* e, const EntityDescriptor* entity, bool rethrow)
+void opensaml::annotateException(XMLToolingException* e, const EntityDescriptor* entity, const Status* status, bool rethrow)
 {
+    const RoleDescriptor* role = NULL;
     if (entity) {
-        auto_ptr_char id(entity->getEntityID());
-        e->addProperty("entityID",id.get());
         const list<XMLObject*>& roles=entity->getOrderedChildren();
-        for (list<XMLObject*>::const_iterator child=roles.begin(); child!=roles.end(); ++child) {
-            const RoleDescriptor* role=dynamic_cast<RoleDescriptor*>(*child);
-            if (role && role->isValid()) {
-                const vector<ContactPerson*>& contacts=role->getContactPersons();
-                for (vector<ContactPerson*>::const_iterator c=contacts.begin(); c!=contacts.end(); ++c) {
-                    const XMLCh* ctype=(*c)->getContactType();
-                    if (ctype && (XMLString::equals(ctype,ContactPerson::CONTACT_SUPPORT)
-                            || XMLString::equals(ctype,ContactPerson::CONTACT_TECHNICAL))) {
-                        GivenName* fname=(*c)->getGivenName();
-                        SurName* lname=(*c)->getSurName();
-                        auto_ptr_char first(fname ? fname->getName() : NULL);
-                        auto_ptr_char last(lname ? lname->getName() : NULL);
-                        if (first.get() && last.get()) {
-                            string contact=string(first.get()) + ' ' + last.get();
-                            e->addProperty("contactName",contact.c_str());
-                        }
-                        else if (first.get())
-                            e->addProperty("contactName",first.get());
-                        else if (last.get())
-                            e->addProperty("contactName",last.get());
-                        const vector<EmailAddress*>& emails=const_cast<const ContactPerson*>(*c)->getEmailAddresss();
-                        if (!emails.empty()) {
-                            auto_ptr_char email(emails.front()->getAddress());
-                            if (email.get())
-                                e->addProperty("contactEmail",email.get());
-                        }
-                        break;
-                    }
-                }
-                if (e->getProperty("contactName") || e->getProperty("contactEmail")) {
-                    auto_ptr_char eurl(role->getErrorURL());
-                    if (eurl.get()) {
-                        e->addProperty("errorURL",eurl.get());
-                    }
-                }
-                break;
-            }
+        for (list<XMLObject*>::const_iterator child=roles.begin(); !role && child!=roles.end(); ++child) {
+            role=dynamic_cast<RoleDescriptor*>(*child);
+            if (role && !role->isValid())
+                role = NULL;
         }
     }
-    
-    if (rethrow)
-        e->raise();
+    annotateException(e, role, status, rethrow);
 }
 
-void opensaml::annotateException(XMLToolingException* e, const RoleDescriptor* role, bool rethrow)
+void opensaml::annotateException(XMLToolingException* e, const RoleDescriptor* role, const Status* status, bool rethrow)
 {
     if (role) {
         auto_ptr_char id(dynamic_cast<EntityDescriptor*>(role->getParent())->getEntityID());
@@ -308,6 +273,22 @@ void opensaml::annotateException(XMLToolingException* e, const RoleDescriptor* r
         }
     }
     
+    if (status) {
+        auto_ptr_char sc(status->getStatusCode() ? status->getStatusCode()->getValue() : NULL);
+        if (sc.get() && *sc.get())
+            e->addProperty("statusCode", sc.get());
+        if (status->getStatusCode()->getStatusCode()) {
+            auto_ptr_char sc2(status->getStatusCode()->getStatusCode()->getValue());
+            if (sc2.get() && *sc.get())
+                e->addProperty("statusCode2", sc2.get());
+        }
+        if (status->getStatusMessage()) {
+            auto_ptr_char msg(status->getStatusMessage()->getMessage());
+            if (msg.get() && *msg.get())
+                e->addProperty("statusMessage", msg.get());
+        }
+    }
+    
     if (rethrow)
         e->raise();
 }
index d66caf5..a691001 100644 (file)
@@ -28,6 +28,9 @@
 
 namespace opensaml {
     
+    namespace saml2p {
+        class SAML_API Status;
+    };
     namespace saml2md {
         class SAML_API EntityDescriptor;
         class SAML_API RoleDescriptor;
@@ -44,18 +47,25 @@ namespace opensaml {
      * rethrows the object. The following named properties are attached, when possible:
      * 
      *  <dl>
-     *  <dt>providerId</dt>     <dd>The unique ID of the entity</dd>
+     *  <dt>entityID</dt>       <dd>The unique ID of the entity</dd>
      *  <dt>errorURL</dt>       <dd>The error support URL of a random role</dd>
      *  <dt>contactName</dt>    <dd>A formatted support or technical contact name</dd>
      *  <dt>contactEmail</dt>   <dd>A contact email address</dd>
+     *  <dt>statusCode</dt>     <dd>Top-level status code from Status object</dd>
+     *  <dt>statusCode2</dt>    <dd>Second-level status code from Status object</dd>
+     *  <dt>statusMessage</dt>  <dd>StatusMessage from Status object</dd>
      *  </dl>
      * 
      * @param e         pointer to exception object
      * @param entity    pointer to entity
+     * @param status    pointer to Status from message 
      * @param rethrow   true iff the exception should be rethrown
      */
     void SAML_API annotateException(
-        xmltooling::XMLToolingException* e, const saml2md::EntityDescriptor* entity, bool rethrow=true
+        xmltooling::XMLToolingException* e,
+        const saml2md::EntityDescriptor* entity,
+        const saml2p::Status* status=NULL,
+        bool rethrow=true
         );
     
     /**
@@ -63,18 +73,24 @@ namespace opensaml {
      * rethrows the object. The following named properties are attached, when possible:
      * 
      *  <dl>
-     *  <dt>providerId</dt>     <dd>The unique ID of the entity</dd>
+     *  <dt>entityID</dt>       <dd>The unique ID of the entity</dd>
      *  <dt>errorURL</dt>       <dd>The error support URL of the role</dd>
      *  <dt>contactName</dt>    <dd>A formatted support or technical contact name</dd>
      *  <dt>contactEmail</dt>   <dd>A contact email address</dd>
+     *  <dt>statusCode</dt>     <dd>Top-level status code from Status object</dd>
+     *  <dt>statusCode2</dt>    <dd>Second-level status code from Status object</dd>
      *  </dl>
      * 
      * @param e         pointer to exception object
      * @param entity    pointer to role
+     * @param status    pointer to Status from message 
      * @param rethrow   true iff the exception should be rethrown
      */
     void SAML_API annotateException(
-        xmltooling::XMLToolingException* e, const saml2md::RoleDescriptor* role, bool rethrow=true
+        xmltooling::XMLToolingException* e,
+        const saml2md::RoleDescriptor* role,
+        const saml2p::Status* status=NULL,
+        bool rethrow=true
         );
 };
 
index 30f0bf7..bcd76c1 100644 (file)
@@ -59,21 +59,31 @@ Response* SAML1SOAPClient::receiveSAML()
 
                 // Check InResponseTo.
                 if (m_correlate && response->getInResponseTo() && !XMLString::equals(m_correlate, response->getInResponseTo()))
-                    throw BindingException("InResponseTo attribute did not correlate with the Request ID.");
+                    throw SecurityPolicyException("InResponseTo attribute did not correlate with the Request ID.");
+                
+                m_soaper.getPolicy().evaluate(*response);
+                
+                if (!m_soaper.getPolicy().isSecure()) {
+                    SecurityPolicyException ex("Security policy could not authenticate the message.");
+                    if (m_soaper.getPolicy().getIssuerMetadata())
+                        annotateException(&ex, m_soaper.getPolicy().getIssuerMetadata());   // throws it
+                    else
+                        ex.raise();
+                }
                 
                 // 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.");
+                    if (code && *code != StatusCode::SUCCESS && handleError(*status)) {
+                        BindingException ex("SAML Response contained an error.");
+                        if (m_soaper.getPolicy().getIssuerMetadata())
+                            annotateException(&ex, m_soaper.getPolicy().getIssuerMetadata());   // throws it
+                        else
+                            ex.raise();
+                    }
                 }
                 
-                m_soaper.getPolicy().evaluate(*response);
-                
-                if (!m_soaper.getPolicy().isSecure())
-                    throw BindingException("Security policy could not authenticate the message.");
-                
                 env.release();
                 body->detach(); // frees Envelope
                 response->detach();   // frees Body
@@ -81,7 +91,11 @@ Response* SAML1SOAPClient::receiveSAML()
             }
         }
         
-        throw BindingException("SOAP Envelope did not contain a SAML Response or a Fault.");
+        BindingException ex("SOAP Envelope did not contain a SAML Response or a Fault.");
+        if (m_soaper.getPolicy().getIssuerMetadata())
+            annotateException(&ex, m_soaper.getPolicy().getIssuerMetadata());   // throws it
+        else
+            ex.raise();
     }
     return NULL;
 }
index f2a4d42..92f835a 100644 (file)
@@ -59,20 +59,24 @@ StatusResponseType* SAML2SOAPClient::receiveSAML()
                 
                 // Check InResponseTo.
                 if (m_correlate && response->getInResponseTo() && !XMLString::equals(m_correlate, response->getInResponseTo()))
-                    throw BindingException("InResponseTo attribute did not correlate with the Request ID.");
-                
+                    throw SecurityPolicyException("InResponseTo attribute did not correlate with the Request ID.");
+
+                m_soaper.getPolicy().evaluate(*response);
+                if (!m_soaper.getPolicy().isSecure()) {
+                    SecurityPolicyException ex("Security policy could not authenticate the message.");
+                    annotateException(&ex, m_soaper.getPolicy().getIssuerMetadata(), response->getStatus());   // throws it
+                }
+
                 // Check Status.
                 Status* status = response->getStatus();
                 if (status) {
                     const XMLCh* code = status->getStatusCode() ? status->getStatusCode()->getValue() : NULL;
-                    if (code && !XMLString::equals(code,StatusCode::SUCCESS) && handleError(*status))
-                        throw BindingException("SAML Response contained an error.");
+                    if (code && !XMLString::equals(code,StatusCode::SUCCESS) && handleError(*status)) {
+                        BindingException ex("SAML response contained an error.");
+                        annotateException(&ex, m_soaper.getPolicy().getIssuerMetadata(), status);   // throws it
+                    }
                 }
                 
-                m_soaper.getPolicy().evaluate(*response);
-                if (!m_soaper.getPolicy().isSecure())
-                    throw BindingException("Security policy could not authenticate the message.");
-
                 env.release();
                 body->detach(); // frees Envelope
                 response->detach();   // frees Body
@@ -80,7 +84,11 @@ StatusResponseType* SAML2SOAPClient::receiveSAML()
             }
         }
         
-        throw BindingException("SOAP Envelope did not contain a SAML Response or a Fault.");
+        BindingException ex("SOAP Envelope did not contain a SAML Response or a Fault.");
+        if (m_soaper.getPolicy().getIssuerMetadata())
+            annotateException(&ex, m_soaper.getPolicy().getIssuerMetadata());   // throws it
+        else
+            ex.raise();
     }
     return NULL;
 }