Miroir du dépôt mysql_auth.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

362 lines
13KB

  1. local modname = minetest.get_current_modname()
  2. local modpath = minetest.get_modpath(modname)
  3. local thismod = {
  4. enabled = false
  5. }
  6. _G[modname] = thismod
  7. local LogI = mysql_base.mklog('action', modname)
  8. local LogE = mysql_base.mklog('error', modname)
  9. if not mysql_base.enabled then
  10. LogI("mysql_base disabled, not loading mod")
  11. return
  12. end
  13. local singleplayer = minetest.is_singleplayer() -- Caching is OK since you can't open a game to
  14. -- multiplayer unless you restart it.
  15. if not minetest.settings:get(modname .. '.enable_singleplayer') and singleplayer then
  16. LogI("Not adding auth handler because of singleplayer game")
  17. return
  18. end
  19. thismod.enabled = true
  20. local LogV = function() end
  21. do
  22. local get = mysql_base.mkget(modname)
  23. if get('verbose') == 'true' then
  24. LogI("Verbose logging enabled")
  25. LogV = mysql_base.mklog('verbose', modname)
  26. end
  27. local conn, dbname = mysql_base.conn, mysql_base.dbname
  28. local tables = {}
  29. thismod.tables = tables
  30. do -- Tables and schema settings
  31. local t_auths = get('db.tables.auths')
  32. if type(t_auths) == 'table' then
  33. tables.auths = t_auths
  34. else
  35. tables.auths = {}
  36. tables.auths.name = get('db.tables.auths.name')
  37. tables.auths.schema = {}
  38. local S = tables.auths.schema
  39. S.userid = get('db.tables.auths.schema.userid')
  40. S.username = get('db.tables.auths.schema.username')
  41. S.password = get('db.tables.auths.schema.password')
  42. S.privs = get('db.tables.auths.schema.privs')
  43. S.lastlogin = get('db.tables.auths.schema.lastlogin')
  44. S.userid_type = get('db.tables.auths.schema.userid_type')
  45. S.username_type = get('db.tables.auths.schema.username_type')
  46. S.password_type = get('db.tables.auths.schema.password_type')
  47. S.privs_type = get('db.tables.auths.schema.privs_type')
  48. S.lastlogin_type = get('db.tables.auths.schema.lastlogin_type')
  49. end
  50. do -- Default values
  51. tables.auths.name = tables.auths.name or 'auths'
  52. tables.auths.schema = tables.auths.schema or {}
  53. local S = tables.auths.schema
  54. S.userid = S.userid or 'userid'
  55. S.username = S.username or 'username'
  56. S.password = S.password or 'password'
  57. S.privs = S.privs or 'privs'
  58. S.lastlogin = S.lastlogin or 'lastlogin'
  59. S.userid_type = S.userid_type or 'INT'
  60. S.username_type = S.username_type or 'VARCHAR(32)'
  61. S.password_type = S.password_type or 'VARCHAR(512)'
  62. S.privs_type = S.privs_type or 'VARCHAR(512)'
  63. S.lastlogin_type = S.lastlogin_type or 'BIGINT'
  64. -- Note lastlogin doesn't use the TIMESTAMP type, which is 32-bit and therefore
  65. -- subject to the year 2038 problem.
  66. end
  67. end
  68. local auth_table_created
  69. -- Auth table existence check and setup
  70. if not mysql_base.table_exists(tables.auths.name) then
  71. -- Auth table doesn't exist, create it
  72. local S = tables.auths.schema
  73. mysql_base.create_table(tables.auths.name, {
  74. columns = {
  75. {S.userid, S.userid_type, notnull = true, autoincrement = true},
  76. {S.username, S.username_type, notnull = true},
  77. {S.password, S.password_type, notnull = true},
  78. {S.privs, S.privs_type, notnull = true},
  79. {S.lastlogin, S.lastlogin_type},
  80. },
  81. pkey = {S.userid},
  82. unique = {S.username},
  83. })
  84. LogI("Created table '" .. dbname .. "." .. tables.auths.name .. "'")
  85. auth_table_created = true
  86. end
  87. local S = tables.auths.schema
  88. local get_auth_stmt, get_auth_params, get_auth_results = mysql_base.prepare_select(
  89. tables.auths.name, {
  90. {S.userid, S.userid_type},
  91. {S.password, S.password_type},
  92. {S.privs, S.privs_type},
  93. {S.lastlogin, S.lastlogin_type}},
  94. S.username .. '=?', {S.username_type})
  95. thismod.get_auth_stmt = get_auth_stmt
  96. local create_auth_stmt, create_auth_params = mysql_base.prepare_insert(
  97. tables.auths.name, {
  98. {S.username, S.username_type},
  99. {S.password, S.password_type},
  100. {S.privs, S.privs_type},
  101. {S.lastlogin, S.lastlogin_type},
  102. })
  103. thismod.create_auth_stmt = create_auth_stmt
  104. thismod.create_auth_params = create_auth_params
  105. local max_name_len = tonumber(create_auth_params.buffer[0].buffer_length)
  106. local max_pass_len = tonumber(create_auth_params.buffer[1].buffer_length)
  107. local delete_auth_stmt, delete_auth_params = mysql_base.prepare_delete(tables.auths.name,
  108. S.username .. '=?', {S.username_type})
  109. local set_password_stmt, set_password_params = mysql_base.prepare_update(tables.auths.name,
  110. {{S.password, S.password_type}},
  111. S.username .. '=?', {S.username_type})
  112. local set_privileges_stmt, set_privileges_params = mysql_base.prepare_update(tables.auths.name,
  113. {{S.privs, S.privs_type}},
  114. S.username .. '=?', {S.username_type})
  115. local max_privs_len = tonumber(set_privileges_params.buffer[0].buffer_length)
  116. local record_login_stmt, record_login_params = mysql_base.prepare_update(tables.auths.name,
  117. {{S.lastlogin, S.lastlogin_type}},
  118. S.username .. '=?', {S.username_type})
  119. local enumerate_auths_query = 'SELECT ' .. S.username .. ',' .. S.password .. ',' .. S.privs ..
  120. ',' .. S.lastlogin .. ' FROM ' .. tables.auths.name
  121. thismod.enumerate_auths_query = enumerate_auths_query
  122. if auth_table_created and get('import_auth_txt_on_table_create') == 'true' then
  123. if not thismod.import_auth_txt then
  124. dofile(modpath .. '/auth_txt_import.lua')
  125. end
  126. thismod.import_auth_txt()
  127. end
  128. thismod.auth_handler = {
  129. get_auth = function(name)
  130. assert(type(name) == 'string')
  131. if name:len() > max_name_len then
  132. LogE("get_auth(" .. name .. ") failed: name too long (max " .. max_name_len .. ")")
  133. return nil
  134. end
  135. get_auth_params:set(1, name)
  136. local success, msg = pcall(get_auth_stmt.exec, get_auth_stmt)
  137. if not success then
  138. LogE("get_auth(" .. name .. ") failed: " .. msg)
  139. return nil
  140. end
  141. get_auth_stmt:store_result()
  142. if not get_auth_stmt:fetch() then
  143. -- No such auth row exists
  144. return nil
  145. end
  146. while get_auth_stmt:fetch() do
  147. error(modname .. ": get_auth(" .. name .. "): multiples lines were returned")
  148. end
  149. local userid, password, privs_str, lastlogin = get_auth_results:get(1),
  150. get_auth_results:get(2), get_auth_results:get(3), get_auth_results:get(4)
  151. local admin
  152. if minetest.settings then
  153. admin = (name == minetest.settings:get("name"))
  154. else
  155. admin = (name == minetest.setting_get("name"))
  156. end
  157. local privs
  158. if singleplayer or admin then
  159. privs = {}
  160. -- If admin, grant all privs, if singleplayer, grant all privs w/ give_to_singleplayer
  161. for priv, def in pairs(core.registered_privileges) do
  162. if (singleplayer and def.give_to_singleplayer) or admin then
  163. privs[priv] = true
  164. end
  165. end
  166. if admin and not thismod.admin_get_auth_called then
  167. thismod.admin_get_auth_called = true
  168. thismod.auth_handler.set_privileges(name, privs)
  169. end
  170. else
  171. privs = minetest.string_to_privs(privs_str)
  172. end
  173. LogV("get_auth(" .. name .. ") -> {userid:" .. userid .. ", privileges: " ..
  174. table.concat(privs, ',') .. "}")
  175. return {
  176. userid = userid,
  177. password = password,
  178. privileges = privs,
  179. last_login = tonumber(lastlogin)
  180. }
  181. end,
  182. create_auth = function(name, password, reason)
  183. assert(type(name) == 'string')
  184. assert(type(password) == 'string')
  185. LogV("create_auth(" .. name .. ", ###" .. (reason and (", " .. reason) or "") .. ")")
  186. LogI("Creating player '" .. name .. "'" .. (reason or ""))
  187. if name:len() > max_name_len then
  188. LogE("create_auth(" .. name .. ") failed: name too long (max " .. max_name_len .. ")")
  189. return false
  190. end
  191. if password:len() > max_pass_len then
  192. LogE("create_auth(" .. name .. ") failed: password too long (max " .. max_pass_len .. ")")
  193. return false
  194. end
  195. create_auth_params:set(1, name)
  196. create_auth_params:set(2, password)
  197. if minetest.settings then
  198. create_auth_params:set(3, minetest.settings:get("default_privs"))
  199. else
  200. create_auth_params:set(3, minetest.setting_get("default_privs"))
  201. end
  202. create_auth_params:set(4, math.floor(os.time()))
  203. local success, msg = pcall(create_auth_stmt.exec, create_auth_stmt)
  204. if not success then
  205. LogE("create_auth(" .. name .. ") failed: " .. msg)
  206. return false
  207. end
  208. if create_auth_stmt:affected_rows() ~= 1 then
  209. LogE("create_auth(" .. name .. ") failed: affected row count is " ..
  210. create_auth_stmt:affected_rows() .. ", expected 1")
  211. return false
  212. end
  213. return true
  214. end,
  215. delete_auth = function(name)
  216. assert(type(name) == 'string')
  217. LogV("delete_auth(" .. name .. ")")
  218. LogI("Deleting player '"..name.."'")
  219. if name:len() > max_name_len then
  220. LogE("delete_auth(" .. name .. ") failed: name too long (max " .. max_name_len .. ")")
  221. return false
  222. end
  223. delete_auth_params:set(1, name)
  224. local success, msg = pcall(delete_auth_stmt.exec, delete_auth_stmt)
  225. if not success then
  226. LogE("delete_auth(" .. name .. ") failed: " .. msg)
  227. return false
  228. end
  229. if delete_auth_stmt:affected_rows() ~= 1 then
  230. LogE("delete_auth(" .. name .. ") failed: affected row count is " ..
  231. delete_auth_stmt:affected_rows() .. ", expected 1")
  232. return false
  233. end
  234. return true
  235. end,
  236. set_password = function(name, password)
  237. assert(type(name) == 'string')
  238. assert(type(password) == 'string')
  239. LogV("set_password(" .. name .. ", ###)")
  240. if name:len() > max_name_len then
  241. LogE("create_auth(" .. name .. ") failed: name too long (max " .. max_name_len .. ")")
  242. return false
  243. end
  244. if password:len() > max_pass_len then
  245. LogE("create_auth(" .. name .. ") failed: password too long (max " .. max_pass_len .. ")")
  246. return false
  247. end
  248. if not thismod.auth_handler.get_auth(name) then
  249. return thismod.auth_handler.create_auth(name, password, " because set_password was requested")
  250. else
  251. LogI("Setting password of player '" .. name .. "'")
  252. set_password_params:set(1, password)
  253. set_password_params:set(2, name)
  254. local success, msg = pcall(set_password_stmt.exec, set_password_stmt)
  255. if not success then
  256. LogE("set_password(" .. name .. ") failed: " .. msg)
  257. return false
  258. end
  259. if set_password_stmt:affected_rows() ~= 1 then
  260. LogE("set_password(" .. name .. ") failed: affected row count is " ..
  261. set_password_stmt:affected_rows() .. ", expected 1")
  262. return false
  263. end
  264. return true
  265. end
  266. end,
  267. set_privileges = function(name, privileges)
  268. assert(type(name) == 'string')
  269. assert(type(privileges) == 'table')
  270. local privstr = minetest.privs_to_string(privileges)
  271. LogV("set_privileges(" .. name .. ", {" .. table.concat(privileges, ', ') .. "}) [" ..
  272. privstr .. "]")
  273. if name:len() > max_name_len then
  274. LogE("set_privileges(" .. name .. ") failed: name too long (max " .. max_name_len .. ")")
  275. return false
  276. end
  277. if privstr:len() > max_privs_len then
  278. LogE("create_auth(" .. name .. ") failed: priv string too long (max " ..
  279. max_privs_len .. ")")
  280. return false
  281. end
  282. set_privileges_params:set(1, privstr)
  283. set_privileges_params:set(2, name)
  284. local success, msg = pcall(set_privileges_stmt.exec, set_privileges_stmt)
  285. if not success then
  286. LogE("set_privileges(" .. name .. ") failed: " .. msg)
  287. return false
  288. end
  289. minetest.notify_authentication_modified(name)
  290. return true
  291. end,
  292. reload = function()
  293. return true
  294. end,
  295. record_login = function(name)
  296. assert(type(name) == 'string')
  297. LogV("record_login(" .. name .. ")")
  298. if name:len() > max_name_len then
  299. LogE("set_privileges(" .. name .. ") failed: name too long (max " .. max_name_len .. ")")
  300. return false
  301. end
  302. record_login_params:set(1, math.floor(os.time()))
  303. record_login_params:set(2, name)
  304. local success, msg = pcall(record_login_stmt.exec, record_login_stmt)
  305. if not success then
  306. LogE("record_login(" .. name .. ") failed: " .. msg)
  307. return false
  308. end
  309. if record_login_stmt:affected_rows() ~= 1 then
  310. LogE("record_login(" .. name .. ") failed: affected row count is " ..
  311. record_login_stmt:affected_rows() .. ", expected 1")
  312. return false
  313. end
  314. return true
  315. end,
  316. enumerate_auths = function()
  317. LogV("enumerate_auths()")
  318. conn:query(enumerate_auths_query)
  319. local res = conn:store_result()
  320. return function()
  321. local row = res:fetch('n')
  322. if not row then
  323. return nil
  324. end
  325. local username, password, privs_str, lastlogin = unpack(row)
  326. return username, {
  327. password = password,
  328. privileges = minetest.string_to_privs(privs_str),
  329. last_login = tonumber(lastlogin)
  330. }
  331. end
  332. end
  333. }
  334. end
  335. minetest.register_authentication_handler(thismod.auth_handler)
  336. LogI("Registered auth handler")
  337. mysql_base.register_on_shutdown(function()
  338. thismod.get_auth_stmt:free_result()
  339. end)