auth.lua 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. -- Minetest: builtin/auth.lua
  2. --
  3. -- Builtin authentication handler
  4. --
  5. local auth_file_path = core.get_worldpath().."/auth.txt"
  6. local auth_table = {}
  7. local function read_auth_file()
  8. local newtable = {}
  9. local file, errmsg = io.open(auth_file_path, 'rb')
  10. if not file then
  11. core.log("info", auth_file_path.." could not be opened for reading ("..errmsg.."); assuming new world")
  12. return
  13. end
  14. for line in file:lines() do
  15. if line ~= "" then
  16. local fields = line:split(":", true)
  17. local name, password, privilege_string, last_login = unpack(fields)
  18. last_login = tonumber(last_login)
  19. if not (name and password and privilege_string) then
  20. error("Invalid line in auth.txt: "..dump(line))
  21. end
  22. local privileges = core.string_to_privs(privilege_string)
  23. newtable[name] = {password=password, privileges=privileges, last_login=last_login}
  24. end
  25. end
  26. io.close(file)
  27. auth_table = newtable
  28. core.notify_authentication_modified()
  29. end
  30. local function save_auth_file()
  31. local newtable = {}
  32. -- Check table for validness before attempting to save
  33. for name, stuff in pairs(auth_table) do
  34. assert(type(name) == "string")
  35. assert(name ~= "")
  36. assert(type(stuff) == "table")
  37. assert(type(stuff.password) == "string")
  38. assert(type(stuff.privileges) == "table")
  39. assert(stuff.last_login == nil or type(stuff.last_login) == "number")
  40. end
  41. local content = {}
  42. for name, stuff in pairs(auth_table) do
  43. local priv_string = core.privs_to_string(stuff.privileges)
  44. local parts = {name, stuff.password, priv_string, stuff.last_login or ""}
  45. content[#content + 1] = table.concat(parts, ":")
  46. end
  47. if not core.safe_file_write(auth_file_path, table.concat(content, "\n")) then
  48. error(auth_file_path.." could not be written to")
  49. end
  50. end
  51. read_auth_file()
  52. core.builtin_auth_handler = {
  53. get_auth = function(name)
  54. assert(type(name) == "string")
  55. -- Figure out what password to use for a new player (singleplayer
  56. -- always has an empty password, otherwise use default, which is
  57. -- usually empty too)
  58. local new_password_hash = ""
  59. -- If not in authentication table, return nil
  60. if not auth_table[name] then
  61. return nil
  62. end
  63. -- Figure out what privileges the player should have.
  64. -- Take a copy of the privilege table
  65. local privileges = {}
  66. for priv, _ in pairs(auth_table[name].privileges) do
  67. privileges[priv] = true
  68. end
  69. -- If singleplayer, give all privileges except those marked as give_to_singleplayer = false
  70. if core.is_singleplayer() then
  71. for priv, def in pairs(core.registered_privileges) do
  72. if def.give_to_singleplayer then
  73. privileges[priv] = true
  74. end
  75. end
  76. -- For the admin, give everything
  77. elseif name == core.settings:get("name") then
  78. for priv, def in pairs(core.registered_privileges) do
  79. if def.give_to_admin then
  80. privileges[priv] = true
  81. end
  82. end
  83. end
  84. -- All done
  85. return {
  86. password = auth_table[name].password,
  87. privileges = privileges,
  88. -- Is set to nil if unknown
  89. last_login = auth_table[name].last_login,
  90. }
  91. end,
  92. create_auth = function(name, password)
  93. assert(type(name) == "string")
  94. assert(type(password) == "string")
  95. core.log('info', "Built-in authentication handler adding player '"..name.."'")
  96. auth_table[name] = {
  97. password = password,
  98. privileges = core.string_to_privs(core.settings:get("default_privs")),
  99. last_login = os.time(),
  100. }
  101. save_auth_file()
  102. end,
  103. delete_auth = function(name)
  104. assert(type(name) == "string")
  105. if not auth_table[name] then
  106. return false
  107. end
  108. core.log('info', "Built-in authentication handler deleting player '"..name.."'")
  109. auth_table[name] = nil
  110. save_auth_file()
  111. return true
  112. end,
  113. set_password = function(name, password)
  114. assert(type(name) == "string")
  115. assert(type(password) == "string")
  116. if not auth_table[name] then
  117. core.builtin_auth_handler.create_auth(name, password)
  118. else
  119. core.log('info', "Built-in authentication handler setting password of player '"..name.."'")
  120. auth_table[name].password = password
  121. save_auth_file()
  122. end
  123. return true
  124. end,
  125. set_privileges = function(name, privileges)
  126. assert(type(name) == "string")
  127. assert(type(privileges) == "table")
  128. if not auth_table[name] then
  129. core.builtin_auth_handler.create_auth(name,
  130. core.get_password_hash(name,
  131. core.settings:get("default_password")))
  132. end
  133. -- Run grant callbacks
  134. for priv, _ in pairs(privileges) do
  135. if not auth_table[name].privileges[priv] then
  136. core.run_priv_callbacks(name, priv, nil, "grant")
  137. end
  138. end
  139. -- Run revoke callbacks
  140. for priv, _ in pairs(auth_table[name].privileges) do
  141. if not privileges[priv] then
  142. core.run_priv_callbacks(name, priv, nil, "revoke")
  143. end
  144. end
  145. auth_table[name].privileges = privileges
  146. core.notify_authentication_modified(name)
  147. save_auth_file()
  148. end,
  149. reload = function()
  150. read_auth_file()
  151. return true
  152. end,
  153. record_login = function(name)
  154. assert(type(name) == "string")
  155. assert(auth_table[name]).last_login = os.time()
  156. save_auth_file()
  157. end,
  158. iterate = function()
  159. local names = {}
  160. for k in pairs(auth_table) do
  161. names[k] = true
  162. end
  163. return pairs(names)
  164. end,
  165. }
  166. core.register_on_prejoinplayer(function(name, ip)
  167. if core.registered_auth_handler ~= nil then
  168. return -- Don't do anything if custom auth handler registered
  169. end
  170. if auth_table[name] ~= nil then
  171. return
  172. end
  173. local name_lower = name:lower()
  174. for k in pairs(auth_table) do
  175. if k:lower() == name_lower then
  176. return string.format("\nCannot create new player called '%s'. "..
  177. "Another account called '%s' is already registered. "..
  178. "Please check the spelling if it's your account "..
  179. "or use a different nickname.", name, k)
  180. end
  181. end
  182. end)
  183. --
  184. -- Authentication API
  185. --
  186. function core.register_authentication_handler(handler)
  187. if core.registered_auth_handler then
  188. error("Add-on authentication handler already registered by "..core.registered_auth_handler_modname)
  189. end
  190. core.registered_auth_handler = handler
  191. core.registered_auth_handler_modname = core.get_current_modname()
  192. handler.mod_origin = core.registered_auth_handler_modname
  193. end
  194. function core.get_auth_handler()
  195. return core.registered_auth_handler or core.builtin_auth_handler
  196. end
  197. local function auth_pass(name)
  198. return function(...)
  199. local auth_handler = core.get_auth_handler()
  200. if auth_handler[name] then
  201. return auth_handler[name](...)
  202. end
  203. return false
  204. end
  205. end
  206. core.set_player_password = auth_pass("set_password")
  207. core.set_player_privs = auth_pass("set_privileges")
  208. core.remove_player_auth = auth_pass("delete_auth")
  209. core.auth_reload = auth_pass("reload")
  210. local record_login = auth_pass("record_login")
  211. core.register_on_joinplayer(function(player)
  212. record_login(player:get_player_name())
  213. end)