mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-26 23:28:14 +03:00
Credentials: Use per-account keychain entries #5830
This requires a lot of migration code: the old entries need to be read, saved to the new locations and then deleted.
This commit is contained in:
parent
0b4fd52d63
commit
671599c8b2
6 changed files with 130 additions and 32 deletions
|
@ -54,6 +54,7 @@ ShibbolethCredentials::ShibbolethCredentials()
|
||||||
, _ready(false)
|
, _ready(false)
|
||||||
, _stillValid(false)
|
, _stillValid(false)
|
||||||
, _browser(0)
|
, _browser(0)
|
||||||
|
, _keychainMigration(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +63,7 @@ ShibbolethCredentials::ShibbolethCredentials(const QNetworkCookie &cookie)
|
||||||
, _stillValid(true)
|
, _stillValid(true)
|
||||||
, _browser(0)
|
, _browser(0)
|
||||||
, _shibCookie(cookie)
|
, _shibCookie(cookie)
|
||||||
|
, _keychainMigration(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,14 +133,21 @@ void ShibbolethCredentials::fetchFromKeychain()
|
||||||
Q_EMIT fetched();
|
Q_EMIT fetched();
|
||||||
} else {
|
} else {
|
||||||
_url = _account->url();
|
_url = _account->url();
|
||||||
|
_keychainMigration = false;
|
||||||
|
fetchFromKeychainHelper();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShibbolethCredentials::fetchFromKeychainHelper()
|
||||||
|
{
|
||||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
||||||
job->setSettings(ConfigFile::settingsWithGroup(Theme::instance()->appName(), job).release());
|
job->setSettings(ConfigFile::settingsWithGroup(Theme::instance()->appName(), job).release());
|
||||||
job->setInsecureFallback(false);
|
job->setInsecureFallback(false);
|
||||||
job->setKey(keychainKey(_account->url().toString(), user()));
|
job->setKey(keychainKey(_url.toString(), user(),
|
||||||
|
_keychainMigration ? QString() : _account->id()));
|
||||||
connect(job, SIGNAL(finished(QKeychain::Job *)), SLOT(slotReadJobDone(QKeychain::Job *)));
|
connect(job, SIGNAL(finished(QKeychain::Job *)), SLOT(slotReadJobDone(QKeychain::Job *)));
|
||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void ShibbolethCredentials::askFromUser()
|
void ShibbolethCredentials::askFromUser()
|
||||||
{
|
{
|
||||||
|
@ -242,6 +251,16 @@ void ShibbolethCredentials::slotBrowserRejected()
|
||||||
|
|
||||||
void ShibbolethCredentials::slotReadJobDone(QKeychain::Job *job)
|
void ShibbolethCredentials::slotReadJobDone(QKeychain::Job *job)
|
||||||
{
|
{
|
||||||
|
// If we can't find the credentials at the keys that include the account id,
|
||||||
|
// try to read them from the legacy locations that don't have a account id.
|
||||||
|
if (!_keychainMigration && job->error() == QKeychain::EntryNotFound) {
|
||||||
|
qCWarning(lcShibboleth)
|
||||||
|
<< "Could not find keychain entry, attempting to read from legacy location";
|
||||||
|
_keychainMigration = true;
|
||||||
|
fetchFromKeychainHelper();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (job->error() == QKeychain::NoError) {
|
if (job->error() == QKeychain::NoError) {
|
||||||
ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(job);
|
ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(job);
|
||||||
delete readJob->settings();
|
delete readJob->settings();
|
||||||
|
@ -260,6 +279,19 @@ void ShibbolethCredentials::slotReadJobDone(QKeychain::Job *job)
|
||||||
_ready = false;
|
_ready = false;
|
||||||
Q_EMIT fetched();
|
Q_EMIT fetched();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If keychain data was read from legacy location, wipe these entries and store new ones
|
||||||
|
if (_keychainMigration && _ready) {
|
||||||
|
persist();
|
||||||
|
|
||||||
|
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
|
||||||
|
job->setSettings(ConfigFile::settingsWithGroup(Theme::instance()->appName(), job).release());
|
||||||
|
job->setKey(keychainKey(_account->url().toString(), user(), QString()));
|
||||||
|
job->start();
|
||||||
|
|
||||||
|
qCWarning(lcShibboleth) << "Migrated old keychain entries";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShibbolethCredentials::showLoginWindow()
|
void ShibbolethCredentials::showLoginWindow()
|
||||||
|
@ -313,7 +345,7 @@ void ShibbolethCredentials::storeShibCookie(const QNetworkCookie &cookie)
|
||||||
job->setSettings(ConfigFile::settingsWithGroup(Theme::instance()->appName(), job).release());
|
job->setSettings(ConfigFile::settingsWithGroup(Theme::instance()->appName(), job).release());
|
||||||
// we don't really care if it works...
|
// we don't really care if it works...
|
||||||
//connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotWriteJobDone(QKeychain::Job*)));
|
//connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotWriteJobDone(QKeychain::Job*)));
|
||||||
job->setKey(keychainKey(_account->url().toString(), user()));
|
job->setKey(keychainKey(_account->url().toString(), user(), _account->id()));
|
||||||
job->setTextData(QString::fromUtf8(cookie.toRawForm()));
|
job->setTextData(QString::fromUtf8(cookie.toRawForm()));
|
||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
|
@ -322,7 +354,7 @@ void ShibbolethCredentials::removeShibCookie()
|
||||||
{
|
{
|
||||||
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
|
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
|
||||||
job->setSettings(ConfigFile::settingsWithGroup(Theme::instance()->appName(), job).release());
|
job->setSettings(ConfigFile::settingsWithGroup(Theme::instance()->appName(), job).release());
|
||||||
job->setKey(keychainKey(_account->url().toString(), user()));
|
job->setKey(keychainKey(_account->url().toString(), user(), _account->id()));
|
||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,5 +368,4 @@ void ShibbolethCredentials::addToCookieJar(const QNetworkCookie &cookie)
|
||||||
jar->blockSignals(false);
|
jar->blockSignals(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|
|
@ -84,6 +84,10 @@ private:
|
||||||
void storeShibCookie(const QNetworkCookie &cookie);
|
void storeShibCookie(const QNetworkCookie &cookie);
|
||||||
void removeShibCookie();
|
void removeShibCookie();
|
||||||
void addToCookieJar(const QNetworkCookie &cookie);
|
void addToCookieJar(const QNetworkCookie &cookie);
|
||||||
|
|
||||||
|
/// Reads data from keychain, progressing to slotReadJobDone
|
||||||
|
void fetchFromKeychainHelper();
|
||||||
|
|
||||||
QUrl _url;
|
QUrl _url;
|
||||||
QByteArray prepareCookieData() const;
|
QByteArray prepareCookieData() const;
|
||||||
|
|
||||||
|
@ -92,6 +96,7 @@ private:
|
||||||
QPointer<ShibbolethWebView> _browser;
|
QPointer<ShibbolethWebView> _browser;
|
||||||
QNetworkCookie _shibCookie;
|
QNetworkCookie _shibCookie;
|
||||||
QString _user;
|
QString _user;
|
||||||
|
bool _keychainMigration;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|
|
@ -34,7 +34,7 @@ void AbstractCredentials::setAccount(Account *account)
|
||||||
_account = account;
|
_account = account;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AbstractCredentials::keychainKey(const QString &url, const QString &user)
|
QString AbstractCredentials::keychainKey(const QString &url, const QString &user, const QString &accountId)
|
||||||
{
|
{
|
||||||
QString u(url);
|
QString u(url);
|
||||||
if (u.isEmpty()) {
|
if (u.isEmpty()) {
|
||||||
|
@ -51,6 +51,9 @@ QString AbstractCredentials::keychainKey(const QString &url, const QString &user
|
||||||
}
|
}
|
||||||
|
|
||||||
QString key = user + QLatin1Char(':') + u;
|
QString key = user + QLatin1Char(':') + u;
|
||||||
|
if (!accountId.isEmpty()) {
|
||||||
|
key += QLatin1Char(':') + accountId;
|
||||||
|
}
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|
|
@ -85,7 +85,7 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void forgetSensitiveData() = 0;
|
virtual void forgetSensitiveData() = 0;
|
||||||
|
|
||||||
static QString keychainKey(const QString &url, const QString &user);
|
static QString keychainKey(const QString &url, const QString &user, const QString &accountId);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
/** Emitted when fetchFromKeychain() is done.
|
/** Emitted when fetchFromKeychain() is done.
|
||||||
|
|
|
@ -104,6 +104,7 @@ static void addSettingsToJob(Account *account, QKeychain::Job *job)
|
||||||
|
|
||||||
HttpCredentials::HttpCredentials()
|
HttpCredentials::HttpCredentials()
|
||||||
: _ready(false)
|
: _ready(false)
|
||||||
|
, _keychainMigration(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,6 +115,7 @@ HttpCredentials::HttpCredentials(const QString &user, const QString &password, c
|
||||||
, _ready(true)
|
, _ready(true)
|
||||||
, _clientSslKey(key)
|
, _clientSslKey(key)
|
||||||
, _clientSslCertificate(certificate)
|
, _clientSslCertificate(certificate)
|
||||||
|
, _keychainMigration(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,21 +177,43 @@ void HttpCredentials::fetchFromKeychain()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString kck = keychainKey(_account->url().toString(), _user);
|
|
||||||
|
|
||||||
if (_ready) {
|
if (_ready) {
|
||||||
Q_EMIT fetched();
|
Q_EMIT fetched();
|
||||||
} else {
|
} else {
|
||||||
|
_keychainMigration = false;
|
||||||
|
fetchFromKeychainHelper();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpCredentials::fetchFromKeychainHelper()
|
||||||
|
{
|
||||||
// Read client cert from keychain
|
// Read client cert from keychain
|
||||||
const QString kck = keychainKey(_account->url().toString(), _user + clientCertificatePEMC);
|
const QString kck = keychainKey(
|
||||||
|
_account->url().toString(),
|
||||||
|
_user + clientCertificatePEMC,
|
||||||
|
_keychainMigration ? QString() : _account->id());
|
||||||
|
|
||||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
||||||
addSettingsToJob(_account, job);
|
addSettingsToJob(_account, job);
|
||||||
job->setInsecureFallback(false);
|
job->setInsecureFallback(false);
|
||||||
job->setKey(kck);
|
job->setKey(kck);
|
||||||
|
|
||||||
connect(job, SIGNAL(finished(QKeychain::Job *)), SLOT(slotReadClientCertPEMJobDone(QKeychain::Job *)));
|
connect(job, SIGNAL(finished(QKeychain::Job *)), SLOT(slotReadClientCertPEMJobDone(QKeychain::Job *)));
|
||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HttpCredentials::deleteOldKeychainEntries()
|
||||||
|
{
|
||||||
|
auto startDeleteJob = [this](QString user) {
|
||||||
|
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
|
||||||
|
addSettingsToJob(_account, job);
|
||||||
|
job->setInsecureFallback(true);
|
||||||
|
job->setKey(keychainKey(_account->url().toString(), user, QString()));
|
||||||
|
job->start();
|
||||||
|
};
|
||||||
|
|
||||||
|
startDeleteJob(_user);
|
||||||
|
startDeleteJob(_user + clientKeyPEMC);
|
||||||
|
startDeleteJob(_user + clientCertificatePEMC);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpCredentials::slotReadClientCertPEMJobDone(QKeychain::Job *incoming)
|
void HttpCredentials::slotReadClientCertPEMJobDone(QKeychain::Job *incoming)
|
||||||
|
@ -204,12 +228,15 @@ void HttpCredentials::slotReadClientCertPEMJobDone(QKeychain::Job *incoming)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load key too
|
// Load key too
|
||||||
const QString kck = keychainKey(_account->url().toString(), _user + clientKeyPEMC);
|
const QString kck = keychainKey(
|
||||||
|
_account->url().toString(),
|
||||||
|
_user + clientKeyPEMC,
|
||||||
|
_keychainMigration ? QString() : _account->id());
|
||||||
|
|
||||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
||||||
addSettingsToJob(_account, job);
|
addSettingsToJob(_account, job);
|
||||||
job->setInsecureFallback(false);
|
job->setInsecureFallback(false);
|
||||||
job->setKey(kck);
|
job->setKey(kck);
|
||||||
|
|
||||||
connect(job, SIGNAL(finished(QKeychain::Job *)), SLOT(slotReadClientKeyPEMJobDone(QKeychain::Job *)));
|
connect(job, SIGNAL(finished(QKeychain::Job *)), SLOT(slotReadClientKeyPEMJobDone(QKeychain::Job *)));
|
||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
|
@ -236,12 +263,15 @@ void HttpCredentials::slotReadClientKeyPEMJobDone(QKeychain::Job *incoming)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now fetch the actual server password
|
// Now fetch the actual server password
|
||||||
const QString kck = keychainKey(_account->url().toString(), _user);
|
const QString kck = keychainKey(
|
||||||
|
_account->url().toString(),
|
||||||
|
_user,
|
||||||
|
_keychainMigration ? QString() : _account->id());
|
||||||
|
|
||||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
||||||
addSettingsToJob(_account, job);
|
addSettingsToJob(_account, job);
|
||||||
job->setInsecureFallback(false);
|
job->setInsecureFallback(false);
|
||||||
job->setKey(kck);
|
job->setKey(kck);
|
||||||
|
|
||||||
connect(job, SIGNAL(finished(QKeychain::Job *)), SLOT(slotReadJobDone(QKeychain::Job *)));
|
connect(job, SIGNAL(finished(QKeychain::Job *)), SLOT(slotReadJobDone(QKeychain::Job *)));
|
||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
|
@ -258,6 +288,17 @@ bool HttpCredentials::stillValid(QNetworkReply *reply)
|
||||||
void HttpCredentials::slotReadJobDone(QKeychain::Job *incomingJob)
|
void HttpCredentials::slotReadJobDone(QKeychain::Job *incomingJob)
|
||||||
{
|
{
|
||||||
QKeychain::ReadPasswordJob *job = static_cast<ReadPasswordJob *>(incomingJob);
|
QKeychain::ReadPasswordJob *job = static_cast<ReadPasswordJob *>(incomingJob);
|
||||||
|
QKeychain::Error error = job->error();
|
||||||
|
|
||||||
|
// If we can't find the credentials at the keys that include the account id,
|
||||||
|
// try to read them from the legacy locations that don't have a account id.
|
||||||
|
if (!_keychainMigration && error == QKeychain::EntryNotFound) {
|
||||||
|
qCWarning(lcHttpCredentials)
|
||||||
|
<< "Could not find keychain entries, attempting to read from legacy locations";
|
||||||
|
_keychainMigration = true;
|
||||||
|
fetchFromKeychainHelper();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool isOauth = _account->credentialSetting(QLatin1String(isOAuthC)).toBool();
|
bool isOauth = _account->credentialSetting(QLatin1String(isOAuthC)).toBool();
|
||||||
if (isOauth) {
|
if (isOauth) {
|
||||||
|
@ -270,8 +311,6 @@ void HttpCredentials::slotReadJobDone(QKeychain::Job *incomingJob)
|
||||||
qCWarning(lcHttpCredentials) << "Strange: User is empty!";
|
qCWarning(lcHttpCredentials) << "Strange: User is empty!";
|
||||||
}
|
}
|
||||||
|
|
||||||
QKeychain::Error error = job->error();
|
|
||||||
|
|
||||||
if (!_refreshToken.isEmpty() && error == NoError) {
|
if (!_refreshToken.isEmpty() && error == NoError) {
|
||||||
refreshAccessToken();
|
refreshAccessToken();
|
||||||
} else if (!_password.isEmpty() && error == NoError) {
|
} else if (!_password.isEmpty() && error == NoError) {
|
||||||
|
@ -290,6 +329,13 @@ void HttpCredentials::slotReadJobDone(QKeychain::Job *incomingJob)
|
||||||
_ready = false;
|
_ready = false;
|
||||||
emit fetched();
|
emit fetched();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If keychain data was read from legacy location, wipe these entries and store new ones
|
||||||
|
if (_keychainMigration && _ready) {
|
||||||
|
persist();
|
||||||
|
deleteOldKeychainEntries();
|
||||||
|
qCWarning(lcHttpCredentials) << "Migrated old keychain entries";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HttpCredentials::refreshAccessToken()
|
bool HttpCredentials::refreshAccessToken()
|
||||||
|
@ -345,7 +391,7 @@ void HttpCredentials::invalidateToken()
|
||||||
// User must be fetched from config file to generate a valid key
|
// User must be fetched from config file to generate a valid key
|
||||||
fetchUser();
|
fetchUser();
|
||||||
|
|
||||||
const QString kck = keychainKey(_account->url().toString(), _user);
|
const QString kck = keychainKey(_account->url().toString(), _user, _account->id());
|
||||||
if (kck.isEmpty()) {
|
if (kck.isEmpty()) {
|
||||||
qCWarning(lcHttpCredentials) << "InvalidateToken: User is empty, bailing out!";
|
qCWarning(lcHttpCredentials) << "InvalidateToken: User is empty, bailing out!";
|
||||||
return;
|
return;
|
||||||
|
@ -407,7 +453,7 @@ void HttpCredentials::persist()
|
||||||
addSettingsToJob(_account, job);
|
addSettingsToJob(_account, job);
|
||||||
job->setInsecureFallback(false);
|
job->setInsecureFallback(false);
|
||||||
connect(job, SIGNAL(finished(QKeychain::Job *)), SLOT(slotWriteClientCertPEMJobDone(QKeychain::Job *)));
|
connect(job, SIGNAL(finished(QKeychain::Job *)), SLOT(slotWriteClientCertPEMJobDone(QKeychain::Job *)));
|
||||||
job->setKey(keychainKey(_account->url().toString(), _user + clientCertificatePEMC));
|
job->setKey(keychainKey(_account->url().toString(), _user + clientCertificatePEMC, _account->id()));
|
||||||
job->setBinaryData(_clientSslCertificate.toPem());
|
job->setBinaryData(_clientSslCertificate.toPem());
|
||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
|
@ -420,7 +466,7 @@ void HttpCredentials::slotWriteClientCertPEMJobDone(Job *incomingJob)
|
||||||
addSettingsToJob(_account, job);
|
addSettingsToJob(_account, job);
|
||||||
job->setInsecureFallback(false);
|
job->setInsecureFallback(false);
|
||||||
connect(job, SIGNAL(finished(QKeychain::Job *)), SLOT(slotWriteClientKeyPEMJobDone(QKeychain::Job *)));
|
connect(job, SIGNAL(finished(QKeychain::Job *)), SLOT(slotWriteClientKeyPEMJobDone(QKeychain::Job *)));
|
||||||
job->setKey(keychainKey(_account->url().toString(), _user + clientKeyPEMC));
|
job->setKey(keychainKey(_account->url().toString(), _user + clientKeyPEMC, _account->id()));
|
||||||
job->setBinaryData(_clientSslKey.toPem());
|
job->setBinaryData(_clientSslKey.toPem());
|
||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
|
@ -432,7 +478,7 @@ void HttpCredentials::slotWriteClientKeyPEMJobDone(Job *incomingJob)
|
||||||
addSettingsToJob(_account, job);
|
addSettingsToJob(_account, job);
|
||||||
job->setInsecureFallback(false);
|
job->setInsecureFallback(false);
|
||||||
connect(job, SIGNAL(finished(QKeychain::Job *)), SLOT(slotWriteJobDone(QKeychain::Job *)));
|
connect(job, SIGNAL(finished(QKeychain::Job *)), SLOT(slotWriteJobDone(QKeychain::Job *)));
|
||||||
job->setKey(keychainKey(_account->url().toString(), _user));
|
job->setKey(keychainKey(_account->url().toString(), _user, _account->id()));
|
||||||
job->setTextData(isUsingOAuth() ? _refreshToken : _password);
|
job->setTextData(isUsingOAuth() ? _refreshToken : _password);
|
||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,6 +119,18 @@ private Q_SLOTS:
|
||||||
void slotWriteJobDone(QKeychain::Job *);
|
void slotWriteJobDone(QKeychain::Job *);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/** Reads data from keychain locations
|
||||||
|
*
|
||||||
|
* Goes through
|
||||||
|
* slotReadClientCertPEMJobDone to
|
||||||
|
* slotReadClientCertPEMJobDone to
|
||||||
|
* slotReadJobDone
|
||||||
|
*/
|
||||||
|
void fetchFromKeychainHelper();
|
||||||
|
|
||||||
|
/// Wipes legacy keychain locations
|
||||||
|
void deleteOldKeychainEntries();
|
||||||
|
|
||||||
QString _user;
|
QString _user;
|
||||||
QString _password; // user's password, or access_token for OAuth
|
QString _password; // user's password, or access_token for OAuth
|
||||||
QString _refreshToken; // OAuth _refreshToken, set if OAuth is used.
|
QString _refreshToken; // OAuth _refreshToken, set if OAuth is used.
|
||||||
|
@ -128,6 +140,7 @@ protected:
|
||||||
bool _ready;
|
bool _ready;
|
||||||
QSslKey _clientSslKey;
|
QSslKey _clientSslKey;
|
||||||
QSslCertificate _clientSslCertificate;
|
QSslCertificate _clientSslCertificate;
|
||||||
|
bool _keychainMigration;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue