summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorKonstantinos Sideris <sideris.konstantin@gmail.com>2018-03-12 22:23:26 +0200
committerKonstantinos Sideris <sideris.konstantin@gmail.com>2018-03-12 22:23:26 +0200
commit4659d0efc274f2a955b203ca5b00cf1dfc26d5fc (patch)
treede791031bf883a20932c878cd7a9a5d240dc3a49 /src
parentUpdate room name & avatar on new rooms (diff)
downloadnheko-4659d0efc274f2a955b203ca5b00cf1dfc26d5fc.tar.xz
Implement user registration with reCAPTCHA
fixes #264
Diffstat (limited to 'src')
-rw-r--r--src/LoginPage.cc2
-rw-r--r--src/MainWindow.cc12
-rw-r--r--src/MatrixClient.cc65
-rw-r--r--src/Register.cc53
-rw-r--r--src/RegisterPage.cc30
-rw-r--r--src/dialogs/ReCaptcha.cpp75
6 files changed, 158 insertions, 79 deletions
diff --git a/src/LoginPage.cc b/src/LoginPage.cc

index 9a048a75..b8207226 100644 --- a/src/LoginPage.cc +++ b/src/LoginPage.cc
@@ -144,7 +144,7 @@ LoginPage::LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent) connect(password_input_, SIGNAL(returnPressed()), login_button_, SLOT(click())); connect(serverInput_, SIGNAL(returnPressed()), login_button_, SLOT(click())); connect(client_.data(), SIGNAL(loginError(QString)), this, SLOT(loginError(QString))); - connect(client_.data(), SIGNAL(loginError(QString)), this, SIGNAL(errorOccured())); + connect(client_.data(), SIGNAL(loginError(QString)), this, SIGNAL(errorOccurred())); connect(matrixid_input_, SIGNAL(editingFinished()), this, SLOT(onMatrixIdEntered())); connect(client_.data(), SIGNAL(versionError(QString)), this, SLOT(versionError(QString))); connect(client_.data(), SIGNAL(versionSuccess()), this, SLOT(versionSuccess())); diff --git a/src/MainWindow.cc b/src/MainWindow.cc
index 4bdd7819..5d5cb598 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc
@@ -85,7 +85,12 @@ MainWindow::MainWindow(QWidget *parent) connect(login_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage())); connect(login_page_, &LoginPage::loggingIn, this, &MainWindow::showOverlayProgressBar); connect( - login_page_, &LoginPage::errorOccured, this, [this]() { removeOverlayProgressBar(); }); + register_page_, &RegisterPage::registering, this, &MainWindow::showOverlayProgressBar); + connect( + login_page_, &LoginPage::errorOccurred, this, [this]() { removeOverlayProgressBar(); }); + connect(register_page_, &RegisterPage::errorOccurred, this, [this]() { + removeOverlayProgressBar(); + }); connect(register_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage())); connect(chat_page_, SIGNAL(close()), this, SLOT(showWelcomePage())); @@ -120,6 +125,11 @@ MainWindow::MainWindow(QWidget *parent) this, SLOT(showChatPage(QString, QString, QString))); + connect(client_.data(), + SIGNAL(registerSuccess(QString, QString, QString)), + this, + SLOT(showChatPage(QString, QString, QString))); + QShortcut *quitShortcut = new QShortcut(QKeySequence::Quit, this); connect(quitShortcut, &QShortcut::activated, this, QApplication::quit); diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc
index f4d2e66a..4a4fc67c 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc
@@ -27,9 +27,10 @@ #include <QPixmap> #include <QSettings> #include <QUrlQuery> +#include <mtx/errors.hpp> +#include "Deserializable.h" #include "MatrixClient.h" -#include "Register.h" MatrixClient::MatrixClient(QString server, QObject *parent) : QNetworkAccessManager(parent) @@ -193,50 +194,66 @@ MatrixClient::logout() noexcept } void -MatrixClient::registerUser(const QString &user, const QString &pass, const QString &server) noexcept +MatrixClient::registerUser(const QString &user, + const QString &pass, + const QString &server, + const QString &session) noexcept { setServer(server); - QUrlQuery query; - query.addQueryItem("kind", "user"); - QUrl endpoint(server_); endpoint.setPath(clientApiUrl_ + "/register"); - endpoint.setQuery(query); QNetworkRequest request(QString(endpoint.toEncoded())); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - RegisterRequest body(user, pass); - auto reply = post(request, body.serialize()); + QJsonObject body{{"username", user}, {"password", pass}}; - connect(reply, &QNetworkReply::finished, this, [this, reply]() { + // We trying to register using the response from the recaptcha. + if (!session.isEmpty()) + body = QJsonObject{ + {"username", user}, + {"password", pass}, + {"auth", QJsonObject{{"type", "m.login.recaptcha"}, {"session", session}}}}; + + auto reply = post(request, QJsonDocument(body).toJson(QJsonDocument::Compact)); + + connect(reply, &QNetworkReply::finished, this, [this, reply, user, pass, server]() { reply->deleteLater(); int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); auto data = reply->readAll(); - auto json = QJsonDocument::fromJson(data); - if (status == 0 || status >= 400) { - if (json.isObject() && json.object().contains("error")) - emit registerError(json.object().value("error").toString()); - else - emit registerError(reply->errorString()); + // Try to parse a regular register response. + try { + mtx::responses::Register res = nlohmann::json::parse(data); + emit registerSuccess(QString::fromStdString(res.user_id.toString()), + QString::fromStdString(res.user_id.hostname()), + QString::fromStdString(res.access_token)); + } catch (const std::exception &e) { + qWarning() << "Register" << e.what(); + } + // Check if the server requires a registration flow. + try { + mtx::responses::RegistrationFlows res = nlohmann::json::parse(data); + emit registrationFlow( + user, pass, server, QString::fromStdString(res.session)); return; + } catch (const std::exception &) { } - RegisterResponse response; + // We encountered an unknown error. + if (status == 0 || status >= 400) { + try { + mtx::errors::Error res = nlohmann::json::parse(data); + emit registerError(QString::fromStdString(res.error)); + return; + } catch (const std::exception &) { + } - try { - response.deserialize(json); - emit registerSuccess(response.getUserId(), - response.getHomeServer(), - response.getAccessToken()); - } catch (DeserializationException &e) { - qWarning() << "Register" << e.what(); - emit registerError("Received malformed response."); + emit registerError(reply->errorString()); } }); } diff --git a/src/Register.cc b/src/Register.cc deleted file mode 100644
index d63f9229..00000000 --- a/src/Register.cc +++ /dev/null
@@ -1,53 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "Register.h" - -RegisterRequest::RegisterRequest(const QString &username, const QString &password) - : user_(username) - , password_(password) -{} - -QByteArray -RegisterRequest::serialize() noexcept -{ - QJsonObject body{{"username", user_}, {"password", password_}}; - - return QJsonDocument(body).toJson(QJsonDocument::Compact); -} - -void -RegisterResponse::deserialize(const QJsonDocument &data) -{ - if (!data.isObject()) - throw DeserializationException("Response is not a JSON object"); - - QJsonObject object = data.object(); - - if (!object.contains("access_token")) - throw DeserializationException("Missing access_token param"); - - if (!object.contains("home_server")) - throw DeserializationException("Missing home_server param"); - - if (!object.contains("user_id")) - throw DeserializationException("Missing user_id param"); - - access_token_ = object.value("access_token").toString(); - home_server_ = object.value("home_server").toString(); - user_id_ = object.value("user_id").toString(); -} diff --git a/src/RegisterPage.cc b/src/RegisterPage.cc
index 044d2fcf..c0fa11a6 100644 --- a/src/RegisterPage.cc +++ b/src/RegisterPage.cc
@@ -16,14 +16,18 @@ */ #include <QStyleOption> +#include <QTimer> #include "Config.h" #include "FlatButton.h" +#include "MainWindow.h" #include "MatrixClient.h" #include "RaisedButton.h" #include "RegisterPage.h" #include "TextField.h" +#include "dialogs/ReCaptcha.hpp" + RegisterPage::RegisterPage(QSharedPointer<MatrixClient> client, QWidget *parent) : QWidget(parent) , client_(client) @@ -126,6 +130,30 @@ RegisterPage::RegisterPage(QSharedPointer<MatrixClient> client, QWidget *parent) SIGNAL(registerError(const QString &)), this, SLOT(registerError(const QString &))); + connect(client_.data(), + &MatrixClient::registrationFlow, + this, + [this](const QString &user, + const QString &pass, + const QString &server, + const QString &session) { + emit errorOccurred(); + + if (!captchaDialog_) { + captchaDialog_ = + std::make_shared<dialogs::ReCaptcha>(server, session, this); + connect(captchaDialog_.get(), + &dialogs::ReCaptcha::closing, + this, + [this, user, pass, server, session]() { + captchaDialog_->close(); + emit registering(); + client_->registerUser(user, pass, server, session); + }); + } + + QTimer::singleShot(1000, this, [this]() { captchaDialog_->show(); }); + }); setLayout(top_layout_); } @@ -139,6 +167,7 @@ RegisterPage::onBackButtonClicked() void RegisterPage::registerError(const QString &msg) { + emit errorOccurred(); error_label_->setText(msg); } @@ -161,6 +190,7 @@ RegisterPage::onRegisterButtonClicked() QString server = server_input_->text(); client_->registerUser(username, password, server); + emit registering(); } } diff --git a/src/dialogs/ReCaptcha.cpp b/src/dialogs/ReCaptcha.cpp new file mode 100644
index 00000000..eb69a6a2 --- /dev/null +++ b/src/dialogs/ReCaptcha.cpp
@@ -0,0 +1,75 @@ +#include <QDesktopServices> +#include <QLabel> +#include <QPaintEvent> +#include <QStyleOption> +#include <QVBoxLayout> + +#include "Config.h" +#include "FlatButton.h" +#include "RaisedButton.h" +#include "Theme.h" + +#include "dialogs/ReCaptcha.hpp" + +using namespace dialogs; + +ReCaptcha::ReCaptcha(const QString &server, const QString &session, QWidget *parent) + : QWidget(parent) +{ + setAutoFillBackground(true); + setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); + setWindowModality(Qt::WindowModal); + + auto layout = new QVBoxLayout(this); + layout->setSpacing(30); + layout->setMargin(20); + + auto buttonLayout = new QHBoxLayout(); + buttonLayout->setSpacing(8); + buttonLayout->setMargin(0); + + openCaptchaBtn_ = new FlatButton("OPEN reCAPTCHA", this); + openCaptchaBtn_->setFontSize(conf::btn::fontSize); + + confirmBtn_ = new RaisedButton(tr("CONFIRM"), this); + confirmBtn_->setFontSize(conf::btn::fontSize); + + cancelBtn_ = new RaisedButton(tr("CANCEL"), this); + cancelBtn_->setFontSize(conf::btn::fontSize); + + buttonLayout->addStretch(1); + buttonLayout->addWidget(openCaptchaBtn_); + buttonLayout->addWidget(confirmBtn_); + buttonLayout->addWidget(cancelBtn_); + + QFont font; + font.setPixelSize(conf::headerFontSize); + + auto label = new QLabel(tr("Solve the reCAPTCHA and press the confirm button"), this); + label->setFont(font); + + layout->addWidget(label); + layout->addLayout(buttonLayout); + + connect(openCaptchaBtn_, &QPushButton::clicked, [server, session, this]() { + const auto url = + QString( + "https://%1/_matrix/client/r0/auth/m.login.recaptcha/fallback/web?session=%2") + .arg(server) + .arg(session); + + QDesktopServices::openUrl(url); + }); + + connect(confirmBtn_, &QPushButton::clicked, this, &dialogs::ReCaptcha::closing); + connect(cancelBtn_, &QPushButton::clicked, this, &dialogs::ReCaptcha::close); +} + +void +ReCaptcha::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +}