Reimplement authentication handler in Lua; now we have 1) infinite privilege names, 2) minetest.register_authentication_handler()

master
Perttu Ahola 2012-03-30 18:42:18 +03:00
parent ed1ff06867
commit 7cad0a2dcd
11 changed files with 477 additions and 727 deletions

View File

@ -12,7 +12,7 @@
print = minetest.debug print = minetest.debug
-- --
-- -- Define some random basic things
-- --
function basic_dump2(o) function basic_dump2(o)
@ -89,6 +89,19 @@ function dump(o, dumped)
end end
end end
function string:split(sep)
local sep, fields = sep or ",", {}
local pattern = string.format("([^%s]+)", sep)
self:gsub(pattern, function(c) fields[#fields+1] = c end)
return fields
end
function string:trim()
return (self:gsub("^%s*(.-)%s*$", "%1"))
end
assert(string.trim("\n \t\tfoo\t ") == "foo")
-- --
-- Item definition helpers -- Item definition helpers
-- --
@ -818,7 +831,7 @@ minetest.register_globalstep(function(dtime)
end) end)
function minetest.after(time, func, param) function minetest.after(time, func, param)
table.insert(minetest.timers_to_add, {time=time, func=func, param=param}) table.insert(minetest.timers_to_add, {time=time, func=func, param=param})
end end
function minetest.check_player_privs(name, privs) function minetest.check_player_privs(name, privs)
@ -848,6 +861,21 @@ function minetest.get_connected_players()
return list return list
end end
minetest.registered_privileges = {}
function minetest.register_privilege(name, description)
minetest.registered_privileges[name] = description
end
minetest.register_privilege("interact", "Can interact with things and modify the world")
minetest.register_privilege("teleport", "Can use /teleport command")
minetest.register_privilege("settime", "Can use /time")
minetest.register_privilege("privs", "Can modify privileges")
minetest.register_privilege("server", "Can do server maintenance stuff")
minetest.register_privilege("shout", "Can speak in chat")
minetest.register_privilege("ban", "Can ban and unban players")
minetest.register_privilege("give", "Can use /give and /giveme")
minetest.register_privilege("password", "Can use /setpassword and /clearpassword")
-- --
-- Chat commands -- Chat commands
-- --
@ -873,7 +901,7 @@ minetest.register_chatcommand("help", {
if def.description and def.description ~= "" then msg = msg .. ": " .. def.description end if def.description and def.description ~= "" then msg = msg .. ": " .. def.description end
return msg return msg
end end
if not param or param == "" then if param == "" then
local msg = "" local msg = ""
cmds = {} cmds = {}
for cmd, def in pairs(minetest.chatcommands) do for cmd, def in pairs(minetest.chatcommands) do
@ -905,18 +933,89 @@ minetest.register_chatcommand("help", {
-- Register C++ commands without functions -- Register C++ commands without functions
minetest.register_chatcommand("me", {params = nil, description = "chat action (eg. /me orders a pizza)"}) minetest.register_chatcommand("me", {params = nil, description = "chat action (eg. /me orders a pizza)"})
minetest.register_chatcommand("status", {description = "print server status line"}) minetest.register_chatcommand("status", {description = "print server status line"})
minetest.register_chatcommand("privs", {params = "<name>", description = "print out privileges of player"})
minetest.register_chatcommand("shutdown", {params = "", description = "shutdown server", privs = {server=true}}) minetest.register_chatcommand("shutdown", {params = "", description = "shutdown server", privs = {server=true}})
minetest.register_chatcommand("setting", {params = "<name> = <value>", description = "set line in configuration file", privs = {server=true}}) minetest.register_chatcommand("setting", {params = "<name> = <value>", description = "set line in configuration file", privs = {server=true}})
minetest.register_chatcommand("clearobjects", {params = "", description = "clear all objects in world", privs = {server=true}}) minetest.register_chatcommand("clearobjects", {params = "", description = "clear all objects in world", privs = {server=true}})
minetest.register_chatcommand("time", {params = "<0...24000>", description = "set time of day", privs = {settime=true}}) minetest.register_chatcommand("time", {params = "<0...24000>", description = "set time of day", privs = {settime=true}})
minetest.register_chatcommand("teleport", {params = "<X>,<Y>,<Z>", description = "teleport to given position", privs = {teleport=true}}) minetest.register_chatcommand("teleport", {params = "<X>,<Y>,<Z>", description = "teleport to given position", privs = {teleport=true}})
minetest.register_chatcommand("grant", {params = "<name> <privilege>", description = "Give privilege to player", privs = {privs=true}})
minetest.register_chatcommand("revoke", {params = "<name> <privilege>", description = "Remove privilege from player", privs = {privs=true}})
minetest.register_chatcommand("ban", {params = "<name>", description = "ban IP of player", privs = {ban=true}}) minetest.register_chatcommand("ban", {params = "<name>", description = "ban IP of player", privs = {ban=true}})
minetest.register_chatcommand("unban", {params = "<name/ip>", description = "remove IP ban", privs = {ban=true}}) minetest.register_chatcommand("unban", {params = "<name/ip>", description = "remove IP ban", privs = {ban=true}})
minetest.register_chatcommand("setpassword", {params = "<name> <password>", description = "set given password", privs = {password=true}})
minetest.register_chatcommand("clearpassword", {params = "<name>", description = "set empty password", privs = {password=true}}) -- Register some other commands
minetest.register_chatcommand("privs", {
params = "<name>",
description = "print out privileges of player",
func = function(name, param)
if param == "" then
param = name
else
if not minetest.check_player_privs(name, {privs=true}) then
minetest.chat_send_player(name, "Privileges of "..param.." are hidden from you.")
end
end
privs = {}
for priv, _ in pairs(minetest.get_player_privs(param)) do
table.insert(privs, priv)
end
minetest.chat_send_player(name, "Privileges of "..param..": "..table.concat(privs, " "))
end,
})
minetest.register_chatcommand("grant", {
params = "<name> <privilege>",
description = "Give privilege to player",
privs = {privs=true},
func = function(name, param)
local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
if not grantname or not grantprivstr then
minetest.chat_send_player(name, "Invalid parameters (see /help grant)")
return
end
local grantprivs = minetest.string_to_privs(grantprivstr)
local privs = minetest.get_player_privs(grantname)
for priv, _ in pairs(grantprivs) do
privs[priv] = true
end
minetest.set_player_privs(grantname, privs)
end,
})
minetest.register_chatcommand("revoke", {
params = "<name> <privilege>",
description = "Remove privilege from player",
privs = {privs=true},
func = function(name, param)
local revokename, revokeprivstr = string.match(param, "([^ ]+) (.+)")
if not revokename or not revokeprivstr then
minetest.chat_send_player(name, "Invalid parameters (see /help revoke)")
return
end
local revokeprivs = minetest.string_to_privs(revokeprivstr)
local privs = minetest.get_player_privs(revokename)
for priv, _ in pairs(revokeprivs) do
table.remove(privs, priv)
end
minetest.set_player_privs(revokename, privs)
end,
})
minetest.register_chatcommand("setpassword", {
params = "<name> <password>",
description = "set given password",
privs = {password=true},
func = function(name, param)
if param == "" then
minetest.chat_send_player(name, "Password field required")
return
end
minetest.set_player_password(name, param)
end,
})
minetest.register_chatcommand("clearpassword", {
params = "<name>",
description = "set empty password",
privs = {password=true},
func = function(name, param)
minetest.set_player_password(name, '')
end,
})
-- --
-- Builtin chat handler -- Builtin chat handler
@ -924,6 +1023,9 @@ minetest.register_chatcommand("clearpassword", {params = "<name>", description =
minetest.register_on_chat_message(function(name, message) minetest.register_on_chat_message(function(name, message)
local cmd, param = string.match(message, "/([^ ]+) *(.*)") local cmd, param = string.match(message, "/([^ ]+) *(.*)")
if not param then
param = ""
end
local cmd_def = minetest.chatcommands[cmd] local cmd_def = minetest.chatcommands[cmd]
if cmd_def then if cmd_def then
if not cmd_def.func then if not cmd_def.func then
@ -942,6 +1044,149 @@ minetest.register_on_chat_message(function(name, message)
return false return false
end) end)
--
-- Authentication handler
--
function minetest.string_to_privs(str)
assert(type(str) == "string")
privs = {}
for _, priv in pairs(string.split(str, ',')) do
privs[priv:trim()] = true
end
return privs
end
function minetest.privs_to_string(privs)
assert(type(privs) == "table")
list = {}
for priv, bool in pairs(privs) do
if bool then
table.insert(list, priv)
end
end
return table.concat(list, ',')
end
assert(minetest.string_to_privs("a,b").b == true)
assert(minetest.privs_to_string({a=true,b=true}) == "a,b")
minetest.auth_file_path = minetest.get_worldpath().."/auth.txt"
minetest.auth_table = {}
local function read_auth_file()
local newtable = {}
local file, errmsg = io.open(minetest.auth_file_path, 'rb')
if not file then
error(minetest.auth_file_path.." could not be opened for reading: "..errmsg)
end
for line in file:lines() do
if line ~= "" then
local name, password, privilegestring = string.match(line, "([^:]*):([^:]*):([^:]*)")
if not name or not password or not privilegestring then
error("Invalid line in auth.txt: "..dump(line))
end
local privileges = minetest.string_to_privs(privilegestring)
newtable[name] = {password=password, privileges=privileges}
end
end
io.close(file)
minetest.auth_table = newtable
end
local function save_auth_file()
local newtable = {}
-- Check table for validness before attempting to save
for name, stuff in pairs(minetest.auth_table) do
assert(type(name) == "string")
assert(name ~= "")
assert(type(stuff) == "table")
assert(type(stuff.password) == "string")
assert(type(stuff.privileges) == "table")
end
local file, errmsg = io.open(minetest.auth_file_path, 'w+b')
if not file then
error(minetest.auth_file_path.." could not be opened for writing: "..errmsg)
end
for name, stuff in pairs(minetest.auth_table) do
local privstring = minetest.privs_to_string(stuff.privileges)
file:write(name..":"..stuff.password..":"..privstring..'\n')
end
io.close(file)
end
read_auth_file()
minetest.builtin_auth_handler = {
get_auth = function(name)
assert(type(name) == "string")
if not minetest.auth_table[name] then
minetest.builtin_auth_handler.create_auth(name, minetest.get_password_hash(name, minetest.setting_get("default_password")))
end
if minetest.is_singleplayer() or name == minetest.setting_get("name") then
return {
password = "",
privileges = minetest.registered_privileges
}
else
return minetest.auth_table[name]
end
end,
create_auth = function(name, password)
assert(type(name) == "string")
assert(type(password) == "string")
minetest.log('info', "Built-in authentication handler adding player '"..name.."'")
minetest.auth_table[name] = {
password = password,
privileges = minetest.string_to_privs(minetest.setting_get("default_privs")),
}
save_auth_file()
end,
set_password = function(name, password)
assert(type(name) == "string")
assert(type(password) == "string")
if not minetest.auth_table[name] then
minetest.builtin_auth_handler.create_auth(name, password)
else
minetest.log('info', "Built-in authentication handler setting password of player '"..name.."'")
minetest.auth_table[name].password = password
save_auth_file()
end
end,
set_privileges = function(name, privileges)
assert(type(name) == "string")
assert(type(privileges) == "table")
if not minetest.auth_table[name] then
minetest.builtin_auth_handler.create_auth(name, minetest.get_password_hash(name, minetest.setting_get("default_password")))
end
minetest.auth_table[name].privileges = privileges
save_auth_file()
end
}
function minetest.register_authentication_handler(handler)
if minetest.registered_auth_handler then
error("Add-on authentication handler already registered by "..minetest.registered_auth_handler_modname)
end
minetest.registered_auth_handler = handler
minetest.registered_auth_handler_modname = minetest.get_current_modname()
end
function minetest.get_auth_handler()
if minetest.registered_auth_handler then
return minetest.registered_auth_handler
end
return minetest.builtin_auth_handler
end
function minetest.set_player_password(name, password)
minetest.get_auth_handler().set_password(name, password)
end
function minetest.set_player_privs(name, privs)
minetest.get_auth_handler().set_privileges(name, privs)
end
-- --
-- Set random seed -- Set random seed
-- --

View File

@ -464,6 +464,8 @@ dump2(obj, name="_", dumped={})
^ Return object serialized as a string, handles reference loops ^ Return object serialized as a string, handles reference loops
dump(obj, dumped={}) dump(obj, dumped={})
^ Return object serialized as a string ^ Return object serialized as a string
string:split(separator)
string:trim()
minetest namespace reference minetest namespace reference
----------------------------- -----------------------------
@ -480,6 +482,7 @@ minetest.log(line)
minetest.log(loglevel, line) minetest.log(loglevel, line)
^ loglevel one of "error", "action", "info", "verbose" ^ loglevel one of "error", "action", "info", "verbose"
Registration functions: (Call these only at load time)
minetest.register_entity(name, prototype table) minetest.register_entity(name, prototype table)
minetest.register_abm(abm definition) minetest.register_abm(abm definition)
minetest.register_node(name, node definition) minetest.register_node(name, node definition)
@ -487,7 +490,6 @@ minetest.register_tool(name, item definition)
minetest.register_craftitem(name, item definition) minetest.register_craftitem(name, item definition)
minetest.register_alias(name, convert_to) minetest.register_alias(name, convert_to)
minetest.register_craft(recipe) minetest.register_craft(recipe)
minetest.register_globalstep(func(dtime)) minetest.register_globalstep(func(dtime))
minetest.register_on_placenode(func(pos, newnode, placer)) minetest.register_on_placenode(func(pos, newnode, placer))
minetest.register_on_dignode(func(pos, oldnode, digger)) minetest.register_on_dignode(func(pos, oldnode, digger))
@ -500,24 +502,40 @@ minetest.register_on_respawnplayer(func(ObjectRef))
^ currently called _before_ repositioning of player occurs ^ currently called _before_ repositioning of player occurs
minetest.register_on_chat_message(func(name, message)) minetest.register_on_chat_message(func(name, message))
minetest.register_chatcommand(cmd, chatcommand definition) minetest.register_chatcommand(cmd, chatcommand definition)
minetest.register_privilege(name, description)
minetest.register_authentication_handler(handler)
^ See minetest.builtin_auth_handler in builtin.lua for reference
minetest.add_to_creative_inventory(itemstring) Setting-related:
minetest.setting_get(name) -> string or nil minetest.setting_get(name) -> string or nil
minetest.setting_getbool(name) -> boolean value or nil minetest.setting_getbool(name) -> boolean value or nil
minetest.add_to_creative_inventory(itemstring)
Authentication:
minetest.get_password_hash(name, raw_password)
minetest.set_player_password(name, password_hash)
minetest.string_to_privs(str) -> {priv1=true,...}
minetest.privs_to_string(privs) -> "priv1,priv2,..."
minetest.set_player_privs(name, {priv1=true,...})
minetest.get_player_privs(name) -> {priv1=true,...}
minetest.check_player_privs(name, {priv1=true,...}) -> bool, missing_privs
Chat:
minetest.chat_send_all(text) minetest.chat_send_all(text)
minetest.chat_send_player(name, text) minetest.chat_send_player(name, text)
minetest.get_player_privs(name) -> set of privs
minetest.check_player_privs(name, {priv1=true,...}) -> bool, missing_privs Inventory:
minetest.get_inventory(location) -> InvRef minetest.get_inventory(location) -> InvRef
^ location = eg. {type="player", name="celeron55"} ^ location = eg. {type="player", name="celeron55"}
{type="node", pos={x=, y=, z=}} {type="node", pos={x=, y=, z=}}
Sounds:
minetest.sound_play(spec, parameters) -> handle minetest.sound_play(spec, parameters) -> handle
^ spec = SimpleSoundSpec ^ spec = SimpleSoundSpec
^ parameters = sound parameter table ^ parameters = sound parameter table
minetest.sound_stop(handle) minetest.sound_stop(handle)
Timing:
minetest.after(time, func, param) minetest.after(time, func, param)
^ Call function after time seconds ^ Call function after time seconds
^ param is optional; to pass multiple parameters, pass a table. ^ param is optional; to pass multiple parameters, pass a table.

View File

@ -173,7 +173,6 @@ set(common_SRCS
mapgen.cpp mapgen.cpp
content_nodemeta.cpp content_nodemeta.cpp
content_mapnode.cpp content_mapnode.cpp
auth.cpp
collision.cpp collision.cpp
nodemetadata.cpp nodemetadata.cpp
serverobject.cpp serverobject.cpp

View File

@ -1,306 +0,0 @@
/*
Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "auth.h"
#include <fstream>
#include <jmutexautolock.h>
//#include "main.h" // for g_settings
#include <sstream>
#include "strfnd.h"
#include "log.h"
std::set<std::string> privsToSet(u64 privs)
{
std::set<std::string> s;
if(privs & PRIV_INTERACT)
s.insert("interact");
if(privs & PRIV_TELEPORT)
s.insert("teleport");
if(privs & PRIV_SETTIME)
s.insert("settime");
if(privs & PRIV_PRIVS)
s.insert("privs");
if(privs & PRIV_SERVER)
s.insert("server");
if(privs & PRIV_SHOUT)
s.insert("shout");
if(privs & PRIV_BAN)
s.insert("ban");
if(privs & PRIV_GIVE)
s.insert("give");
if(privs & PRIV_PASSWORD)
s.insert("password");
return s;
}
// Convert a privileges value into a human-readable string,
// with each component separated by a comma.
std::string privsToString(u64 privs)
{
std::ostringstream os(std::ios_base::binary);
if(privs & PRIV_INTERACT)
os<<"interact,";
if(privs & PRIV_TELEPORT)
os<<"teleport,";
if(privs & PRIV_SETTIME)
os<<"settime,";
if(privs & PRIV_PRIVS)
os<<"privs,";
if(privs & PRIV_SERVER)
os<<"server,";
if(privs & PRIV_SHOUT)
os<<"shout,";
if(privs & PRIV_BAN)
os<<"ban,";
if(privs & PRIV_GIVE)
os<<"give,";
if(privs & PRIV_PASSWORD)
os<<"password,";
if(os.tellp())
{
// Drop the trailing comma. (Why on earth can't
// you truncate a C++ stream anyway???)
std::string tmp = os.str();
return tmp.substr(0, tmp.length() -1);
}
return os.str();
}
// Converts a comma-seperated list of privilege values into a
// privileges value. The reverse of privsToString(). Returns
// PRIV_INVALID if there is anything wrong with the input.
u64 stringToPrivs(std::string str)
{
u64 privs=0;
Strfnd f(str);
while(f.atend() == false)
{
std::string s = trim(f.next(","));
if(s == "build")
privs |= PRIV_INTERACT;
else if(s == "interact")
privs |= PRIV_INTERACT;
else if(s == "teleport")
privs |= PRIV_TELEPORT;
else if(s == "settime")
privs |= PRIV_SETTIME;
else if(s == "privs")
privs |= PRIV_PRIVS;
else if(s == "server")
privs |= PRIV_SERVER;
else if(s == "shout")
privs |= PRIV_SHOUT;
else if(s == "ban")
privs |= PRIV_BAN;
else if(s == "give")
privs |= PRIV_GIVE;
else if(s == "password")
privs |= PRIV_PASSWORD;
else
return PRIV_INVALID;
}
return privs;
}
AuthManager::AuthManager(const std::string &authfilepath):
m_authfilepath(authfilepath),
m_modified(false)
{
m_mutex.Init();
try{
load();
}
catch(SerializationError &e)
{
infostream<<"WARNING: AuthManager: creating "
<<m_authfilepath<<std::endl;
}
}
AuthManager::~AuthManager()
{
save();
}
void AuthManager::load()
{
JMutexAutoLock lock(m_mutex);
infostream<<"AuthManager: loading from "<<m_authfilepath<<std::endl;
std::ifstream is(m_authfilepath.c_str(), std::ios::binary);
if(is.good() == false)
{
infostream<<"AuthManager: failed loading from "<<m_authfilepath<<std::endl;
throw SerializationError("AuthManager::load(): Couldn't open file");
}
for(;;)
{
if(is.eof() || is.good() == false)
break;
// Read a line
std::string line;
std::getline(is, line, '\n');
std::istringstream iss(line);
// Read name
std::string name;
std::getline(iss, name, ':');
// Read password
std::string pwd;
std::getline(iss, pwd, ':');
// Read privileges
std::string stringprivs;
std::getline(iss, stringprivs, ':');
u64 privs = stringToPrivs(stringprivs);
// Store it
AuthData ad;
ad.pwd = pwd;
ad.privs = privs;
m_authdata[name] = ad;
}
m_modified = false;
}
void AuthManager::save()
{
JMutexAutoLock lock(m_mutex);
infostream<<"AuthManager: saving to "<<m_authfilepath<<std::endl;
std::ofstream os(m_authfilepath.c_str(), std::ios::binary);
if(os.good() == false)
{
infostream<<"AuthManager: failed saving to "<<m_authfilepath<<std::endl;
throw SerializationError("AuthManager::save(): Couldn't open file");
}
for(core::map<std::string, AuthData>::Iterator
i = m_authdata.getIterator();
i.atEnd()==false; i++)
{
std::string name = i.getNode()->getKey();
if(name == "")
continue;
AuthData ad = i.getNode()->getValue();
os<<name<<":"<<ad.pwd<<":"<<privsToString(ad.privs)<<"\n";
}
m_modified = false;
}
bool AuthManager::exists(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
return false;
return true;
}
void AuthManager::set(const std::string &username, AuthData ad)
{
JMutexAutoLock lock(m_mutex);
m_authdata[username] = ad;
m_modified = true;
}
void AuthManager::add(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
m_authdata[username] = AuthData();
m_modified = true;
}
std::string AuthManager::getPassword(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
return n->getValue().pwd;
}
void AuthManager::setPassword(const std::string &username,
const std::string &password)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
AuthData ad = n->getValue();
ad.pwd = password;
n->setValue(ad);
m_modified = true;
}
u64 AuthManager::getPrivs(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
return n->getValue().privs;
}
void AuthManager::setPrivs(const std::string &username, u64 privs)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
AuthData ad = n->getValue();
ad.privs = privs;
n->setValue(ad);
m_modified = true;
}
bool AuthManager::isModified()
{
JMutexAutoLock lock(m_mutex);
return m_modified;
}

View File

@ -1,107 +0,0 @@
/*
Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef AUTH_HEADER
#define AUTH_HEADER
#include <set>
#include <string>
#include <jthread.h>
#include <jmutex.h>
#include "irrlichttypes.h"
#include "exceptions.h"
// Player privileges. These form a bitmask stored in the privs field
// of the player, and define things they're allowed to do. See also
// the static methods Player::privsToString and stringToPrivs that
// convert these to human-readable form.
const u64 PRIV_INTERACT = 1; // Can interact
const u64 PRIV_TELEPORT = 2; // Can teleport
const u64 PRIV_SETTIME = 4; // Can set the time
const u64 PRIV_PRIVS = 8; // Can grant and revoke privileges
const u64 PRIV_SERVER = 16; // Can manage the server (e.g. shutodwn
// ,settings)
const u64 PRIV_SHOUT = 32; // Can broadcast chat messages to all
// players
const u64 PRIV_BAN = 64; // Can ban players
const u64 PRIV_GIVE = 128; // Can give stuff
const u64 PRIV_PASSWORD = 256; // Can set other players' passwords
// Default privileges - these can be overriden for new players using the
// config option "default_privs" - however, this value still applies for
// players that existed before the privileges system was added.
const u64 PRIV_DEFAULT = PRIV_INTERACT|PRIV_SHOUT;
const u64 PRIV_ALL = 0x7FFFFFFFFFFFFFFFULL;
const u64 PRIV_INVALID = 0x8000000000000000ULL;
std::set<std::string> privsToSet(u64 privs);
// Convert a privileges value into a human-readable string,
// with each component separated by a comma.
std::string privsToString(u64 privs);
// Converts a comma-seperated list of privilege values into a
// privileges value. The reverse of privsToString(). Returns
// PRIV_INVALID if there is anything wrong with the input.
u64 stringToPrivs(std::string str);
struct AuthData
{
std::string pwd;
u64 privs;
AuthData():
privs(PRIV_DEFAULT)
{
}
};
class AuthNotFoundException : public BaseException
{
public:
AuthNotFoundException(const char *s):
BaseException(s)
{}
};
class AuthManager
{
public:
AuthManager(const std::string &authfilepath);
~AuthManager();
void load();
void save();
bool exists(const std::string &username);
void set(const std::string &username, AuthData ad);
void add(const std::string &username);
std::string getPassword(const std::string &username);
void setPassword(const std::string &username,
const std::string &password);
u64 getPrivs(const std::string &username);
void setPrivs(const std::string &username, u64 privs);
bool isModified();
private:
JMutex m_mutex;
std::string m_authfilepath;
core::map<std::string, AuthData> m_authdata;
bool m_modified;
};
#endif

View File

@ -141,14 +141,14 @@ static Server* get_server(lua_State *L)
return server; return server;
} }
static ServerEnvironment* get_env(lua_State *L) /*static ServerEnvironment* get_env(lua_State *L)
{ {
// Get environment from registry // Get environment from registry
lua_getfield(L, LUA_REGISTRYINDEX, "minetest_env"); lua_getfield(L, LUA_REGISTRYINDEX, "minetest_env");
ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1); ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
return env; return env;
} }*/
static void objectref_get(lua_State *L, u16 id) static void objectref_get(lua_State *L, u16 id)
{ {
@ -647,6 +647,27 @@ static void read_groups(lua_State *L, int index,
} }
} }
/*
Privileges
*/
static void read_privileges(lua_State *L, int index,
std::set<std::string> &result)
{
result.clear();
lua_pushnil(L);
if(index < 0)
index -= 1;
while(lua_next(L, index) != 0){
// key at index -2 and value at index -1
std::string key = luaL_checkstring(L, -2);
bool value = lua_toboolean(L, -1);
if(value)
result.insert(key);
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
}
/* /*
ToolCapabilities ToolCapabilities
*/ */
@ -3837,8 +3858,7 @@ static int l_get_player_privs(lua_State *L)
// Do it // Do it
lua_newtable(L); lua_newtable(L);
int table = lua_gettop(L); int table = lua_gettop(L);
u64 privs_i = server->getPlayerEffectivePrivs(name); std::set<std::string> privs_s = server->getPlayerEffectivePrivs(name);
std::set<std::string> privs_s = privsToSet(privs_i);
for(std::set<std::string>::const_iterator for(std::set<std::string>::const_iterator
i = privs_s.begin(); i != privs_s.end(); i++){ i = privs_s.begin(); i != privs_s.end(); i++){
lua_pushboolean(L, true); lua_pushboolean(L, true);
@ -3954,6 +3974,17 @@ static int l_is_singleplayer(lua_State *L)
return 1; return 1;
} }
// get_password_hash(name, raw_password)
static int l_get_password_hash(lua_State *L)
{
std::string name = luaL_checkstring(L, 1);
std::string raw_password = luaL_checkstring(L, 2);
std::string hash = translatePassword(name,
narrow_to_wide(raw_password));
lua_pushstring(L, hash.c_str());
return 1;
}
static const struct luaL_Reg minetest_f [] = { static const struct luaL_Reg minetest_f [] = {
{"debug", l_debug}, {"debug", l_debug},
{"log", l_log}, {"log", l_log},
@ -3974,6 +4005,7 @@ static const struct luaL_Reg minetest_f [] = {
{"sound_play", l_sound_play}, {"sound_play", l_sound_play},
{"sound_stop", l_sound_stop}, {"sound_stop", l_sound_stop},
{"is_singleplayer", l_is_singleplayer}, {"is_singleplayer", l_is_singleplayer},
{"get_password_hash", l_get_password_hash},
{NULL, NULL} {NULL, NULL}
}; };
@ -4421,6 +4453,10 @@ void scriptapi_on_leaveplayer(lua_State *L, ServerActiveObject *player)
void scriptapi_get_creative_inventory(lua_State *L, ServerActiveObject *player) void scriptapi_get_creative_inventory(lua_State *L, ServerActiveObject *player)
{ {
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
Inventory *inv = player->getInventory(); Inventory *inv = player->getInventory();
assert(inv); assert(inv);
@ -4430,6 +4466,91 @@ void scriptapi_get_creative_inventory(lua_State *L, ServerActiveObject *player)
inventory_set_list_from_lua(inv, "main", L, -1, PLAYER_INVENTORY_SIZE); inventory_set_list_from_lua(inv, "main", L, -1, PLAYER_INVENTORY_SIZE);
} }
static void get_auth_handler(lua_State *L)
{
lua_getglobal(L, "minetest");
lua_getfield(L, -1, "registered_auth_handler");
if(lua_isnil(L, -1)){
lua_pop(L, 1);
lua_getfield(L, -1, "builtin_auth_handler");
}
if(lua_type(L, -1) != LUA_TTABLE)
throw LuaError(L, "Authentication handler table not valid");
}
bool scriptapi_get_auth(lua_State *L, const std::string &playername,
std::string *dst_password, std::set<std::string> *dst_privs)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
get_auth_handler(L);
lua_getfield(L, -1, "get_auth");
if(lua_type(L, -1) != LUA_TFUNCTION)
throw LuaError(L, "Authentication handler missing get_auth");
lua_pushstring(L, playername.c_str());
if(lua_pcall(L, 1, 1, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
// nil = login not allowed
if(lua_isnil(L, -1))
return false;
luaL_checktype(L, -1, LUA_TTABLE);
std::string password;
bool found = getstringfield(L, -1, "password", password);
if(!found)
throw LuaError(L, "Authentication handler didn't return password");
if(dst_password)
*dst_password = password;
lua_getfield(L, -1, "privileges");
if(!lua_istable(L, -1))
throw LuaError(L,
"Authentication handler didn't return privilege table");
if(dst_privs)
read_privileges(L, -1, *dst_privs);
lua_pop(L, 1);
return true;
}
void scriptapi_create_auth(lua_State *L, const std::string &playername,
const std::string &password)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
get_auth_handler(L);
lua_getfield(L, -1, "create_auth");
if(lua_type(L, -1) != LUA_TFUNCTION)
throw LuaError(L, "Authentication handler missing create_auth");
lua_pushstring(L, playername.c_str());
lua_pushstring(L, password.c_str());
if(lua_pcall(L, 2, 0, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
}
bool scriptapi_set_password(lua_State *L, const std::string &playername,
const std::string &password)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
get_auth_handler(L);
lua_getfield(L, -1, "set_password");
if(lua_type(L, -1) != LUA_TFUNCTION)
throw LuaError(L, "Authentication handler missing set_password");
lua_pushstring(L, playername.c_str());
lua_pushstring(L, password.c_str());
if(lua_pcall(L, 2, 1, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
return lua_toboolean(L, -1);
}
/* /*
item callbacks and node callbacks item callbacks and node callbacks
*/ */

View File

@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes.h" #include "irrlichttypes.h"
#include <string> #include <string>
#include "mapnode.h" #include "mapnode.h"
#include <set>
class Server; class Server;
class ServerEnvironment; class ServerEnvironment;
@ -60,6 +61,12 @@ bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *player);
void scriptapi_on_joinplayer(lua_State *L, ServerActiveObject *player); void scriptapi_on_joinplayer(lua_State *L, ServerActiveObject *player);
void scriptapi_on_leaveplayer(lua_State *L, ServerActiveObject *player); void scriptapi_on_leaveplayer(lua_State *L, ServerActiveObject *player);
void scriptapi_get_creative_inventory(lua_State *L, ServerActiveObject *player); void scriptapi_get_creative_inventory(lua_State *L, ServerActiveObject *player);
bool scriptapi_get_auth(lua_State *L, const std::string &playername,
std::string *dst_password, std::set<std::string> *dst_privs);
void scriptapi_create_auth(lua_State *L, const std::string &playername,
const std::string &password);
bool scriptapi_set_password(lua_State *L, const std::string &playername,
const std::string &password);
/* item callbacks */ /* item callbacks */
bool scriptapi_item_on_drop(lua_State *L, ItemStack &item, bool scriptapi_item_on_drop(lua_State *L, ItemStack &item,

View File

@ -142,6 +142,10 @@ void * ServerThread::Thread()
{ {
m_server->setAsyncFatalError(e.what()); m_server->setAsyncFatalError(e.what());
} }
catch(LuaError &e)
{
m_server->setAsyncFatalError(e.what());
}
} }
END_DEBUG_EXCEPTION_HANDLER(errorstream) END_DEBUG_EXCEPTION_HANDLER(errorstream)
@ -905,7 +909,6 @@ Server::Server(
m_async_fatal_error(""), m_async_fatal_error(""),
m_env(NULL), m_env(NULL),
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
m_authmanager(path_world+DIR_DELIM+"auth.txt"),
m_banmanager(path_world+DIR_DELIM+"ipban.txt"), m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
m_lua(NULL), m_lua(NULL),
m_itemdef(createItemDefManager()), m_itemdef(createItemDefManager()),
@ -1844,10 +1847,6 @@ void Server::AsyncRunStep()
ScopeProfiler sp(g_profiler, "Server: saving stuff"); ScopeProfiler sp(g_profiler, "Server: saving stuff");
// Auth stuff
if(m_authmanager.isModified())
m_authmanager.save();
//Ban stuff //Ban stuff
if(m_banmanager.isModified()) if(m_banmanager.isModified())
m_banmanager.save(); m_banmanager.save();
@ -2083,34 +2082,30 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
password[PASSWORD_SIZE-1] = 0; password[PASSWORD_SIZE-1] = 0;
} }
// Add player to auth manager std::string checkpwd;
if(m_authmanager.exists(playername) == false) bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
{
std::wstring default_password = if(!has_auth){
std::wstring raw_default_password =
narrow_to_wide(g_settings->get("default_password")); narrow_to_wide(g_settings->get("default_password"));
std::string translated_default_password = std::string use_password =
translatePassword(playername, default_password); translatePassword(playername, raw_default_password);
// If default_password is empty, allow any initial password // If default_password is empty, allow any initial password
if (default_password.length() == 0) if (raw_default_password.length() == 0)
translated_default_password = password; use_password = password;
infostream<<"Server: adding player "<<playername scriptapi_create_auth(m_lua, playername, use_password);
<<" to auth manager"<<std::endl;
m_authmanager.add(playername);
m_authmanager.setPassword(playername, translated_default_password);
m_authmanager.setPrivs(playername,
stringToPrivs(g_settings->get("default_privs")));
m_authmanager.save();
} }
std::string checkpwd = m_authmanager.getPassword(playername); has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
/*infostream<<"Server: Client gave password '"<<password if(!has_auth){
<<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/ SendAccessDenied(m_con, peer_id, L"Not allowed to login");
return;
}
if(password != checkpwd) if(password != checkpwd){
{
infostream<<"Server: peer_id="<<peer_id infostream<<"Server: peer_id="<<peer_id
<<": supplied invalid password for " <<": supplied invalid password for "
<<playername<<std::endl; <<playername<<std::endl;
@ -2131,8 +2126,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Enforce user limit. // Enforce user limit.
// Don't enforce for users that have some admin right // Don't enforce for users that have some admin right
if(m_clients.size() >= g_settings->getU16("max_users") && if(m_clients.size() >= g_settings->getU16("max_users") &&
(m_authmanager.getPrivs(playername) !checkPriv(playername, "server") &&
& (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS|PRIV_PASSWORD)) == 0 && !checkPriv(playername, "ban") &&
!checkPriv(playername, "privs") &&
!checkPriv(playername, "password") &&
playername != g_settings->get("name")) playername != g_settings->get("name"))
{ {
actionstream<<"Server: "<<playername<<" tried to join, but there" actionstream<<"Server: "<<playername<<" tried to join, but there"
@ -2407,7 +2404,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
} }
else if(command == TOSERVER_SIGNNODETEXT) else if(command == TOSERVER_SIGNNODETEXT)
{ {
if((getPlayerPrivs(player) & PRIV_INTERACT) == 0) if(!checkPriv(player->getName(), "interact"))
return; return;
/* /*
u16 command u16 command
@ -2519,9 +2516,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Disallow moving items in elsewhere than player's inventory // Disallow moving items in elsewhere than player's inventory
// if not allowed to interact // if not allowed to interact
if((getPlayerPrivs(player) & PRIV_INTERACT) == 0 if(!checkPriv(player->getName(), "interact") &&
&& (!from_inv_is_current_player (!from_inv_is_current_player ||
|| !to_inv_is_current_player)) !to_inv_is_current_player))
{ {
infostream<<"Cannot move outside of player's inventory: " infostream<<"Cannot move outside of player's inventory: "
<<"No interact privilege"<<std::endl; <<"No interact privilege"<<std::endl;
@ -2530,7 +2527,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
} }
// If player is not an admin, check for ownership of src and dst // If player is not an admin, check for ownership of src and dst
if((getPlayerPrivs(player) & PRIV_SERVER) == 0) if(!checkPriv(player->getName(), "server"))
{ {
std::string owner_from = getInventoryOwner(ma->from_inv); std::string owner_from = getInventoryOwner(ma->from_inv);
if(owner_from != "" && owner_from != player->getName()) if(owner_from != "" && owner_from != player->getName())
@ -2565,13 +2562,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
setInventoryModified(da->from_inv); setInventoryModified(da->from_inv);
// Disallow dropping items if not allowed to interact // Disallow dropping items if not allowed to interact
if((getPlayerPrivs(player) & PRIV_INTERACT) == 0) if(!checkPriv(player->getName(), "interact"))
{ {
delete a; delete a;
return; return;
} }
// If player is not an admin, check for ownership // If player is not an admin, check for ownership
else if((getPlayerPrivs(player) & PRIV_SERVER) == 0) else if(!checkPriv(player->getName(), "server"))
{ {
std::string owner_from = getInventoryOwner(da->from_inv); std::string owner_from = getInventoryOwner(da->from_inv);
if(owner_from != "" && owner_from != player->getName()) if(owner_from != "" && owner_from != player->getName())
@ -2600,7 +2597,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// (ca->craft_inv.name == player->getName()); // (ca->craft_inv.name == player->getName());
// Disallow crafting if not allowed to interact // Disallow crafting if not allowed to interact
if((getPlayerPrivs(player) & PRIV_INTERACT) == 0) if(!checkPriv(player->getName(), "interact"))
{ {
infostream<<"Cannot craft: " infostream<<"Cannot craft: "
<<"No interact privilege"<<std::endl; <<"No interact privilege"<<std::endl;
@ -2609,7 +2606,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
} }
// If player is not an admin, check for ownership of inventory // If player is not an admin, check for ownership of inventory
if((getPlayerPrivs(player) & PRIV_SERVER) == 0) if(!checkPriv(player->getName(), "server"))
{ {
std::string owner_craft = getInventoryOwner(ca->craft_inv); std::string owner_craft = getInventoryOwner(ca->craft_inv);
if(owner_craft != "" && owner_craft != player->getName()) if(owner_craft != "" && owner_craft != player->getName())
@ -2667,10 +2664,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Whether to send to other players // Whether to send to other players
bool send_to_others = false; bool send_to_others = false;
// Local player gets all privileges regardless of
// what's set on their account.
u64 privs = getPlayerPrivs(player);
// Parse commands // Parse commands
if(message[0] == L'/') if(message[0] == L'/')
{ {
@ -2688,8 +2681,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
paramstring, paramstring,
this, this,
m_env, m_env,
player, player);
privs);
std::wstring reply(processServerCommand(ctx)); std::wstring reply(processServerCommand(ctx));
send_to_sender = ctx->flags & SEND_TO_SENDER; send_to_sender = ctx->flags & SEND_TO_SENDER;
@ -2705,16 +2697,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
} }
else else
{ {
if(privs & PRIV_SHOUT) if(checkPriv(player->getName(), "shout")){
{
line += L"<"; line += L"<";
line += name; line += name;
line += L"> "; line += L"> ";
line += message; line += message;
send_to_others = true; send_to_others = true;
} } else {
else
{
line += L"Server: You are not allowed to shout"; line += L"Server: You are not allowed to shout";
send_to_sender = true; send_to_sender = true;
} }
@ -2803,15 +2792,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
std::string playername = player->getName(); std::string playername = player->getName();
if(m_authmanager.exists(playername) == false) std::string checkpwd;
{ scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
infostream<<"Server: playername not found in authmanager"<<std::endl;
// Wrong old password supplied!!
SendChatMessage(peer_id, L"playername not found in authmanager");
return;
}
std::string checkpwd = m_authmanager.getPassword(playername);
if(oldpwd != checkpwd) if(oldpwd != checkpwd)
{ {
@ -2821,13 +2803,15 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
return; return;
} }
actionstream<<player->getName()<<" changes password"<<std::endl; bool success = scriptapi_set_password(m_lua, playername, newpwd);
if(success){
m_authmanager.setPassword(playername, newpwd); actionstream<<player->getName()<<" changes password"<<std::endl;
SendChatMessage(peer_id, L"Password change successful");
infostream<<"Server: password change successful for "<<playername } else {
<<std::endl; actionstream<<player->getName()<<" tries to change password but "
SendChatMessage(peer_id, L"Password change successful"); <<"it fails"<<std::endl;
SendChatMessage(peer_id, L"Password change failed or inavailable");
}
} }
else if(command == TOSERVER_PLAYERITEM) else if(command == TOSERVER_PLAYERITEM)
{ {
@ -2970,11 +2954,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
/* /*
Make sure the player is allowed to do it Make sure the player is allowed to do it
*/ */
if((getPlayerPrivs(player) & PRIV_INTERACT) == 0) if(!checkPriv(player->getName(), "interact"))
{ {
infostream<<"Ignoring interaction from player "<<player->getName() infostream<<"Ignoring interaction from player "<<player->getName()
<<" because privileges are "<<getPlayerPrivs(player) <<" (no interact privilege)"<<std::endl;
<<std::endl;
return; return;
} }
@ -4277,54 +4260,17 @@ std::wstring Server::getStatusString()
return os.str(); return os.str();
} }
u64 Server::getPlayerAuthPrivs(const std::string &name) std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
{ {
try{ std::set<std::string> privs;
return m_authmanager.getPrivs(name); scriptapi_get_auth(m_lua, name, NULL, &privs);
} return privs;
catch(AuthNotFoundException &e)
{
dstream<<"WARNING: Auth not found for "<<name<<std::endl;
return 0;
}
} }
void Server::setPlayerAuthPrivs(const std::string &name, u64 privs) bool Server::checkPriv(const std::string &name, const std::string &priv)
{ {
try{ std::set<std::string> privs = getPlayerEffectivePrivs(name);
return m_authmanager.setPrivs(name, privs); return (privs.count(priv) != 0);
}
catch(AuthNotFoundException &e)
{
dstream<<"WARNING: Auth not found for "<<name<<std::endl;
}
}
u64 Server::getPlayerEffectivePrivs(const std::string &name)
{
// Local player gets all privileges regardless of
// what's set on their account.
if(m_simple_singleplayer_mode)
return PRIV_ALL;
if(name == g_settings->get("name"))
return PRIV_ALL;
return getPlayerAuthPrivs(name);
}
void Server::setPlayerPassword(const std::string &name, const std::wstring &password)
{
// Add player to auth manager
if(m_authmanager.exists(name) == false)
{
infostream<<"Server: adding player "<<name
<<" to auth manager"<<std::endl;
m_authmanager.add(name);
m_authmanager.setPrivs(name,
stringToPrivs(g_settings->get("default_privs")));
}
// Change password and save
m_authmanager.setPassword(name, translatePassword(name, password));
m_authmanager.save();
} }
// Saves g_settings to configpath given at initialization // Saves g_settings to configpath given at initialization
@ -4698,14 +4644,6 @@ void Server::handlePeerChanges()
} }
} }
u64 Server::getPlayerPrivs(Player *player)
{
if(player==NULL)
return 0;
std::string playername = player->getName();
return getPlayerEffectivePrivs(playername);
}
void dedicated_server_loop(Server &server, bool &kill) void dedicated_server_loop(Server &server, bool &kill)
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);

View File

@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h" #include "porting.h"
#include "map.h" #include "map.h"
#include "inventory.h" #include "inventory.h"
#include "auth.h"
#include "ban.h" #include "ban.h"
#include "gamedef.h" #include "gamedef.h"
#include "serialization.h" // For SER_FMT_VER_INVALID #include "serialization.h" // For SER_FMT_VER_INVALID
@ -500,14 +499,9 @@ public:
s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams &params); s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams &params);
void stopSound(s32 handle); void stopSound(s32 handle);
// Thread-safe // Envlock + conlock
u64 getPlayerAuthPrivs(const std::string &name); std::set<std::string> getPlayerEffectivePrivs(const std::string &name);
void setPlayerAuthPrivs(const std::string &name, u64 privs); bool checkPriv(const std::string &name, const std::string &priv);
u64 getPlayerEffectivePrivs(const std::string &name);
// Changes a player's password, password must be given as plaintext
// If the player doesn't exist, a new entry is added to the auth manager
void setPlayerPassword(const std::string &name, const std::wstring &password);
// Saves g_settings to configpath given at initialization // Saves g_settings to configpath given at initialization
void saveConfig(); void saveConfig();
@ -670,8 +664,6 @@ private:
void handlePeerChange(PeerChange &c); void handlePeerChange(PeerChange &c);
void handlePeerChanges(); void handlePeerChanges();
u64 getPlayerPrivs(Player *player);
/* /*
Variables Variables
*/ */
@ -710,9 +702,6 @@ private:
// Connected clients (behind the con mutex) // Connected clients (behind the con mutex)
core::map<u16, RemoteClient*> m_clients; core::map<u16, RemoteClient*> m_clients;
// User authentication
AuthManager m_authmanager;
// Bann checking // Bann checking
BanManager m_banmanager; BanManager m_banmanager;

View File

@ -38,98 +38,6 @@ void cmd_me(std::wostringstream &os,
ctx->flags |= SEND_TO_OTHERS | SEND_NO_PREFIX; ctx->flags |= SEND_TO_OTHERS | SEND_NO_PREFIX;
} }
void cmd_privs(std::wostringstream &os,
ServerCommandContext *ctx)
{
if(ctx->parms.size() == 1)
{
// Show our own real privs, without any adjustments
// made for admin status
os<<L"-!- " + narrow_to_wide(privsToString(
ctx->server->getPlayerAuthPrivs(ctx->player->getName())));
return;
}
if((ctx->privs & PRIV_PRIVS) == 0)
{
os<<L"-!- You don't have permission to do that";
return;
}
Player *tp = ctx->env->getPlayer(wide_to_narrow(ctx->parms[1]).c_str());
if(tp == NULL)
{
os<<L"-!- No such player";
return;
}
os<<L"-!- " + narrow_to_wide(privsToString(ctx->server->getPlayerAuthPrivs(tp->getName())));
}
void cmd_grantrevoke(std::wostringstream &os,
ServerCommandContext *ctx)
{
if(ctx->parms.size() != 3)
{
os<<L"-!- Missing parameter";
return;
}
if((ctx->privs & PRIV_PRIVS) == 0)
{
os<<L"-!- You don't have permission to do that";
return;
}
u64 newprivs = stringToPrivs(wide_to_narrow(ctx->parms[2]));
if(newprivs == PRIV_INVALID)
{
os<<L"-!- Invalid privileges specified";
return;
}
Player *tp = ctx->env->getPlayer(wide_to_narrow(ctx->parms[1]).c_str());
if(tp == NULL)
{
os<<L"-!- No such player";
return;
}
std::string playername = wide_to_narrow(ctx->parms[1]);
u64 privs = ctx->server->getPlayerAuthPrivs(playername);
if(ctx->parms[0] == L"grant"){
privs |= newprivs;
actionstream<<ctx->player->getName()<<" grants "
<<wide_to_narrow(ctx->parms[2])<<" to "
<<playername<<std::endl;
std::wstring msg;
msg += narrow_to_wide(ctx->player->getName());
msg += L" granted you the privilege \"";
msg += ctx->parms[2];
msg += L"\"";
ctx->server->notifyPlayer(playername.c_str(), msg);
} else {
privs &= ~newprivs;
actionstream<<ctx->player->getName()<<" revokes "
<<wide_to_narrow(ctx->parms[2])<<" from "
<<playername<<std::endl;
std::wstring msg;
msg += narrow_to_wide(ctx->player->getName());
msg += L" revoked from you the privilege \"";
msg += ctx->parms[2];
msg += L"\"";
ctx->server->notifyPlayer(playername.c_str(), msg);
}
ctx->server->setPlayerAuthPrivs(playername, privs);
os<<L"-!- Privileges change to ";
os<<narrow_to_wide(privsToString(privs));
}
void cmd_time(std::wostringstream &os, void cmd_time(std::wostringstream &os,
ServerCommandContext *ctx) ServerCommandContext *ctx)
{ {
@ -139,7 +47,7 @@ void cmd_time(std::wostringstream &os,
return; return;
} }
if((ctx->privs & PRIV_SETTIME) ==0) if(!ctx->server->checkPriv(ctx->player->getName(), "settime"))
{ {
os<<L"-!- You don't have permission to do that"; os<<L"-!- You don't have permission to do that";
return; return;
@ -156,7 +64,7 @@ void cmd_time(std::wostringstream &os,
void cmd_shutdown(std::wostringstream &os, void cmd_shutdown(std::wostringstream &os,
ServerCommandContext *ctx) ServerCommandContext *ctx)
{ {
if((ctx->privs & PRIV_SERVER) ==0) if(!ctx->server->checkPriv(ctx->player->getName(), "server"))
{ {
os<<L"-!- You don't have permission to do that"; os<<L"-!- You don't have permission to do that";
return; return;
@ -174,7 +82,7 @@ void cmd_shutdown(std::wostringstream &os,
void cmd_setting(std::wostringstream &os, void cmd_setting(std::wostringstream &os,
ServerCommandContext *ctx) ServerCommandContext *ctx)
{ {
if((ctx->privs & PRIV_SERVER) ==0) if(!ctx->server->checkPriv(ctx->player->getName(), "server"))
{ {
os<<L"-!- You don't have permission to do that"; os<<L"-!- You don't have permission to do that";
return; return;
@ -198,7 +106,7 @@ void cmd_setting(std::wostringstream &os,
void cmd_teleport(std::wostringstream &os, void cmd_teleport(std::wostringstream &os,
ServerCommandContext *ctx) ServerCommandContext *ctx)
{ {
if((ctx->privs & PRIV_TELEPORT) ==0) if(!ctx->server->checkPriv(ctx->player->getName(), "teleport"))
{ {
os<<L"-!- You don't have permission to do that"; os<<L"-!- You don't have permission to do that";
return; return;
@ -241,7 +149,7 @@ void cmd_teleport(std::wostringstream &os,
void cmd_banunban(std::wostringstream &os, ServerCommandContext *ctx) void cmd_banunban(std::wostringstream &os, ServerCommandContext *ctx)
{ {
if((ctx->privs & PRIV_BAN) == 0) if(!ctx->server->checkPriv(ctx->player->getName(), "ban"))
{ {
os<<L"-!- You don't have permission to do that"; os<<L"-!- You don't have permission to do that";
return; return;
@ -288,62 +196,10 @@ void cmd_banunban(std::wostringstream &os, ServerCommandContext *ctx)
} }
} }
void cmd_setclearpassword(std::wostringstream &os,
ServerCommandContext *ctx)
{
if((ctx->privs & PRIV_PASSWORD) == 0)
{
os<<L"-!- You don't have permission to do that";
return;
}
std::string playername;
std::wstring password;
if(ctx->parms[0] == L"setpassword")
{
if(ctx->parms.size() != 3)
{
os<<L"-!- Missing parameter";
return;
}
playername = wide_to_narrow(ctx->parms[1]);
password = ctx->parms[2];
actionstream<<ctx->player->getName()<<" sets password of "
<<playername<<std::endl;
}
else
{
// clearpassword
if(ctx->parms.size() != 2)
{
os<<L"-!- Missing parameter";
return;
}
playername = wide_to_narrow(ctx->parms[1]);
password = L"";
actionstream<<ctx->player->getName()<<" clears password of"
<<playername<<std::endl;
}
ctx->server->setPlayerPassword(playername, password);
std::wostringstream msg;
msg<<ctx->player->getName()<<L" changed your password";
ctx->server->notifyPlayer(playername.c_str(), msg.str());
os<<L"-!- Password change for "<<narrow_to_wide(playername)<<" successful";
}
void cmd_clearobjects(std::wostringstream &os, void cmd_clearobjects(std::wostringstream &os,
ServerCommandContext *ctx) ServerCommandContext *ctx)
{ {
if((ctx->privs & PRIV_SERVER) ==0) if(!ctx->server->checkPriv(ctx->player->getName(), "server"))
{ {
os<<L"-!- You don't have permission to do that"; os<<L"-!- You don't have permission to do that";
return; return;
@ -378,10 +234,6 @@ std::wstring processServerCommand(ServerCommandContext *ctx)
if(ctx->parms[0] == L"status") if(ctx->parms[0] == L"status")
cmd_status(os, ctx); cmd_status(os, ctx);
else if(ctx->parms[0] == L"privs")
cmd_privs(os, ctx);
else if(ctx->parms[0] == L"grant" || ctx->parms[0] == L"revoke")
cmd_grantrevoke(os, ctx);
else if(ctx->parms[0] == L"time") else if(ctx->parms[0] == L"time")
cmd_time(os, ctx); cmd_time(os, ctx);
else if(ctx->parms[0] == L"shutdown") else if(ctx->parms[0] == L"shutdown")
@ -392,8 +244,6 @@ std::wstring processServerCommand(ServerCommandContext *ctx)
cmd_teleport(os, ctx); cmd_teleport(os, ctx);
else if(ctx->parms[0] == L"ban" || ctx->parms[0] == L"unban") else if(ctx->parms[0] == L"ban" || ctx->parms[0] == L"unban")
cmd_banunban(os, ctx); cmd_banunban(os, ctx);
else if(ctx->parms[0] == L"setpassword" || ctx->parms[0] == L"clearpassword")
cmd_setclearpassword(os, ctx);
else if(ctx->parms[0] == L"me") else if(ctx->parms[0] == L"me")
cmd_me(os, ctx); cmd_me(os, ctx);
else if(ctx->parms[0] == L"clearobjects") else if(ctx->parms[0] == L"clearobjects")

View File

@ -36,9 +36,6 @@ struct ServerCommandContext
Server* server; Server* server;
ServerEnvironment *env; ServerEnvironment *env;
Player* player; Player* player;
// Effective privs for the player, which may be different to their
// stored ones - e.g. if they are named in the config as an admin.
u64 privs;
u32 flags; u32 flags;
ServerCommandContext( ServerCommandContext(
@ -46,10 +43,9 @@ struct ServerCommandContext
std::wstring paramstring, std::wstring paramstring,
Server* server, Server* server,
ServerEnvironment *env, ServerEnvironment *env,
Player* player, Player* player)
u64 privs)
: parms(parms), paramstring(paramstring), : parms(parms), paramstring(paramstring),
server(server), env(env), player(player), privs(privs) server(server), env(env), player(player)
{ {
} }