diff --git a/x-telepathy-password-auth-operation.cpp b/x-telepathy-password-auth-operation.cpp
index be0ed99..9e8bdcc 100644
--- a/x-telepathy-password-auth-operation.cpp
+++ b/x-telepathy-password-auth-operation.cpp
@@ -1,236 +1,236 @@
/*
* Copyright (C) 2011 Collabora Ltd.
* @author Andre Moreira Magalhaes
* Copyright (C) 2011 David Edmundson
* Copyright (C) 2011 Daniele E. Domenichelli
* Copyright (C) 2014 Martin Klapetek
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "x-telepathy-password-auth-operation.h"
#include "x-telepathy-password-prompt.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
XTelepathyPasswordAuthOperation::XTelepathyPasswordAuthOperation(
const Tp::AccountPtr &account,
int accountStorageId,
Tp::Client::ChannelInterfaceSASLAuthenticationInterface *saslIface,
bool canTryAgain) :
Tp::PendingOperation(account),
m_account(account),
m_saslIface(saslIface),
m_canTryAgain(canTryAgain),
m_canFinish(false),
m_accountStorageId(accountStorageId)
{
connect(m_saslIface,
SIGNAL(SASLStatusChanged(uint,QString,QVariantMap)),
SLOT(onSASLStatusChanged(uint,QString,QVariantMap)));
m_config = KSharedConfig::openConfig(QStringLiteral("kaccounts-ktprc"));
m_lastLoginFailedConfig = m_config->group(QStringLiteral("lastLoginFailed"));
}
XTelepathyPasswordAuthOperation::~XTelepathyPasswordAuthOperation()
{
}
void XTelepathyPasswordAuthOperation::onSASLStatusChanged(uint status, const QString &reason,
const QVariantMap &details)
{
if (status == Tp::SASLStatusNotStarted) {
qDebug() << "Requesting password";
// if we have non-null id AND if the last attempt didn't fail,
// proceed with the credentials receieved from the SSO;
// otherwise prompt the user
if (!m_lastLoginFailedConfig.hasKey(m_account->objectPath())) {
GetCredentialsJob *credentialsJob = new GetCredentialsJob(m_accountStorageId, QStringLiteral("password"), QStringLiteral("password"), this);
connect(credentialsJob, &GetCredentialsJob::finished, [this](KJob *job){
if (job->error()) {
qWarning() << "Credentials job error:" << job->errorText();
qDebug() << "Prompting for password";
promptUser();
} else {
m_canFinish = true;
QByteArray secret = qobject_cast(job)->credentialsData().value("Secret").toByteArray();
m_saslIface->StartMechanismWithData(QLatin1String("X-TELEPATHY-PASSWORD"), secret);
}
});
credentialsJob->start();
} else {
promptUser();
}
} else if (status == Tp::SASLStatusServerSucceeded) {
qDebug() << "Authentication handshake";
m_saslIface->AcceptSASL();
} else if (status == Tp::SASLStatusSucceeded) {
qDebug() << "Authentication succeeded";
if (m_lastLoginFailedConfig.hasKey(m_account->objectPath())) {
m_lastLoginFailedConfig.deleteEntry(m_account->objectPath());
}
if (m_canFinish) {
// if the credentials storage has finished, just finish
setFinished();
} else {
// ...otherwise set this to true and it will finish
// when credentials are finished
m_canFinish = true;
}
} else if (status == Tp::SASLStatusInProgress) {
qDebug() << "Authenticating...";
} else if (status == Tp::SASLStatusServerFailed) {
qDebug() << "Error authenticating - reason:" << reason << "- details:" << details;
if (m_canTryAgain) {
qDebug() << "Retrying...";
promptUser();
} else {
qWarning() << "Authentication failed and cannot try again";
m_lastLoginFailedConfig.writeEntry(m_account->objectPath(), "1");
// We cannot try again, but we can request again to set the account
// online. A new channel will be created, but since we set the
// lastLoginFailed entry, next time we will prompt for password
// and the user won't see any difference except for an
// authentication error notification
Tp::Presence requestedPresence = m_account->requestedPresence();
m_account->setRequestedPresence(requestedPresence);
QString errorMessage = details[QLatin1String("server-message")].toString();
setFinishedWithError(reason, errorMessage.isEmpty() ? i18n("Authentication error") : errorMessage);
}
}
}
void XTelepathyPasswordAuthOperation::promptUser()
{
m_dialog = new XTelepathyPasswordPrompt(m_account);
connect(m_dialog.data(),
SIGNAL(finished(int)),
SLOT(onDialogFinished(int)));
m_dialog.data()->show();
}
void XTelepathyPasswordAuthOperation::onDialogFinished(int result)
{
switch (result) {
case QDialog::Rejected:
qDebug() << "Authentication cancelled";
m_saslIface->AbortSASL(Tp::SASLAbortReasonUserAbort, i18n("User cancelled auth"));
setFinished();
if (!m_dialog.isNull()) {
m_dialog.data()->deleteLater();
}
return;
case QDialog::Accepted:
// save password in kwallet if necessary...
if (!m_dialog.isNull()) {
if (m_dialog.data()->savePassword()) {
qDebug() << "Saving password in SSO";
m_canFinish = false;
storeCredentials(m_dialog.data()->password());
} else {
m_canFinish = true;
}
m_dialog.data()->deleteLater();
m_saslIface->StartMechanismWithData(QLatin1String("X-TELEPATHY-PASSWORD"), m_dialog.data()->password().toUtf8());
}
}
}
void XTelepathyPasswordAuthOperation::storeCredentials(const QString &secret)
{
QString username = m_account->parameters().value(QStringLiteral("account")).toString();
Accounts::Manager *manager = KAccounts::accountsManager();
Accounts::Account *account = manager->account(m_accountStorageId);
- SignOn::Identity *identity;
+ SignOn::Identity *identity = nullptr;
if (account) {
Accounts::AccountService *service = new Accounts::AccountService(account, manager->service(QString()), this);
Accounts::AuthData authData = service->authData();
identity = SignOn::Identity::existingIdentity(authData.credentialsId(), this);
} else {
// there's no valid KAccounts account, so let's try creating one
QString providerName = QStringLiteral("ktp-");
providerName.append(m_account->serviceName());
qDebug() << "Creating account with providerName" << providerName;
account = manager->createAccount(providerName);
account->setDisplayName(m_account->displayName());
account->setValue("uid", m_account->objectPath());
account->setValue("username", username);
account->setValue(QStringLiteral("auth/mechanism"), QStringLiteral("password"));
account->setValue(QStringLiteral("auth/method"), QStringLiteral("password"));
account->setEnabled(true);
Accounts::ServiceList services = account->services();
Q_FOREACH(const Accounts::Service &service, services) {
account->selectService(service);
account->setEnabled(true);
}
}
SignOn::IdentityInfo info;
info.setUserName(username);
info.setSecret(secret);
info.setCaption(username);
info.setAccessControlList(QStringList(QLatin1String("*")));
info.setType(SignOn::IdentityInfo::Application);
if (!identity) {
// we don't have a valid SignOn::Identity, let's create new one
identity = SignOn::Identity::newIdentity(info, this);
}
identity->storeCredentials(info);
connect(identity, &SignOn::Identity::credentialsStored, [=](const quint32 id) {
account->setCredentialsId(id);
account->sync();
connect(account, &Accounts::Account::synced, [=]() {
m_accountStorageId = account->id();
qDebug() << "Account credentials synchronisation finished";
if (m_canFinish) {
// if the sasl channel has already finished, set finished
setFinished();
} else {
// ...otherwise set this to true and when the auth succeeds,
// it will finish then
m_canFinish = true;
}
});
});
}