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:
proller 2015-12-17 20:58:34 +03:00
parent 2b37ccbd1a
commit 573fa4b334
10 changed files with 270 additions and 38 deletions

215
builtin/game/fm_auth.lua Normal file
View 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)

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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);