diff --git a/src/plugins/axivion/dashboard/dashboardclient.cpp b/src/plugins/axivion/dashboard/dashboardclient.cpp index 54b826c7591..6dc2b9cb590 100644 --- a/src/plugins/axivion/dashboard/dashboardclient.cpp +++ b/src/plugins/axivion/dashboard/dashboardclient.cpp @@ -37,6 +37,28 @@ QFuture CredentialProvider::getCredential() return QtFuture::makeReadyFuture(Credential(settings().server.token)); } +QFuture CredentialProvider::authenticationFailure(const Credential &credential) +{ + Q_UNUSED(credential); + // ToDo: invalidate stored credential to prevent further accesses with it. + // This is to prevent account locking on password change day due to to many + // authentication failuers caused by automated requests. + return QtFuture::makeReadyFuture(); +} + +QFuture CredentialProvider::authenticationSuccess(const Credential &credential) +{ + Q_UNUSED(credential); + // ToDo: store (now verified) credential on disk if not already happened. + return QtFuture::makeReadyFuture(); +} + +bool CredentialProvider::canReRequestPasswordOnAuthenticationFailure() +{ + // ToDo: support on-demand password input dialog. + return false; +} + ClientData::ClientData(Utils::NetworkAccessManager &networkAccessManager) : networkAccessManager(networkAccessManager), credentialProvider(std::make_unique()) @@ -105,10 +127,44 @@ static Utils::expected, Error> parseResponse(ResponseData rawB } } +static void fetch(QPromise promise, std::shared_ptr clientData, QUrl url); + +static void processResponse(QPromise promise, + std::shared_ptr clientData, + QNetworkReply *reply, + Credential credential) +{ + ResponseData response = readResponse(*reply, jsonContentType); + if (!response + && response.error().isInvalidCredentialsError() + && clientData->credentialProvider->canReRequestPasswordOnAuthenticationFailure()) { + QFutureWatcher *watcher = new QFutureWatcher(&clientData->networkAccessManager); + QObject::connect(watcher, + &QFutureWatcher::finished, + &clientData->networkAccessManager, + [promise = std::move(promise), + clientData, + url = reply->url(), + watcher]() mutable { + fetch(std::move(promise), std::move(clientData), std::move(url)); + watcher->deleteLater(); + }); + watcher->setFuture(clientData->credentialProvider->authenticationFailure(credential)); + return; + } + if (response) { + clientData->credentialProvider->authenticationSuccess(credential); + } else if (response.error().isInvalidCredentialsError()) { + clientData->credentialProvider->authenticationFailure(credential); + } + promise.addResult(std::move(response)); + promise.finish(); +} + static void fetch(QPromise promise, std::shared_ptr clientData, const QUrl &url, - const Credential &credential) + Credential credential) { QNetworkRequest request{ url }; request.setRawHeader(QByteArrayLiteral(u8"Accept"), @@ -124,21 +180,22 @@ static void fetch(QPromise promise, QObject::connect(reply, &QNetworkReply::finished, reply, - [promise = std::move(promise), reply]() mutable { - promise.addResult(readResponse(*reply, jsonContentType)); - promise.finish(); + [promise = std::move(promise), + clientData = std::move(clientData), + reply, + credential = std::move(credential)]() mutable { + processResponse(std::move(promise), + std::move(clientData), + reply, + std::move(credential)); reply->deleteLater(); }); } -static QFuture fetch(std::shared_ptr clientData, - const std::optional &base, - const QUrl &target) +static void fetch(QPromise promise, + std::shared_ptr clientData, + QUrl url) { - QPromise promise; - promise.start(); - QFuture future = promise.future(); - QUrl url = base ? base->resolved(target) : target; QFutureWatcher *watcher = new QFutureWatcher(&clientData->networkAccessManager); QObject::connect(watcher, &QFutureWatcher::finished, @@ -151,6 +208,18 @@ static QFuture fetch(std::shared_ptr clientData, watcher->deleteLater();; }); watcher->setFuture(clientData->credentialProvider->getCredential()); +} + +static QFuture fetch(std::shared_ptr clientData, + const std::optional &base, + const QUrl &target) +{ + QPromise promise; + promise.start(); + QFuture future = promise.future(); + fetch(std::move(promise), + std::move(clientData), + base ? base->resolved(target) : target); return future; } diff --git a/src/plugins/axivion/dashboard/dashboardclient.h b/src/plugins/axivion/dashboard/dashboardclient.h index 29fdf315c14..bdb44ecaea7 100644 --- a/src/plugins/axivion/dashboard/dashboardclient.h +++ b/src/plugins/axivion/dashboard/dashboardclient.h @@ -44,6 +44,12 @@ class CredentialProvider { public: QFuture getCredential(); + + QFuture authenticationFailure(const Credential &credential); + + QFuture authenticationSuccess(const Credential &credential); + + bool canReRequestPasswordOnAuthenticationFailure(); }; class ClientData diff --git a/src/plugins/axivion/dashboard/error.cpp b/src/plugins/axivion/dashboard/error.cpp index f583a6f7815..d28bf46bc3c 100644 --- a/src/plugins/axivion/dashboard/error.cpp +++ b/src/plugins/axivion/dashboard/error.cpp @@ -104,4 +104,11 @@ QString Error::message() const }, this->m_error); } +bool Error::isInvalidCredentialsError() +{ + DashboardError *dashboardError = std::get_if(&this->m_error); + return dashboardError != nullptr + && dashboardError->type == QLatin1String("InvalidCredentialsException"); +} + } // namespace Axivion::Internal diff --git a/src/plugins/axivion/dashboard/error.h b/src/plugins/axivion/dashboard/error.h index 4e2a434ab81..2a0f34196fe 100644 --- a/src/plugins/axivion/dashboard/error.h +++ b/src/plugins/axivion/dashboard/error.h @@ -76,6 +76,8 @@ public: QString message() const; + bool isInvalidCredentialsError(); + private: std::variant