Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package qt6-networkauth for openSUSE:Factory
checked in at 2023-10-13 23:14:19
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/qt6-networkauth (Old)
and /work/SRC/openSUSE:Factory/.qt6-networkauth.new.20540 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "qt6-networkauth"
Fri Oct 13 23:14:19 2023 rev:24 rq:1116941 version:6.6.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/qt6-networkauth/qt6-networkauth.changes
2023-10-02 20:07:27.868958053 +0200
+++
/work/SRC/openSUSE:Factory/.qt6-networkauth.new.20540/qt6-networkauth.changes
2023-10-13 23:14:47.399274081 +0200
@@ -1,0 +2,6 @@
+Tue Oct 10 09:39:54 UTC 2023 - Christophe Marin <[email protected]>
+
+- Update to 6.6.0
+ * https://www.qt.io/blog/qt-6.6-released
+
+-------------------------------------------------------------------
Old:
----
qtnetworkauth-everywhere-src-6.5.3.tar.xz
New:
----
qtnetworkauth-everywhere-src-6.6.0.tar.xz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ qt6-networkauth.spec ++++++
--- /var/tmp/diff_new_pack.KeHN71/_old 2023-10-13 23:14:49.051334002 +0200
+++ /var/tmp/diff_new_pack.KeHN71/_new 2023-10-13 23:14:49.051334002 +0200
@@ -16,8 +16,8 @@
#
-%define real_version 6.5.3
-%define short_version 6.5
+%define real_version 6.6.0
+%define short_version 6.6
%define short_name qtnetworkauth
%define tar_name qtnetworkauth-everywhere-src
%define tar_suffix %{nil}
@@ -28,7 +28,7 @@
%endif
#
Name: qt6-networkauth%{?pkg_suffix}
-Version: 6.5.3
+Version: 6.6.0
Release: 0
Summary: Set of APIs to obtain limited access to online accounts and
HTTP services
License: GPL-3.0-only WITH Qt-GPL-exception-1.0
++++++ qtnetworkauth-everywhere-src-6.5.3.tar.xz ->
qtnetworkauth-everywhere-src-6.6.0.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/qtnetworkauth-everywhere-src-6.5.3/.cmake.conf
new/qtnetworkauth-everywhere-src-6.6.0/.cmake.conf
--- old/qtnetworkauth-everywhere-src-6.5.3/.cmake.conf 2023-09-24
09:06:35.000000000 +0200
+++ new/qtnetworkauth-everywhere-src-6.6.0/.cmake.conf 2023-10-02
05:05:49.000000000 +0200
@@ -1,3 +1,3 @@
-set(QT_REPO_MODULE_VERSION "6.5.3")
+set(QT_REPO_MODULE_VERSION "6.6.0")
set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1")
set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_AS_CONST=1")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/qtnetworkauth-everywhere-src-6.5.3/.tag
new/qtnetworkauth-everywhere-src-6.6.0/.tag
--- old/qtnetworkauth-everywhere-src-6.5.3/.tag 2023-09-24 09:06:35.000000000
+0200
+++ new/qtnetworkauth-everywhere-src-6.6.0/.tag 2023-10-02 05:05:49.000000000
+0200
@@ -1 +1 @@
-70e2a96d3bfdd8fab6c48a80ee0147a56233b894
+8c7a6264d1ae09d2d0143655642d6f6530701420
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qtnetworkauth-everywhere-src-6.5.3/coin/axivion/ci_config_linux.json
new/qtnetworkauth-everywhere-src-6.6.0/coin/axivion/ci_config_linux.json
--- old/qtnetworkauth-everywhere-src-6.5.3/coin/axivion/ci_config_linux.json
1970-01-01 01:00:00.000000000 +0100
+++ new/qtnetworkauth-everywhere-src-6.6.0/coin/axivion/ci_config_linux.json
2023-10-02 05:05:49.000000000 +0200
@@ -0,0 +1,59 @@
+{
+ "Project": {
+ "Git": {
+ "_active": true,
+ "sourceserver_gitdir":
"/data/axivion/databases/$(env:TESTED_MODULE_COIN).git"
+ },
+ "BuildSystemIntegration": {
+ "child_order": [
+ "GCCSetup",
+ "CMake",
+ "LinkLibraries"
+ ]
+ },
+ "CMake": {
+ "_active": true,
+ "_copy_from": "CMakeIntegration",
+ "build_environment": {},
+ "build_options": "-j4",
+ "generate_options": "--fresh",
+ "generator": "Ninja"
+ },
+ "GCCSetup": {
+ "_active": true,
+ "_copy_from": "Command",
+ "build_command": "gccsetup --cc gcc --cxx g++ --config
../../../axivion/"
+ },
+ "LinkLibraries": {
+ "_active": true,
+ "_copy_from": "AxivionLinker",
+ "input_files": [
+ "build/lib/lib*.so*.ir"
+ ],
+ "ir": "build/$(env:TESTED_MODULE_COIN).ir"
+ },
+ "Project-GlobalOptions": {
+ "directory": "../work/qt/$(env:TESTED_MODULE_COIN)",
+ "ir": "build/$(env:TESTED_MODULE_COIN).ir",
+ "name": "qt_$(env:TESTED_MODULE_COIN)_dev_$(env:TARGET_OS_COIN)"
+ }
+ },
+ "Results": {
+ "Dashboard": {
+ "dashboard_url": "https://axivion-srv.ci.qt.io/axivion/"
+ },
+ "Database": {
+ "ci_mode": {
+ "directory": "/data/axivion/databases"
+ }
+ }
+ },
+ "_Format": "1.0",
+ "_Version": "trunk-9e0ef9c5818",
+ "_VersionNum": [
+ 7,
+ 6,
+ 9999,
+ 11489
+ ]
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/qtnetworkauth-everywhere-src-6.5.3/dependencies.yaml
new/qtnetworkauth-everywhere-src-6.6.0/dependencies.yaml
--- old/qtnetworkauth-everywhere-src-6.5.3/dependencies.yaml 2023-09-24
09:06:35.000000000 +0200
+++ new/qtnetworkauth-everywhere-src-6.6.0/dependencies.yaml 2023-10-02
05:05:49.000000000 +0200
@@ -1,4 +1,4 @@
dependencies:
../qtbase:
- ref: 372eaedc5b8c771c46acc4c96e91bbade4ca3624
+ ref: 33f5e985e480283bb0ca9dea5f82643e825ba87c
required: true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qtnetworkauth-everywhere-src-6.5.3/src/oauth/qabstractoauth.cpp
new/qtnetworkauth-everywhere-src-6.6.0/src/oauth/qabstractoauth.cpp
--- old/qtnetworkauth-everywhere-src-6.5.3/src/oauth/qabstractoauth.cpp
2023-09-24 09:06:35.000000000 +0200
+++ new/qtnetworkauth-everywhere-src-6.6.0/src/oauth/qabstractoauth.cpp
2023-10-02 05:05:49.000000000 +0200
@@ -91,7 +91,8 @@
\value NetworkError Failed to connect to the server.
\value ServerError The server answered the
- request with an error.
+ request with an error, or its response was not successfully received
+ (for example, due to a state mismatch).
\value OAuthTokenNotFoundError The server's response to
a token request provided no token identifier.
@@ -163,6 +164,16 @@
*/
/*!
+ \fn void QAbstractOAuth::requestFailed(const QAbstractOAuth::Error error)
+
+ This signal is emitted to indicate that a request to a server has failed.
+ The \a error supplied indicates how the request failed.
+
+ \sa QAbstractOAuth2::error()
+ \sa QAbstractOAuthReplyHandler::tokenRequestErrorOccurred()
+*/
+
+/*!
\fn QNetworkReply *QAbstractOAuth::head(const QUrl &url, const QVariantMap
¶meters)
Sends an authenticated HEAD request and returns a new
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qtnetworkauth-everywhere-src-6.5.3/src/oauth/qabstractoauth2.cpp
new/qtnetworkauth-everywhere-src-6.6.0/src/oauth/qabstractoauth2.cpp
--- old/qtnetworkauth-everywhere-src-6.5.3/src/oauth/qabstractoauth2.cpp
2023-09-24 09:06:35.000000000 +0200
+++ new/qtnetworkauth-everywhere-src-6.6.0/src/oauth/qabstractoauth2.cpp
2023-10-02 05:05:49.000000000 +0200
@@ -79,10 +79,14 @@
/*!
\fn QAbstractOAuth2::error(const QString &error, const QString
&errorDescription, const QUrl &uri)
- Signal emitted when the server responds to the request with an
- error: \a error is the name of the error; \a errorDescription describes
- the error and \a uri is an optional URI containing more
- information about the error.
+ Signal emitted when the server responds to the authorization request with
+ an error as defined in \l
{https://www.rfc-editor.org/rfc/rfc6749#section-5.2}
+ {RFC 6749 error response}.
+
+ \a error is the name of the error; \a errorDescription describes the error
+ and \a uri is an optional URI containing more information about the error.
+
+ \sa QAbstractOAuth::requestFailed()
*/
/*!
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qtnetworkauth-everywhere-src-6.5.3/src/oauth/qabstractoauthreplyhandler.cpp
new/qtnetworkauth-everywhere-src-6.6.0/src/oauth/qabstractoauthreplyhandler.cpp
---
old/qtnetworkauth-everywhere-src-6.5.3/src/oauth/qabstractoauthreplyhandler.cpp
2023-09-24 09:06:35.000000000 +0200
+++
new/qtnetworkauth-everywhere-src-6.6.0/src/oauth/qabstractoauthreplyhandler.cpp
2023-10-02 05:05:49.000000000 +0200
@@ -69,6 +69,18 @@
*/
/*!
+
+ \fn void
QAbstractOAuthReplyHandler::tokenRequestErrorOccurred(QAbstractOAuth::Error
error,
+ const QString&
errorString)
+
+ This signal is emitted when a token request or refresh \a error has
+ occurred. The \a errorString may provide further details on the error.
+
+ \sa QAbstractOAuth::requestFailed()
+ \since 6.6
+*/
+
+/*!
\fn void QAbstractOAuthReplyHandler::replyDataReceived(const QByteArray
&data)
This signal is emitted when an HTTP request finishes and the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qtnetworkauth-everywhere-src-6.5.3/src/oauth/qabstractoauthreplyhandler.h
new/qtnetworkauth-everywhere-src-6.6.0/src/oauth/qabstractoauthreplyhandler.h
---
old/qtnetworkauth-everywhere-src-6.5.3/src/oauth/qabstractoauthreplyhandler.h
2023-09-24 09:06:35.000000000 +0200
+++
new/qtnetworkauth-everywhere-src-6.6.0/src/oauth/qabstractoauthreplyhandler.h
2023-10-02 05:05:49.000000000 +0200
@@ -29,6 +29,7 @@
Q_SIGNALS:
void callbackReceived(const QVariantMap &values);
void tokensReceived(const QVariantMap &tokens);
+ void tokenRequestErrorOccurred(QAbstractOAuth::Error error, const QString&
errorString);
void replyDataReceived(const QByteArray &data);
void callbackDataReceived(const QByteArray &data);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qtnetworkauth-everywhere-src-6.5.3/src/oauth/qoauth2authorizationcodeflow.cpp
new/qtnetworkauth-everywhere-src-6.6.0/src/oauth/qoauth2authorizationcodeflow.cpp
---
old/qtnetworkauth-everywhere-src-6.5.3/src/oauth/qoauth2authorizationcodeflow.cpp
2023-09-24 09:06:35.000000000 +0200
+++
new/qtnetworkauth-everywhere-src-6.6.0/src/oauth/qoauth2authorizationcodeflow.cpp
2023-10-02 05:05:49.000000000 +0200
@@ -19,6 +19,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
/*!
\class QOAuth2AuthorizationCodeFlow
\inmodule QtNetworkAuth
@@ -61,7 +63,8 @@
using Key = QAbstractOAuth2Private::OAuth2KeyString;
if (status != QAbstractOAuth::Status::NotAuthenticated) {
- qCWarning(loggingCategory, "Unexpected call");
+ qCWarning(loggingCategory) << "Authorization stage: callback in
unexpected status:"
+ << static_cast<int>(status) << ", ignoring
the callback";
return;
}
@@ -71,23 +74,30 @@
const QString code = data.value(Key::code).toString();
const QString receivedState = data.value(Key::state).toString();
if (error.size()) {
+ // RFC 6749, Section 5.2 Error Response
const QString uri = data.value(Key::errorUri).toString();
const QString description =
data.value(Key::errorDescription).toString();
- qCWarning(loggingCategory, "AuthenticationError: %s(%s): %s",
- qPrintable(error), qPrintable(uri), qPrintable(description));
+ qCWarning(loggingCategory, "Authorization stage: AuthenticationError:
%s(%s): %s",
+ qPrintable(error), qPrintable(uri), qPrintable(description));
Q_EMIT q->error(error, description, uri);
+ // Emit also requestFailed() so that it is a signal for all errors
+ emit q->requestFailed(QAbstractOAuth::Error::ServerError);
return;
}
+
if (code.isEmpty()) {
- qCWarning(loggingCategory, "AuthenticationError: Code not received");
+ qCWarning(loggingCategory, "Authorization stage: Code not received");
+ emit q->requestFailed(QAbstractOAuth::Error::OAuthTokenNotFoundError);
return;
}
if (receivedState.isEmpty()) {
- qCWarning(loggingCategory, "State not received");
+ qCWarning(loggingCategory, "Authorization stage: State not received");
+ emit q->requestFailed(QAbstractOAuth::Error::ServerError);
return;
}
if (state != receivedState) {
- qCWarning(loggingCategory, "State mismatch");
+ qCWarning(loggingCategory) << "Authorization stage: State mismatch";
+ emit q->requestFailed(QAbstractOAuth::Error::ServerError);
return;
}
@@ -105,8 +115,8 @@
using Key = QAbstractOAuth2Private::OAuth2KeyString;
if (values.contains(Key::error)) {
- const QString error = values.value(Key::error).toString();
- qCWarning(loggingCategory, "Error: %s", qPrintable(error));
+ _q_accessTokenRequestFailed(QAbstractOAuth::Error::ServerError,
+ values.value(Key::error).toString());
return;
}
@@ -120,7 +130,8 @@
q->setRefreshToken(values.value(Key::refreshToken).toString());
scope = values.value(Key::scope).toString();
if (accessToken.isEmpty()) {
- qCWarning(loggingCategory, "Access token not received");
+
_q_accessTokenRequestFailed(QAbstractOAuth::Error::OAuthTokenNotFoundError,
+ "Access token not received"_L1);
return;
}
q->setToken(accessToken);
@@ -142,6 +153,23 @@
setStatus(QAbstractOAuth::Status::Granted);
}
+void
QOAuth2AuthorizationCodeFlowPrivate::_q_accessTokenRequestFailed(QAbstractOAuth::Error
error,
+ const
QString& errorString)
+{
+ Q_Q(QOAuth2AuthorizationCodeFlow);
+ qCWarning(loggingCategory) << "Token request failed:" << errorString;
+ // If we were refreshing, reset status to Granted if we have an access
token.
+ // The access token might still be valid, and even if it wouldn't be,
+ // refreshing can be attempted again.
+ if (q->status() == QAbstractOAuth::Status::RefreshingToken) {
+ if (!q->token().isEmpty())
+ setStatus(QAbstractOAuth::Status::Granted);
+ else
+ setStatus(QAbstractOAuth::Status::NotAuthenticated);
+ }
+ emit q->requestFailed(error);
+}
+
void QOAuth2AuthorizationCodeFlowPrivate::_q_authenticate(QNetworkReply *reply,
QAuthenticator
*authenticator)
{
@@ -273,8 +301,12 @@
permanent. After a time specified along with the access token
when it was obtained, the access token will become invalid.
- \b {See also}:
- \l {https://tools.ietf.org/html/rfc6749#section-1.5}{Refresh
+ If refreshing the token fails and an access token exists, the status is
+ set to QAbstractOAuth::Status::Granted, and to
+ QAbstractOAuth::Status::NotAuthenticated otherwise.
+
+ \sa QAbstractOAuth::requestFailed()
+ \sa {https://tools.ietf.org/html/rfc6749#section-1.5}{Refresh
Token}
*/
void QOAuth2AuthorizationCodeFlow::refreshAccessToken()
@@ -313,7 +345,7 @@
const QString data = query.toString(QUrl::FullyEncoded);
d->currentReply = d->networkAccessManager()->post(request, data.toUtf8());
- d->status = Status::RefreshingToken;
+ setStatus(Status::RefreshingToken);
QNetworkReply *reply = d->currentReply.data();
QAbstractOAuthReplyHandler *handler = replyHandler();
@@ -327,6 +359,10 @@
&QNetworkAccessManager::authenticationRequired,
d,
&QOAuth2AuthorizationCodeFlowPrivate::_q_authenticate,
Qt::UniqueConnection);
+ QObjectPrivate::connect(d->replyHandler.data(),
+
&QAbstractOAuthReplyHandler::tokenRequestErrorOccurred,
+ d,
&QOAuth2AuthorizationCodeFlowPrivate::_q_accessTokenRequestFailed,
+ Qt::UniqueConnection);
}
/*!
@@ -404,6 +440,10 @@
&QNetworkAccessManager::authenticationRequired,
d,
&QOAuth2AuthorizationCodeFlowPrivate::_q_authenticate,
Qt::UniqueConnection);
+ QObjectPrivate::connect(d->replyHandler.data(),
+
&QAbstractOAuthReplyHandler::tokenRequestErrorOccurred,
+ d,
&QOAuth2AuthorizationCodeFlowPrivate::_q_accessTokenRequestFailed,
+ Qt::UniqueConnection);
}
/*!
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qtnetworkauth-everywhere-src-6.5.3/src/oauth/qoauth2authorizationcodeflow_p.h
new/qtnetworkauth-everywhere-src-6.6.0/src/oauth/qoauth2authorizationcodeflow_p.h
---
old/qtnetworkauth-everywhere-src-6.5.3/src/oauth/qoauth2authorizationcodeflow_p.h
2023-09-24 09:06:35.000000000 +0200
+++
new/qtnetworkauth-everywhere-src-6.6.0/src/oauth/qoauth2authorizationcodeflow_p.h
2023-10-02 05:05:49.000000000 +0200
@@ -39,6 +39,7 @@
void _q_handleCallback(const QVariantMap &data);
void _q_accessTokenRequestFinished(const QVariantMap &values);
+ void _q_accessTokenRequestFailed(QAbstractOAuth::Error error, const
QString &errorString = {});
void _q_authenticate(QNetworkReply *reply, QAuthenticator *authenticator);
QUrl accessTokenUrl;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qtnetworkauth-everywhere-src-6.5.3/src/oauth/qoauthoobreplyhandler.cpp
new/qtnetworkauth-everywhere-src-6.6.0/src/oauth/qoauthoobreplyhandler.cpp
--- old/qtnetworkauth-everywhere-src-6.5.3/src/oauth/qoauthoobreplyhandler.cpp
2023-09-24 09:06:35.000000000 +0200
+++ new/qtnetworkauth-everywhere-src-6.6.0/src/oauth/qoauthoobreplyhandler.cpp
2023-10-02 05:05:49.000000000 +0200
@@ -15,6 +15,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QOAuthOobReplyHandler::QOAuthOobReplyHandler(QObject *parent)
: QAbstractOAuthReplyHandler(parent)
{}
@@ -27,11 +29,12 @@
void QOAuthOobReplyHandler::networkReplyFinished(QNetworkReply *reply)
{
if (reply->error() != QNetworkReply::NoError) {
- qCWarning(lcReplyHandler, "%s", qPrintable(reply->errorString()));
+ emit tokenRequestErrorOccurred(QAbstractOAuth::Error::NetworkError,
reply->errorString());
return;
}
if (reply->header(QNetworkRequest::ContentTypeHeader).isNull()) {
- qCWarning(lcReplyHandler, "Empty Content-type header");
+ emit tokenRequestErrorOccurred(QAbstractOAuth::Error::NetworkError,
+ u"Empty Content-type header"_s);
return;
}
const QString contentType =
reply->header(QNetworkRequest::ContentTypeHeader).isNull() ?
@@ -39,7 +42,7 @@
reply->header(QNetworkRequest::ContentTypeHeader).toString();
const QByteArray data = reply->readAll();
if (data.isEmpty()) {
- qCWarning(lcReplyHandler, "No received data");
+ emit tokenRequestErrorOccurred(QAbstractOAuth::Error::NetworkError,
u"No received data"_s);
return;
}
@@ -54,8 +57,8 @@
|| contentType.startsWith(QStringLiteral("text/javascript"))) {
const QJsonDocument document = QJsonDocument::fromJson(data);
if (!document.isObject()) {
- qCWarning(lcReplyHandler, "Received data is not a JSON object: %s",
- qPrintable(QString::fromUtf8(data)));
+ emit tokenRequestErrorOccurred(QAbstractOAuth::Error::ServerError,
+ u"Received data is not a JSON object:
%1"_s.arg(QString::fromUtf8(data)));
return;
}
const QJsonObject object = document.object();
@@ -65,7 +68,8 @@
}
ret = object.toVariantMap();
} else {
- qCWarning(lcReplyHandler, "Unknown Content-type: %s",
qPrintable(contentType));
+ emit tokenRequestErrorOccurred(QAbstractOAuth::Error::ServerError,
+ u"Unknown Content-type %1"_s.arg(contentType));
return;
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/qtnetworkauth-everywhere-src-6.5.3/tests/auto/oauth2/tst_oauth2.cpp
new/qtnetworkauth-everywhere-src-6.6.0/tests/auto/oauth2/tst_oauth2.cpp
--- old/qtnetworkauth-everywhere-src-6.5.3/tests/auto/oauth2/tst_oauth2.cpp
2023-09-24 09:06:35.000000000 +0200
+++ new/qtnetworkauth-everywhere-src-6.6.0/tests/auto/oauth2/tst_oauth2.cpp
2023-10-02 05:05:49.000000000 +0200
@@ -13,6 +13,8 @@
#include "webserver.h"
#include "tlswebserver.h"
+using namespace Qt::StringLiterals;
+
class tst_OAuth2 : public QObject
{
Q_OBJECT
@@ -22,6 +24,8 @@
void getToken();
void refreshToken();
void getAndRefreshToken();
+ void tokenRequestErrors();
+ void authorizationErrors();
void prepareRequest();
#ifndef QT_NO_SSL
void setSslConfig();
@@ -39,13 +43,19 @@
return QLatin1String("test");
}
+ QAbstractOAuth::Error aTokenRequestError = QAbstractOAuth::Error::NoError;
+
void networkReplyFinished(QNetworkReply *reply) override
{
QVariantMap data;
const auto items = QUrlQuery(reply->readAll()).queryItems();
for (const auto &pair : items)
data.insert(pair.first, pair.second);
- Q_EMIT tokensReceived(data);
+
+ if (aTokenRequestError == QAbstractOAuth::Error::NoError)
+ emit tokensReceived(data);
+ else
+ emit tokenRequestErrorOccurred(aTokenRequestError, "a token
request error");
}
void emitCallbackReceived(const QVariantMap &data)
@@ -56,6 +66,7 @@
void tst_OAuth2::initTestCase()
{
+ // QLoggingCategory::setFilterRules(QStringLiteral("qt.networkauth* =
true"));
testDataDir = QFileInfo(QFINDTESTDATA("certs")).absolutePath();
if (testDataDir.isEmpty())
testDataDir = QCoreApplication::applicationDirPath();
@@ -63,6 +74,91 @@
testDataDir += QLatin1String("/");
}
+void tst_OAuth2::authorizationErrors()
+{
+ // This tests failures in authorization stage. For this test we don't need
a web server
+ // as we emit the final (failing) callbackReceived directly.
+ // Helper to catch the expected warning messages:
+ constexpr auto expectWarning = [](){
+ static const QRegularExpression authStageWarning{"Authorization
stage:.*"};
+ QTest::ignoreMessage(QtWarningMsg, authStageWarning);
+ };
+
+ QOAuth2AuthorizationCodeFlow oauth2;
+ oauth2.setAuthorizationUrl(QUrl{"authorization"_L1});
+ oauth2.setAccessTokenUrl(QUrl{"accessToken"_L1});
+ ReplyHandler replyHandler;
+ oauth2.setReplyHandler(&replyHandler);
+
+ QVariantMap callbackParameters;
+ connect(&oauth2, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser,
+ &oauth2, [&](const QUrl& /* url */) {
+ replyHandler.emitCallbackReceived(callbackParameters);
+ });
+
+ QSignalSpy requestFailedSpy(&oauth2, &QAbstractOAuth::requestFailed);
+ QSignalSpy errorSpy(&oauth2, &QAbstractOAuth2::error);
+ QSignalSpy statusSpy(&oauth2, &QAbstractOAuth2::statusChanged);
+ auto clearSpies = [&](){
+ requestFailedSpy.clear();
+ errorSpy.clear();
+ statusSpy.clear();
+ };
+
+ // Test error response from the authorization server (RFC 6749 section 5.2)
+ callbackParameters = {{"error"_L1, "invalid_grant"_L1},
+ {"error_description"_L1, "The error description"_L1},
+ {"error_uri"_L1, "The error URI"_L1}};
+ expectWarning();
+ oauth2.grant();
+ QTRY_COMPARE(errorSpy.count(), 1);
+ QTRY_COMPARE(requestFailedSpy.count(), 1);
+ QCOMPARE(errorSpy.first().at(0).toString(), "invalid_grant"_L1);
+ QCOMPARE(errorSpy.first().at(1).toString(), "The error description"_L1);
+ QCOMPARE(errorSpy.first().at(2).toString(), "The error URI"_L1);
+ QCOMPARE(requestFailedSpy.first().at(0).value<QAbstractOAuth::Error>(),
+ QAbstractOAuth::Error::ServerError);
+ QVERIFY(statusSpy.isEmpty());
+ QCOMPARE(oauth2.status(), QAbstractOAuth::Status::NotAuthenticated);
+
+ // Test not providing authorization code
+ clearSpies();
+ callbackParameters = {{"state"_L1, "thestate"_L1}};
+ expectWarning();
+ oauth2.grant();
+ QTRY_COMPARE(requestFailedSpy.count(), 1);
+ QCOMPARE(requestFailedSpy.first().at(0).value<QAbstractOAuth::Error>(),
+ QAbstractOAuth::Error::OAuthTokenNotFoundError);
+ QCOMPARE(errorSpy.count(), 0);
+ QVERIFY(statusSpy.isEmpty());
+ QCOMPARE(oauth2.status(), QAbstractOAuth::Status::NotAuthenticated);
+
+ // Test not providing a state
+ clearSpies();
+ callbackParameters = {{"code"_L1, "thecode"_L1}};
+ expectWarning();
+ oauth2.grant();
+ QTRY_COMPARE(requestFailedSpy.count(), 1);
+ QCOMPARE(requestFailedSpy.first().at(0).value<QAbstractOAuth::Error>(),
+ QAbstractOAuth::Error::ServerError);
+ QCOMPARE(errorSpy.count(), 0);
+ QVERIFY(statusSpy.isEmpty());
+ QCOMPARE(oauth2.status(), QAbstractOAuth::Status::NotAuthenticated);
+
+ // Test state mismatch (here we use "thestate" while the actual, expected,
state is a
+ // random generated string varying each run
+ clearSpies();
+ callbackParameters = {{"code"_L1, "thecode"_L1}, {"state"_L1,
"thestate"_L1}};
+ expectWarning();
+ oauth2.grant();
+ QTRY_COMPARE(requestFailedSpy.count(), 1);
+ QCOMPARE(requestFailedSpy.first().at(0).value<QAbstractOAuth::Error>(),
+ QAbstractOAuth::Error::ServerError);
+ QCOMPARE(errorSpy.count(), 0);
+ QVERIFY(statusSpy.isEmpty());
+ QCOMPARE(oauth2.status(), QAbstractOAuth::Status::NotAuthenticated);
+}
+
void tst_OAuth2::getToken()
{
WebServer webServer([](const WebServer::HttpRequest &request, QTcpSocket
*socket) {
@@ -163,6 +259,122 @@
QCOMPARE(oauth2.token(), QLatin1String("refresh_token"));
}
+void tst_OAuth2::tokenRequestErrors()
+{
+ // This test tests the token acquisition and refreshing errors.
+ // Helper to catch the expected warning messages:
+ constexpr auto expectWarning = [](){
+ static const QRegularExpression tokenWarning{"Token request
failed:.*"};
+ QTest::ignoreMessage(QtWarningMsg, tokenWarning);
+ };
+
+ QByteArray accessTokenResponse; // Varying reply for the auth server
+ WebServer authServer([&](const WebServer::HttpRequest &request, QTcpSocket
*socket) {
+ if (request.url.path() == QLatin1String("/accessToken"))
+ socket->write(accessTokenResponse);
+ });
+
+ QOAuth2AuthorizationCodeFlow oauth2;
+ oauth2.setAuthorizationUrl(authServer.url(QLatin1String("authorization")));
+ oauth2.setAccessTokenUrl(authServer.url(QLatin1String("accessToken")));
+
+ ReplyHandler replyHandler;
+ oauth2.setReplyHandler(&replyHandler);
+
+ QSignalSpy requestFailedSpy(&oauth2, &QAbstractOAuth::requestFailed);
+ QSignalSpy grantedSpy(&oauth2, &QOAuth2AuthorizationCodeFlow::granted);
+ QSignalSpy statusSpy(&oauth2, &QAbstractOAuth2::statusChanged);
+ auto clearSpies = [&](){
+ requestFailedSpy.clear();
+ grantedSpy.clear();
+ statusSpy.clear();
+ };
+
+ connect(&oauth2, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser,
+ &oauth2, [&](const QUrl &url) {
+ // Successful authorization stage, after which we can test token
requests.
+ // For clarity: in these tests we omit browser interaction by directly
triggering
+ // the emission of replyhandler::callbackReceived() signal
+ const QUrlQuery query(url.query());
+ replyHandler.emitCallbackReceived(QVariantMap {
+ { QLatin1String("code"), QLatin1String("test") },
+ { QLatin1String("state"),
+ query.queryItemValue(QLatin1String("state")) }
+ });
+ });
+
+ // Check the initial state
+ QVERIFY(requestFailedSpy.isEmpty());
+ QVERIFY(grantedSpy.isEmpty());
+ QVERIFY(statusSpy.isEmpty());
+ QCOMPARE(oauth2.status(), QAbstractOAuth::Status::NotAuthenticated);
+
+ // Try to get an access token with an invalid response
+ accessTokenResponse = "an invalid response"_ba;
+ expectWarning();
+ oauth2.grant();
+ QTRY_COMPARE(requestFailedSpy.size(), 1);
+ QVERIFY(grantedSpy.isEmpty());
+ QCOMPARE(statusSpy.size(), 1); // Authorization was successful so we get
one signal
+ QCOMPARE(oauth2.status(),
QAbstractOAuth::Status::TemporaryCredentialsReceived);
+
+ // Try to get an access token, but replyhandler indicates an error
+ clearSpies();
+ replyHandler.aTokenRequestError = QAbstractOAuth::Error::NetworkError;
+ expectWarning();
+ oauth2.grant();
+ QTRY_COMPARE(requestFailedSpy.size(), 1);
+ QVERIFY(grantedSpy.isEmpty());
+ QCOMPARE(oauth2.status(),
QAbstractOAuth::Status::TemporaryCredentialsReceived);
+
+ // Make a successful access & refresh token acquisition
+ replyHandler.aTokenRequestError = QAbstractOAuth::Error::NoError;
+ clearSpies();
+ accessTokenResponse =
+ "HTTP/1.0 200 OK\r\n"
+ "Content-Type: application/x-www-form-urlencoded;
charset=\"utf-8\"\r\n"
+ "\r\n"
+
"access_token=the_access_token&token_type=bearer&refresh_token=the_refresh_token"_ba;
+ oauth2.grant();
+ QTRY_COMPARE(grantedSpy.size(), 1);
+ QCOMPARE(statusSpy.size(), 3);
+ // First status change is going from TempCred back to NotAuthenticated
+ QCOMPARE(statusSpy.takeFirst().at(0).value<QAbstractOAuth::Status>(),
+ QAbstractOAuth::Status::NotAuthenticated);
+ QCOMPARE(statusSpy.takeFirst().at(0).value<QAbstractOAuth::Status>(),
+ QAbstractOAuth::Status::TemporaryCredentialsReceived);
+ QCOMPARE(statusSpy.takeFirst().at(0).value<QAbstractOAuth::Status>(),
+ QAbstractOAuth::Status::Granted);
+ QVERIFY(requestFailedSpy.isEmpty());
+ QCOMPARE(oauth2.status(), QAbstractOAuth::Status::Granted);
+ QCOMPARE(oauth2.token(), u"the_access_token"_s);
+ QCOMPARE(oauth2.refreshToken(), u"the_refresh_token"_s);
+
+ // Successfully refresh access token
+ clearSpies();
+ oauth2.refreshAccessToken();
+ QTRY_COMPARE(statusSpy.size(), 2);
+ QCOMPARE(statusSpy.takeFirst().at(0).value<QAbstractOAuth::Status>(),
+ QAbstractOAuth::Status::RefreshingToken);
+ QCOMPARE(statusSpy.takeFirst().at(0).value<QAbstractOAuth::Status>(),
+ QAbstractOAuth::Status::Granted);
+ QCOMPARE(oauth2.status(), QAbstractOAuth::Status::Granted);
+ QVERIFY(requestFailedSpy.isEmpty());
+
+ // Failed access token refresh
+ clearSpies();
+ replyHandler.aTokenRequestError = QAbstractOAuth::Error::ServerError;
+ expectWarning();
+ oauth2.refreshAccessToken();
+ QTRY_COMPARE(statusSpy.size(), 2);
+ QCOMPARE(statusSpy.takeFirst().at(0).value<QAbstractOAuth::Status>(),
+ QAbstractOAuth::Status::RefreshingToken);
+ QCOMPARE(statusSpy.takeFirst().at(0).value<QAbstractOAuth::Status>(),
+ QAbstractOAuth::Status::Granted); // back to granted since we
have an access token
+ QCOMPARE(requestFailedSpy.size(), 1);
+ QCOMPARE(oauth2.status(), QAbstractOAuth::Status::Granted);
+}
+
void tst_OAuth2::prepareRequest()
{
QOAuth2AuthorizationCodeFlow oauth2;