closes #94
master
Nicolas Werner 5 years ago
parent 813790e603
commit 7b1fa60cc6
  1. 4
      CMakeLists.txt
  2. 8
      src/ChatPage.cpp
  3. 113
      src/LoginPage.cpp
  4. 11
      src/LoginPage.h
  5. 54
      src/SSOHandler.cpp
  6. 24
      src/SSOHandler.h
  7. 5125
      third_party/cpp-httplib-0.5.12/httplib.h

@ -294,6 +294,7 @@ set(SRC_FILES
src/RegisterPage.cpp src/RegisterPage.cpp
src/RoomInfoListItem.cpp src/RoomInfoListItem.cpp
src/RoomList.cpp src/RoomList.cpp
src/SSOHandler.cpp
src/SideBarActions.cpp src/SideBarActions.cpp
src/Splitter.cpp src/Splitter.cpp
src/TextInputWidget.cpp src/TextInputWidget.cpp
@ -493,6 +494,7 @@ qt5_wrap_cpp(MOC_HEADERS
src/RegisterPage.h src/RegisterPage.h
src/RoomInfoListItem.h src/RoomInfoListItem.h
src/RoomList.h src/RoomList.h
src/SSOHandler.h
src/SideBarActions.h src/SideBarActions.h
src/Splitter.h src/Splitter.h
src/TextInputWidget.h src/TextInputWidget.h
@ -556,7 +558,7 @@ elseif(WIN32)
else() else()
target_link_libraries (nheko PRIVATE Qt5::DBus) target_link_libraries (nheko PRIVATE Qt5::DBus)
endif() endif()
target_include_directories(nheko PRIVATE src includes third_party/blurhash) target_include_directories(nheko PRIVATE src includes third_party/blurhash third_party/cpp-httplib-0.5.12)
target_link_libraries(nheko PRIVATE target_link_libraries(nheko PRIVATE
MatrixClient::MatrixClient MatrixClient::MatrixClient

@ -988,8 +988,12 @@ ChatPage::trySync()
const auto err_code = mtx::errors::to_string(err->matrix_error.errcode); const auto err_code = mtx::errors::to_string(err->matrix_error.errcode);
const int status_code = static_cast<int>(err->status_code); const int status_code = static_cast<int>(err->status_code);
if (http::is_logged_in() && err->matrix_error.errcode == if ((http::is_logged_in() &&
mtx::errors::ErrorCode::M_UNKNOWN_TOKEN) { (err->matrix_error.errcode ==
mtx::errors::ErrorCode::M_UNKNOWN_TOKEN ||
err->matrix_error.errcode ==
mtx::errors::ErrorCode::M_MISSING_TOKEN)) ||
!http::is_logged_in()) {
emit dropToLoginPageCb(msg); emit dropToLoginPageCb(msg);
return; return;
} }

@ -15,28 +15,35 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <QDesktopServices>
#include <QPainter> #include <QPainter>
#include <QStyleOption> #include <QStyleOption>
#include <mtx/identifiers.hpp> #include <mtx/identifiers.hpp>
#include <mtx/requests.hpp>
#include <mtx/responses/login.hpp> #include <mtx/responses/login.hpp>
#include "Config.h" #include "Config.h"
#include "Logging.h" #include "Logging.h"
#include "LoginPage.h" #include "LoginPage.h"
#include "MatrixClient.h" #include "MatrixClient.h"
#include "SSOHandler.h"
#include "ui/FlatButton.h" #include "ui/FlatButton.h"
#include "ui/LoadingIndicator.h" #include "ui/LoadingIndicator.h"
#include "ui/OverlayModal.h" #include "ui/OverlayModal.h"
#include "ui/RaisedButton.h" #include "ui/RaisedButton.h"
#include "ui/TextField.h" #include "ui/TextField.h"
Q_DECLARE_METATYPE(LoginPage::LoginMethod)
using namespace mtx::identifiers; using namespace mtx::identifiers;
LoginPage::LoginPage(QWidget *parent) LoginPage::LoginPage(QWidget *parent)
: QWidget(parent) : QWidget(parent)
, inferredServerAddress_() , inferredServerAddress_()
{ {
qRegisterMetaType<LoginPage::LoginMethod>("LoginPage::LoginMethod");
top_layout_ = new QVBoxLayout(); top_layout_ = new QVBoxLayout();
top_bar_layout_ = new QHBoxLayout(); top_bar_layout_ = new QHBoxLayout();
@ -226,7 +233,8 @@ LoginPage::onMatrixIdEntered()
emit versionErrorCb(tr("Autodiscovery failed. Unknown error when " emit versionErrorCb(tr("Autodiscovery failed. Unknown error when "
"requesting .well-known.")); "requesting .well-known."));
nhlog::net()->error("Autodiscovery failed. Unknown error when " nhlog::net()->error("Autodiscovery failed. Unknown error when "
"requesting .well-known."); "requesting .well-known. {}",
err->status_code);
return; return;
} }
@ -263,7 +271,16 @@ LoginPage::checkHomeserverVersion()
return; return;
} }
emit versionOkCb(); http::client()->get_login(
[this](mtx::responses::LoginFlows flows, mtx::http::RequestErr err) {
if (err || flows.flows.empty())
emit versionOkCb(LoginMethod::Password);
if (flows.flows[0].type == mtx::user_interactive::auth_types::sso)
emit versionOkCb(LoginMethod::SSO);
else
emit versionOkCb(LoginMethod::Password);
});
}); });
} }
@ -294,12 +311,22 @@ LoginPage::versionError(const QString &error)
} }
void void
LoginPage::versionOk() LoginPage::versionOk(LoginMethod loginMethod)
{ {
this->loginMethod = loginMethod;
serverLayout_->removeWidget(spinner_); serverLayout_->removeWidget(spinner_);
matrixidLayout_->removeWidget(spinner_); matrixidLayout_->removeWidget(spinner_);
spinner_->stop(); spinner_->stop();
if (loginMethod == LoginMethod::SSO) {
password_input_->hide();
login_button_->setText(tr("SSO LOGIN"));
} else {
password_input_->show();
login_button_->setText(tr("LOGIN"));
}
if (serverInput_->isVisible()) if (serverInput_->isVisible())
serverInput_->hide(); serverInput_->hide();
} }
@ -317,29 +344,68 @@ LoginPage::onLoginButtonClicked()
return loginError("You have entered an invalid Matrix ID e.g @joe:matrix.org"); return loginError("You have entered an invalid Matrix ID e.g @joe:matrix.org");
} }
if (password_input_->text().isEmpty()) if (loginMethod == LoginMethod::Password) {
return loginError(tr("Empty password")); if (password_input_->text().isEmpty())
return loginError(tr("Empty password"));
http::client()->login(
user.localpart(),
password_input_->text().toStdString(),
deviceName_->text().trimmed().isEmpty() ? initialDeviceName()
: deviceName_->text().toStdString(),
[this](const mtx::responses::Login &res, mtx::http::RequestErr err) {
if (err) {
emit loginError(QString::fromStdString(err->matrix_error.error));
emit errorOccurred();
return;
}
http::client()->login( if (res.well_known) {
user.localpart(), http::client()->set_server(res.well_known->homeserver.base_url);
password_input_->text().toStdString(), nhlog::net()->info("Login requested to user server: " +
deviceName_->text().trimmed().isEmpty() ? initialDeviceName() res.well_known->homeserver.base_url);
: deviceName_->text().toStdString(), }
[this](const mtx::responses::Login &res, mtx::http::RequestErr err) {
if (err) {
emit loginError(QString::fromStdString(err->matrix_error.error));
emit errorOccurred();
return;
}
if (res.well_known) { emit loginOk(res);
http::client()->set_server(res.well_known->homeserver.base_url); });
nhlog::net()->info("Login requested to user server: " + } else {
res.well_known->homeserver.base_url); auto sso = new SSOHandler();
} connect(sso, &SSOHandler::ssoSuccess, this, [this, sso](std::string token) {
mtx::requests::Login req{};
req.token = token;
req.type = mtx::user_interactive::auth_types::token;
req.device_id = deviceName_->text().trimmed().isEmpty()
? initialDeviceName()
: deviceName_->text().toStdString();
http::client()->login(
req, [this](const mtx::responses::Login &res, mtx::http::RequestErr err) {
if (err) {
emit loginError(
QString::fromStdString(err->matrix_error.error));
emit errorOccurred();
return;
}
if (res.well_known) {
http::client()->set_server(
res.well_known->homeserver.base_url);
nhlog::net()->info("Login requested to user server: " +
res.well_known->homeserver.base_url);
}
emit loginOk(res);
});
sso->deleteLater();
});
connect(sso, &SSOHandler::ssoFailed, this, [this, sso]() {
emit loginError(tr("SSO login failed"));
emit errorOccurred();
sso->deleteLater();
});
emit loginOk(res); QDesktopServices::openUrl(
}); QString::fromStdString(http::client()->login_sso_redirect(sso->url())));
}
emit loggingIn(); emit loggingIn();
} }
@ -349,6 +415,7 @@ LoginPage::reset()
{ {
matrixid_input_->clear(); matrixid_input_->clear();
password_input_->clear(); password_input_->clear();
password_input_->show();
serverInput_->clear(); serverInput_->clear();
spinner_->stop(); spinner_->stop();

@ -38,6 +38,12 @@ class LoginPage : public QWidget
Q_OBJECT Q_OBJECT
public: public:
enum class LoginMethod
{
Password,
SSO,
};
LoginPage(QWidget *parent = nullptr); LoginPage(QWidget *parent = nullptr);
void reset(); void reset();
@ -50,7 +56,7 @@ signals:
//! Used to trigger the corresponding slot outside of the main thread. //! Used to trigger the corresponding slot outside of the main thread.
void versionErrorCb(const QString &err); void versionErrorCb(const QString &err);
void loginErrorCb(const QString &err); void loginErrorCb(const QString &err);
void versionOkCb(); void versionOkCb(LoginPage::LoginMethod method);
void loginOk(const mtx::responses::Login &res); void loginOk(const mtx::responses::Login &res);
@ -77,7 +83,7 @@ private slots:
// Callback for errors produced during server probing // Callback for errors produced during server probing
void versionError(const QString &error_message); void versionError(const QString &error_message);
// Callback for successful server probing // Callback for successful server probing
void versionOk(); void versionOk(LoginPage::LoginMethod method);
private: private:
bool isMatrixIdValid(); bool isMatrixIdValid();
@ -123,4 +129,5 @@ private:
TextField *password_input_; TextField *password_input_;
TextField *deviceName_; TextField *deviceName_;
TextField *serverInput_; TextField *serverInput_;
LoginMethod loginMethod = LoginMethod::Password;
}; };

