diff --git a/doc/CHANGES_1_8.txt b/doc/CHANGES_1_8.txt index a39bbdd65..5c5e936b7 100644 --- a/doc/CHANGES_1_8.txt +++ b/doc/CHANGES_1_8.txt @@ -1,6 +1,7 @@ General: ======== * Fix internally-created faults lacking an XML element name (so e.g. toXml() would abort) +* KDSoapMessage::messageAddressingProperties() is now correctly filled in when receiving a message with WS-Addressing in the header Client-side: ============ @@ -12,6 +13,8 @@ Client-side: * Fix error code when authentication failed * Autodeletion of jobs is now configurable (github pull #125) * Add error details in faultAsString() - and the generated lastError() - coming from the SOAP 1.2 detail element. +* Fix memory leak in KDSoapClientInterface::callNoReply +* Add support for WS-UsernameToken, see KDSoapAuthentication Server-side: ============ diff --git a/kdwsdl2cpp/libkode/style.cpp b/kdwsdl2cpp/libkode/style.cpp index e981cbede..8cddf348b 100644 --- a/kdwsdl2cpp/libkode/style.cpp +++ b/kdwsdl2cpp/libkode/style.cpp @@ -83,6 +83,8 @@ QString Style::lowerFirst( const QString &str ) QString Style::makeIdentifier( const QString &str ) { + Q_ASSERT(!str.isEmpty()); + QString identifier = str; identifier.replace( "-", "_" ); identifier.replace( ".", "_" ); diff --git a/kdwsdl2cpp/schema/parser.cpp b/kdwsdl2cpp/schema/parser.cpp index 687e03580..adb32d835 100644 --- a/kdwsdl2cpp/schema/parser.cpp +++ b/kdwsdl2cpp/schema/parser.cpp @@ -487,7 +487,7 @@ Element Parser::parseElement(ParserContext *context, newElement.setNillable(stringToBoolean(element.attribute(QLatin1String("nillable")))); if (element.hasAttribute(QLatin1String("type"))) { - QName typeName(element.attribute(QLatin1String("type"))); + QName typeName(element.attribute(QLatin1String("type")).trimmed()); typeName.setNameSpace(context->namespaceManager()->uri(typeName.prefix())); if (debugParsing()) { qDebug() << "typeName=" << typeName.qname() << "namespace=" << context->namespaceManager()->uri(typeName.prefix()); diff --git a/kdwsdl2cpp/src/compiler.cpp b/kdwsdl2cpp/src/compiler.cpp index 1076faa5e..7075b77ec 100644 --- a/kdwsdl2cpp/src/compiler.cpp +++ b/kdwsdl2cpp/src/compiler.cpp @@ -103,7 +103,6 @@ void Compiler::parse(const QDomElement &element) definitions.fixUpDefinitions(/*&context, element*/); - KODE::Code::setDefaultIndentation(4); WSDL wsdl; wsdl.setDefinitions(definitions); @@ -116,7 +115,19 @@ void Compiler::parse(const QDomElement &element) QCoreApplication::exit(4); } else { KWSDL::Creator creator; - creator.create(converter.classes()); + creator.setOutputDirectory(Settings::self()->outputDirectory()); + creator.setSourceFile(Settings::self()->wsdlFileName()); + creator.setHeaderFileName(Settings::self()->headerFileName()); + creator.setImplementationFileName(Settings::self()->implementationFileName()); + + creator.setClasses(converter.classes()); + if (Settings::self()->generateHeader()) { + creator.createHeader(); + } + if (Settings::self()->generateImplementation()) { + creator.createImplementation(); + } + QCoreApplication::exit(0); } } else { diff --git a/kdwsdl2cpp/src/converter_clientstub.cpp b/kdwsdl2cpp/src/converter_clientstub.cpp index fb5c6803a..0ef15cd0e 100644 --- a/kdwsdl2cpp/src/converter_clientstub.cpp +++ b/kdwsdl2cpp/src/converter_clientstub.cpp @@ -164,6 +164,16 @@ bool Converter::convertClientService() setEndPoint.setDocs(QLatin1String("Overwrite the end point defined in the .wsdl file, with another http/https URL.")); newClass.addFunction(setEndPoint); } + // endPoint() accessor + { + KODE::Function getEndPoint(QLatin1String("endPoint"), QLatin1String("QString")); + getEndPoint.setConst(true); + KODE::Code code; + code += "return d_ptr->m_endPoint;"; + getEndPoint.setBody(code); + getEndPoint.setDocs(QLatin1String("Return the end point that will be used.")); + newClass.addFunction(getEndPoint); + } //setSoapVersion() method { KODE::Function setSoapVersion(QLatin1String("setSoapVersion"), QLatin1String("void")); @@ -175,6 +185,16 @@ bool Converter::convertClientService() "version can be KDSoapClientInterface::SOAP1_1 or KDSoapClientInterface::SOAP1_2")); newClass.addFunction(setSoapVersion); } + //soapVersion() method + { + KODE::Function getSoapVersion(QLatin1String("soapVersion"), QLatin1String("KDSoapClientInterface::SoapVersion")); + getSoapVersion.setConst(true); + KODE::Code code; + code += "return clientInterface()->soapVersion();"; + getSoapVersion.setBody(code); + getSoapVersion.setDocs(QLatin1String("Return the soap version used.n")); + newClass.addFunction(getSoapVersion); + } // lastErrorCode() method { KODE::Function lastError(QLatin1String("lastErrorCode"), QLatin1String("int")); @@ -385,6 +405,7 @@ bool Converter::convertClientService() if (hasAction) { callLine += QLatin1String(", action"); } + callLine += QLatin1String(", requestHeaders()"); callLine += QLatin1String(");"); doStartCode += callLine; diff --git a/kdwsdl2cpp/src/converter_complextype.cpp b/kdwsdl2cpp/src/converter_complextype.cpp index 97932e911..2307a55ee 100644 --- a/kdwsdl2cpp/src/converter_complextype.cpp +++ b/kdwsdl2cpp/src/converter_complextype.cpp @@ -192,7 +192,8 @@ void Converter::convertComplexType(const XSD::ComplexType *type) typeName = mTypeMap.localType(attribute.type()); if (typeName.isEmpty()) { - qDebug() << "ERROR: attribute with unknown type:" << attribute.name() << attribute.type() << "in" << typeName; + qWarning() << "ERROR: attribute with unknown type:" << attribute.name() << attribute.type() << "in" << typeName; + continue; } inputTypeName = mTypeMap.localInputType(attribute.type(), QName()); //qDebug() << "Attribute" << attribute.name(); @@ -643,6 +644,9 @@ void Converter::createComplexTypeSerializer(KODE::Class &newClass, const XSD::Co bool first = true; Q_FOREACH (const XSD::Attribute &attribute, attributes) { const QString attrName = attribute.name(); + if (attrName.isEmpty()) { + continue; + } const QString variableName = QLatin1String("d_ptr->") + KODE::MemberVariable::memberVariableName(attrName); demarshalCode.addBlock(demarshalNameTest(attribute.type(), attrName, &first)); diff --git a/kdwsdl2cpp/src/creator.cpp b/kdwsdl2cpp/src/creator.cpp index 0ea3a1b43..53f331983 100644 --- a/kdwsdl2cpp/src/creator.cpp +++ b/kdwsdl2cpp/src/creator.cpp @@ -31,43 +31,53 @@ using namespace KWSDL; Creator::Creator() { -} - -void Creator::create(const KODE::Class::List &classes) -{ - KODE::Printer printer; - printer.setOutputDirectory(Settings::self()->outputDirectory()); + KODE::Code::setDefaultIndentation(4); // Set generated header details. - printer.setCreationWarning(true); - printer.setGenerator(QLatin1String("KDAB's kdwsdl2cpp")); - printer.setSourceFile(Settings::self()->wsdlFileName()); + _printer.setCreationWarning(true); + _printer.setGenerator(QLatin1String("KDAB's kdwsdl2cpp")); // Qt-like coding style - printer.setLabelsDefineIndent(false); - printer.setIndentLabels(false); + _printer.setLabelsDefineIndent(false); + _printer.setIndentLabels(false); - //qDebug() << "Create server=" << Settings::self()->generateServerCode() << "impl=" << Settings::self()->generateImplementation(); + _file.setLicense(KODE::License::GeneratedNoRestriction); +} - KODE::File file; +void Creator::setOutputDirectory(const QString &outputDirectory) +{ + _printer.setOutputDirectory(outputDirectory); +} - if (Settings::self()->generateImplementation()) { - file.setImplementationFilename(Settings::self()->outputFileName()); - file.setHeaderFilename(Settings::self()->headerFile()); - } else { - file.setHeaderFilename(Settings::self()->outputFileName()); - } +void Creator::setSourceFile(const QString &sourceFile) +{ + _printer.setSourceFile(sourceFile); +} - file.setLicense(KODE::License::GeneratedNoRestriction); +void Creator::setHeaderFileName(const QString &headerFileName) +{ + _file.setHeaderFilename(headerFileName); +} +void Creator::setImplementationFileName(const QString &implementationFileName) +{ + _file.setImplementationFilename(implementationFileName); +} + +void Creator::setClasses(const KODE::Class::List &list) +{ KODE::Class::List::ConstIterator it; - for (it = classes.constBegin(); it != classes.constEnd(); ++it) { - file.insertClass(*it); + for (it = list.constBegin(); it != list.constEnd(); ++it) { + _file.insertClass(*it); } +} - if (Settings::self()->generateImplementation()) { - printer.printImplementation(file); - } else { - printer.printHeader(file); - } +void Creator::createHeader() +{ + _printer.printHeader(_file); +} + +void Creator::createImplementation() +{ + _printer.printImplementation(_file); } diff --git a/kdwsdl2cpp/src/creator.h b/kdwsdl2cpp/src/creator.h index ec891997d..0f4a41474 100644 --- a/kdwsdl2cpp/src/creator.h +++ b/kdwsdl2cpp/src/creator.h @@ -21,6 +21,9 @@ #define KWSDL_CREATOR_H #include +#include +#include +#include namespace KWSDL { @@ -30,7 +33,19 @@ class Creator public: Creator(); - void create(const KODE::Class::List &list); + void setOutputDirectory(const QString &outputDirectory); + void setSourceFile(const QString &sourceFile); + + void setHeaderFileName(const QString &headerFileName); + void setImplementationFileName(const QString &implementationFileName); + void setClasses(const KODE::Class::List &list); + + void createHeader(); + void createImplementation(); + +private: + KODE::File _file; + KODE::Printer _printer; }; } diff --git a/kdwsdl2cpp/src/main.cpp b/kdwsdl2cpp/src/main.cpp index 29b15f486..e5dbb881c 100644 --- a/kdwsdl2cpp/src/main.cpp +++ b/kdwsdl2cpp/src/main.cpp @@ -28,20 +28,32 @@ #include static const char *WSDL2CPP_DESCRIPTION = "KDAB's WSDL to C++ compiler"; -static const char *WSDL2CPP_VERSION_STR = "2.0"; +static const char *WSDL2CPP_VERSION_STR = "2.1"; static void showHelp(const char *appName) { fprintf(stderr, "%s %s\n", WSDL2CPP_DESCRIPTION, WSDL2CPP_VERSION_STR); - fprintf(stderr, "Usage: %s [options] [-impl ] \n\n" + fprintf(stderr, + "Usage:\n" + " Header file: %s [options] -o \n" + " Impl. file: %s [options] -o -impl \n" + " Both files : %s [options] -both \n" + "\n" + "Options:\n" " -h, -help display this help and exit\n" " -v, -version display version\n" " -s, -service name of the service to generate\n" - " -o generate the header file into \n" - " -impl generate the implementation file, and #include \n" + " -o output the generated file into \n" + " -impl generate the implementation(.cpp) file, and #include \n" + " -both generate both the header(.h) and the implementation(.cpp) file\n" " -server generate server-side base class, instead of client service\n" " -exportMacro set the export declaration to use for generated classes\n" " -namespace put all generated classes into the given C++ namespace\n" + " -namespaceMapping \n" + " add the uri=code mapping\n" + " if begins with '@', read from file instead\n" + " one entry per line\n" + " (affects the generated class names)\n" " -optional-element-type \n" " use as the getter return value for optional elements.\n" " can be either raw-pointer or boost-optional\n" @@ -55,7 +67,7 @@ static void showHelp(const char *appName) " use of the import-path option\n" " -help-on-missing When groups or basic types could not be found, display\n" " available types (helps with wrong namespaces)\n" - "\n", appName); + "\n", appName, appName, appName); } int main(int argc, char **argv) @@ -64,12 +76,16 @@ int main(int argc, char **argv) const char *fileName = 0; QFileInfo outputFile; + bool both = false; bool impl = false; + bool outfileGiven = false; bool server = false; + QString baseFile; QString headerFile; QString serviceName; QString exportMacro; QString nameSpace; + Settings::NSMapping nsmapping; // XML mappings from URL to short code Settings::OptionalElementType optionalElementType = Settings::ENone; bool keepUnusedTypes = false; QStringList importPathList; @@ -90,6 +106,15 @@ int main(int argc, char **argv) return 1; } headerFile = QFile::decodeName(argv[arg]); + } else if (opt == QLatin1String("-both")) { + both = true; + ++arg; + if (!argv[arg]) { + showHelp(argv[0]); + return 1; + } + baseFile = QFile::decodeName(argv[arg]); + outputFile.setFile(baseFile); } else if (opt == QLatin1String("-server")) { server = true; } else if (opt == QLatin1String("-v") || opt == QLatin1String("-version")) { @@ -101,6 +126,7 @@ int main(int argc, char **argv) showHelp(argv[0]); return 1; } + outfileGiven = true; outputFile.setFile(QFile::decodeName(argv[arg])); } else if (opt == QLatin1String("-s") || opt == QLatin1String("-service")) { ++arg; @@ -123,6 +149,40 @@ int main(int argc, char **argv) return 1; } nameSpace = argv[arg]; + } else if (opt == QLatin1String("-namespaceMapping")) { + ++arg; + if (!argv[arg]) { + showHelp(argv[0]); + return 1; + } + QString mapping = argv[arg]; + if (mapping.startsWith('@')) { + QString mappingFileName = QFile::decodeName(argv[arg] + 1); // +1 to skip the '@' + QFile file(mappingFileName); + if (!file.open(QIODevice::ReadOnly)) { + fprintf(stderr, "Error reading %s: %s\n", QFile::encodeName(mappingFileName).constData(), qPrintable(file.errorString())); + showHelp(argv[0]); + return 1; + } + + while (!file.atEnd()) { + QString mapping = file.readLine().trimmed(); + if (mapping.startsWith('#')) { + continue; + } + + QString uri = mapping.section("=", 0, -2); + QString target = mapping.section("=", -1, -1); + if (!uri.isEmpty() && !target.isEmpty()) { + nsmapping[uri] = target; + } + } + + } else { + QString uri = mapping.section("=", 0, -2); + QString target = mapping.section("=", -1, -1); + nsmapping[uri] = target; + } } else if (opt == QLatin1String("-optional-element-type")) { ++arg; if (!argv[arg]) { @@ -163,19 +223,45 @@ int main(int argc, char **argv) return 1; } + // if you're saying "just make the impl-file", you can't + // also say "make both the header and the impl" + if (both && (outfileGiven || impl)) { + showHelp(argv[0]); + return 1; + } + + + if (both) { + Settings::self()->setGenerateHeader(true); + Settings::self()->setGenerateImplementation(true); + Settings::self()->setHeaderFileName(baseFile + ".h"); + Settings::self()->setImplementationFileName(baseFile + ".cpp"); + } else if (impl) { + Settings::self()->setGenerateHeader(false); + Settings::self()->setGenerateImplementation(true); + Settings::self()->setHeaderFileName(headerFile); + Settings::self()->setImplementationFileName(outputFile.fileName()); + } else { + Settings::self()->setGenerateHeader(true); + Settings::self()->setGenerateImplementation(false); + Settings::self()->setHeaderFileName(outputFile.fileName()); + Settings::self()->setImplementationFileName("UNUSED"); + } + + Settings::self()->setGenerateServerCode(server); - Settings::self()->setGenerateImplementation(impl, headerFile); - Settings::self()->setOutputFileName(outputFile.fileName()); Settings::self()->setOutputDirectory(outputFile.absolutePath()); Settings::self()->setWsdlFile(fileName); Settings::self()->setWantedService(serviceName); Settings::self()->setExportDeclaration(exportMacro); Settings::self()->setNameSpace(nameSpace); + Settings::self()->setNamespaceMapping(nsmapping); Settings::self()->setOptionalElementType(optionalElementType); Settings::self()->setKeepUnusedTypes(keepUnusedTypes); Settings::self()->setImportPathList(importPathList); Settings::self()->setUseLocalFilesOnly(useLocalFilesOnly); Settings::self()->setHelpOnMissing(helpOnMissing); + KWSDL::Compiler compiler; // so that we have an event loop, for downloads diff --git a/kdwsdl2cpp/src/settings.cpp b/kdwsdl2cpp/src/settings.cpp index a83f3bfbc..dbc01ef52 100644 --- a/kdwsdl2cpp/src/settings.cpp +++ b/kdwsdl2cpp/src/settings.cpp @@ -39,7 +39,8 @@ Q_GLOBAL_STATIC(SettingsSingleton, s_settings) Settings::Settings() { mOutputDirectory = QDir::current().path(); - mOutputFileName = QString::fromLatin1("kwsdl_generated"); + mHeaderFileName = QString::fromLatin1("kwsdl_generated"); + mImplementationFileName = QString::fromLatin1("kwsdl_generated"); mImpl = false; mServer = false; mKeepUnusedTypes = false; @@ -101,19 +102,14 @@ QString Settings::wsdlFileName() const return strUrl.mid(strUrl.lastIndexOf(QLatin1Char('/')) + 1); } -void Settings::setOutputFileName(const QString &outputFileName) +void Settings::setHeaderFileName(const QString &headerFileName) { - mOutputFileName = outputFileName; + mHeaderFileName = headerFileName; } -QString Settings::outputFileName() const +void Settings::setImplementationFileName(const QString &implementationFileName) { - if (mOutputFileName.isEmpty()) { - QFileInfo fi(wsdlFileName()); - return QLatin1String("wsdl_") + fi.completeBaseName() + QLatin1String(mImpl ? ".cpp" : ".h"); - } - - return mOutputFileName; + mImplementationFileName = implementationFileName; } void Settings::setOutputDirectory(const QString &outputDirectory) @@ -160,10 +156,9 @@ Settings::NSMapping Settings::namespaceMapping() const return mNamespaceMapping; } -void Settings::setGenerateImplementation(bool b, const QString &headerFile) +void Settings::setGenerateImplementation(bool b) { mImpl = b; - mHeaderFile = headerFile; } bool Settings::generateImplementation() const @@ -171,11 +166,27 @@ bool Settings::generateImplementation() const return mImpl; } -QString Settings::headerFile() const +void Settings::setGenerateHeader(bool b) +{ + mHeader = b; +} + +bool Settings::generateHeader() const +{ + return mHeader; +} + +QString Settings::headerFileName() const +{ + return mHeaderFileName; +} + +QString Settings::implementationFileName() const { - return mHeaderFile; + return mImplementationFileName; } + void Settings::setWantedService(const QString &service) { mWantedService = service; diff --git a/kdwsdl2cpp/src/settings.h b/kdwsdl2cpp/src/settings.h index 1717e571e..17f6cb04e 100644 --- a/kdwsdl2cpp/src/settings.h +++ b/kdwsdl2cpp/src/settings.h @@ -35,9 +35,16 @@ class Settings static Settings *self(); - void setGenerateImplementation(bool b, const QString &headerFile); + void setImplementationFileName(const QString &implFileName); + void setHeaderFileName(const QString &implFileName); + QString headerFileName() const; + QString implementationFileName() const; + + void setGenerateImplementation(bool b); bool generateImplementation() const; - QString headerFile() const; + + void setGenerateHeader(bool b); + bool generateHeader() const; void setGenerateServerCode(bool b); bool generateServerCode() const; @@ -47,9 +54,6 @@ class Settings QString wsdlBaseUrl() const; QString wsdlFileName() const; - void setOutputFileName(const QString &outputFileName); - QString outputFileName() const; - void setOutputDirectory(const QString &outputDirectory); QString outputDirectory() const; @@ -86,14 +90,15 @@ class Settings Settings(); QUrl mWsdlUrl; - QString mOutputFileName; QString mOutputDirectory; - QString mHeaderFile; + QString mHeaderFileName; + QString mImplementationFileName; QString mWantedService; QString mExportDeclaration; QString mNameSpace; QStringList mImportPathList; NSMapping mNamespaceMapping; + bool mHeader; bool mImpl; bool mServer; OptionalElementType mOptionalElementType; diff --git a/src/KDSoapClient/KDSoapAuthentication.cpp b/src/KDSoapClient/KDSoapAuthentication.cpp index 06e5e9274..3ad595c60 100644 --- a/src/KDSoapClient/KDSoapAuthentication.cpp +++ b/src/KDSoapClient/KDSoapAuthentication.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** Copyright (C) 2010-2018 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com. +** Copyright (C) 2019 Casper Meijn ** All rights reserved. ** ** This file is part of the KD Soap library. @@ -21,15 +22,26 @@ ** **********************************************************************/ #include "KDSoapAuthentication.h" -#include -#include #include +#include +#include +#include +#include +#include "KDSoapNamespacePrefixes_p.h" +#include "KDSoapNamespaceManager.h" class KDSoapAuthentication::Private { public: + Private() : + useWSUsernameToken(false) + {} + QString user; QString password; + bool useWSUsernameToken; + QDateTime overrideWSUsernameCreatedTime; + QByteArray overrideWSUsernameNonce; }; KDSoapAuthentication::KDSoapAuthentication() @@ -64,6 +76,21 @@ void KDSoapAuthentication::setPassword(const QString &password) d->password = password; } +void KDSoapAuthentication::setUseWSUsernameToken(bool useWSUsernameToken) +{ + d->useWSUsernameToken = useWSUsernameToken; +} + +void KDSoapAuthentication::setOverrideWSUsernameCreatedTime(QDateTime overrideWSUsernameCreatedTime) +{ + d->overrideWSUsernameCreatedTime = overrideWSUsernameCreatedTime; +} + +void KDSoapAuthentication::setOverrideWSUsernameNonce(QByteArray overrideWSUsernameNonce) +{ + d->overrideWSUsernameNonce = overrideWSUsernameNonce; +} + QString KDSoapAuthentication::user() const { return d->user; @@ -74,6 +101,21 @@ QString KDSoapAuthentication::password() const return d->password; } +bool KDSoapAuthentication::useWSUsernameToken() const +{ + return d->useWSUsernameToken; +} + +QDateTime KDSoapAuthentication::overrideWSUsernameCreatedTime() const +{ + return d->overrideWSUsernameCreatedTime; +} + +QByteArray KDSoapAuthentication::overrideWSUsernameNonce() const +{ + return d->overrideWSUsernameNonce; +} + bool KDSoapAuthentication::hasAuth() const { return !d->user.isEmpty() || !d->password.isEmpty(); @@ -91,3 +133,53 @@ void KDSoapAuthentication::handleAuthenticationRequired(QNetworkReply *reply, QA reply->setProperty("authAdded", true); } } + +bool KDSoapAuthentication::hasWSUsernameTokenHeader() const +{ + return hasAuth() && d->useWSUsernameToken; +} + +void KDSoapAuthentication::writeWSUsernameTokenHeader(QXmlStreamWriter &writer) const +{ + if (!hasAuth()) { + return; + } + + const QString securityExtentionNS = KDSoapNamespaceManager::soapSecurityExtention(); + const QString securityUtilityNS = KDSoapNamespaceManager::soapSecurityUtility(); + + QByteArray nonce = "kdsoap" + QByteArray::number(qrand()); + if (!d->overrideWSUsernameNonce.isEmpty()) { + nonce = d->overrideWSUsernameNonce; + } + QDateTime time = QDateTime::currentDateTimeUtc(); + if (d->overrideWSUsernameCreatedTime.isValid()) { + time = d->overrideWSUsernameCreatedTime; + } + QString timestamp = time.toString(QLatin1String("yyyy-MM-ddTHH:mm:ssZ")); + QByteArray passwordConcat = nonce + timestamp.toUtf8() + d->password.toUtf8(); + QByteArray passwordHash = QCryptographicHash::hash(passwordConcat, QCryptographicHash::Sha1); + + writer.writeStartElement(securityExtentionNS, QLatin1String("Security")); + writer.writeStartElement(securityExtentionNS, QLatin1String("UsernameToken")); + + writer.writeStartElement(securityExtentionNS, QLatin1String("Nonce")); + writer.writeCharacters(QString::fromLatin1(nonce.toBase64().constData())); + writer.writeEndElement(); + + writer.writeStartElement(securityUtilityNS, QLatin1String("Created")); + writer.writeCharacters(timestamp); + writer.writeEndElement(); + + writer.writeStartElement(securityExtentionNS, QLatin1String("Password")); + writer.writeAttribute(QLatin1String("Type"), QLatin1String("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest")); + writer.writeCharacters(QString::fromLatin1(passwordHash.toBase64().constData())); + writer.writeEndElement(); + + writer.writeStartElement(securityExtentionNS, QLatin1String("Username")); + writer.writeCharacters(d->user); + writer.writeEndElement(); + + writer.writeEndElement(); + writer.writeEndElement(); +} diff --git a/src/KDSoapClient/KDSoapAuthentication.h b/src/KDSoapClient/KDSoapAuthentication.h index e02c6c9c4..0f7c29322 100644 --- a/src/KDSoapClient/KDSoapAuthentication.h +++ b/src/KDSoapClient/KDSoapAuthentication.h @@ -1,5 +1,6 @@ /**************************************************************************** ** Copyright (C) 2010-2018 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com. +** Copyright (C) 2019 Casper Meijn ** All rights reserved. ** ** This file is part of the KD Soap library. @@ -27,8 +28,11 @@ #include QT_BEGIN_NAMESPACE class QAuthenticator; +class QDateTime; class QNetworkReply; +class QXmlStreamWriter; QT_END_NAMESPACE +class KDSoapNamespacePrefixes; /** * KDSoapAuthentication provides an authentication object. @@ -40,6 +44,10 @@ QT_END_NAMESPACE class KDSOAP_EXPORT KDSoapAuthentication { public: + friend class KDSoapMessageWriter; + friend class KDSoapClientInterfacePrivate; + friend class KDSoapThreadTask; + /** * Constructs an empty authentication object. */ @@ -71,6 +79,43 @@ class KDSOAP_EXPORT KDSoapAuthentication */ QString password() const; + /** + * Sets whether WS-UsernameToken is used for authentication. When + * set, the WS-UsernameToken headers are included in each request. + * See https://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf + * \since 1.8 + */ + void setUseWSUsernameToken(bool useWSUsernameToken); + /** + * \since 1.8 + * \return whether WS-UsernameToken is used for authentication + */ + bool useWSUsernameToken() const; + + /** + * Sets the created time used during WS-UsernameToken authentication + * This is useful for devices with an incorrect time and during testing + * \since 1.8 + */ + void setOverrideWSUsernameCreatedTime(QDateTime overrideWSUsernameCreatedTime); + /** + * \since 1.8 + * \return the created time used during WS-UsernameToken authentication + */ + QDateTime overrideWSUsernameCreatedTime() const; + + /** + * Sets the nonce used during WS-UsernameToken authentication + * This is useful during testing + * \since 1.8 + */ + void setOverrideWSUsernameNonce(QByteArray overrideWSUsernameNonce); + /** + * \since 1.8 + * \return the created time used during WS-UsernameToken authentication + */ + QByteArray overrideWSUsernameNonce() const; + /** * \return \c true if authentication was defined, or * \c false if this object is only a default-constructed KDSoapAuthentication(). @@ -82,11 +127,22 @@ class KDSOAP_EXPORT KDSoapAuthentication */ KDSoapAuthentication &operator=(const KDSoapAuthentication &other); +private: /** * \internal */ void handleAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator); + /** + * \internal + */ + bool hasWSUsernameTokenHeader() const; + + /** + * \internal + */ + void writeWSUsernameTokenHeader(QXmlStreamWriter &writer) const; + private: class Private; Private *const d; diff --git a/src/KDSoapClient/KDSoapClientInterface.cpp b/src/KDSoapClient/KDSoapClientInterface.cpp index 2b8470aab..5f18bc53d 100644 --- a/src/KDSoapClient/KDSoapClientInterface.cpp +++ b/src/KDSoapClient/KDSoapClientInterface.cpp @@ -58,7 +58,7 @@ void KDSoapClientInterface::setSoapVersion(KDSoapClientInterface::SoapVersion ve d->m_version = static_cast(version); } -KDSoapClientInterface::SoapVersion KDSoapClientInterface::soapVersion() +KDSoapClientInterface::SoapVersion KDSoapClientInterface::soapVersion() const { return static_cast(d->m_version); } @@ -147,7 +147,7 @@ QBuffer *KDSoapClientInterfacePrivate::prepareRequestBuffer(const QString &metho KDSoapMessageWriter msgWriter; msgWriter.setMessageNamespace(m_messageNamespace); msgWriter.setVersion(m_version); - const QByteArray data = msgWriter.messageToXml(message, (m_style == KDSoapClientInterface::RPCStyle) ? method : QString(), headers, m_persistentHeaders); + const QByteArray data = msgWriter.messageToXml(message, (m_style == KDSoapClientInterface::RPCStyle) ? method : QString(), headers, m_persistentHeaders, m_authentication); QBuffer *buffer = new QBuffer; buffer->setData(data); buffer->open(QIODevice::ReadOnly); @@ -192,6 +192,7 @@ void KDSoapClientInterface::callNoReply(const QString &method, const KDSoapMessa QNetworkReply *reply = d->accessManager()->post(request, buffer); d->setupReply(reply); QObject::connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater())); + QObject::connect(reply, SIGNAL(finished()), buffer, SLOT(deleteLater())); } void KDSoapClientInterfacePrivate::_kd_slotAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) diff --git a/src/KDSoapClient/KDSoapClientInterface.h b/src/KDSoapClient/KDSoapClientInterface.h index 3b1039e02..d5c21803b 100644 --- a/src/KDSoapClient/KDSoapClientInterface.h +++ b/src/KDSoapClient/KDSoapClientInterface.h @@ -197,7 +197,7 @@ class KDSOAP_EXPORT KDSoapClientInterface /** * Returns the version of SOAP being used in this instance. */ - KDSoapClientInterface::SoapVersion soapVersion(); + KDSoapClientInterface::SoapVersion soapVersion() const; /** * Returns the end point of the SOAP service. diff --git a/src/KDSoapClient/KDSoapJob.cpp b/src/KDSoapClient/KDSoapJob.cpp index 87eb3b662..0793b4459 100644 --- a/src/KDSoapClient/KDSoapJob.cpp +++ b/src/KDSoapClient/KDSoapJob.cpp @@ -27,6 +27,7 @@ class KDSoapJob::Private { public: + KDSoapHeaders requestHeaders; KDSoapMessage reply; KDSoapHeaders replyHeaders; bool isAutoDelete; @@ -44,6 +45,16 @@ KDSoapJob::~KDSoapJob() delete d; } +KDSoapHeaders KDSoapJob::requestHeaders() const +{ + return d->requestHeaders; +} + +void KDSoapJob::setRequestHeaders(const KDSoapHeaders &headers) +{ + d->requestHeaders = headers; +} + void KDSoapJob::start() { QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); @@ -69,6 +80,11 @@ KDSoapMessage KDSoapJob::reply() const return d->reply; } +KDSoapHeaders KDSoapJob::replyHeaders() const +{ + return d->replyHeaders; +} + bool KDSoapJob::isFault() const { return d->reply.isFault(); diff --git a/src/KDSoapClient/KDSoapJob.h b/src/KDSoapClient/KDSoapJob.h index d3198515f..dde6d2d91 100644 --- a/src/KDSoapClient/KDSoapJob.h +++ b/src/KDSoapClient/KDSoapJob.h @@ -78,6 +78,20 @@ class KDSOAP_EXPORT KDSoapJob : public QObject */ ~KDSoapJob(); + /** + * Returns the reply headers received from the SOAP server once the request was completed. + * Only valid once the request is completed and finished() was emitted. + */ + KDSoapHeaders requestHeaders() const; + + /** + * Sets request headers to be sent to the SOAP server. These are sent in addition + * to the persistent headers set via the client interface. + * + * \since 1.8 + */ + void setRequestHeaders(const KDSoapHeaders &headers); + /** * Returns whether the reply message (see reply()) represents a fault. */ @@ -97,8 +111,10 @@ class KDSOAP_EXPORT KDSoapJob : public QObject /** * Returns the reply headers received from the SOAP server once the request was completed. * Only valid once the request is completed and finished() was emitted. + * + * \since 1.8 */ - KDSoapHeaders returnHeaders() const; + KDSoapHeaders replyHeaders() const; /** * Starts the job. The job will emit finished() once done. diff --git a/src/KDSoapClient/KDSoapMessageAddressingProperties.cpp b/src/KDSoapClient/KDSoapMessageAddressingProperties.cpp index 1da845c27..8666c5d59 100644 --- a/src/KDSoapClient/KDSoapMessageAddressingProperties.cpp +++ b/src/KDSoapClient/KDSoapMessageAddressingProperties.cpp @@ -221,6 +221,14 @@ QString KDSoapMessageAddressingProperties::predefinedAddressToString(KDSoapMessa } } +bool KDSoapMessageAddressingProperties::isWSAddressingNamespace(const QString &namespaceUri) +{ + return namespaceUri == KDSoapNamespaceManager::soapMessageAddressing() || + namespaceUri == KDSoapNamespaceManager::soapMessageAddressing200303() || + namespaceUri == KDSoapNamespaceManager::soapMessageAddressing200403() || + namespaceUri == KDSoapNamespaceManager::soapMessageAddressing200408(); +} + static void writeAddressField(QXmlStreamWriter &writer, const QString &address) { writer.writeStartElement(KDSoapNamespaceManager::soapMessageAddressing(), QLatin1String("Address")); @@ -261,7 +269,7 @@ void KDSoapMessageAddressingProperties::writeMessageAddressingProperties(KDSoapN Q_UNUSED(messageNamespace); Q_UNUSED(forceQualified); - if (d->destination == predefinedAddressToString(None) || d->destination.isEmpty()) { + if (d->destination == predefinedAddressToString(None)) { return; } @@ -271,13 +279,17 @@ void KDSoapMessageAddressingProperties::writeMessageAddressingProperties(KDSoapN const QString addressingNS = KDSoapNamespaceManager::soapMessageAddressing(); - writer.writeStartElement(addressingNS, QLatin1String("To")); - writer.writeCharacters(d->destination); - writer.writeEndElement(); + if (!d->destination.isEmpty()) { + writer.writeStartElement(addressingNS, QLatin1String("To")); + writer.writeCharacters(d->destination); + writer.writeEndElement(); + } - writer.writeStartElement(addressingNS, QLatin1String("From")); - writeAddressField(writer, d->sourceEndpoint.address()); - writer.writeEndElement(); + if (!d->sourceEndpoint.isEmpty()) { + writer.writeStartElement(addressingNS, QLatin1String("From")); + writeAddressField(writer, d->sourceEndpoint.address()); + writer.writeEndElement(); + } if (!d->replyEndpoint.isEmpty()) { writer.writeStartElement(addressingNS, QLatin1String("ReplyTo")); @@ -331,6 +343,37 @@ void KDSoapMessageAddressingProperties::writeMessageAddressingProperties(KDSoapN } } +void KDSoapMessageAddressingProperties::readMessageAddressingProperty(const KDSoapValue &value) +{ + if (value.name() == QLatin1String("Action")) { + d->action = value.value().toString(); + } else if (value.name() == QLatin1String("MessageID")) { + d->messageID = value.value().toString(); + } else if (value.name() == QLatin1String("To")) { + d->destination = value.value().toString(); + } else if (value.name() == QLatin1String("From")) { + d->sourceEndpoint.setAddress(value.childValues().child(QLatin1String("Address")).value().toString()); + } else if (value.name() == QLatin1String("ReplyTo")) { + d->replyEndpoint.setAddress(value.childValues().child(QLatin1String("Address")).value().toString()); + } else if (value.name() == QLatin1String("RelatesTo")) { + KDSoapMessageRelationship::Relationship relationship; + relationship.uri = (value.value().toString()); + relationship.relationshipType = QLatin1String("http://www.w3.org/2005/08/addressing/reply"); + foreach (KDSoapValue attr, value.childValues().attributes()) { + if (attr.name() == QLatin1String("RelationshipType")) { + relationship.relationshipType = attr.value().toString(); + } + } + d->relationships.append(relationship); + } else if (value.name() == QLatin1String("FaultTo")) { + d->faultEndpoint.setAddress(value.childValues().child(QLatin1String("Address")).value().toString()); + } else if (value.name() == QLatin1String("ReferenceParameters")) { + d->referenceParameters = value.childValues(); + } else if (value.name() == QLatin1String("Metadata")) { + d->metadata = value.childValues(); + } +} + QDebug operator <<(QDebug dbg, const KDSoapMessageAddressingProperties &msg) { dbg << msg.action() << msg.destination() << msg.sourceEndpoint().address() << msg.replyEndpoint().address() << msg.faultEndpoint().address() << msg.messageID(); diff --git a/src/KDSoapClient/KDSoapMessageAddressingProperties.h b/src/KDSoapClient/KDSoapMessageAddressingProperties.h index abbbf7183..98b461a1b 100644 --- a/src/KDSoapClient/KDSoapMessageAddressingProperties.h +++ b/src/KDSoapClient/KDSoapMessageAddressingProperties.h @@ -83,6 +83,7 @@ class KDSOAP_EXPORT KDSoapMessageAddressingProperties { public: friend class KDSoapMessageWriter; + friend class KDSoapMessageReader; /** * This enum contains all the predefined addresses defined by the ws addressing specification @@ -270,12 +271,22 @@ class KDSOAP_EXPORT KDSoapMessageAddressingProperties */ static QString predefinedAddressToString(KDSoapAddressingPredefinedAddress address); + /** + * Helper function that compares \p namespaceUri with the known WS-Addressing namespaces + */ + static bool isWSAddressingNamespace(const QString& namespaceUri); + private: /** * Private method called to write the properties to the soap header, using QXmlStreamWriter */ void writeMessageAddressingProperties(KDSoapNamespacePrefixes &namespacePrefixes, QXmlStreamWriter &writer, const QString &messageNamespace, bool forceQualified) const; + /** + * Private method called to read a property from a soap header + */ + void readMessageAddressingProperty(const KDSoapValue& value); + private: QSharedDataPointer d; }; diff --git a/src/KDSoapClient/KDSoapMessageReader.cpp b/src/KDSoapClient/KDSoapMessageReader.cpp index b98c8e9fd..645b06dc0 100644 --- a/src/KDSoapClient/KDSoapMessageReader.cpp +++ b/src/KDSoapClient/KDSoapMessageReader.cpp @@ -214,11 +214,18 @@ KDSoapMessageReader::XmlError KDSoapMessageReader::xmlToMessage(const QByteArray if (readNextStartElement(reader)) { if (reader.name() == QLatin1String("Header") && (reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope() || reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope200305())) { + KDSoapMessageAddressingProperties messageAddressingProperties; while (readNextStartElement(reader)) { - KDSoapMessage header; - static_cast(header) = parseElement(reader, envNsDecls); - pRequestHeaders->append(header); + if (KDSoapMessageAddressingProperties::isWSAddressingNamespace(reader.namespaceUri().toString())) { + KDSoapValue value = parseElement(reader, envNsDecls); + messageAddressingProperties.readMessageAddressingProperty(value); + } else { + KDSoapMessage header; + static_cast(header) = parseElement(reader, envNsDecls); + pRequestHeaders->append(header); + } } + pMsg->setMessageAddressingProperties(messageAddressingProperties); readNextStartElement(reader); // read } if (reader.name() == QLatin1String("Body") && (reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope() || diff --git a/src/KDSoapClient/KDSoapMessageWriter.cpp b/src/KDSoapClient/KDSoapMessageWriter.cpp index 59e210b64..7d66ea0f3 100644 --- a/src/KDSoapClient/KDSoapMessageWriter.cpp +++ b/src/KDSoapClient/KDSoapMessageWriter.cpp @@ -44,7 +44,8 @@ void KDSoapMessageWriter::setMessageNamespace(const QString &ns) } QByteArray KDSoapMessageWriter::messageToXml(const KDSoapMessage &message, const QString &method, - const KDSoapHeaders &headers, const QMap &persistentHeaders) const + const KDSoapHeaders &headers, const QMap &persistentHeaders, + const KDSoapAuthentication &authentication) const { QByteArray data; QXmlStreamWriter writer(&data); @@ -73,7 +74,7 @@ QByteArray KDSoapMessageWriter::messageToXml(const KDSoapMessage &message, const messageNamespace = message.namespaceUri(); } - if (!headers.isEmpty() || !persistentHeaders.isEmpty() || message.hasMessageAddressingProperties()) { + if (!headers.isEmpty() || !persistentHeaders.isEmpty() || message.hasMessageAddressingProperties() || authentication.hasWSUsernameTokenHeader()) { // This writeNamespace line adds the xmlns:n1 to , which looks ugly and unusual (and breaks all unittests) // However it's the best solution in case of headers, otherwise we get n1 in the header and n2 in the body, // and xsi:type attributes that refer to n1, which isn't defined in the body... @@ -88,6 +89,9 @@ QByteArray KDSoapMessageWriter::messageToXml(const KDSoapMessage &message, const if (message.hasMessageAddressingProperties()) { message.messageAddressingProperties().writeMessageAddressingProperties(namespacePrefixes, writer, messageNamespace, true); } + if (authentication.hasWSUsernameTokenHeader()) { + authentication.writeWSUsernameTokenHeader(writer); + } writer.writeEndElement(); // Header } else { // So in the standard case (no headers) we just rely on Qt calling it n1 and insert it into the map. diff --git a/src/KDSoapClient/KDSoapMessageWriter_p.h b/src/KDSoapClient/KDSoapMessageWriter_p.h index 6bf921889..37828ce88 100644 --- a/src/KDSoapClient/KDSoapMessageWriter_p.h +++ b/src/KDSoapClient/KDSoapMessageWriter_p.h @@ -24,6 +24,7 @@ #define KDSOAPMESSAGEWRITER_P_H #include "KDSoapMessage.h" +#include "KDSoapAuthentication.h" #include "KDSoapClientInterface.h" #include #include @@ -49,7 +50,8 @@ class KDSOAP_EXPORT KDSoapMessageWriter QByteArray messageToXml(const KDSoapMessage &message, const QString &method /*empty in document style*/, const KDSoapHeaders &headers, - const QMap &persistentHeaders) const; + const QMap &persistentHeaders, + const KDSoapAuthentication &authentication = KDSoapAuthentication()) const; private: QString m_messageNamespace; diff --git a/src/KDSoapClient/KDSoapNamespaceManager.cpp b/src/KDSoapClient/KDSoapNamespaceManager.cpp index 6b8b15594..0e2135e47 100644 --- a/src/KDSoapClient/KDSoapNamespaceManager.cpp +++ b/src/KDSoapClient/KDSoapNamespaceManager.cpp @@ -70,3 +70,28 @@ QString KDSoapNamespaceManager::soapMessageAddressing() { return QString::fromLatin1("http://www.w3.org/2005/08/addressing"); } + +QString KDSoapNamespaceManager::soapSecurityExtention() +{ + return QString::fromLatin1("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); +} + +QString KDSoapNamespaceManager::soapSecurityUtility() +{ + return QString::fromLatin1("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); +} + +QString KDSoapNamespaceManager::soapMessageAddressing200303() +{ + return QString::fromLatin1("http://schemas.xmlsoap.org/ws/2003/03/addressing"); +} + +QString KDSoapNamespaceManager::soapMessageAddressing200403() +{ + return QString::fromLatin1("http://schemas.xmlsoap.org/ws/2004/03/addressing"); +} + +QString KDSoapNamespaceManager::soapMessageAddressing200408() +{ + return QString::fromLatin1("http://schemas.xmlsoap.org/ws/2004/08/addressing"); +} diff --git a/src/KDSoapClient/KDSoapNamespaceManager.h b/src/KDSoapClient/KDSoapNamespaceManager.h index eb8aca17b..61ad1bfee 100644 --- a/src/KDSoapClient/KDSoapNamespaceManager.h +++ b/src/KDSoapClient/KDSoapNamespaceManager.h @@ -41,6 +41,11 @@ class KDSOAP_EXPORT KDSoapNamespaceManager //krazy:exclude=dpointer static QString soapEncoding(); static QString soapEncoding200305(); static QString soapMessageAddressing(); + static QString soapSecurityExtention(); + static QString soapSecurityUtility(); + static QString soapMessageAddressing200303(); + static QString soapMessageAddressing200403(); + static QString soapMessageAddressing200408(); private: // TODO instantiate to handle custom namespaces per clientinterface KDSoapNamespaceManager(); diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 584409874..f059a721f 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -71,6 +71,7 @@ add_subdirectory(encapsecurity) add_subdirectory(prefix_wsdl) add_subdirectory(vidyo) add_subdirectory(ws_addressing_support) +add_subdirectory(ws_usernametoken_support) add_subdirectory(empty_element_wsdl) # These need internet access diff --git a/unittests/dwservice_12_wsdl/test_dwservice_12_wsdl.cpp b/unittests/dwservice_12_wsdl/test_dwservice_12_wsdl.cpp index 7de010ca7..a16dfecb9 100644 --- a/unittests/dwservice_12_wsdl/test_dwservice_12_wsdl.cpp +++ b/unittests/dwservice_12_wsdl/test_dwservice_12_wsdl.cpp @@ -24,7 +24,6 @@ #include "KDSoapClientInterface.h" #include "KDSoapMessage.h" #include "KDSoapValue.h" -#include "KDSoapAuthentication.h" #include "KDDateTime.h" #include "wsdl_DWService_12.h" #include "httpserver_p.h" diff --git a/unittests/dwservice_combined_wsdl/test_dwservice_combined_wsdl.cpp b/unittests/dwservice_combined_wsdl/test_dwservice_combined_wsdl.cpp index 1851ea9ae..4a38b8343 100644 --- a/unittests/dwservice_combined_wsdl/test_dwservice_combined_wsdl.cpp +++ b/unittests/dwservice_combined_wsdl/test_dwservice_combined_wsdl.cpp @@ -24,7 +24,6 @@ #include "KDSoapClientInterface.h" #include "KDSoapMessage.h" #include "KDSoapValue.h" -#include "KDSoapAuthentication.h" #include "KDDateTime.h" #include "httpserver_p.h" #include diff --git a/unittests/dwservice_wsdl/test_dwservice_wsdl.cpp b/unittests/dwservice_wsdl/test_dwservice_wsdl.cpp index c7b9c6b1d..a68170a54 100644 --- a/unittests/dwservice_wsdl/test_dwservice_wsdl.cpp +++ b/unittests/dwservice_wsdl/test_dwservice_wsdl.cpp @@ -24,7 +24,6 @@ #include "KDSoapClientInterface.h" #include "KDSoapMessage.h" #include "KDSoapValue.h" -#include "KDSoapAuthentication.h" #include "KDDateTime.h" #include "wsdl_DWService.h" #include "httpserver_p.h" diff --git a/unittests/encapsecurity/test_encapsecurity.cpp b/unittests/encapsecurity/test_encapsecurity.cpp index e83a4db37..c514775e1 100644 --- a/unittests/encapsecurity/test_encapsecurity.cpp +++ b/unittests/encapsecurity/test_encapsecurity.cpp @@ -25,7 +25,6 @@ #include "KDSoapMessage.h" #include "KDSoapValue.h" #include "KDSoapPendingCallWatcher.h" -#include "KDSoapAuthentication.h" #include "wsdl_authstateless.h" #include "httpserver_p.h" #include diff --git a/unittests/groupwise_wsdl/test_groupwise_wsdl.cpp b/unittests/groupwise_wsdl/test_groupwise_wsdl.cpp index 90c56ef1c..557c05977 100644 --- a/unittests/groupwise_wsdl/test_groupwise_wsdl.cpp +++ b/unittests/groupwise_wsdl/test_groupwise_wsdl.cpp @@ -24,7 +24,6 @@ #include "KDSoapClientInterface.h" #include "KDSoapMessage.h" #include "KDSoapValue.h" -#include "KDSoapAuthentication.h" #include "KDDateTime.h" #include "wsdl_groupwise.h" #include "httpserver_p.h" diff --git a/unittests/import_definition/test_import_definition.cpp b/unittests/import_definition/test_import_definition.cpp index 11a8db8d4..880b22f7e 100644 --- a/unittests/import_definition/test_import_definition.cpp +++ b/unittests/import_definition/test_import_definition.cpp @@ -25,7 +25,6 @@ #include "KDSoapMessage.h" #include "KDSoapValue.h" #include "KDSoapPendingCallWatcher.h" -#include "KDSoapAuthentication.h" #include "wsdl_import_definition.h" #include "httpserver_p.h" #include diff --git a/unittests/literal_true_false/test_literal.cpp b/unittests/literal_true_false/test_literal.cpp index 20d9b4a87..2779a903e 100644 --- a/unittests/literal_true_false/test_literal.cpp +++ b/unittests/literal_true_false/test_literal.cpp @@ -25,7 +25,6 @@ #include "KDSoapMessage.h" #include "KDSoapValue.h" #include "KDSoapPendingCallWatcher.h" -#include "KDSoapAuthentication.h" #include "wsdl_literal.h" #include "httpserver_p.h" #include diff --git a/unittests/msexchange_noservice_wsdl/msexchange_noservice_wsdl.cpp b/unittests/msexchange_noservice_wsdl/msexchange_noservice_wsdl.cpp index e9b8cf2eb..c47b728ec 100644 --- a/unittests/msexchange_noservice_wsdl/msexchange_noservice_wsdl.cpp +++ b/unittests/msexchange_noservice_wsdl/msexchange_noservice_wsdl.cpp @@ -25,7 +25,6 @@ #include "KDSoapMessage.h" #include "KDSoapValue.h" #include "KDSoapPendingCallWatcher.h" -#include "KDSoapAuthentication.h" #include "wsdl_Services_noservice.h" #include "httpserver_p.h" #include diff --git a/unittests/msexchange_wsdl/msexchange_wsdl.cpp b/unittests/msexchange_wsdl/msexchange_wsdl.cpp index fb40cfe9c..f2deb316c 100644 --- a/unittests/msexchange_wsdl/msexchange_wsdl.cpp +++ b/unittests/msexchange_wsdl/msexchange_wsdl.cpp @@ -25,7 +25,6 @@ #include "KDSoapMessage.h" #include "KDSoapValue.h" #include "KDSoapPendingCallWatcher.h" -#include "KDSoapAuthentication.h" #include "wsdl_Services.h" #include "httpserver_p.h" #include diff --git a/unittests/salesforce_wsdl/salesforce_wsdl.cpp b/unittests/salesforce_wsdl/salesforce_wsdl.cpp index 9b1ae94d2..d45f99e60 100644 --- a/unittests/salesforce_wsdl/salesforce_wsdl.cpp +++ b/unittests/salesforce_wsdl/salesforce_wsdl.cpp @@ -24,7 +24,6 @@ #include "KDSoapClientInterface.h" #include "KDSoapMessage.h" #include "KDSoapValue.h" -#include "KDSoapAuthentication.h" #include "wsdl_salesforce-partner.h" #include "httpserver_p.h" #include diff --git a/unittests/unittests.pro b/unittests/unittests.pro index 40c9a6a27..19eb996a7 100644 --- a/unittests/unittests.pro +++ b/unittests/unittests.pro @@ -45,7 +45,8 @@ SUBDIRS = \ date_example \ dv_terminalauth \ test_calc \ - ws_addressing_support + ws_addressing_support \ + ws_usernametoken_support # These need internet access SUBDIRS += webcalls webcalls_wsdl diff --git a/unittests/unqualified_formdefault/test_unqualified.cpp b/unittests/unqualified_formdefault/test_unqualified.cpp index 354983613..c13aa9fb2 100644 --- a/unittests/unqualified_formdefault/test_unqualified.cpp +++ b/unittests/unqualified_formdefault/test_unqualified.cpp @@ -25,7 +25,6 @@ #include "KDSoapMessage.h" #include "KDSoapValue.h" #include "KDSoapPendingCallWatcher.h" -#include "KDSoapAuthentication.h" #include "wsdl_unqualified.h" #include "httpserver_p.h" #include diff --git a/unittests/webcalls_wsdl/webcalls_wsdl.cpp b/unittests/webcalls_wsdl/webcalls_wsdl.cpp index 9ee8ee89d..d612d969d 100644 --- a/unittests/webcalls_wsdl/webcalls_wsdl.cpp +++ b/unittests/webcalls_wsdl/webcalls_wsdl.cpp @@ -22,8 +22,6 @@ **********************************************************************/ #include "KDSoapClientInterface.h" -//#include "KDSoapMessage.h" -//#include "KDSoapValue.h" #include "wsdl_soapresponder.h" #include "wsdl_holidays.h" #include "wsdl_BFGlobalService.h" diff --git a/unittests/ws_addressing_support/wsaddressingtest.cpp b/unittests/ws_addressing_support/wsaddressingtest.cpp index 6977325d6..c5dc9f655 100644 --- a/unittests/ws_addressing_support/wsaddressingtest.cpp +++ b/unittests/ws_addressing_support/wsaddressingtest.cpp @@ -62,6 +62,59 @@ private Q_SLOTS: KDSoapClientInterface client(server.endPoint(), "http://www.ecerami.com/wsdl/HelloService"); KDSoapMessage message; + const QString action = QString::fromLatin1("sayHello"); + message.setUse(KDSoapMessage::EncodedUse); + message.addArgument(QString::fromLatin1("msg"), QVariant::fromValue(QString("HelloContentMessage")), KDSoapNamespaceManager::xmlSchema2001(), QString::fromLatin1("string")); + message.setNamespaceUri(QString::fromLatin1("http://www.ecerami.com/wsdl/HelloService.wsdl")); + + // WHEN + message.setMessageAddressingProperties(addressingProperties()); + KDSoapMessage reply = client.call(QLatin1String("sayHello"), message, action); + + // THEN + QVERIFY(xmlBufferCompare(server.receivedData(), expectedSoapMessage())); + } + + void shouldReadAProperSoapMessageWithRightsAddressingProperties() + { + // GIVEN + HttpServerThread server(expectedSoapMessage(), HttpServerThread::Public); + KDSoapClientInterface client(server.endPoint(), "http://www.ecerami.com/wsdl/HelloService"); + + KDSoapMessage message; + const QString action = QString::fromLatin1("sayHello"); + + // WHEN + KDSoapMessage reply = client.call(QLatin1String("sayHello"), message, action); + + // THEN + QVERIFY(reply.hasMessageAddressingProperties()); + KDSoapMessageAddressingProperties map = reply.messageAddressingProperties(); + QCOMPARE(map.action(), QString("sayHello")); + QCOMPARE(map.destination(), QString("http://www.ecerami.com/wsdl/HelloService")); + QCOMPARE(map.sourceEndpointAddress(), QString("http://www.ecerami.com/wsdl/source")); + QCOMPARE(map.faultEndpointAddress(), QString("http://www.ecerami.com/wsdl/fault")); + QCOMPARE(map.messageID(), QString("uuid:e197db59-0982-4c9c-9702-4234d204f7f4")); + QCOMPARE(map.replyEndpointAddress(), QString("http://www.w3.org/2005/08/addressing/anonymous")); + QCOMPARE(map.relationships().at(0).uri, QString("uuid:http://www.ecerami.com/wsdl/someUniqueString")); + QCOMPARE(map.relationships().at(0).relationshipType, QString("http://www.w3.org/2005/08/addressing/reply")); + QCOMPARE(map.relationships().at(1).uri, QString("uuid:http://www.ecerami.com/wsdl/someUniqueStringBis")); + QCOMPARE(map.relationships().at(1).relationshipType, QString("CustomTypeReply")); + QCOMPARE(map.referenceParameters().at(0).name(), QString("myReferenceParameter")); + QCOMPARE(map.referenceParameters().at(0).value().toString(), QString("ReferencParameterContent")); + QCOMPARE(map.referenceParameters().at(1).name(), QString("myReferenceParameterWithChildren")); + QCOMPARE(map.referenceParameters().at(1).childValues().size(), 2); + QCOMPARE(map.metadata().at(0).name(), QString("myMetadata")); + QCOMPARE(map.metadata().at(0).value().toString(), QString("MetadataContent")); + QCOMPARE(map.metadata().at(1).name(), QString("myMetadataBis")); + QCOMPARE(map.metadata().at(1).childValues().size(), 1); + QCOMPARE(map.metadata().at(1).childValues().first().name(), QString("myMetadataBisChild")); + QCOMPARE(map.metadata().at(1).childValues().first().value().toString(), QString("MetadataBisChildContent")); + } + +private: + static KDSoapMessageAddressingProperties addressingProperties() + { KDSoapMessageAddressingProperties map; // with some message addressing properties @@ -104,21 +157,9 @@ private Q_SLOTS: map.setMetadata(metadataContainer); map.addMetadata(metadataBis); - // and some request content - const QString action = QString::fromLatin1("sayHello"); - message.setUse(KDSoapMessage::EncodedUse); - message.addArgument(QString::fromLatin1("msg"), QVariant::fromValue(QString("HelloContentMessage")), KDSoapNamespaceManager::xmlSchema2001(), QString::fromLatin1("string")); - message.setNamespaceUri(QString::fromLatin1("http://www.ecerami.com/wsdl/HelloService.wsdl")); - - // WHEN - message.setMessageAddressingProperties(map); - KDSoapMessage reply = client.call(QLatin1String("sayHello"), message, action); - - // THEN - QVERIFY(xmlBufferCompare(server.receivedData(), expectedSoapMessage())); + return map; } -private: static QByteArray expectedSoapMessage() { return QByteArray(xmlEnvBegin11()) + " xmlns:wsa=\"http://www.w3.org/2005/08/addressing\"" diff --git a/unittests/ws_usernametoken_support/CMakeLists.txt b/unittests/ws_usernametoken_support/CMakeLists.txt new file mode 100644 index 000000000..4eb1ba39c --- /dev/null +++ b/unittests/ws_usernametoken_support/CMakeLists.txt @@ -0,0 +1,8 @@ +project(ws_usernametoken_support) + +set(WSDL_FILES wsusernametoken.wsdl) +set(ws_usernametoken_support_SRCS wsusernametokentest.cpp ) + +set(EXTRA_LIBS ${QT_QTXML_LIBRARY}) + +add_unittest(${ws_usernametoken_support_SRCS} ) diff --git a/unittests/ws_usernametoken_support/ws_usernametoken_support.pro b/unittests/ws_usernametoken_support/ws_usernametoken_support.pro new file mode 100644 index 000000000..480de2c1b --- /dev/null +++ b/unittests/ws_usernametoken_support/ws_usernametoken_support.pro @@ -0,0 +1,9 @@ +include( $${TOP_SOURCE_DIR}/unittests/unittests.pri ) +QT += network xml +SOURCES = wsusernametokentest.cpp +test.target = test +test.commands = ./$(TARGET) +test.depends = $(TARGET) +QMAKE_EXTRA_TARGETS += test + +KDWSDL = wsusernametoken.wsdl diff --git a/unittests/ws_usernametoken_support/wsusernametoken.wsdl b/unittests/ws_usernametoken_support/wsusernametoken.wsdl new file mode 100644 index 000000000..fbb67b5da --- /dev/null +++ b/unittests/ws_usernametoken_support/wsusernametoken.wsdl @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WSDL File for HelloService + + + + + + http://localhost:8081/hello + + example:Inventory + + + 123456789 + ABCDEFG + + + + + + diff --git a/unittests/ws_usernametoken_support/wsusernametokentest.cpp b/unittests/ws_usernametoken_support/wsusernametokentest.cpp new file mode 100644 index 000000000..a10e3272f --- /dev/null +++ b/unittests/ws_usernametoken_support/wsusernametokentest.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** Copyright (C) 2010-2018 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com. +** Copyright (C) 2019 Casper Meijn +** All rights reserved. +** +** This file is part of the KD Soap library. +** +** Licensees holding valid commercial KD Soap licenses may use this file in +** accordance with the KD Soap Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2.1 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "httpserver_p.h" + +#include +#include + +#include "KDSoapNamespaceManager.h" +#include "KDSoapAuthentication.h" +#include "wsdl_wsusernametoken.h" + +using namespace KDSoapUnitTestHelpers; + +class WSUsernameTokenTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + + void shouldWriteAProperSoapMessageWithRightUsernameToken() + { + // GIVEN + HttpServerThread server(emptyResponse(), HttpServerThread::Public); + KDSoapClientInterface client(server.endPoint(), "http://www.ecerami.com/wsdl/HelloService"); + + KDSoapAuthentication auth; + auth.setUser("admin"); + auth.setPassword("userpassword"); + auth.setUseWSUsernameToken(true); + + // Override the nonce and timestamp to make the test output consistent. + auth.setOverrideWSUsernameNonce(QByteArray::fromBase64("LKqI6G/AikKCQrN0zqZFlg==")); + auth.setOverrideWSUsernameCreatedTime(QDateTime(QDate(2010, 9, 16), QTime(7, 50, 45), Qt::UTC));//2010-09-16T07:50:45Z + + client.setAuthentication(auth); + + // make a request + KDSoapMessage message; + const QString action = QString::fromLatin1("sayHello"); + message.setUse(KDSoapMessage::EncodedUse); + message.addArgument(QString::fromLatin1("msg"), QVariant::fromValue(QString("HelloContentMessage")), KDSoapNamespaceManager::xmlSchema2001(), QString::fromLatin1("string")); + message.setNamespaceUri(QString::fromLatin1("http://www.ecerami.com/wsdl/HelloService.wsdl")); + + // WHEN + KDSoapMessage reply = client.call(QLatin1String("sayHello"), message, action); + + // THEN + QVERIFY(xmlBufferCompare(server.receivedData(), expectedSoapMessage())); + } + +private: + static QByteArray expectedSoapMessage() + { + return QByteArray(xmlEnvBegin11()) + + " xmlns:n1=\"http://www.ecerami.com/wsdl/HelloService.wsdl\">" + " " + " " + " " + " LKqI6G/AikKCQrN0zqZFlg== " + " 2010-09-16T07:50:45Z " + " tuOSpGlFlIXsozq4HFNeeGeFLEI= " + " admin " + " " + " " + " " + " " + " HelloContentMessage " + " " + xmlEnvEnd(); + } + + static QByteArray emptyResponse() + { + return QByteArray(xmlEnvBegin11()) + ">"; + } +}; + +QTEST_MAIN(WSUsernameTokenTest) + +#include "wsusernametokentest.moc"