aboutsummaryrefslogtreecommitdiff
path: root/src/saved_auth.cpp
diff options
context:
space:
mode:
authorOsmium Sorcerer <os@sof.beauty>2026-03-22 18:55:26 +0000
committerOsmium Sorcerer <os@sof.beauty>2026-03-29 22:22:25 +0000
commita124f46861d549ddc13485536962e34d80de939a (patch)
tree3553849323aa70fef1e198f3476a2abcc7adfe39 /src/saved_auth.cpp
parentb1ad938c37f4e175e5509f727d1033b074b134d4 (diff)
Add authentication dialog
Introduce start_auth_flow, a function invoked by typing `/auth username` in OOC. It sends an public-key authentication request to the server, starting the entire flow. The flow invoves two dialogs: to select the key, and to enter the passphrase to unlock the key. For convenience, each successful unlock also remembers the key for that username on the server, storing this in `saved_auth.json` (I chose JSON because I wanted it to stay human-editable; INI would be better, but it suffers from bad platform quirks in Qt).
Diffstat (limited to 'src/saved_auth.cpp')
-rw-r--r--src/saved_auth.cpp124
1 files changed, 124 insertions, 0 deletions
diff --git a/src/saved_auth.cpp b/src/saved_auth.cpp
new file mode 100644
index 0000000..aa1c696
--- /dev/null
+++ b/src/saved_auth.cpp
@@ -0,0 +1,124 @@
+#include "saved_auth.h"
+
+#include <QDir>
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+
+#include "file_functions.h"
+
+// I wish I could use INI for settings to let users edit it manually if they
+// wanted to, like ~/.ssh/config. Unfortunately, due to adherence to idiotic
+// conventions, INI keys are case-insensitive on Windows, while the usernames
+// aren't. Rather than defining my own stupid encoding to work around this
+// issue (thus ruining convenience I originally strived for), I'll ditch INI
+// altogether. I could use CBOR for consistency, but I'm using JSON because
+// it's supposed to be manually editable, and I don't want to complicate
+// things by creating a new settings format.
+bool SavedAuth::load()
+{
+ QFile saved_auth_file(QDir(get_app_path()).filePath("saved_auth.json"));
+ if (!saved_auth_file.open(QIODevice::ReadOnly))
+ {
+ return false;
+ }
+ QByteArray raw_data = saved_auth_file.readAll();
+ saved_auth_file.close();
+
+ QJsonParseError err;
+ QJsonDocument doc = QJsonDocument::fromJson(raw_data, &err);
+ if (err.error != QJsonParseError::NoError || !doc.isObject())
+ {
+ return false;
+ }
+
+ m_table.clear();
+ QJsonObject root = doc.object();
+ for (auto host_it = root.begin(); host_it != root.end(); ++host_it)
+ {
+ if (!host_it.value().isObject())
+ {
+ continue;
+ }
+
+ QByteArray host = host_it.key().toUtf8();
+ QJsonObject users = host_it.value().toObject();
+
+ for (auto user_it = users.begin(); user_it != users.end(); ++user_it)
+ {
+ if (!user_it.value().isString())
+ {
+ continue;
+ }
+
+ QByteArray user = user_it.key().toUtf8();
+ QByteArray saved_key = QByteArray::fromBase64(user_it.value().toString().toUtf8());
+
+ m_table[host + '\0' + user] = saved_key;
+ }
+ }
+
+ return true;
+}
+
+QByteArray SavedAuth::lookup(QByteArrayView host, QByteArrayView user) const
+{
+ return m_table.value(flatten_key(host, user));
+}
+
+void SavedAuth::insert(QByteArrayView host, QByteArrayView user, QByteArrayView saved_key)
+{
+ m_table[flatten_key(host, user)] = QByteArray(saved_key);
+ save();
+}
+
+void SavedAuth::remove(QByteArrayView host, QByteArrayView user)
+{
+ m_table.remove(flatten_key(host, user));
+ save();
+}
+
+bool SavedAuth::save() const
+{
+ QJsonObject root;
+
+ for (auto it = m_table.begin(); it != m_table.end(); ++it)
+ {
+ const QByteArray &flat = it.key();
+ const QByteArray &key_id = it.value();
+
+ int sep = flat.indexOf('\0');
+ if (sep < 0)
+ {
+ continue;
+ }
+
+ QString host = QString::fromUtf8(flat.left(sep));
+ QString user = QString::fromUtf8(flat.mid(sep + 1));
+
+ QJsonObject host_obj = root.value(host).toObject();
+
+ host_obj[user] = QString::fromUtf8(key_id.toBase64(QByteArray::OmitTrailingEquals));
+
+ root[host] = host_obj;
+ }
+
+ QJsonDocument doc(root);
+
+ QFile f(QDir(get_app_path()).filePath("saved_auth.json"));
+ if (!f.open(QIODevice::WriteOnly))
+ return false;
+
+ f.write(doc.toJson(QJsonDocument::Indented));
+ return true;
+}
+
+QByteArray SavedAuth::flatten_key(QByteArrayView host, QByteArrayView user) const
+{
+ QByteArray key;
+ key.reserve(host.size() + 1 + user.size());
+ key.append(host);
+ key.append('\0');
+ key.append(user);
+ return key;
+}