Reducing header overuse, non-inlining selected methods (CPPOST-35).
[shibboleth/cpp-opensaml.git] / samltest / binding.h
1 /*\r
2  *  Copyright 2001-2007 Internet2\r
3  * \r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *     http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 #include "internal.h"\r
18 \r
19 #include <saml/SAMLConfig.h>\r
20 #include <saml/binding/MessageDecoder.h>\r
21 #include <saml/binding/MessageEncoder.h>\r
22 #include <saml/binding/SecurityPolicy.h>\r
23 #include <saml/binding/SecurityPolicyRule.h>\r
24 #include <saml/saml2/metadata/Metadata.h>\r
25 #include <saml/saml2/metadata/MetadataProvider.h>\r
26 #include <xmltooling/io/HTTPRequest.h>\r
27 #include <xmltooling/io/HTTPResponse.h>\r
28 #include <xmltooling/security/Credential.h>\r
29 #include <xmltooling/security/TrustEngine.h>\r
30 #include <xmltooling/util/URLEncoder.h>\r
31 \r
32 using namespace opensaml::saml2md;\r
33 using namespace opensaml;\r
34 using namespace xmlsignature;\r
35 \r
36 class SAMLBindingBaseTestCase : public HTTPRequest, public HTTPResponse\r
37 {\r
38 protected:\r
39     CredentialResolver* m_creds; \r
40     MetadataProvider* m_metadata;\r
41     TrustEngine* m_trust;\r
42     map<string,string> m_fields;\r
43     map<string,string> m_headers;\r
44     string m_method,m_url,m_query;\r
45     vector<XSECCryptoX509*> m_clientCerts;\r
46     vector<const SecurityPolicyRule*> m_rules;\r
47 \r
48 public:\r
49     void setUp() {\r
50         m_creds=NULL;\r
51         m_metadata=NULL;\r
52         m_trust=NULL;\r
53         m_fields.clear();\r
54         m_headers.clear();\r
55         m_method.erase();\r
56         m_url.erase();\r
57         m_query.erase();\r
58 \r
59         try {\r
60             string config = data_path + "binding/ExampleMetadataProvider.xml";\r
61             ifstream in(config.c_str());\r
62             DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(in);\r
63             XercesJanitor<DOMDocument> janitor(doc);\r
64     \r
65             auto_ptr_XMLCh path("path");\r
66             string s = data_path + "binding/example-metadata.xml";\r
67             auto_ptr_XMLCh file(s.c_str());\r
68             doc->getDocumentElement()->setAttributeNS(NULL,path.get(),file.get());\r
69     \r
70             m_metadata = SAMLConfig::getConfig().MetadataProviderManager.newPlugin(\r
71                 XML_METADATA_PROVIDER,doc->getDocumentElement()\r
72                 );\r
73             m_metadata->init();\r
74 \r
75             config = data_path + "FilesystemCredentialResolver.xml";\r
76             ifstream in2(config.c_str());\r
77             DOMDocument* doc2=XMLToolingConfig::getConfig().getParser().parse(in2);\r
78             XercesJanitor<DOMDocument> janitor2(doc2);\r
79             m_creds = XMLToolingConfig::getConfig().CredentialResolverManager.newPlugin(\r
80                 FILESYSTEM_CREDENTIAL_RESOLVER,doc2->getDocumentElement()\r
81                 );\r
82                 \r
83             m_trust = XMLToolingConfig::getConfig().TrustEngineManager.newPlugin(EXPLICIT_KEY_TRUSTENGINE, NULL);\r
84 \r
85             m_rules.push_back(SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(MESSAGEFLOW_POLICY_RULE,NULL));\r
86             m_rules.push_back(SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(SIMPLESIGNING_POLICY_RULE,NULL));\r
87             m_rules.push_back(SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(XMLSIGNING_POLICY_RULE,NULL));\r
88         }\r
89         catch (XMLToolingException& ex) {\r
90             TS_TRACE(ex.what());\r
91             tearDown();\r
92             throw;\r
93         }\r
94 \r
95     }\r
96     \r
97     void tearDown() {\r
98         for_each(m_rules.begin(), m_rules.end(), xmltooling::cleanup<SecurityPolicyRule>());\r
99         m_rules.clear();\r
100         delete m_creds;\r
101         delete m_metadata;\r
102         delete m_trust;\r
103         m_creds=NULL;\r
104         m_metadata=NULL;\r
105         m_trust=NULL;\r
106         m_fields.clear();\r
107         m_headers.clear();\r
108         m_method.erase();\r
109         m_url.erase();\r
110         m_query.erase();\r
111     }\r
112 \r
113     // HTTPRequest methods\r
114 \r
115     const char* getMethod() const {\r
116         return m_method.c_str();\r
117     }\r
118 \r
119     const char* getScheme() const {\r
120         return "https";\r
121     }\r
122 \r
123     const char* getHostname() const {\r
124         return "localhost";\r
125     }\r
126 \r
127     int getPort() const {\r
128         return 443;\r
129     }\r
130 \r
131     string getContentType() const {\r
132         return "application/x-www-form-urlencoded";\r
133     }\r
134 \r
135     long getContentLength() const {\r
136         return -1;\r
137     }\r
138 \r
139     const char* getRequestURI() const {\r
140         return "/";\r
141     }\r
142 \r
143     const char* getRequestURL() const {\r
144         return m_url.c_str();\r
145     }\r
146     \r
147     const char* getRequestBody() const {\r
148         return NULL;\r
149     }\r
150     \r
151     const char* getQueryString() const {\r
152         return m_query.c_str();\r
153     }\r
154     \r
155     string getRemoteUser() const {\r
156         return "";\r
157     }\r
158 \r
159     string getRemoteAddr() const {\r
160         return "127.0.0.1";\r
161     }\r
162 \r
163     const std::vector<XSECCryptoX509*>& getClientCertificates() const {\r
164         return m_clientCerts;\r
165     }\r
166 \r
167     string getHeader(const char* name) const {\r
168         map<string,string>::const_iterator i=m_headers.find(name);\r
169         return i==m_headers.end() ? "" : i->second;\r
170     }\r
171     \r
172     const char* getParameter(const char* name) const {\r
173         map<string,string>::const_iterator i=m_fields.find(name);\r
174         return i==m_fields.end() ? NULL : i->second.c_str();\r
175     }\r
176 \r
177     vector<const char*>::size_type getParameters(const char* name, vector<const char*>& values) const {\r
178         values.clear();\r
179         map<string,string>::const_iterator i=m_fields.find(name);\r
180         if (i!=m_fields.end())\r
181             values.push_back(i->second.c_str());\r
182         return values.size();\r
183     }\r
184     \r
185     // HTTPResponse methods\r
186     \r
187     void setResponseHeader(const char* name, const char* value) {\r
188         m_headers[name] = value ? value : "";\r
189     }\r
190 \r
191     // The amount of error checking missing from this is incredible, but as long\r
192     // as the test data isn't unexpected or malformed, it should work.\r
193     \r
194     long sendRedirect(const char* url) {\r
195         m_method = "GET";\r
196         char* dup = strdup(url);\r
197         char* pch = strchr(dup,'?');\r
198         if (pch) {\r
199             *pch++=0;\r
200             m_query = pch;\r
201             char* name=pch;\r
202             while (name && *name) {\r
203                 pch=strchr(pch,'=');\r
204                 *pch++=0;\r
205                 char* value=pch;\r
206                 pch=strchr(pch,'&');\r
207                 if (pch)\r
208                     *pch++=0;\r
209                 XMLToolingConfig::getConfig().getURLEncoder()->decode(value);\r
210                 m_fields[name] = value;\r
211                 name = pch; \r
212             }\r
213         }\r
214         m_url = dup;\r
215         free(dup);\r
216         return m_fields.size();\r
217     }\r
218     \r
219     string html_decode(const string& s) const {\r
220         string decoded;\r
221         const char* ch=s.c_str();\r
222         while (*ch) {\r
223             if (*ch=='&') {\r
224                 if (!strncmp(ch,"&lt;",4)) {\r
225                     decoded+='<'; ch+=4;\r
226                 }\r
227                 else if (!strncmp(ch,"&gt;",4)) {\r
228                     decoded+='>'; ch+=4;\r
229                 }\r
230                 else if (!strncmp(ch,"&quot;",6)) {\r
231                     decoded+='"'; ch+=6;\r
232                 }\r
233                 else if (*++ch=='#') {\r
234                     decoded+=(char)atoi(++ch);\r
235                     ch=strchr(ch,';')+1;\r
236                 }\r
237             }\r
238             else {\r
239                 decoded+=*ch++;\r
240             }\r
241         }\r
242         return decoded;\r
243     }\r
244     \r
245     using HTTPResponse::sendResponse;\r
246 \r
247     long sendResponse(std::istream& inputStream, long status) {\r
248         m_method="POST";\r
249         string page,line;\r
250         while (getline(inputStream,line))\r
251             page += line + '\n';\r
252             \r
253         const char* pch=strstr(page.c_str(),"action=\"");\r
254         pch+=strlen("action=\"");\r
255         m_url = html_decode(page.substr(pch-page.c_str(),strchr(pch,'"')-pch));\r
256 \r
257         while (pch=strstr(pch,"<input type=\"hidden\" name=\"")) {\r
258             pch+=strlen("<input type=\"hidden\" name=\"");\r
259             string name = page.substr(pch-page.c_str(),strchr(pch,'"')-pch);\r
260             pch=strstr(pch,"value=\"");\r
261             pch+=strlen("value=\"");\r
262             m_fields[name] = html_decode(page.substr(pch-page.c_str(),strchr(pch,'"')-pch));\r
263         }\r
264         return m_fields.size();\r
265     }\r
266 };\r