forked from qt-creator/qt-creator
Axivion: Prepare for re-requesting credentials
In case the server returns with an invalid credentials message, the user shall get a dialog box to enter new ones. Change-Id: Ied6cc8b0eae6f0cbb49fbfedec282a821ece58aa Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -37,6 +37,28 @@ QFuture<Credential> CredentialProvider::getCredential()
|
|||||||
return QtFuture::makeReadyFuture(Credential(settings().server.token));
|
return QtFuture::makeReadyFuture(Credential(settings().server.token));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QFuture<void> 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<void> 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)
|
ClientData::ClientData(Utils::NetworkAccessManager &networkAccessManager)
|
||||||
: networkAccessManager(networkAccessManager),
|
: networkAccessManager(networkAccessManager),
|
||||||
credentialProvider(std::make_unique<CredentialProvider>())
|
credentialProvider(std::make_unique<CredentialProvider>())
|
||||||
@@ -105,10 +127,44 @@ static Utils::expected<DataWithOrigin<T>, Error> parseResponse(ResponseData rawB
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void fetch(QPromise<ResponseData> promise, std::shared_ptr<ClientData> clientData, QUrl url);
|
||||||
|
|
||||||
|
static void processResponse(QPromise<ResponseData> promise,
|
||||||
|
std::shared_ptr<ClientData> clientData,
|
||||||
|
QNetworkReply *reply,
|
||||||
|
Credential credential)
|
||||||
|
{
|
||||||
|
ResponseData response = readResponse(*reply, jsonContentType);
|
||||||
|
if (!response
|
||||||
|
&& response.error().isInvalidCredentialsError()
|
||||||
|
&& clientData->credentialProvider->canReRequestPasswordOnAuthenticationFailure()) {
|
||||||
|
QFutureWatcher<void> *watcher = new QFutureWatcher<void>(&clientData->networkAccessManager);
|
||||||
|
QObject::connect(watcher,
|
||||||
|
&QFutureWatcher<void>::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<ResponseData> promise,
|
static void fetch(QPromise<ResponseData> promise,
|
||||||
std::shared_ptr<ClientData> clientData,
|
std::shared_ptr<ClientData> clientData,
|
||||||
const QUrl &url,
|
const QUrl &url,
|
||||||
const Credential &credential)
|
Credential credential)
|
||||||
{
|
{
|
||||||
QNetworkRequest request{ url };
|
QNetworkRequest request{ url };
|
||||||
request.setRawHeader(QByteArrayLiteral(u8"Accept"),
|
request.setRawHeader(QByteArrayLiteral(u8"Accept"),
|
||||||
@@ -124,21 +180,22 @@ static void fetch(QPromise<ResponseData> promise,
|
|||||||
QObject::connect(reply,
|
QObject::connect(reply,
|
||||||
&QNetworkReply::finished,
|
&QNetworkReply::finished,
|
||||||
reply,
|
reply,
|
||||||
[promise = std::move(promise), reply]() mutable {
|
[promise = std::move(promise),
|
||||||
promise.addResult(readResponse(*reply, jsonContentType));
|
clientData = std::move(clientData),
|
||||||
promise.finish();
|
reply,
|
||||||
|
credential = std::move(credential)]() mutable {
|
||||||
|
processResponse(std::move(promise),
|
||||||
|
std::move(clientData),
|
||||||
|
reply,
|
||||||
|
std::move(credential));
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static QFuture<ResponseData> fetch(std::shared_ptr<ClientData> clientData,
|
static void fetch(QPromise<ResponseData> promise,
|
||||||
const std::optional<QUrl> &base,
|
std::shared_ptr<ClientData> clientData,
|
||||||
const QUrl &target)
|
QUrl url)
|
||||||
{
|
{
|
||||||
QPromise<ResponseData> promise;
|
|
||||||
promise.start();
|
|
||||||
QFuture<ResponseData> future = promise.future();
|
|
||||||
QUrl url = base ? base->resolved(target) : target;
|
|
||||||
QFutureWatcher<Credential> *watcher = new QFutureWatcher<Credential>(&clientData->networkAccessManager);
|
QFutureWatcher<Credential> *watcher = new QFutureWatcher<Credential>(&clientData->networkAccessManager);
|
||||||
QObject::connect(watcher,
|
QObject::connect(watcher,
|
||||||
&QFutureWatcher<Credential>::finished,
|
&QFutureWatcher<Credential>::finished,
|
||||||
@@ -151,6 +208,18 @@ static QFuture<ResponseData> fetch(std::shared_ptr<ClientData> clientData,
|
|||||||
watcher->deleteLater();;
|
watcher->deleteLater();;
|
||||||
});
|
});
|
||||||
watcher->setFuture(clientData->credentialProvider->getCredential());
|
watcher->setFuture(clientData->credentialProvider->getCredential());
|
||||||
|
}
|
||||||
|
|
||||||
|
static QFuture<ResponseData> fetch(std::shared_ptr<ClientData> clientData,
|
||||||
|
const std::optional<QUrl> &base,
|
||||||
|
const QUrl &target)
|
||||||
|
{
|
||||||
|
QPromise<ResponseData> promise;
|
||||||
|
promise.start();
|
||||||
|
QFuture<ResponseData> future = promise.future();
|
||||||
|
fetch(std::move(promise),
|
||||||
|
std::move(clientData),
|
||||||
|
base ? base->resolved(target) : target);
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -44,6 +44,12 @@ class CredentialProvider
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QFuture<Credential> getCredential();
|
QFuture<Credential> getCredential();
|
||||||
|
|
||||||
|
QFuture<void> authenticationFailure(const Credential &credential);
|
||||||
|
|
||||||
|
QFuture<void> authenticationSuccess(const Credential &credential);
|
||||||
|
|
||||||
|
bool canReRequestPasswordOnAuthenticationFailure();
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClientData
|
class ClientData
|
||||||
|
@@ -104,4 +104,11 @@ QString Error::message() const
|
|||||||
}, this->m_error);
|
}, this->m_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Error::isInvalidCredentialsError()
|
||||||
|
{
|
||||||
|
DashboardError *dashboardError = std::get_if<DashboardError>(&this->m_error);
|
||||||
|
return dashboardError != nullptr
|
||||||
|
&& dashboardError->type == QLatin1String("InvalidCredentialsException");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Axivion::Internal
|
} // namespace Axivion::Internal
|
||||||
|
@@ -76,6 +76,8 @@ public:
|
|||||||
|
|
||||||
QString message() const;
|
QString message() const;
|
||||||
|
|
||||||
|
bool isInvalidCredentialsError();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::variant<GeneralError,
|
std::variant<GeneralError,
|
||||||
NetworkError,
|
NetworkError,
|
||||||
|
Reference in New Issue
Block a user