1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20 
21 #ifdef WIN32
22 # define _CRT_SECURE_NO_DEPRECATE 1
23 # define _CRT_NONSTDC_NO_DEPRECATE 1
24 #endif
25 
26 #include <cxxtest/TestSuite.h>
27 
28 #include <saml/exceptions.h>
29 #include <saml/util/SAMLConstants.h>
30 
31 #include <fstream>
32 #include <xmltooling/XMLObject.h>
33 #include <xmltooling/XMLObjectBuilder.h>
34 #include <xmltooling/XMLToolingConfig.h>
35 #include <xmltooling/util/ParserPool.h>
36 #include <xmltooling/validation/Validator.h>
37 
38 using namespace xmltooling;
39 using namespace xercesc;
40 using namespace std;
41 
42 using boost::scoped_ptr;
43 
44 extern string data_path;
45 
46 class SAMLObjectBaseTestCase
47 {
48 protected:
49     /** Location of file containing a single element with NO optional attributes */
50     string singleElementFile;
51 
52     /** Location of file containing a single element with all optional attributes */
53     string singleElementOptionalAttributesFile;
54 
55     /** Location of file containing a single element with child elements */
56     string childElementsFile;
57 
58     /** The expected result of a marshalled single element with no optional attributes */
59     DOMDocument* expectedDOM;
60 
61     /** The expected result of a marshalled single element with all optional attributes */
62     DOMDocument* expectedOptionalAttributesDOM;
63 
64     /** The expected result of a marshalled single element with child elements */
65     DOMDocument* expectedChildElementsDOM;
66 
67     /**
68      * Unmarshalls an element file into its SAML XMLObject.
69      *
70      * @return the SAML XMLObject from the file
71      */
unmarshallElement(string elementFile)72     XMLObject* unmarshallElement(string elementFile) {
73         try {
74             ParserPool& p=XMLToolingConfig::getConfig().getParser();
75             ifstream fs(elementFile.c_str());
76             DOMDocument* doc = p.parse(fs);
77             const XMLObjectBuilder* b = XMLObjectBuilder::getBuilder(doc->getDocumentElement());
78             return b->buildFromDocument(doc);
79         }
80         catch (XMLToolingException& e) {
81             TS_TRACE(typeid(e).name());
82             TS_TRACE(e.what());
83             throw;
84         }
85     }
86 
87     void assertEquals(const char* failMessage, DOMDocument* expectedDOM, XMLObject* xmlObject, bool canMarshall=true) {
88         DOMElement* generatedDOM = xmlObject->getDOM();
89         if (!generatedDOM) {
90             if (!canMarshall) {
91                 TSM_ASSERT("DOM not available", false);
92             }
93             else {
94                 generatedDOM = xmlObject->marshall();
95             }
96         }
97         if (!generatedDOM->isEqualNode(expectedDOM->getDocumentElement())) {
98             string buf;
99             XMLHelper::serialize(generatedDOM, buf);
100             TS_TRACE(buf.c_str());
101             buf.erase();
102             XMLHelper::serialize(expectedDOM->getDocumentElement(), buf);
103             TS_TRACE(buf.c_str());
104             TSM_ASSERT(failMessage, false);
105         }
106     }
107 
108     void assertEquals(DOMDocument* expectedDOM, XMLObject* xmlObject, bool canMarshall=true) {
109         xmlObject->releaseThisAndChildrenDOM();
110         auto_ptr<XMLObject> cloned(xmlObject->clone());
111         assertEquals("Marshalled DOM was not the same as the expected DOM", expectedDOM, cloned.get(), canMarshall);
112         delete xmlObject;
113     }
114 
assertEquals(const char * failMessage,const XMLCh * expectedString,const XMLCh * testString)115     void assertEquals(const char* failMessage, const XMLCh* expectedString, const XMLCh* testString) {
116         char* buf = nullptr;
117         if (!XMLString::equals(expectedString, testString)) {
118             buf = XMLString::transcode(testString);
119             TS_TRACE(buf ? buf : "(NULL)");
120             XMLString::release(&buf);
121             buf = XMLString::transcode(expectedString);
122             TS_TRACE(buf ? buf : "(NULL)");
123             XMLString::release(&buf);
124             TSM_ASSERT(failMessage, false);
125         }
126     }
127 
skipNetworked()128     void skipNetworked() {
129         if (!getenv("SAMLTEST_NETWORKED")) {
130 #ifdef TS_SKIP
131             TS_SKIP("requires network access");
132 #endif
133         }
134     }
135 
136 public:
setUp()137     void setUp() {
138         ParserPool& p=XMLToolingConfig::getConfig().getParser();
139         if (!singleElementFile.empty()) {
140             ifstream fs(singleElementFile.c_str());
141             expectedDOM = p.parse(fs);
142         }
143 
144         if (!singleElementOptionalAttributesFile.empty()) {
145             ifstream fs(singleElementOptionalAttributesFile.c_str());
146             expectedOptionalAttributesDOM = p.parse(fs);
147         }
148 
149         if (!childElementsFile.empty()) {
150             ifstream fs(childElementsFile.c_str());
151             expectedChildElementsDOM = p.parse(fs);
152         }
153     }
154 
tearDown()155     void tearDown() {
156         if (expectedDOM) expectedDOM->release();
157         if (expectedOptionalAttributesDOM) expectedOptionalAttributesDOM->release();
158         if (expectedChildElementsDOM) expectedChildElementsDOM->release();
159     }
160 };
161 
162 class SAMLObjectValidatorBaseTestCase : virtual public SAMLObjectBaseTestCase {
163 
164     public:
SAMLObjectValidatorBaseTestCase()165         SAMLObjectValidatorBaseTestCase() : builder(nullptr) {}
166 
~SAMLObjectValidatorBaseTestCase()167         virtual ~SAMLObjectValidatorBaseTestCase() {}
168 
169     protected:
170         /** The primary XMLObject which will be the target of a given test run */
171         scoped_ptr<XMLObject> target;
172 
173         /** QName of the object to be tested */
174         xmltooling::QName targetQName;
175 
176         /** Builder for XMLObjects of type targetQName */
177         const XMLObjectBuilder* builder;
178 
179         /** Validator for the type corresponding to the test target */
180         scoped_ptr<Validator> validator;
181 
182         /** Subclasses should override to populate required elements and attributes */
populateRequiredData()183         virtual void populateRequiredData() { }
184 
185         /**
186          * Asserts that the validation of default test XMLObject target
187          * was successful, as expected.
188          *
189          * @param message
190          */
assertValidationPass(const char * message)191         void assertValidationPass(const char* message) {
192             assertValidationPass(message, target);
193         }
194 
195         /**
196          * Asserts that the validation of the specified XMLObject target
197          * was successful, as expected.
198          *
199          * @param message
200          * @param validateTarget
201          */
assertValidationPass(const char * message,scoped_ptr<XMLObject> & validateTarget)202         void assertValidationPass(const char* message, scoped_ptr<XMLObject>& validateTarget) {
203             try {
204                 validator->validate(validateTarget.get());
205             } catch (const ValidationException &e) {
206                 TS_TRACE(message);
207                 TS_TRACE("Expected success, but validation failure raised following ValidationException: ");
208                 TS_FAIL(e.getMessage());
209             }
210         }
211 
212         /**
213          * Asserts that the validation of the default test XMLObject target
214          * failed, as expected.
215          *
216          * @param message
217          */
assertValidationFail(const char * message)218         void assertValidationFail(const char* message) {
219             assertValidationFail(message, target);
220         }
221 
222         /**
223          * Asserts that the validation of the specified XMLObject target
224          * failed, as expected.
225          *
226          * @param message
227          * @param validateTarget
228          */
assertValidationFail(const char * message,scoped_ptr<XMLObject> & validateTarget)229         void assertValidationFail(const char* message, scoped_ptr<XMLObject>& validateTarget) {
230             try {
231                 validator->validate(validateTarget.get());
232                 TS_TRACE(message);
233                 TS_FAIL("Validation success, expected failure to raise ValidationException");
234             } catch (const ValidationException&) {
235             }
236         }
237 
238         /**
239          * Build an XMLObject based on the specified QName
240          *
241          * @param targetQName QName of the type of object to build
242          * @returns new XMLObject of type targetQName
243          */
buildXMLObject(const xmltooling::QName & targetQName)244         XMLObject* buildXMLObject(const xmltooling::QName &targetQName) {
245             // Create the builder on the first request only, for efficiency
246             if (builder == nullptr) {
247                 builder = XMLObjectBuilder::getBuilder(targetQName);
248                 TSM_ASSERT("Unable to retrieve builder for object QName: " + targetQName.toString(), builder!=nullptr);
249             }
250             return builder->buildObject(targetQName.getNamespaceURI(), targetQName.getLocalPart(), targetQName.getPrefix());
251 
252         }
253 
254     public:
255 
setUp()256         void setUp() {
257             SAMLObjectBaseTestCase::setUp();
258 
259             TSM_ASSERT("targetQName was empty", targetQName.hasLocalPart());
260 
261             TSM_ASSERT("validator was null", validator.get() != nullptr);
262 
263             target.reset(buildXMLObject(targetQName));
264             TSM_ASSERT("XMLObject target was NULL", target.get() != nullptr);
265             populateRequiredData();
266         }
267 
tearDown()268         void tearDown() {
269             target.reset(nullptr);
270             validator.reset(nullptr);
271             SAMLObjectBaseTestCase::tearDown();
272         }
273 
274 };
275