@ -0,0 +1,54 @@
#include "SSOHandler.h"
#include <QTimer>
#include <thread>
#include "Logging.h"
SSOHandler::SSOHandler(QObject *)
{
QTimer::singleShot(120000, this, &SSOHandler::ssoFailed);
using namespace httplib;
svr.set_logger([](const Request &req, const Response &res) {
nhlog::net()->info("req: {}, res: {}", req.path, res.status);
});
svr.Get("/sso", [this](const Request &req, Response &res) {
if (req.has_param("loginToken")) {
auto val = req.get_param_value("loginToken");
res.set_content("SSO success", "text/plain");
emit ssoSuccess(val);
} else {
res.set_content("Missing loginToken for SSO login!", "text/plain");
emit ssoFailed();
}
});
std::thread t([this]() {
this->port = svr.bind_to_any_port("localhost");
svr.listen_after_bind();
});
t.detach();
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
SSOHandler::~SSOHandler()
{
svr.stop();
while (svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
std::string
SSOHandler::url() const
{
return "http://localhost:" + std::to_string(port) + "/sso";
}

@ -0,0 +1,24 @@
#include "httplib.h"
#include <QObject>
#include <string>
class SSOHandler : public QObject
{
Q_OBJECT
public:
SSOHandler(QObject *parent = nullptr);
~SSOHandler();
std::string url() const;
signals:
void ssoSuccess(std::string token);
void ssoFailed();
private:
httplib::Server svr;
int port = 0;
};

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save