Use non blocking passord reading
This also remove the do while loop that might cause problem and expose the error message to the user. Signed-off-by: Carl Schwan <carl@carlschwan.eu>
This commit is contained in:
@@ -262,31 +262,48 @@ void Controller::invokeLogin()
|
|||||||
id = accountId;
|
id = accountId;
|
||||||
}
|
}
|
||||||
if (!account.homeserver().isEmpty()) {
|
if (!account.homeserver().isEmpty()) {
|
||||||
auto accessToken = loadAccessTokenFromKeyChain(account);
|
auto accessTokenLoadingJob = loadAccessTokenFromKeyChain(account);
|
||||||
|
connect(accessTokenLoadingJob, &QKeychain::Job::finished, this, [accountId, id, this, accessTokenLoadingJob](QKeychain::Job *) {
|
||||||
auto connection = new Connection(account.homeserver());
|
AccountSettings account{accountId};
|
||||||
connect(connection, &Connection::connected, this, [this, connection, id] {
|
QString accessToken;
|
||||||
connection->loadState();
|
if (accessTokenLoadingJob->error() == QKeychain::Error::NoError) {
|
||||||
addConnection(connection);
|
accessToken = accessTokenLoadingJob->binaryData();
|
||||||
if (connection->userId() == id) {
|
|
||||||
setActiveConnection(connection);
|
|
||||||
connectSingleShot(connection, &Connection::syncDone, this, &Controller::initiated);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
connect(connection, &Connection::loginError, this, [this, connection](const QString &error, const QString &) {
|
|
||||||
if (error == "Unrecognised access token") {
|
|
||||||
Q_EMIT errorOccured(i18n("Login Failed: Access Token invalid or revoked"));
|
|
||||||
logout(connection, false);
|
|
||||||
} else {
|
} else {
|
||||||
Q_EMIT errorOccured(i18n("Login Failed: %1", error));
|
// No access token from the keychain, try token file
|
||||||
logout(connection, true);
|
// TODO FIXME this code is racy since the file might have
|
||||||
|
// already been removed. But since the other code do a blocking
|
||||||
|
// dbus call, the probability are not high that it will happen.
|
||||||
|
// loadAccessTokenFromFile is also mostly legacy nowadays
|
||||||
|
accessToken = loadAccessTokenFromFile(account);
|
||||||
|
if (accessToken.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Q_EMIT initiated();
|
|
||||||
|
auto connection = new Connection(account.homeserver());
|
||||||
|
connect(connection, &Connection::connected, this, [this, connection, id] {
|
||||||
|
connection->loadState();
|
||||||
|
addConnection(connection);
|
||||||
|
if (connection->userId() == id) {
|
||||||
|
setActiveConnection(connection);
|
||||||
|
connectSingleShot(connection, &Connection::syncDone, this, &Controller::initiated);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(connection, &Connection::loginError, this, [this, connection](const QString &error, const QString &) {
|
||||||
|
if (error == "Unrecognised access token") {
|
||||||
|
Q_EMIT errorOccured(i18n("Login Failed: Access Token invalid or revoked"));
|
||||||
|
logout(connection, false);
|
||||||
|
} else {
|
||||||
|
Q_EMIT errorOccured(i18n("Login Failed: %1", error));
|
||||||
|
logout(connection, true);
|
||||||
|
}
|
||||||
|
Q_EMIT initiated();
|
||||||
|
});
|
||||||
|
connect(connection, &Connection::networkError, this, [this](const QString &error, const QString &, int, int) {
|
||||||
|
Q_EMIT errorOccured(i18n("Network Error: %1", error));
|
||||||
|
});
|
||||||
|
connection->assumeIdentity(account.userId(), accessToken, account.deviceId());
|
||||||
});
|
});
|
||||||
connect(connection, &Connection::networkError, this, [this](const QString &error, const QString &, int, int) {
|
|
||||||
Q_EMIT errorOccured(i18n("Network Error: %1", error));
|
|
||||||
});
|
|
||||||
connection->assumeIdentity(account.userId(), accessToken, account.deviceId());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (accounts.isEmpty()) {
|
if (accounts.isEmpty()) {
|
||||||
@@ -309,48 +326,54 @@ QByteArray Controller::loadAccessTokenFromFile(const AccountSettings &account)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray Controller::loadAccessTokenFromKeyChain(const AccountSettings &account)
|
QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const AccountSettings &account)
|
||||||
{
|
{
|
||||||
QKeychain::Error error;
|
qDebug() << "Reading access token from the keychain for" << account.userId();
|
||||||
QString errorString;
|
auto job = new QKeychain::ReadPasswordJob(qAppName(), this);
|
||||||
do {
|
job->setKey(account.userId());
|
||||||
qDebug() << "Reading access token from the keychain for" << account.userId();
|
|
||||||
QKeychain::ReadPasswordJob job(qAppName());
|
|
||||||
job.setAutoDelete(false);
|
|
||||||
job.setKey(account.userId());
|
|
||||||
QEventLoop loop;
|
|
||||||
QKeychain::ReadPasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
|
|
||||||
job.start();
|
|
||||||
loop.exec();
|
|
||||||
|
|
||||||
if (job.error() == QKeychain::Error::NoError) {
|
// Handling of errors
|
||||||
return job.binaryData();
|
connect(job, &QKeychain::Job::emitFinishedWithError, this, [this, &account, job](QKeychain::Error error, const QString &errorString) {
|
||||||
}
|
if (error == QKeychain::Error::EntryNotFound) {
|
||||||
Q_EMIT globalErrorOccured(i18n("Unable to read access token"), i18n("Please make sure that the keychain is opened."));
|
// no access token from the keychain, try token file
|
||||||
error = job.error();
|
auto accessToken = loadAccessTokenFromFile(account);
|
||||||
errorString = job.errorString();
|
if (!accessToken.isEmpty()) {
|
||||||
} while (error == QKeychain::Error::OtherError);
|
qDebug() << "Migrating the access token from file to the keychain for " << account.userId();
|
||||||
|
bool removed = false;
|
||||||
qWarning() << "Could not read the access token from the keychain:" << errorString;
|
bool saved = saveAccessTokenToKeyChain(account, accessToken);
|
||||||
// no access token from the keychain, try token file
|
if (saved) {
|
||||||
auto accessToken = loadAccessTokenFromFile(account);
|
QFile accountTokenFile{accessTokenFileName(account)};
|
||||||
if (error == QKeychain::Error::EntryNotFound) {
|
removed = accountTokenFile.remove();
|
||||||
if (!accessToken.isEmpty()) {
|
}
|
||||||
qDebug() << "Migrating the access token from file to the keychain for " << account.userId();
|
if (!(saved && removed)) {
|
||||||
bool removed = false;
|
qDebug() << "Migrating the access token from the file to the keychain "
|
||||||
bool saved = saveAccessTokenToKeyChain(account, accessToken);
|
"failed";
|
||||||
if (saved) {
|
}
|
||||||
QFile accountTokenFile{accessTokenFileName(account)};
|
return;
|
||||||
removed = accountTokenFile.remove();
|
|
||||||
}
|
|
||||||
if (!(saved && removed)) {
|
|
||||||
qDebug() << "Migrating the access token from the file to the keychain "
|
|
||||||
"failed";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return accessToken;
|
switch (error) {
|
||||||
|
case QKeychain::EntryNotFound:
|
||||||
|
Q_EMIT globalErrorOccured(i18n("Access token wasn't found"), i18n("Maybe it was deleted?"));
|
||||||
|
break;
|
||||||
|
case QKeychain::AccessDeniedByUser:
|
||||||
|
case QKeychain::AccessDenied:
|
||||||
|
Q_EMIT globalErrorOccured(i18n("Access to keychain was denied."), i18n("Please allow NeoChat to read the access token"));
|
||||||
|
break;
|
||||||
|
case QKeychain::NoBackendAvailable:
|
||||||
|
Q_EMIT globalErrorOccured(i18n("No keychain available."), i18n("Please install a keychain, e.g. KWallet or GNOME keyring on Linux"));
|
||||||
|
break;
|
||||||
|
case QKeychain::OtherError:
|
||||||
|
Q_EMIT globalErrorOccured(i18n("Unable to read access token"), errorString);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
job->start();
|
||||||
|
|
||||||
|
return job;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::saveAccessTokenToFile(const AccountSettings &account, const QByteArray &accessToken)
|
bool Controller::saveAccessTokenToFile(const AccountSettings &account, const QByteArray &accessToken)
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ class NeoChatRoom;
|
|||||||
class NeoChatUser;
|
class NeoChatUser;
|
||||||
class QQuickWindow;
|
class QQuickWindow;
|
||||||
|
|
||||||
|
namespace QKeychain
|
||||||
|
{
|
||||||
|
class ReadPasswordJob;
|
||||||
|
}
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
class Controller : public QObject
|
class Controller : public QObject
|
||||||
@@ -99,7 +104,7 @@ private:
|
|||||||
bool m_busy = false;
|
bool m_busy = false;
|
||||||
|
|
||||||
static QByteArray loadAccessTokenFromFile(const AccountSettings &account);
|
static QByteArray loadAccessTokenFromFile(const AccountSettings &account);
|
||||||
QByteArray loadAccessTokenFromKeyChain(const AccountSettings &account);
|
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const AccountSettings &account);
|
||||||
|
|
||||||
void loadSettings();
|
void loadSettings();
|
||||||
void saveSettings() const;
|
void saveSettings() const;
|
||||||
|
|||||||
Reference in New Issue
Block a user