Add simple signing support for POST binding.
[shibboleth/cpp-opensaml.git] / saml / binding / impl / MessageRoutingRule.cpp
1 /*
2  *  Copyright 2001-2006 Internet2
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * MessageRoutingRule.cpp
19  * 
20  * XML Signature checking SecurityPolicyRule
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "binding/HTTPRequest.h"
26 #include "binding/MessageRoutingRule.h"
27 #include "saml1/core/Protocols.h"
28 #include "saml2/core/Protocols.h"
29
30 #include <xmltooling/util/NDC.h>
31 #include <xmltooling/util/ReplayCache.h>
32 #include <log4cpp/Category.hh>
33
34 using namespace opensaml;
35 using namespace xmltooling;
36 using namespace log4cpp;
37 using namespace std;
38
39 namespace opensaml {
40     SecurityPolicyRule* SAML_DLLLOCAL MessageRoutingRuleFactory(const DOMElement* const & e)
41     {
42         return new MessageRoutingRule(e);
43     }
44 };
45
46 static const XMLCh mandatory[] = UNICODE_LITERAL_9(m,a,n,d,a,t,o,r,y);
47
48 MessageRoutingRule::MessageRoutingRule(const DOMElement* e) : m_mandatory(false)
49 {
50     if (e) {
51         const XMLCh* attr = e->getAttributeNS(NULL, mandatory);
52         if (attr && (*attr==chLatin_t || *attr==chDigit_1))
53             m_mandatory = true;
54     }
55 }
56
57 pair<saml2::Issuer*,const saml2md::RoleDescriptor*> MessageRoutingRule::evaluate(
58     const GenericRequest& request,
59     const XMLObject& message,
60     const saml2md::MetadataProvider* metadataProvider,
61     const QName* role,
62     const opensaml::TrustEngine* trustEngine
63     ) const
64 {
65     Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.MessageRouting");
66     log.debug("evaluating message routing policy");
67     
68     try {
69         const char* to = dynamic_cast<const HTTPRequest&>(request).getRequestURL();
70         if (!to || !*to) {
71             if (m_mandatory)
72                 throw BindingException("Unable to determine delivery location.");
73             log.debug("unable to determine delivery location, ignoring message");
74             return pair<saml2::Issuer*,const saml2md::RoleDescriptor*>(NULL,NULL);
75         }
76         auto_ptr_char dest(getDestination(message));
77         if (dest.get() && *dest.get()) {
78             if (!XMLString::equals(to, dest.get())) {
79                 log.error("Message intended for (%s), but delivered to (%s)", dest.get(), to);
80                 throw BindingException("Message delivered to incorrect address.");
81             }
82         }
83         else if (m_mandatory)
84             throw BindingException("Message did not contain intended address.");
85     }
86     catch (bad_cast&) {
87         throw BindingException("Message was not of a recognized type.");
88     }
89     return pair<saml2::Issuer*,const saml2md::RoleDescriptor*>(NULL,NULL);
90 }
91
92 const XMLCh* MessageRoutingRule::getDestination(const XMLObject& message) const
93 {
94     // We just let any bad casts throw here.
95
96     // Shortcuts some of the casting.
97     const XMLCh* ns = message.getElementQName().getNamespaceURI();
98     if (ns) {
99         if (XMLString::equals(ns, samlconstants::SAML20P_NS)) {
100             const saml2p::StatusResponseType* response = dynamic_cast<const saml2p::StatusResponseType*>(&message);
101             if (response)
102                 return response->getDestination();
103             return dynamic_cast<const saml2p::RequestAbstractType&>(message).getDestination();
104         }
105         else if (XMLString::equals(ns, samlconstants::SAML1P_NS)) {
106             // Should be a samlp:Response, at least in OpenSAML.
107             return dynamic_cast<const saml1p::Response&>(message).getRecipient();
108         }
109     }
110     return NULL;
111 }