mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
added insecure credential store
This commit is contained in:
parent
eadf5355ee
commit
7c6f744e73
8 changed files with 118 additions and 74 deletions
|
@ -179,7 +179,6 @@ SOURCES += \
|
||||||
src/widgets/AttachedWindow.cpp \
|
src/widgets/AttachedWindow.cpp \
|
||||||
src/widgets/dialogs/EmotePopup.cpp \
|
src/widgets/dialogs/EmotePopup.cpp \
|
||||||
src/widgets/dialogs/IrcConnectionEditor.cpp \
|
src/widgets/dialogs/IrcConnectionEditor.cpp \
|
||||||
src/widgets/dialogs/IrcConnectionPopup.cpp \
|
|
||||||
src/widgets/dialogs/LastRunCrashDialog.cpp \
|
src/widgets/dialogs/LastRunCrashDialog.cpp \
|
||||||
src/widgets/dialogs/LoginDialog.cpp \
|
src/widgets/dialogs/LoginDialog.cpp \
|
||||||
src/widgets/dialogs/LogsPopup.cpp \
|
src/widgets/dialogs/LogsPopup.cpp \
|
||||||
|
@ -360,7 +359,6 @@ HEADERS += \
|
||||||
src/widgets/AttachedWindow.hpp \
|
src/widgets/AttachedWindow.hpp \
|
||||||
src/widgets/dialogs/EmotePopup.hpp \
|
src/widgets/dialogs/EmotePopup.hpp \
|
||||||
src/widgets/dialogs/IrcConnectionEditor.hpp \
|
src/widgets/dialogs/IrcConnectionEditor.hpp \
|
||||||
src/widgets/dialogs/IrcConnectionPopup.hpp \
|
|
||||||
src/widgets/dialogs/LastRunCrashDialog.hpp \
|
src/widgets/dialogs/LastRunCrashDialog.hpp \
|
||||||
src/widgets/dialogs/LoginDialog.hpp \
|
src/widgets/dialogs/LoginDialog.hpp \
|
||||||
src/widgets/dialogs/LogsPopup.hpp \
|
src/widgets/dialogs/LogsPopup.hpp \
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
#include "keychain.h"
|
#include "keychain.h"
|
||||||
#include "singletons/Paths.hpp"
|
#include "singletons/Paths.hpp"
|
||||||
|
#include "singletons/Settings.hpp"
|
||||||
|
#include "util/CombinePath.hpp"
|
||||||
|
|
||||||
|
#include <QSaveFile>
|
||||||
|
|
||||||
#define FORMAT_NAME \
|
#define FORMAT_NAME \
|
||||||
([&] { \
|
([&] { \
|
||||||
|
@ -11,6 +15,65 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
bool useKeyring()
|
||||||
|
{
|
||||||
|
if (getPaths()->isPortable())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
return getSettings()->useKeyring;
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insecure storage:
|
||||||
|
QString insecurePath()
|
||||||
|
{
|
||||||
|
return combinePath(getPaths()->settingsDirectory, "credentials.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument loadInsecure()
|
||||||
|
{
|
||||||
|
QFile file(insecurePath());
|
||||||
|
file.open(QIODevice::ReadOnly);
|
||||||
|
return QJsonDocument::fromJson(file.readAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
void storeInsecure(const QJsonDocument &doc)
|
||||||
|
{
|
||||||
|
QSaveFile file(insecurePath());
|
||||||
|
file.open(QIODevice::WriteOnly);
|
||||||
|
file.write(doc.toJson());
|
||||||
|
file.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument &insecureInstance()
|
||||||
|
{
|
||||||
|
static auto store = loadInsecure();
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
void queueInsecureSave()
|
||||||
|
{
|
||||||
|
static bool isQueued = false;
|
||||||
|
|
||||||
|
if (!isQueued)
|
||||||
|
{
|
||||||
|
isQueued = true;
|
||||||
|
QTimer::singleShot(200, qApp, [] {
|
||||||
|
storeInsecure(insecureInstance());
|
||||||
|
isQueued = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
Credentials &Credentials::getInstance()
|
Credentials &Credentials::getInstance()
|
||||||
{
|
{
|
||||||
static Credentials creds;
|
static Credentials creds;
|
||||||
|
@ -24,15 +87,11 @@ Credentials::Credentials()
|
||||||
QString Credentials::get(const QString &provider, const QString &name_,
|
QString Credentials::get(const QString &provider, const QString &name_,
|
||||||
std::function<void(QString)> &&onLoaded)
|
std::function<void(QString)> &&onLoaded)
|
||||||
{
|
{
|
||||||
|
assertInGuiThread();
|
||||||
|
|
||||||
auto name = FORMAT_NAME;
|
auto name = FORMAT_NAME;
|
||||||
|
|
||||||
if (getPaths()->isPortable())
|
if (useKeyring())
|
||||||
{
|
|
||||||
assert(false);
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
auto job = new QKeychain::ReadPasswordJob("chatterino");
|
auto job = new QKeychain::ReadPasswordJob("chatterino");
|
||||||
job->setAutoDelete(true);
|
job->setAutoDelete(true);
|
||||||
|
@ -45,44 +104,69 @@ QString Credentials::get(const QString &provider, const QString &name_,
|
||||||
|
|
||||||
return job->textData();
|
return job->textData();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto &instance = insecureInstance();
|
||||||
|
|
||||||
|
return instance.object().find(name).value().toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Credentials::set(const QString &provider, const QString &name_,
|
void Credentials::set(const QString &provider, const QString &name_,
|
||||||
const QString &credential)
|
const QString &credential)
|
||||||
{
|
{
|
||||||
|
assertInGuiThread();
|
||||||
|
|
||||||
|
/// On linux, we try to use a keychain but show a message to disable it when it fails.
|
||||||
|
/// XXX: add said message
|
||||||
|
|
||||||
auto name = FORMAT_NAME;
|
auto name = FORMAT_NAME;
|
||||||
|
|
||||||
if (getPaths()->isPortable())
|
if (useKeyring())
|
||||||
{
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
auto job = new QKeychain::WritePasswordJob("chatterino");
|
auto job = new QKeychain::WritePasswordJob("chatterino");
|
||||||
job->setAutoDelete(true);
|
job->setAutoDelete(true);
|
||||||
job->setKey(name);
|
job->setKey(name);
|
||||||
job->setTextData(credential);
|
job->setTextData(credential);
|
||||||
QObject::connect(job, &QKeychain::Job::finished, qApp, [](auto) {});
|
//QObject::connect(job, &QKeychain::Job::finished, qApp, [](auto) {});
|
||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto &instance = insecureInstance();
|
||||||
|
|
||||||
|
instance.object()[name] = credential;
|
||||||
|
|
||||||
|
queueInsecureSave();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Credentials::erase(const QString &provider, const QString &name_)
|
void Credentials::erase(const QString &provider, const QString &name_)
|
||||||
{
|
{
|
||||||
|
assertInGuiThread();
|
||||||
|
|
||||||
auto name = FORMAT_NAME;
|
auto name = FORMAT_NAME;
|
||||||
|
|
||||||
if (getPaths()->isPortable())
|
if (useKeyring())
|
||||||
{
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
auto job = new QKeychain::DeletePasswordJob("chatterino");
|
auto job = new QKeychain::DeletePasswordJob("chatterino");
|
||||||
job->setAutoDelete(true);
|
job->setAutoDelete(true);
|
||||||
job->setKey(name);
|
job->setKey(name);
|
||||||
QObject::connect(job, &QKeychain::Job::finished, qApp, [](auto) {});
|
//QObject::connect(job, &QKeychain::Job::finished, qApp, [](auto) {});
|
||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto &instance = insecureInstance();
|
||||||
|
|
||||||
|
if (auto it = instance.object().find(name);
|
||||||
|
it != instance.object().end())
|
||||||
|
{
|
||||||
|
instance.object().erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
queueInsecureSave();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -199,6 +199,10 @@ public:
|
||||||
|
|
||||||
/// Misc
|
/// Misc
|
||||||
BoolSetting betaUpdates = {"/misc/beta", false};
|
BoolSetting betaUpdates = {"/misc/beta", false};
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
BoolSetting useKeyring = {"/misc/useKeyring", true};
|
||||||
|
#endif
|
||||||
|
|
||||||
IntSetting startUpNotification = {"/misc/startUpNotification", 0};
|
IntSetting startUpNotification = {"/misc/startUpNotification", 0};
|
||||||
QStringSetting currentVersion = {"/misc/currentVersion", ""};
|
QStringSetting currentVersion = {"/misc/currentVersion", ""};
|
||||||
BoolSetting loadTwitchMessageHistoryOnConnect = {
|
BoolSetting loadTwitchMessageHistoryOnConnect = {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "widgets/Window.hpp"
|
#include "widgets/Window.hpp"
|
||||||
|
|
||||||
#include "Application.hpp"
|
#include "Application.hpp"
|
||||||
|
#include "common/Credentials.hpp"
|
||||||
#include "common/Version.hpp"
|
#include "common/Version.hpp"
|
||||||
#include "controllers/accounts/AccountController.hpp"
|
#include "controllers/accounts/AccountController.hpp"
|
||||||
#include "providers/twitch/TwitchServer.hpp"
|
#include "providers/twitch/TwitchServer.hpp"
|
||||||
|
@ -103,7 +104,7 @@ bool Window::event(QEvent *event)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:;
|
default:;
|
||||||
};
|
}
|
||||||
|
|
||||||
return BaseWindow::event(event);
|
return BaseWindow::event(event);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
#include "IrcConnectionPopup.hpp"
|
|
||||||
|
|
||||||
#include "providers/irc/Irc2.hpp"
|
|
||||||
#include "util/LayoutHelper.hpp"
|
|
||||||
#include "widgets/helper/EditableModelView.hpp"
|
|
||||||
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QTableView>
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
IrcConnectionPopup::IrcConnectionPopup(QWidget *parent)
|
|
||||||
: BaseWindow(parent, BaseWindow::Flags::EnableCustomFrame)
|
|
||||||
{
|
|
||||||
this->setWindowTitle("Edit Irc Connections");
|
|
||||||
|
|
||||||
// view
|
|
||||||
auto view =
|
|
||||||
new EditableModelView(Irc::getInstance().newConnectionModel(this));
|
|
||||||
|
|
||||||
view->setTitles({"host", "port", "ssl", "user", "nick", "password"});
|
|
||||||
view->getTableView()->horizontalHeader()->resizeSection(0, 140);
|
|
||||||
view->getTableView()->horizontalHeader()->resizeSection(1, 30);
|
|
||||||
view->getTableView()->horizontalHeader()->resizeSection(2, 30);
|
|
||||||
|
|
||||||
this->setScaleIndependantSize(800, 500);
|
|
||||||
|
|
||||||
view->addButtonPressed.connect([] {
|
|
||||||
auto unique = IrcServerData{};
|
|
||||||
unique.id = Irc::getInstance().uniqueId();
|
|
||||||
Irc::getInstance().connections.appendItem(unique);
|
|
||||||
});
|
|
||||||
|
|
||||||
// init layout
|
|
||||||
this->getLayoutContainer()->setLayout(makeLayout<QHBoxLayout>({view}));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace chatterino
|
|
|
@ -1,13 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "widgets/BaseWindow.hpp"
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
class IrcConnectionPopup : public BaseWindow
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
IrcConnectionPopup(QWidget *parent);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace chatterino
|
|
|
@ -6,7 +6,6 @@
|
||||||
#include "util/LayoutCreator.hpp"
|
#include "util/LayoutCreator.hpp"
|
||||||
#include "widgets/Notebook.hpp"
|
#include "widgets/Notebook.hpp"
|
||||||
#include "widgets/dialogs/IrcConnectionEditor.hpp"
|
#include "widgets/dialogs/IrcConnectionEditor.hpp"
|
||||||
#include "widgets/dialogs/IrcConnectionPopup.hpp"
|
|
||||||
#include "widgets/helper/NotebookTab.hpp"
|
#include "widgets/helper/NotebookTab.hpp"
|
||||||
|
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
|
|
|
@ -377,6 +377,15 @@ void GeneralPage::initLayout(SettingsLayout &layout)
|
||||||
|
|
||||||
layout.addTitle("Miscellaneous");
|
layout.addTitle("Miscellaneous");
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
if (!getPaths()->isPortable())
|
||||||
|
{
|
||||||
|
layout.addCheckbox(
|
||||||
|
"Use libsecret/KWallet/Gnome keychain to secure passwords",
|
||||||
|
s.useKeyring);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
layout.addCheckbox("Show moderation messages", s.hideModerationActions,
|
layout.addCheckbox("Show moderation messages", s.hideModerationActions,
|
||||||
true);
|
true);
|
||||||
layout.addCheckbox("Random username color for users who never set a color",
|
layout.addCheckbox("Random username color for users who never set a color",
|
||||||
|
|
Loading…
Reference in a new issue