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