mirror of
https://github.com/minetest/minetest.git
synced 2024-08-19 04:54:18 +02:00
Reimplement authentication handler in Lua; now we have 1) infinite privilege names, 2) minetest.register_authentication_handler()
This commit is contained in:
parent
ed1ff06867
commit
7cad0a2dcd
@ -12,7 +12,7 @@
|
||||
print = minetest.debug
|
||||
|
||||
--
|
||||
--
|
||||
-- Define some random basic things
|
||||
--
|
||||
|
||||
function basic_dump2(o)
|
||||
@ -89,6 +89,19 @@ function dump(o, dumped)
|
||||
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
|
||||
--
|
||||
@ -818,7 +831,7 @@ minetest.register_globalstep(function(dtime)
|
||||
end)
|
||||
|
||||
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
|
||||
|
||||
function minetest.check_player_privs(name, privs)
|
||||
@ -848,6 +861,21 @@ function minetest.get_connected_players()
|
||||
return list
|
||||
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
|
||||
--
|
||||
@ -873,7 +901,7 @@ minetest.register_chatcommand("help", {
|
||||
if def.description and def.description ~= "" then msg = msg .. ": " .. def.description end
|
||||
return msg
|
||||
end
|
||||
if not param or param == "" then
|
||||
if param == "" then
|
||||
local msg = ""
|
||||
cmds = {}
|
||||
for cmd, def in pairs(minetest.chatcommands) do
|
||||
@ -905,18 +933,89 @@ minetest.register_chatcommand("help", {
|
||||
-- Register C++ commands without functions
|
||||
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("privs", {params = "<name>", description = "print out privileges of player"})
|
||||
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("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("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("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
|
||||
@ -924,6 +1023,9 @@ minetest.register_chatcommand("clearpassword", {params = "<name>", description =
|
||||
|
||||
minetest.register_on_chat_message(function(name, message)
|
||||
local cmd, param = string.match(message, "/([^ ]+) *(.*)")
|
||||
if not param then
|
||||
param = ""
|
||||
end
|
||||
local cmd_def = minetest.chatcommands[cmd]
|
||||
if cmd_def then
|
||||
if not cmd_def.func then
|
||||
@ -942,6 +1044,149 @@ minetest.register_on_chat_message(function(name, message)
|
||||
return false
|
||||
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
|
||||
--
|
||||
|
@ -464,6 +464,8 @@ dump2(obj, name="_", dumped={})
|
||||
^ Return object serialized as a string, handles reference loops
|
||||
dump(obj, dumped={})
|
||||
^ Return object serialized as a string
|
||||
string:split(separator)
|
||||
string:trim()
|
||||
|
||||
minetest namespace reference
|
||||
-----------------------------
|
||||
@ -480,6 +482,7 @@ minetest.log(line)
|
||||
minetest.log(loglevel, line)
|
||||
^ loglevel one of "error", "action", "info", "verbose"
|
||||
|
||||
Registration functions: (Call these only at load time)
|
||||
minetest.register_entity(name, prototype table)
|
||||
minetest.register_abm(abm 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_alias(name, convert_to)
|
||||
minetest.register_craft(recipe)
|
||||
|
||||
minetest.register_globalstep(func(dtime))
|
||||
minetest.register_on_placenode(func(pos, newnode, placer))
|
||||
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
|
||||
minetest.register_on_chat_message(func(name, message))
|
||||
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_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_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
|
||||
^ location = eg. {type="player", name="celeron55"}
|
||||
{type="node", pos={x=, y=, z=}}
|
||||
|
||||
Sounds:
|
||||
minetest.sound_play(spec, parameters) -> handle
|
||||
^ spec = SimpleSoundSpec
|
||||
^ parameters = sound parameter table
|
||||
minetest.sound_stop(handle)
|
||||
|
||||
Timing:
|
||||
minetest.after(time, func, param)
|
||||
^ Call function after time seconds
|
||||
^ param is optional; to pass multiple parameters, pass a table.
|
||||
|
@ -173,7 +173,6 @@ set(common_SRCS
|
||||
mapgen.cpp
|
||||
content_nodemeta.cpp
|
||||
content_mapnode.cpp
|
||||
auth.cpp
|
||||
collision.cpp
|
||||
nodemetadata.cpp
|
||||
serverobject.cpp
|
||||
|
306
src/auth.cpp
306
src/auth.cpp
@ -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;
|
||||
}
|
||||
|
||||
|
107
src/auth.h
107
src/auth.h
@ -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
|
||||
|
@ -141,14 +141,14 @@ static Server* get_server(lua_State *L)
|
||||
return server;
|
||||
}
|
||||
|
||||
static ServerEnvironment* get_env(lua_State *L)
|
||||
/*static ServerEnvironment* get_env(lua_State *L)
|
||||
{
|
||||
// Get environment from registry
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, "minetest_env");
|
||||
ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return env;
|
||||
}
|
||||
}*/
|
||||
|
||||
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
|
||||
*/
|
||||
@ -3837,8 +3858,7 @@ static int l_get_player_privs(lua_State *L)
|
||||
// Do it
|
||||
lua_newtable(L);
|
||||
int table = lua_gettop(L);
|
||||
u64 privs_i = server->getPlayerEffectivePrivs(name);
|
||||
std::set<std::string> privs_s = privsToSet(privs_i);
|
||||
std::set<std::string> privs_s = server->getPlayerEffectivePrivs(name);
|
||||
for(std::set<std::string>::const_iterator
|
||||
i = privs_s.begin(); i != privs_s.end(); i++){
|
||||
lua_pushboolean(L, true);
|
||||
@ -3954,6 +3974,17 @@ static int l_is_singleplayer(lua_State *L)
|
||||
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 [] = {
|
||||
{"debug", l_debug},
|
||||
{"log", l_log},
|
||||
@ -3974,6 +4005,7 @@ static const struct luaL_Reg minetest_f [] = {
|
||||
{"sound_play", l_sound_play},
|
||||
{"sound_stop", l_sound_stop},
|
||||
{"is_singleplayer", l_is_singleplayer},
|
||||
{"get_password_hash", l_get_password_hash},
|
||||
{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)
|
||||
{
|
||||
realitycheck(L);
|
||||
assert(lua_checkstack(L, 20));
|
||||
StackUnroller stack_unroller(L);
|
||||
|
||||
Inventory *inv = player->getInventory();
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "irrlichttypes.h"
|
||||
#include <string>
|
||||
#include "mapnode.h"
|
||||
#include <set>
|
||||
|
||||
class Server;
|
||||
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_leaveplayer(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 */
|
||||
bool scriptapi_item_on_drop(lua_State *L, ItemStack &item,
|
||||
|
178
src/server.cpp
178
src/server.cpp
@ -142,6 +142,10 @@ void * ServerThread::Thread()
|
||||
{
|
||||
m_server->setAsyncFatalError(e.what());
|
||||
}
|
||||
catch(LuaError &e)
|
||||
{
|
||||
m_server->setAsyncFatalError(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
END_DEBUG_EXCEPTION_HANDLER(errorstream)
|
||||
@ -905,7 +909,6 @@ Server::Server(
|
||||
m_async_fatal_error(""),
|
||||
m_env(NULL),
|
||||
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_lua(NULL),
|
||||
m_itemdef(createItemDefManager()),
|
||||
@ -1844,10 +1847,6 @@ void Server::AsyncRunStep()
|
||||
|
||||
ScopeProfiler sp(g_profiler, "Server: saving stuff");
|
||||
|
||||
// Auth stuff
|
||||
if(m_authmanager.isModified())
|
||||
m_authmanager.save();
|
||||
|
||||
//Ban stuff
|
||||
if(m_banmanager.isModified())
|
||||
m_banmanager.save();
|
||||
@ -2083,34 +2082,30 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
||||
password[PASSWORD_SIZE-1] = 0;
|
||||
}
|
||||
|
||||
// Add player to auth manager
|
||||
if(m_authmanager.exists(playername) == false)
|
||||
{
|
||||
std::wstring default_password =
|
||||
std::string checkpwd;
|
||||
bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
|
||||
|
||||
if(!has_auth){
|
||||
std::wstring raw_default_password =
|
||||
narrow_to_wide(g_settings->get("default_password"));
|
||||
std::string translated_default_password =
|
||||
translatePassword(playername, default_password);
|
||||
std::string use_password =
|
||||
translatePassword(playername, raw_default_password);
|
||||
|
||||
// If default_password is empty, allow any initial password
|
||||
if (default_password.length() == 0)
|
||||
translated_default_password = password;
|
||||
if (raw_default_password.length() == 0)
|
||||
use_password = password;
|
||||
|
||||
infostream<<"Server: adding player "<<playername
|
||||
<<" 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();
|
||||
scriptapi_create_auth(m_lua, playername, use_password);
|
||||
}
|
||||
|
||||
has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
|
||||
|
||||
if(!has_auth){
|
||||
SendAccessDenied(m_con, peer_id, L"Not allowed to login");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string checkpwd = m_authmanager.getPassword(playername);
|
||||
|
||||
/*infostream<<"Server: Client gave password '"<<password
|
||||
<<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
|
||||
|
||||
if(password != checkpwd)
|
||||
{
|
||||
if(password != checkpwd){
|
||||
infostream<<"Server: peer_id="<<peer_id
|
||||
<<": supplied invalid password for "
|
||||
<<playername<<std::endl;
|
||||
@ -2131,8 +2126,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
||||
// Enforce user limit.
|
||||
// Don't enforce for users that have some admin right
|
||||
if(m_clients.size() >= g_settings->getU16("max_users") &&
|
||||
(m_authmanager.getPrivs(playername)
|
||||
& (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS|PRIV_PASSWORD)) == 0 &&
|
||||
!checkPriv(playername, "server") &&
|
||||
!checkPriv(playername, "ban") &&
|
||||
!checkPriv(playername, "privs") &&
|
||||
!checkPriv(playername, "password") &&
|
||||
playername != g_settings->get("name"))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
|
||||
if(!checkPriv(player->getName(), "interact"))
|
||||
return;
|
||||
/*
|
||||
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
|
||||
// if not allowed to interact
|
||||
if((getPlayerPrivs(player) & PRIV_INTERACT) == 0
|
||||
&& (!from_inv_is_current_player
|
||||
|| !to_inv_is_current_player))
|
||||
if(!checkPriv(player->getName(), "interact") &&
|
||||
(!from_inv_is_current_player ||
|
||||
!to_inv_is_current_player))
|
||||
{
|
||||
infostream<<"Cannot move outside of player's inventory: "
|
||||
<<"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((getPlayerPrivs(player) & PRIV_SERVER) == 0)
|
||||
if(!checkPriv(player->getName(), "server"))
|
||||
{
|
||||
std::string owner_from = getInventoryOwner(ma->from_inv);
|
||||
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);
|
||||
|
||||
// Disallow dropping items if not allowed to interact
|
||||
if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
|
||||
if(!checkPriv(player->getName(), "interact"))
|
||||
{
|
||||
delete a;
|
||||
return;
|
||||
}
|
||||
// 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);
|
||||
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());
|
||||
|
||||
// Disallow crafting if not allowed to interact
|
||||
if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
|
||||
if(!checkPriv(player->getName(), "interact"))
|
||||
{
|
||||
infostream<<"Cannot craft: "
|
||||
<<"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((getPlayerPrivs(player) & PRIV_SERVER) == 0)
|
||||
if(!checkPriv(player->getName(), "server"))
|
||||
{
|
||||
std::string owner_craft = getInventoryOwner(ca->craft_inv);
|
||||
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
|
||||
bool send_to_others = false;
|
||||
|
||||
// Local player gets all privileges regardless of
|
||||
// what's set on their account.
|
||||
u64 privs = getPlayerPrivs(player);
|
||||
|
||||
// Parse commands
|
||||
if(message[0] == L'/')
|
||||
{
|
||||
@ -2688,8 +2681,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
||||
paramstring,
|
||||
this,
|
||||
m_env,
|
||||
player,
|
||||
privs);
|
||||
player);
|
||||
|
||||
std::wstring reply(processServerCommand(ctx));
|
||||
send_to_sender = ctx->flags & SEND_TO_SENDER;
|
||||
@ -2705,16 +2697,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
||||
}
|
||||
else
|
||||
{
|
||||
if(privs & PRIV_SHOUT)
|
||||
{
|
||||
if(checkPriv(player->getName(), "shout")){
|
||||
line += L"<";
|
||||
line += name;
|
||||
line += L"> ";
|
||||
line += message;
|
||||
send_to_others = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
line += L"Server: You are not allowed to shout";
|
||||
send_to_sender = true;
|
||||
}
|
||||
@ -2803,15 +2792,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
||||
|
||||
std::string playername = player->getName();
|
||||
|
||||
if(m_authmanager.exists(playername) == false)
|
||||
{
|
||||
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);
|
||||
std::string checkpwd;
|
||||
scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
|
||||
|
||||
if(oldpwd != checkpwd)
|
||||
{
|
||||
@ -2821,13 +2803,15 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
||||
return;
|
||||
}
|
||||
|
||||
actionstream<<player->getName()<<" changes password"<<std::endl;
|
||||
|
||||
m_authmanager.setPassword(playername, newpwd);
|
||||
|
||||
infostream<<"Server: password change successful for "<<playername
|
||||
<<std::endl;
|
||||
SendChatMessage(peer_id, L"Password change successful");
|
||||
bool success = scriptapi_set_password(m_lua, playername, newpwd);
|
||||
if(success){
|
||||
actionstream<<player->getName()<<" changes password"<<std::endl;
|
||||
SendChatMessage(peer_id, L"Password change successful");
|
||||
} else {
|
||||
actionstream<<player->getName()<<" tries to change password but "
|
||||
<<"it fails"<<std::endl;
|
||||
SendChatMessage(peer_id, L"Password change failed or inavailable");
|
||||
}
|
||||
}
|
||||
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
|
||||
*/
|
||||
if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
|
||||
if(!checkPriv(player->getName(), "interact"))
|
||||
{
|
||||
infostream<<"Ignoring interaction from player "<<player->getName()
|
||||
<<" because privileges are "<<getPlayerPrivs(player)
|
||||
<<std::endl;
|
||||
<<" (no interact privilege)"<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -4277,54 +4260,17 @@ std::wstring Server::getStatusString()
|
||||
return os.str();
|
||||
}
|
||||
|
||||
u64 Server::getPlayerAuthPrivs(const std::string &name)
|
||||
std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
|
||||
{
|
||||
try{
|
||||
return m_authmanager.getPrivs(name);
|
||||
}
|
||||
catch(AuthNotFoundException &e)
|
||||
{
|
||||
dstream<<"WARNING: Auth not found for "<<name<<std::endl;
|
||||
return 0;
|
||||
}
|
||||
std::set<std::string> privs;
|
||||
scriptapi_get_auth(m_lua, name, NULL, &privs);
|
||||
return privs;
|
||||
}
|
||||
|
||||
void Server::setPlayerAuthPrivs(const std::string &name, u64 privs)
|
||||
bool Server::checkPriv(const std::string &name, const std::string &priv)
|
||||
{
|
||||
try{
|
||||
return m_authmanager.setPrivs(name, privs);
|
||||
}
|
||||
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();
|
||||
std::set<std::string> privs = getPlayerEffectivePrivs(name);
|
||||
return (privs.count(priv) != 0);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
DSTACK(__FUNCTION_NAME);
|
||||
|
17
src/server.h
17
src/server.h
@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "porting.h"
|
||||
#include "map.h"
|
||||
#include "inventory.h"
|
||||
#include "auth.h"
|
||||
#include "ban.h"
|
||||
#include "gamedef.h"
|
||||
#include "serialization.h" // For SER_FMT_VER_INVALID
|
||||
@ -500,15 +499,10 @@ public:
|
||||
s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams ¶ms);
|
||||
void stopSound(s32 handle);
|
||||
|
||||
// Thread-safe
|
||||
u64 getPlayerAuthPrivs(const std::string &name);
|
||||
void setPlayerAuthPrivs(const std::string &name, u64 privs);
|
||||
u64 getPlayerEffectivePrivs(const std::string &name);
|
||||
// Envlock + conlock
|
||||
std::set<std::string> getPlayerEffectivePrivs(const std::string &name);
|
||||
bool checkPriv(const std::string &name, const std::string &priv);
|
||||
|
||||
// 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
|
||||
void saveConfig();
|
||||
|
||||
@ -670,8 +664,6 @@ private:
|
||||
void handlePeerChange(PeerChange &c);
|
||||
void handlePeerChanges();
|
||||
|
||||
u64 getPlayerPrivs(Player *player);
|
||||
|
||||
/*
|
||||
Variables
|
||||
*/
|
||||
@ -710,9 +702,6 @@ private:
|
||||
// Connected clients (behind the con mutex)
|
||||
core::map<u16, RemoteClient*> m_clients;
|
||||
|
||||
// User authentication
|
||||
AuthManager m_authmanager;
|
||||
|
||||
// Bann checking
|
||||
BanManager m_banmanager;
|
||||
|
||||
|
@ -38,98 +38,6 @@ void cmd_me(std::wostringstream &os,
|
||||
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,
|
||||
ServerCommandContext *ctx)
|
||||
{
|
||||
@ -138,8 +46,8 @@ void cmd_time(std::wostringstream &os,
|
||||
os<<L"-!- Missing parameter";
|
||||
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";
|
||||
return;
|
||||
@ -156,7 +64,7 @@ void cmd_time(std::wostringstream &os,
|
||||
void cmd_shutdown(std::wostringstream &os,
|
||||
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";
|
||||
return;
|
||||
@ -174,7 +82,7 @@ void cmd_shutdown(std::wostringstream &os,
|
||||
void cmd_setting(std::wostringstream &os,
|
||||
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";
|
||||
return;
|
||||
@ -198,7 +106,7 @@ void cmd_setting(std::wostringstream &os,
|
||||
void cmd_teleport(std::wostringstream &os,
|
||||
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";
|
||||
return;
|
||||
@ -241,7 +149,7 @@ void cmd_teleport(std::wostringstream &os,
|
||||
|
||||
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";
|
||||
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,
|
||||
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";
|
||||
return;
|
||||
@ -378,10 +234,6 @@ std::wstring processServerCommand(ServerCommandContext *ctx)
|
||||
|
||||
if(ctx->parms[0] == L"status")
|
||||
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")
|
||||
cmd_time(os, ctx);
|
||||
else if(ctx->parms[0] == L"shutdown")
|
||||
@ -392,8 +244,6 @@ std::wstring processServerCommand(ServerCommandContext *ctx)
|
||||
cmd_teleport(os, ctx);
|
||||
else if(ctx->parms[0] == L"ban" || ctx->parms[0] == L"unban")
|
||||
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")
|
||||
cmd_me(os, ctx);
|
||||
else if(ctx->parms[0] == L"clearobjects")
|
||||
|
@ -36,9 +36,6 @@ struct ServerCommandContext
|
||||
Server* server;
|
||||
ServerEnvironment *env;
|
||||
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;
|
||||
|
||||
ServerCommandContext(
|
||||
@ -46,10 +43,9 @@ struct ServerCommandContext
|
||||
std::wstring paramstring,
|
||||
Server* server,
|
||||
ServerEnvironment *env,
|
||||
Player* player,
|
||||
u64 privs)
|
||||
Player* player)
|
||||
: parms(parms), paramstring(paramstring),
|
||||
server(server), env(env), player(player), privs(privs)
|
||||
server(server), env(env), player(player)
|
||||
{
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user