auth.lua 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. -- Minetest: builtin/auth.lua
  2. --
  3. -- Authentication handler
  4. --
  5. core.auth_file_path = core.get_worldpath().."/auth.txt"
  6. core.auth_table = {}
  7. local function read_auth_file()
  8. local newtable = {}
  9. local file, errmsg = io.open(core.auth_file_path, 'rb')
  10. if not file then
  11. core.log("info", core.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. core.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(core.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 file, errmsg = io.open(core.auth_file_path, 'w+b')
  42. if not file then
  43. error(core.auth_file_path.." could not be opened for writing: "..errmsg)
  44. end
  45. for name, stuff in pairs(core.auth_table) do
  46. local priv_string = core.privs_to_string(stuff.privileges)
  47. local parts = {name, stuff.password, priv_string, stuff.last_login or ""}
  48. file:write(table.concat(parts, ":").."\n")
  49. end
  50. io.close(file)
  51. end
  52. read_auth_file()
  53. core.builtin_auth_handler = {
  54. get_auth = function(name)
  55. assert(type(name) == "string")
  56. -- Figure out what password to use for a new player (singleplayer
  57. -- always has an empty password, otherwise use default, which is
  58. -- usually empty too)
  59. local new_password_hash = ""
  60. -- If not in authentication table, return nil
  61. if not core.auth_table[name] then
  62. return nil
  63. end
  64. -- Figure out what privileges the player should have.
  65. -- Take a copy of the privilege table
  66. local privileges = {}
  67. for priv, _ in pairs(core.auth_table[name].privileges) do
  68. privileges[priv] = true
  69. end
  70. -- If singleplayer, give all privileges except those marked as give_to_singleplayer = false
  71. if core.is_singleplayer() then
  72. for priv, def in pairs(core.registered_privileges) do
  73. if def.give_to_singleplayer then
  74. privileges[priv] = true
  75. end
  76. end
  77. -- For the admin, give everything
  78. elseif name == core.settings:get("name") then
  79. for priv, def in pairs(core.registered_privileges) do
  80. privileges[priv] = true
  81. end
  82. end
  83. -- All done
  84. return {
  85. password = core.auth_table[name].password,
  86. privileges = privileges,
  87. -- Is set to nil if unknown
  88. last_login = core.auth_table[name].last_login,
  89. }
  90. end,
  91. create_auth = function(name, password)
  92. assert(type(name) == "string")
  93. assert(type(password) == "string")
  94. core.log('info', "Built-in authentication handler adding player '"..name.."'")
  95. core.auth_table[name] = {
  96. password = password,
  97. privileges = core.string_to_privs(core.settings:get("default_privs")),
  98. last_login = os.time(),
  99. }
  100. save_auth_file()
  101. end,
  102. set_password = function(name, password)
  103. assert(type(name) == "string")
  104. assert(type(password) == "string")
  105. if not core.auth_table[name] then
  106. core.builtin_auth_handler.create_auth(name, password)
  107. else
  108. core.log('info', "Built-in authentication handler setting password of player '"..name.."'")
  109. core.auth_table[name].password = password
  110. save_auth_file()
  111. end
  112. return true
  113. end,
  114. set_privileges = function(name, privileges)
  115. assert(type(name) == "string")
  116. assert(type(privileges) == "table")
  117. if not core.auth_table[name] then
  118. core.builtin_auth_handler.create_auth(name,
  119. core.get_password_hash(name,
  120. core.settings:get("default_password")))
  121. end
  122. core.auth_table[name].privileges = privileges
  123. core.notify_authentication_modified(name)
  124. save_auth_file()
  125. end,
  126. reload = function()
  127. read_auth_file()
  128. return true
  129. end,
  130. record_login = function(name)
  131. assert(type(name) == "string")
  132. assert(core.auth_table[name]).last_login = os.time()
  133. save_auth_file()
  134. end,
  135. }
  136. function core.register_authentication_handler(handler)
  137. if core.registered_auth_handler then
  138. error("Add-on authentication handler already registered by "..core.registered_auth_handler_modname)
  139. end
  140. core.registered_auth_handler = handler
  141. core.registered_auth_handler_modname = core.get_current_modname()
  142. handler.mod_origin = core.registered_auth_handler_modname
  143. end
  144. function core.get_auth_handler()
  145. return core.registered_auth_handler or core.builtin_auth_handler
  146. end
  147. local function auth_pass(name)
  148. return function(...)
  149. local auth_handler = core.get_auth_handler()
  150. if auth_handler[name] then
  151. return auth_handler[name](...)
  152. end
  153. return false
  154. end
  155. end
  156. core.set_player_password = auth_pass("set_password")
  157. core.set_player_privs = auth_pass("set_privileges")
  158. core.auth_reload = auth_pass("reload")
  159. local record_login = auth_pass("record_login")
  160. core.register_on_joinplayer(function(player)
  161. record_login(player:get_player_name())
  162. end)
  163. core.register_on_prejoinplayer(function(name, ip)
  164. local auth = core.auth_table
  165. if auth[name] ~= nil then
  166. return
  167. end
  168. local name_lower = name:lower()
  169. for k in pairs(auth) do
  170. if k:lower() == name_lower then
  171. return string.format("\nCannot create new player called '%s'. "..
  172. "Another account called '%s' is already registered. "..
  173. "Please check the spelling if it's your account "..
  174. "or use a different nickname.", name, k)
  175. end
  176. end
  177. end)