summaryrefslogtreecommitdiff
path: root/src/database
diff options
context:
space:
mode:
authorBen Deutsch <ben@bendeutsch.de>2018-08-05 13:13:38 +0200
committerLoïc Blot <nerzhul@users.noreply.github.com>2018-08-05 13:13:38 +0200
commit153fb211ac2342907eb766a79c1f41824f981ab5 (patch)
tree58a927bbf9a7d3d3811df6a703de02362b6474fb /src/database
parent18368824958139f1428d534082852d778982b4c9 (diff)
downloadminetest-153fb211ac2342907eb766a79c1f41824f981ab5.tar.gz
minetest-153fb211ac2342907eb766a79c1f41824f981ab5.tar.bz2
minetest-153fb211ac2342907eb766a79c1f41824f981ab5.zip
Replace auth.txt with SQLite auth database (#7279)
* Replace auth.txt with SQLite auth database
Diffstat (limited to 'src/database')
-rw-r--r--src/database/database-files.cpp103
-rw-r--r--src/database/database-files.h21
-rw-r--r--src/database/database-sqlite3.cpp167
-rw-r--r--src/database/database-sqlite3.h41
-rw-r--r--src/database/database.h23
5 files changed, 355 insertions, 0 deletions
diff --git a/src/database/database-files.cpp b/src/database/database-files.cpp
index 70de8c8d2..64eca394e 100644
--- a/src/database/database-files.cpp
+++ b/src/database/database-files.cpp
@@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h"
#include "porting.h"
#include "filesys.h"
+#include "util/string.h"
// !!! WARNING !!!
// This backend is intended to be used on Minetest 0.4.16 only for the transition backend
@@ -177,3 +178,105 @@ void PlayerDatabaseFiles::listPlayers(std::vector<std::string> &res)
res.emplace_back(player.getName());
}
}
+
+AuthDatabaseFiles::AuthDatabaseFiles(const std::string &savedir) : m_savedir(savedir)
+{
+ readAuthFile();
+}
+
+bool AuthDatabaseFiles::getAuth(const std::string &name, AuthEntry &res)
+{
+ const auto res_i = m_auth_list.find(name);
+ if (res_i == m_auth_list.end()) {
+ return false;
+ }
+ res = res_i->second;
+ return true;
+}
+
+bool AuthDatabaseFiles::saveAuth(const AuthEntry &authEntry)
+{
+ m_auth_list[authEntry.name] = authEntry;
+
+ // save entire file
+ return writeAuthFile();
+}
+
+bool AuthDatabaseFiles::createAuth(AuthEntry &authEntry)
+{
+ m_auth_list[authEntry.name] = authEntry;
+
+ // save entire file
+ return writeAuthFile();
+}
+
+bool AuthDatabaseFiles::deleteAuth(const std::string &name)
+{
+ if (!m_auth_list.erase(name)) {
+ // did not delete anything -> hadn't existed
+ return false;
+ }
+ return writeAuthFile();
+}
+
+void AuthDatabaseFiles::listNames(std::vector<std::string> &res)
+{
+ res.clear();
+ res.reserve(m_auth_list.size());
+ for (const auto &res_pair : m_auth_list) {
+ res.push_back(res_pair.first);
+ }
+}
+
+void AuthDatabaseFiles::reload()
+{
+ readAuthFile();
+}
+
+bool AuthDatabaseFiles::readAuthFile()
+{
+ std::string path = m_savedir + DIR_DELIM + "auth.txt";
+ std::ifstream file(path, std::ios::binary);
+ if (!file.good()) {
+ return false;
+ }
+ m_auth_list.clear();
+ while (file.good()) {
+ std::string line;
+ std::getline(file, line);
+ std::vector<std::string> parts = str_split(line, ':');
+ if (parts.size() < 3) // also: empty line at end
+ continue;
+ const std::string &name = parts[0];
+ const std::string &password = parts[1];
+ std::vector<std::string> privileges = str_split(parts[2], ',');
+ s64 last_login = parts.size() > 3 ? atol(parts[3].c_str()) : 0;
+
+ m_auth_list[name] = {
+ 1,
+ name,
+ password,
+ privileges,
+ last_login,
+ };
+ }
+ return true;
+}
+
+bool AuthDatabaseFiles::writeAuthFile()
+{
+ std::string path = m_savedir + DIR_DELIM + "auth.txt";
+ std::ostringstream output(std::ios_base::binary);
+ for (const auto &auth_i : m_auth_list) {
+ const AuthEntry &authEntry = auth_i.second;
+ output << authEntry.name << ":" << authEntry.password << ":";
+ output << str_join(authEntry.privileges, ",");
+ output << ":" << authEntry.last_login;
+ output << std::endl;
+ }
+ if (!fs::safeWriteToFile(path, output.str())) {
+ infostream << "Failed to write " << path << std::endl;
+ return false;
+ }
+ return true;
+}
diff --git a/src/database/database-files.h b/src/database/database-files.h
index f0824a304..218815cf7 100644
--- a/src/database/database-files.h
+++ b/src/database/database-files.h
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// for player files
#include "database.h"
+#include <unordered_map>
class PlayerDatabaseFiles : public PlayerDatabase
{
@@ -41,3 +42,23 @@ private:
std::string m_savedir;
};
+
+class AuthDatabaseFiles : public AuthDatabase
+{
+public:
+ AuthDatabaseFiles(const std::string &savedir);
+ virtual ~AuthDatabaseFiles() = default;
+
+ virtual bool getAuth(const std::string &name, AuthEntry &res);
+ virtual bool saveAuth(const AuthEntry &authEntry);
+ virtual bool createAuth(AuthEntry &authEntry);
+ virtual bool deleteAuth(const std::string &name);
+ virtual void listNames(std::vector<std::string> &res);
+ virtual void reload();
+
+private:
+ std::unordered_map<std::string, AuthEntry> m_auth_list;
+ std::string m_savedir;
+ bool readAuthFile();
+ bool writeAuthFile();
+};
diff --git a/src/database/database-sqlite3.cpp b/src/database/database-sqlite3.cpp
index 76935ada4..97b0fd36a 100644
--- a/src/database/database-sqlite3.cpp
+++ b/src/database/database-sqlite3.cpp
@@ -606,3 +606,170 @@ void PlayerDatabaseSQLite3::listPlayers(std::vector<std::string> &res)
sqlite3_reset(m_stmt_player_list);
}
+
+/*
+ * Auth database
+ */
+
+AuthDatabaseSQLite3::AuthDatabaseSQLite3(const std::string &savedir) :
+ Database_SQLite3(savedir, "auth"), AuthDatabase()
+{
+}
+
+AuthDatabaseSQLite3::~AuthDatabaseSQLite3()
+{
+ FINALIZE_STATEMENT(m_stmt_read)
+ FINALIZE_STATEMENT(m_stmt_write)
+ FINALIZE_STATEMENT(m_stmt_create)
+ FINALIZE_STATEMENT(m_stmt_delete)
+ FINALIZE_STATEMENT(m_stmt_list_names)
+ FINALIZE_STATEMENT(m_stmt_read_privs)
+ FINALIZE_STATEMENT(m_stmt_write_privs)
+ FINALIZE_STATEMENT(m_stmt_delete_privs)
+ FINALIZE_STATEMENT(m_stmt_last_insert_rowid)
+}
+
+void AuthDatabaseSQLite3::createDatabase()
+{
+ assert(m_database); // Pre-condition
+
+ SQLOK(sqlite3_exec(m_database,
+ "CREATE TABLE IF NOT EXISTS `auth` ("
+ "`id` INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "`name` VARCHAR(32) UNIQUE,"
+ "`password` VARCHAR(512),"
+ "`last_login` INTEGER"
+ ");",
+ NULL, NULL, NULL),
+ "Failed to create auth table");
+
+ SQLOK(sqlite3_exec(m_database,
+ "CREATE TABLE IF NOT EXISTS `user_privileges` ("
+ "`id` INTEGER,"
+ "`privilege` VARCHAR(32),"
+ "PRIMARY KEY (id, privilege)"
+ "CONSTRAINT fk_id FOREIGN KEY (id) REFERENCES auth (id) ON DELETE CASCADE"
+ ");",
+ NULL, NULL, NULL),
+ "Failed to create auth privileges table");
+}
+
+void AuthDatabaseSQLite3::initStatements()
+{
+ PREPARE_STATEMENT(read, "SELECT id, name, password, last_login FROM auth WHERE name = ?");
+ PREPARE_STATEMENT(write, "UPDATE auth set name = ?, password = ?, last_login = ? WHERE id = ?");
+ PREPARE_STATEMENT(create, "INSERT INTO auth (name, password, last_login) VALUES (?, ?, ?)");
+ PREPARE_STATEMENT(delete, "DELETE FROM auth WHERE name = ?");
+
+ PREPARE_STATEMENT(list_names, "SELECT name FROM auth ORDER BY name DESC");
+
+ PREPARE_STATEMENT(read_privs, "SELECT privilege FROM user_privileges WHERE id = ?");
+ PREPARE_STATEMENT(write_privs, "INSERT OR IGNORE INTO user_privileges (id, privilege) VALUES (?, ?)");
+ PREPARE_STATEMENT(delete_privs, "DELETE FROM user_privileges WHERE id = ?");
+
+ PREPARE_STATEMENT(last_insert_rowid, "SELECT last_insert_rowid()");
+}
+
+bool AuthDatabaseSQLite3::getAuth(const std::string &name, AuthEntry &res)
+{
+ verifyDatabase();
+ str_to_sqlite(m_stmt_read, 1, name);
+ if (sqlite3_step(m_stmt_read) != SQLITE_ROW) {
+ sqlite3_reset(m_stmt_read);
+ return false;
+ }
+ res.id = sqlite_to_uint(m_stmt_read, 0);
+ res.name = sqlite_to_string(m_stmt_read, 1);
+ res.password = sqlite_to_string(m_stmt_read, 2);
+ res.last_login = sqlite_to_int64(m_stmt_read, 3);
+ sqlite3_reset(m_stmt_read);
+
+ int64_to_sqlite(m_stmt_read_privs, 1, res.id);
+ while (sqlite3_step(m_stmt_read_privs) == SQLITE_ROW) {
+ res.privileges.emplace_back(sqlite_to_string(m_stmt_read_privs, 0));
+ }
+ sqlite3_reset(m_stmt_read_privs);
+
+ return true;
+}
+
+bool AuthDatabaseSQLite3::saveAuth(const AuthEntry &authEntry)
+{
+ beginSave();
+
+ str_to_sqlite(m_stmt_write, 1, authEntry.name);
+ str_to_sqlite(m_stmt_write, 2, authEntry.password);
+ int64_to_sqlite(m_stmt_write, 3, authEntry.last_login);
+ int64_to_sqlite(m_stmt_write, 4, authEntry.id);
+ sqlite3_vrfy(sqlite3_step(m_stmt_write), SQLITE_DONE);
+ sqlite3_reset(m_stmt_write);
+
+ writePrivileges(authEntry);
+
+ endSave();
+ return true;
+}
+
+bool AuthDatabaseSQLite3::createAuth(AuthEntry &authEntry)
+{
+ beginSave();
+
+ // id autoincrements
+ str_to_sqlite(m_stmt_create, 1, authEntry.name);
+ str_to_sqlite(m_stmt_create, 2, authEntry.password);
+ int64_to_sqlite(m_stmt_create, 3, authEntry.last_login);
+ sqlite3_vrfy(sqlite3_step(m_stmt_create), SQLITE_DONE);
+ sqlite3_reset(m_stmt_create);
+
+ // obtain id and write back to original authEntry
+ sqlite3_step(m_stmt_last_insert_rowid);
+ authEntry.id = sqlite_to_uint(m_stmt_last_insert_rowid, 0);
+ sqlite3_reset(m_stmt_last_insert_rowid);
+
+ writePrivileges(authEntry);
+
+ endSave();
+ return true;
+}
+
+bool AuthDatabaseSQLite3::deleteAuth(const std::string &name)
+{
+ verifyDatabase();
+
+ str_to_sqlite(m_stmt_delete, 1, name);
+ sqlite3_vrfy(sqlite3_step(m_stmt_delete), SQLITE_DONE);
+ int changes = sqlite3_changes(m_database);
+ sqlite3_reset(m_stmt_delete);
+
+ // privileges deleted by foreign key on delete cascade
+
+ return changes > 0;
+}
+
+void AuthDatabaseSQLite3::listNames(std::vector<std::string> &res)
+{
+ verifyDatabase();
+
+ while (sqlite3_step(m_stmt_list_names) == SQLITE_ROW) {
+ res.push_back(sqlite_to_string(m_stmt_list_names, 0));
+ }
+ sqlite3_reset(m_stmt_list_names);
+}
+
+void AuthDatabaseSQLite3::reload()
+{
+ // noop for SQLite
+}
+
+void AuthDatabaseSQLite3::writePrivileges(const AuthEntry &authEntry)
+{
+ int64_to_sqlite(m_stmt_delete_privs, 1, authEntry.id);
+ sqlite3_vrfy(sqlite3_step(m_stmt_delete_privs), SQLITE_DONE);
+ sqlite3_reset(m_stmt_delete_privs);
+ for (const std::string &privilege : authEntry.privileges) {
+ int64_to_sqlite(m_stmt_write_privs, 1, authEntry.id);
+ str_to_sqlite(m_stmt_write_privs, 2, privilege);
+ sqlite3_vrfy(sqlite3_step(m_stmt_write_privs), SQLITE_DONE);
+ sqlite3_reset(m_stmt_write_privs);
+ }
+}
diff --git a/src/database/database-sqlite3.h b/src/database/database-sqlite3.h
index 8d9f91f21..d7202a918 100644
--- a/src/database/database-sqlite3.h
+++ b/src/database/database-sqlite3.h
@@ -85,6 +85,16 @@ protected:
return (u32) sqlite3_column_int(s, iCol);
}
+ inline s64 sqlite_to_int64(sqlite3_stmt *s, int iCol)
+ {
+ return (s64) sqlite3_column_int64(s, iCol);
+ }
+
+ inline u64 sqlite_to_uint64(sqlite3_stmt *s, int iCol)
+ {
+ return (u64) sqlite3_column_int64(s, iCol);
+ }
+
inline float sqlite_to_float(sqlite3_stmt *s, int iCol)
{
return (float) sqlite3_column_double(s, iCol);
@@ -191,3 +201,34 @@ private:
sqlite3_stmt *m_stmt_player_metadata_remove = nullptr;
sqlite3_stmt *m_stmt_player_metadata_add = nullptr;
};
+
+class AuthDatabaseSQLite3 : private Database_SQLite3, public AuthDatabase
+{
+public:
+ AuthDatabaseSQLite3(const std::string &savedir);
+ virtual ~AuthDatabaseSQLite3();
+
+ virtual bool getAuth(const std::string &name, AuthEntry &res);
+ virtual bool saveAuth(const AuthEntry &authEntry);
+ virtual bool createAuth(AuthEntry &authEntry);
+ virtual bool deleteAuth(const std::string &name);
+ virtual void listNames(std::vector<std::string> &res);
+ virtual void reload();
+
+protected:
+ virtual void createDatabase();
+ virtual void initStatements();
+
+private:
+ virtual void writePrivileges(const AuthEntry &authEntry);
+
+ sqlite3_stmt *m_stmt_read = nullptr;
+ sqlite3_stmt *m_stmt_write = nullptr;
+ sqlite3_stmt *m_stmt_create = nullptr;
+ sqlite3_stmt *m_stmt_delete = nullptr;
+ sqlite3_stmt *m_stmt_list_names = nullptr;
+ sqlite3_stmt *m_stmt_read_privs = nullptr;
+ sqlite3_stmt *m_stmt_write_privs = nullptr;
+ sqlite3_stmt *m_stmt_delete_privs = nullptr;
+ sqlite3_stmt *m_stmt_last_insert_rowid = nullptr;
+};
diff --git a/src/database/database.h b/src/database/database.h
index 9926c7b93..b7d551935 100644
--- a/src/database/database.h
+++ b/src/database/database.h
@@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
+#include <set>
#include <string>
#include <vector>
#include "irr_v3d.h"
@@ -61,3 +62,25 @@ public:
virtual bool removePlayer(const std::string &name) = 0;
virtual void listPlayers(std::vector<std::string> &res) = 0;
};
+
+struct AuthEntry
+{
+ u64 id;
+ std::string name;
+ std::string password;
+ std::vector<std::string> privileges;
+ s64 last_login;
+};
+
+class AuthDatabase
+{
+public:
+ virtual ~AuthDatabase() = default;
+
+ virtual bool getAuth(const std::string &name, AuthEntry &res) = 0;
+ virtual bool saveAuth(const AuthEntry &authEntry) = 0;
+ virtual bool createAuth(AuthEntry &authEntry) = 0;
+ virtual bool deleteAuth(const std::string &name) = 0;
+ virtual void listNames(std::vector<std::string> &res) = 0;
+ virtual void reload() = 0;
+};