mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
Replace auth.txt with SQLite auth database (#7279)
* Replace auth.txt with SQLite auth database
This commit is contained in:
parent
1836882495
commit
153fb211ac
19 changed files with 1153 additions and 82 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue