allow use kv db with any name from lua. add auth kv backend (not used by default, to enable: auth_kv=1 )
This commit is contained in:
parent
2b37ccbd1a
commit
573fa4b334
215
builtin/game/fm_auth.lua
Normal file
215
builtin/game/fm_auth.lua
Normal file
@ -0,0 +1,215 @@
|
||||
--
|
||||
-- Authentication handler
|
||||
--
|
||||
|
||||
function core.string_to_privs(str, delim)
|
||||
if type(str) ~= "string" then return end
|
||||
delim = delim or ','
|
||||
local privs = {}
|
||||
for _, priv in pairs(string.split(str, delim)) do
|
||||
privs[priv:trim()] = true
|
||||
end
|
||||
return privs
|
||||
end
|
||||
|
||||
core.auth_file_path = core.get_worldpath().."/auth.txt"
|
||||
core.auth_table = {}
|
||||
core.auth_prefix = "auth_"
|
||||
|
||||
local function read_auth(name)
|
||||
core.auth_table[name] = core.kv_get(core.auth_prefix .. name, "player_auth")
|
||||
return core.auth_table[name]
|
||||
--core.notify_authentication_modified(name)
|
||||
end
|
||||
|
||||
local function save_auth(name, data)
|
||||
if not data then data = core.auth_table[name] end
|
||||
return core.kv_put(core.auth_prefix .. name, data, "player_auth")
|
||||
end
|
||||
|
||||
|
||||
local hex={}
|
||||
for i=0,255 do
|
||||
hex[string.format("%0x",i)]=string.char(i)
|
||||
hex[string.format("%0X",i)]=string.char(i)
|
||||
end
|
||||
|
||||
local function uri_decode(str)
|
||||
str = string.gsub (str, "+", " ")
|
||||
return (str:gsub('%%(%x%x)',hex))
|
||||
end
|
||||
|
||||
function uri_encode (str)
|
||||
str = string.gsub (str, "([^0-9a-zA-Z_ -])", function (c) return string.format ("%%%02X", string.byte(c)) end)
|
||||
str = string.gsub (str, " ", "+")
|
||||
return str
|
||||
end
|
||||
|
||||
local function auth_convert(force)
|
||||
local newtable = {}
|
||||
local file, errmsg = io.open(core.auth_file_path, 'rb')
|
||||
if not file then
|
||||
--core.log("info", core.auth_file_path.." could not be opened for reading ("..errmsg.."); assuming new world")
|
||||
return
|
||||
end
|
||||
core.log("action", "Converting auth to kv " .. core.auth_file_path .. " ")
|
||||
local n = 0
|
||||
for line in file:lines() do
|
||||
n = n + 1
|
||||
if line ~= "" then
|
||||
local fields = line:split(":", true)
|
||||
local name, password, privilege_string, last_login = unpack(fields)
|
||||
last_login = tonumber(last_login)
|
||||
if not (name and password and privilege_string) then
|
||||
print("Invalid line in auth.txt:" .. n .. " " .. dump(line))
|
||||
else
|
||||
name = uri_decode(name);
|
||||
local old = read_auth(name) --core.kv_get(core.auth_prefix .. name, "player_auth")
|
||||
--print("readed " .. name .. " d=" .. core.serialize(old))
|
||||
if old and not force then
|
||||
print("Player [" .. name .. "] already converted, skipping")
|
||||
else
|
||||
--print("Saving player " .. name .. " p="..password)
|
||||
local privileges = core.string_to_privs(privilege_string)
|
||||
local data = {name=name, password=password, privileges=privileges, last_login=last_login}
|
||||
save_auth(name, data)
|
||||
--print("save res= " .. core.serialize(read_auth(name)))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
io.close(file)
|
||||
os.rename(core.auth_file_path, core.auth_file_path..'.old')
|
||||
core.auth_table = {}
|
||||
end
|
||||
|
||||
local converted = nil
|
||||
|
||||
core.builtin_auth_handler = {
|
||||
get_auth = function(name)
|
||||
if not conveeted then auth_convert(); converted = 1 end -- here because env needed
|
||||
assert(type(name) == "string")
|
||||
read_auth(name)
|
||||
|
||||
-- Figure out what password to use for a new player (singleplayer
|
||||
-- always has an empty password, otherwise use default, which is
|
||||
-- usually empty too)
|
||||
local new_password_hash = ""
|
||||
-- If not in authentication table, return nil
|
||||
if not core.auth_table[name] then
|
||||
return nil
|
||||
end
|
||||
-- Figure out what privileges the player should have.
|
||||
-- Take a copy of the privilege table
|
||||
local privileges = {}
|
||||
for priv, _ in pairs(core.auth_table[name].privileges) do
|
||||
privileges[priv] = true
|
||||
end
|
||||
-- If singleplayer, give all privileges except those marked as give_to_singleplayer = false
|
||||
if core.is_singleplayer() then
|
||||
for priv, def in pairs(core.registered_privileges) do
|
||||
if def.give_to_singleplayer then
|
||||
privileges[priv] = true
|
||||
end
|
||||
end
|
||||
-- For the admin, give everything
|
||||
elseif name == core.setting_get("name") then
|
||||
for priv, def in pairs(core.registered_privileges) do
|
||||
privileges[priv] = true
|
||||
end
|
||||
end
|
||||
-- All done
|
||||
return {
|
||||
password = core.auth_table[name].password,
|
||||
privileges = privileges,
|
||||
-- Is set to nil if unknown
|
||||
last_login = core.auth_table[name].last_login,
|
||||
}
|
||||
end,
|
||||
create_auth = function(name, password)
|
||||
assert(type(name) == "string")
|
||||
assert(type(password) == "string")
|
||||
core.log('info', "Built-in authentication handler adding player '"..name.."'")
|
||||
local privs = core.setting_get("default_privs")
|
||||
if core.setting_getbool("creative_mode") and core.setting_get("default_privs_creative") then
|
||||
privs = core.setting_get("default_privs_creative")
|
||||
end
|
||||
core.auth_table[name] = {
|
||||
password = password,
|
||||
privileges = core.string_to_privs(privs),
|
||||
last_login = os.time(),
|
||||
}
|
||||
save_auth(name)
|
||||
end,
|
||||
set_password = function(name, password)
|
||||
assert(type(name) == "string")
|
||||
assert(type(password) == "string")
|
||||
read_auth(name)
|
||||
if not core.auth_table[name] then
|
||||
core.builtin_auth_handler.create_auth(name, password)
|
||||
else
|
||||
core.log('info', "Built-in authentication handler setting password of player '"..name.."'")
|
||||
core.auth_table[name].password = password
|
||||
save_auth(name)
|
||||
end
|
||||
return true
|
||||
end,
|
||||
set_privileges = function(name, privileges)
|
||||
assert(type(name) == "string")
|
||||
assert(type(privileges) == "table")
|
||||
read_auth(name)
|
||||
if not core.auth_table[name] then
|
||||
core.builtin_auth_handler.create_auth(name,
|
||||
core.get_password_hash(name,
|
||||
core.setting_get("default_password")))
|
||||
end
|
||||
core.auth_table[name].privileges = privileges
|
||||
core.notify_authentication_modified(name)
|
||||
save_auth(name)
|
||||
end,
|
||||
reload = function()
|
||||
--read_auth_file()
|
||||
return true
|
||||
end,
|
||||
record_login = function(name)
|
||||
assert(type(name) == "string")
|
||||
read_auth(name)
|
||||
assert(core.auth_table[name]).last_login = os.time()
|
||||
save_auth(name)
|
||||
end,
|
||||
}
|
||||
|
||||
function core.register_authentication_handler(handler)
|
||||
if core.registered_auth_handler then
|
||||
error("Add-on authentication handler already registered by "..core.registered_auth_handler_modname)
|
||||
end
|
||||
core.registered_auth_handler = handler
|
||||
core.registered_auth_handler_modname = core.get_current_modname()
|
||||
handler.mod_origin = core.registered_auth_handler_modname
|
||||
end
|
||||
|
||||
function core.get_auth_handler()
|
||||
return core.registered_auth_handler or core.builtin_auth_handler
|
||||
end
|
||||
|
||||
local function auth_pass(name)
|
||||
return function(...)
|
||||
local auth_handler = core.get_auth_handler()
|
||||
if auth_handler[name] then
|
||||
return auth_handler[name](...)
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
core.set_player_password = auth_pass("set_password")
|
||||
core.set_player_privs = auth_pass("set_privileges")
|
||||
core.auth_reload = auth_pass("reload")
|
||||
|
||||
|
||||
local record_login = auth_pass("record_login")
|
||||
|
||||
core.register_on_joinplayer(function(player)
|
||||
record_login(player:get_player_name())
|
||||
end)
|
||||
|
@ -17,7 +17,11 @@ dofile(gamepath.."item_entity.lua")
|
||||
dofile(gamepath.."deprecated.lua")
|
||||
dofile(gamepath.."misc.lua")
|
||||
dofile(gamepath.."privileges.lua")
|
||||
dofile(gamepath.."auth.lua")
|
||||
if core.setting_getbool("auth_kv") then
|
||||
dofile(gamepath.."fm_auth.lua")
|
||||
else
|
||||
dofile(gamepath.."auth.lua")
|
||||
end
|
||||
dofile(gamepath.."stat.lua")
|
||||
dofile(gamepath.."chatcommands.lua")
|
||||
dofile(gamepath.."static_spawn.lua")
|
||||
|
@ -2,25 +2,27 @@
|
||||
-- Key-value storage stuff
|
||||
--
|
||||
|
||||
function core.kv_put(key, data)
|
||||
function core.kv_put(key, data, db)
|
||||
local json = core.write_json(data)
|
||||
if not json then
|
||||
core.log("error", "kv_put: Error in json serialize key=".. key .. " luaized_data=" .. core.serialize(data))
|
||||
return
|
||||
end
|
||||
return core.kv_put_string(key, json)
|
||||
--core.log("action", "core.kv_put: key=".. key .. " json=" .. json .. " db=" .. db)
|
||||
return core.kv_put_string(key, json, db)
|
||||
end
|
||||
|
||||
function core.kv_get(key)
|
||||
local data = core.kv_get_string(key)
|
||||
function core.kv_get(key, db)
|
||||
local data = core.kv_get_string(key, db)
|
||||
--core.log("action", "core.kv_get: key=".. key .. " json=" .. (data or '') .. " db=" .. db)
|
||||
if data ~= nil then
|
||||
data = core.parse_json(data)
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
function core.kv_rename(key1, key2)
|
||||
local data = core.kv_get_string(key1)
|
||||
core.kv_delete(key1)
|
||||
core.kv_put_string(key2, data)
|
||||
function core.kv_rename(key1, key2, db)
|
||||
local data = core.kv_get_string(key1, db)
|
||||
core.kv_delete(key1, db)
|
||||
core.kv_put_string(key2, data, db)
|
||||
end
|
||||
|
@ -1143,6 +1143,9 @@ selectionbox_color () string (0,0,0)
|
||||
|
||||
[*Server]
|
||||
|
||||
# Save auth data to leveldb kv storage
|
||||
auth_kv () bool false
|
||||
|
||||
# If true, blocks are cached (and generated if not before) before a player is spawned.
|
||||
cache_block_before_spawn () bool true
|
||||
|
||||
|
@ -374,8 +374,6 @@ ServerEnvironment::ServerEnvironment(ServerMap *map,
|
||||
m_script(scriptIface),
|
||||
m_gamedef(gamedef),
|
||||
m_circuit(m_script, map, gamedef->ndef(), path_world),
|
||||
m_key_value_storage(path_world, "key_value_storage"),
|
||||
m_players_storage(path_world, "players"),
|
||||
m_path_world(path_world),
|
||||
m_send_recommended_timer(0),
|
||||
m_active_objects_last(0),
|
||||
@ -393,11 +391,6 @@ ServerEnvironment::ServerEnvironment(ServerMap *map,
|
||||
m_use_weather = g_settings->getBool("weather");
|
||||
m_use_weather_biome = g_settings->getBool("weather_biome");
|
||||
|
||||
if (!m_key_value_storage.db)
|
||||
errorstream << "Cant open KV storage: "<< m_key_value_storage.error << std::endl;
|
||||
if (!m_players_storage.db)
|
||||
errorstream << "Cant open players storage: "<< m_players_storage.error << std::endl;
|
||||
|
||||
// Init custom SAO
|
||||
v3f nullpos;
|
||||
//epixel::Creature* c = new epixel::Creature(NULL, nullpos, "", "");
|
||||
@ -449,9 +442,15 @@ ServerMap & ServerEnvironment::getServerMap()
|
||||
return *m_map;
|
||||
}
|
||||
|
||||
KeyValueStorage *ServerEnvironment::getKeyValueStorage()
|
||||
KeyValueStorage &ServerEnvironment::getKeyValueStorage(std::string name)
|
||||
{
|
||||
return &m_key_value_storage;
|
||||
if (name.empty()) {
|
||||
name = "key_value_storage";
|
||||
}
|
||||
if (!m_key_value_storage.count(name)) {
|
||||
m_key_value_storage.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(m_path_world, name));
|
||||
}
|
||||
return m_key_value_storage.at(name);
|
||||
}
|
||||
|
||||
bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
|
||||
@ -516,7 +515,7 @@ void ServerEnvironment::savePlayer(RemotePlayer *player)
|
||||
return;
|
||||
Json::Value player_json;
|
||||
player_json << *player;
|
||||
m_players_storage.put_json("p." + player->getName(), player_json);
|
||||
getPlayerStorage().put_json("p." + player->getName(), player_json);
|
||||
}
|
||||
|
||||
Player * ServerEnvironment::loadPlayer(const std::string &playername)
|
||||
@ -532,7 +531,7 @@ Player * ServerEnvironment::loadPlayer(const std::string &playername)
|
||||
|
||||
try {
|
||||
Json::Value player_json;
|
||||
m_players_storage.get_json("p." + playername, player_json);
|
||||
getPlayerStorage().get_json("p." + playername, player_json);
|
||||
verbosestream<<"Reading kv player "<<playername<<std::endl;
|
||||
if (!player_json.empty()) {
|
||||
player_json >> *player;
|
||||
|
@ -290,7 +290,8 @@ public:
|
||||
//Player * getPlayer(u16 peer_id) { return Environment::getPlayer(peer_id); };
|
||||
//Player * getPlayer(const std::string &name);
|
||||
|
||||
KeyValueStorage *getKeyValueStorage();
|
||||
KeyValueStorage &getKeyValueStorage(std::string name = "key_value_storage");
|
||||
KeyValueStorage &getPlayerStorage() { return getKeyValueStorage("player"); };
|
||||
|
||||
void kickAllPlayers(AccessDeniedCode reason,
|
||||
const std::string &str_reason, bool reconnect);
|
||||
@ -477,8 +478,7 @@ private:
|
||||
Circuit m_circuit;
|
||||
// Key-value storage
|
||||
public:
|
||||
KeyValueStorage m_key_value_storage;
|
||||
KeyValueStorage m_players_storage;
|
||||
std::unordered_map<std::string, KeyValueStorage> m_key_value_storage;
|
||||
private:
|
||||
|
||||
// World path
|
||||
|
@ -294,8 +294,7 @@ void Server::maintenance_start() {
|
||||
m_env->getServerMap().m_map_saving_enabled = false;
|
||||
m_env->getServerMap().m_map_loading_enabled = false;
|
||||
m_env->getServerMap().dbase->close();
|
||||
m_env->m_key_value_storage.close();
|
||||
m_env->m_players_storage.close();
|
||||
m_env->m_key_value_storage.clear();
|
||||
stat.close();
|
||||
actionstream << "Server: Starting maintenance: bases closed now." << std::endl;
|
||||
|
||||
@ -303,8 +302,6 @@ void Server::maintenance_start() {
|
||||
|
||||
void Server::maintenance_end() {
|
||||
m_env->getServerMap().dbase->open();
|
||||
m_env->m_key_value_storage.open();
|
||||
m_env->m_players_storage.open();
|
||||
stat.open();
|
||||
m_env->getServerMap().m_map_saving_enabled = true;
|
||||
m_env->getServerMap().m_map_loading_enabled = true;
|
||||
|
@ -36,6 +36,7 @@ bool KeyValueStorage::open() {
|
||||
options.create_if_missing = true;
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
auto status = leveldb::DB::Open(options, fullpath, &db);
|
||||
//errorstream<<"KeyValueStorage::open() "<<db_name << " st="<< status.ok()<< " e="<<status.ToString()<<std::endl;
|
||||
if (!status.ok()) {
|
||||
error = status.ToString();
|
||||
errorstream<< "Trying to repair database ["<<error<<"]"<<std::endl;
|
||||
@ -66,6 +67,7 @@ void KeyValueStorage::close()
|
||||
|
||||
KeyValueStorage::~KeyValueStorage()
|
||||
{
|
||||
//errorstream<<"KeyValueStorage::~KeyValueStorage() "<<db_name<<std::endl;
|
||||
close();
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ public:
|
||||
#endif
|
||||
std::string error;
|
||||
private:
|
||||
const std::string &db_name;
|
||||
const std::string db_name;
|
||||
std::string fullpath;
|
||||
Json::FastWriter json_writer;
|
||||
Json::Reader json_reader;
|
||||
|
@ -33,9 +33,13 @@ int ModApiKeyValueStorage::l_kv_put_string(lua_State *L)
|
||||
{
|
||||
GET_ENV_PTR_NO_MAP_LOCK;
|
||||
|
||||
const char *key = luaL_checkstring(L, 1);
|
||||
const char *data = luaL_checkstring(L, 2);
|
||||
env->getKeyValueStorage()->put(key, data);
|
||||
std::string key = luaL_checkstring(L, 1);
|
||||
std::string data = luaL_checkstring(L, 2);
|
||||
|
||||
std::string db;
|
||||
if (lua_isstring(L, 3))
|
||||
db = luaL_checkstring(L, 3);
|
||||
env->getKeyValueStorage(db).put(key, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -44,9 +48,12 @@ int ModApiKeyValueStorage::l_kv_get_string(lua_State *L)
|
||||
{
|
||||
GET_ENV_PTR_NO_MAP_LOCK;
|
||||
|
||||
const char *key = luaL_checkstring(L, 1);
|
||||
std::string key = luaL_checkstring(L, 1);
|
||||
std::string db;
|
||||
if (lua_isstring(L, 2))
|
||||
db = luaL_checkstring(L, 2);
|
||||
std::string data;
|
||||
if(env->getKeyValueStorage()->get(key, data)) {
|
||||
if(env->getKeyValueStorage(db).get(key, data)) {
|
||||
lua_pushstring(L, data.c_str());
|
||||
return 1;
|
||||
} else {
|
||||
@ -58,8 +65,11 @@ int ModApiKeyValueStorage::l_kv_delete(lua_State *L)
|
||||
{
|
||||
GET_ENV_PTR_NO_MAP_LOCK;
|
||||
|
||||
const char *key = luaL_checkstring(L, 1);
|
||||
env->getKeyValueStorage()->del(key);
|
||||
std::string key = luaL_checkstring(L, 1);
|
||||
std::string db;
|
||||
if (lua_isstring(L, 2))
|
||||
db = luaL_checkstring(L, 2);
|
||||
env->getKeyValueStorage(db).del(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -73,7 +83,7 @@ int ModApiKeyValueStorage::l_stat_get(lua_State *L)
|
||||
{
|
||||
GET_ENV_PTR_NO_MAP_LOCK;
|
||||
|
||||
const char *key = luaL_checkstring(L, 1);
|
||||
std::string key = luaL_checkstring(L, 1);
|
||||
lua_pushnumber(L, getServer(L)->stat.get(key));
|
||||
return 1;
|
||||
}
|
||||
@ -82,9 +92,9 @@ int ModApiKeyValueStorage::l_stat_add(lua_State *L)
|
||||
{
|
||||
GET_ENV_PTR_NO_MAP_LOCK;
|
||||
|
||||
const char *key = luaL_checkstring(L, 1);
|
||||
std::string key = luaL_checkstring(L, 1);
|
||||
|
||||
const char *name = "";
|
||||
std::string name;
|
||||
if(lua_isstring(L, 2))
|
||||
name = lua_tostring(L, 1);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user