Only create one alias metatable
[oweals/minetest.git] / builtin / auth.lua
1 -- Minetest: builtin/auth.lua
2
3 --
4 -- Authentication handler
5 --
6
7 function minetest.string_to_privs(str, delim)
8         assert(type(str) == "string")
9         delim = delim or ','
10         privs = {}
11         for _, priv in pairs(string.split(str, delim)) do
12                 privs[priv:trim()] = true
13         end
14         return privs
15 end
16
17 function minetest.privs_to_string(privs, delim)
18         assert(type(privs) == "table")
19         delim = delim or ','
20         list = {}
21         for priv, bool in pairs(privs) do
22                 if bool then
23                         table.insert(list, priv)
24                 end
25         end
26         return table.concat(list, delim)
27 end
28
29 assert(minetest.string_to_privs("a,b").b == true)
30 assert(minetest.privs_to_string({a=true,b=true}) == "a,b")
31
32 minetest.auth_file_path = minetest.get_worldpath().."/auth.txt"
33 minetest.auth_table = {}
34
35 local function read_auth_file()
36         local newtable = {}
37         local file, errmsg = io.open(minetest.auth_file_path, 'rb')
38         if not file then
39                 minetest.log("info", minetest.auth_file_path.." could not be opened for reading ("..errmsg.."); assuming new world")
40                 return
41         end
42         for line in file:lines() do
43                 if line ~= "" then
44                         local name, password, privilegestring = string.match(line, "([^:]*):([^:]*):([^:]*)")
45                         if not name or not password or not privilegestring then
46                                 error("Invalid line in auth.txt: "..dump(line))
47                         end
48                         local privileges = minetest.string_to_privs(privilegestring)
49                         newtable[name] = {password=password, privileges=privileges}
50                 end
51         end
52         io.close(file)
53         minetest.auth_table = newtable
54         minetest.notify_authentication_modified()
55 end
56
57 local function save_auth_file()
58         local newtable = {}
59         -- Check table for validness before attempting to save
60         for name, stuff in pairs(minetest.auth_table) do
61                 assert(type(name) == "string")
62                 assert(name ~= "")
63                 assert(type(stuff) == "table")
64                 assert(type(stuff.password) == "string")
65                 assert(type(stuff.privileges) == "table")
66         end
67         local file, errmsg = io.open(minetest.auth_file_path, 'w+b')
68         if not file then
69                 error(minetest.auth_file_path.." could not be opened for writing: "..errmsg)
70         end
71         for name, stuff in pairs(minetest.auth_table) do
72                 local privstring = minetest.privs_to_string(stuff.privileges)
73                 file:write(name..":"..stuff.password..":"..privstring..'\n')
74         end
75         io.close(file)
76 end
77
78 read_auth_file()
79
80 minetest.builtin_auth_handler = {
81         get_auth = function(name)
82                 assert(type(name) == "string")
83                 -- Figure out what password to use for a new player (singleplayer
84                 -- always has an empty password, otherwise use default, which is
85                 -- usually empty too)
86                 local new_password_hash = ""
87                 -- If not in authentication table, return nil
88                 if not minetest.auth_table[name] then
89                         return nil
90                 end
91                 -- Figure out what privileges the player should have.
92                 -- Take a copy of the privilege table
93                 local privileges = {}
94                 for priv, _ in pairs(minetest.auth_table[name].privileges) do
95                         privileges[priv] = true
96                 end
97                 -- If singleplayer, give all privileges except those marked as give_to_singleplayer = false
98                 if minetest.is_singleplayer() then
99                         for priv, def in pairs(minetest.registered_privileges) do
100                                 if def.give_to_singleplayer then
101                                         privileges[priv] = true
102                                 end
103                         end
104                 -- For the admin, give everything
105                 elseif name == minetest.setting_get("name") then
106                         for priv, def in pairs(minetest.registered_privileges) do
107                                 privileges[priv] = true
108                         end
109                 end
110                 -- All done
111                 return {
112                         password = minetest.auth_table[name].password,
113                         privileges = privileges,
114                 }
115         end,
116         create_auth = function(name, password)
117                 assert(type(name) == "string")
118                 assert(type(password) == "string")
119                 minetest.log('info', "Built-in authentication handler adding player '"..name.."'")
120                 minetest.auth_table[name] = {
121                         password = password,
122                         privileges = minetest.string_to_privs(minetest.setting_get("default_privs")),
123                 }
124                 save_auth_file()
125         end,
126         set_password = function(name, password)
127                 assert(type(name) == "string")
128                 assert(type(password) == "string")
129                 if not minetest.auth_table[name] then
130                         minetest.builtin_auth_handler.create_auth(name, password)
131                 else
132                         minetest.log('info', "Built-in authentication handler setting password of player '"..name.."'")
133                         minetest.auth_table[name].password = password
134                         save_auth_file()
135                 end
136                 return true
137         end,
138         set_privileges = function(name, privileges)
139                 assert(type(name) == "string")
140                 assert(type(privileges) == "table")
141                 if not minetest.auth_table[name] then
142                         minetest.builtin_auth_handler.create_auth(name, minetest.get_password_hash(name, minetest.setting_get("default_password")))
143                 end
144                 minetest.auth_table[name].privileges = privileges
145                 minetest.notify_authentication_modified(name)
146                 save_auth_file()
147         end,
148         reload = function()
149                 read_auth_file()
150                 return true
151         end,
152 }
153
154 function minetest.register_authentication_handler(handler)
155         if minetest.registered_auth_handler then
156                 error("Add-on authentication handler already registered by "..minetest.registered_auth_handler_modname)
157         end
158         minetest.registered_auth_handler = handler
159         minetest.registered_auth_handler_modname = minetest.get_current_modname()
160 end
161
162 function minetest.get_auth_handler()
163         if minetest.registered_auth_handler then
164                 return minetest.registered_auth_handler
165         end
166         return minetest.builtin_auth_handler
167 end
168
169 function minetest.set_player_password(name, password)
170         if minetest.get_auth_handler().set_password then
171                 minetest.get_auth_handler().set_password(name, password)
172         end
173 end
174
175 function minetest.set_player_privs(name, privs)
176         if minetest.get_auth_handler().set_privileges then
177                 minetest.get_auth_handler().set_privileges(name, privs)
178         end
179 end
180
181 function minetest.auth_reload()
182         if minetest.get_auth_handler().reload then
183                 return minetest.get_auth_handler().reload()
184         end
185         return false
186 end
187
188