Advertisement
Guest User

Untitled

a guest
Mar 8th, 2017
140
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.20 KB | None | 0 0
  1. local modname = minetest.get_current_modname()
  2. local modpath = minetest.get_modpath(modname)
  3.  
  4. local thismod = {}
  5. _G[modname] = thismod
  6.  
  7. local singleplayer = minetest.is_singleplayer() -- Caching is OK since you can't open a game to
  8. -- multiplayer unless you restart it.
  9. if not minetest.setting_get(modname .. '.enable_singleplayer') and singleplayer then
  10. core.log('action', modname .. ": Not adding auth handler because of singleplayer game")
  11. return
  12. end
  13.  
  14. local function setoverlay(tab, orig)
  15. local mt = getmetatable(tab) or {}
  16. mt.__index = function (tab, key)
  17. if rawget(tab, key) ~= nil then
  18. return rawget(tab, key)
  19. else
  20. return orig[key]
  21. end
  22. end
  23. setmetatable(tab, mt)
  24. end
  25.  
  26. local function string_splitdots(s)
  27. local temp = {}
  28. local index = 0
  29. local last_index = string.len(s)
  30. while true do
  31. local i, e = string.find(s, '%.', index)
  32. if i and e then
  33. local next_index = e + 1
  34. local word_bound = i - 1
  35. table.insert(temp, string.sub(s, index, word_bound))
  36. index = next_index
  37. else
  38. if index > 0 and index <= last_index then
  39. table.insert(temp, string.sub(s, index, last_index))
  40. elseif index == 0 then
  41. temp = nil
  42. end
  43. break
  44. end
  45. end
  46. return temp
  47. end
  48.  
  49. local ie = minetest.request_insecure_environment()
  50. if not ie then error("Failed to get insecure environment - make sure this mod is trusted!") end
  51. local mysql
  52. do -- MySQL module loading
  53. local env = {
  54. require = function (module)
  55. if module == 'mysql_h' then
  56. return ie.dofile(modpath .. '/mysql/mysql_h.lua')
  57. else
  58. return ie.require(module)
  59. end
  60. end
  61. }
  62. setoverlay(env, _G)
  63. local fn, msg = ie.loadfile(modpath .. '/mysql/mysql.lua')
  64. if not fn then error(msg) end
  65. setfenv(fn, env)
  66. local status
  67. status, mysql = pcall(fn, {})
  68. if not status then
  69. error(modname .. ' failed to load MySQL FFI interface: ' .. mysql)
  70. end
  71. end
  72.  
  73. do
  74. local get
  75. do
  76. get = function (name) return minetest.setting_get(modname .. '.' .. name) end
  77. local cfgfile = get('cfgfile')
  78. if type(cfgfile) == 'string' and cfgfile ~= '' then
  79. local file = io.open(cfgfile, 'rb')
  80. if not file then
  81. error(modname .. ' failed to load specified config file at ' .. cfgfile)
  82. end
  83. local cfg, msg = minetest.deserialize(file:read('*a'))
  84. file:close()
  85. if not cfg then
  86. error(modname .. ' failed to parse specified config file at ' .. cfgfile .. ': ' .. msg)
  87. end
  88. get = function (name)
  89. if type(name) ~= 'string' or name == '' then
  90. return nil
  91. end
  92. local parts = string_splitdots(name)
  93. if not parts then
  94. return cfg[name]
  95. end
  96. local tbl = cfg[parts[1]]
  97. for n = 2, #parts do
  98. if tbl == nil then
  99. return nil
  100. end
  101. tbl = tbl[parts[n]]
  102. end
  103. return tbl
  104. end
  105. end
  106. end
  107.  
  108. local conn, dbname
  109. do
  110. -- MySQL API backend
  111. mysql.config(get('db.api'))
  112.  
  113. local connopts = get('db.connopts')
  114. if (get('db.db') == nil) and (type(connopts) == 'table' and connopts.db == nil) then
  115. error(modname .. ": missing database name parameter")
  116. end
  117. if type(connopts) ~= 'table' then
  118. connopts = {}
  119. -- Traditional connection parameters
  120. connopts.host, connopts.user, connopts.port, connopts.pass, connopts.db =
  121. get('db.host') or 'localhost', get('db.user'), get('db.port'), get('db.pass'), get('db.db')
  122. end
  123. connopts.charset = 'utf8'
  124. connopts.options = connopts.options or {}
  125. connopts.options.MYSQL_OPT_RECONNECT = true
  126. conn = mysql.connect(connopts)
  127. dbname = connopts.db
  128. thismod.conn = conn
  129.  
  130. -- LuaPower's MySQL interface throws an error when the connection fails, no need to check if
  131. -- it succeeded.
  132.  
  133. -- Ensure UTF-8 is in use.
  134. -- If you use another encoding, kill yourself (unless it's UTF-32).
  135. conn:query("SET NAMES 'utf8'")
  136. conn:query("SET CHARACTER SET utf8")
  137. conn:query("SET character_set_results = 'utf8', character_set_client = 'utf8'," ..
  138. "character_set_connection = 'utf8', character_set_database = 'utf8'," ..
  139. "character_set_server = 'utf8'")
  140.  
  141. local set = function(setting, val) conn:query('SET ' .. setting .. '=' .. val) end
  142. pcall(set, 'wait_timeout', 3600)
  143. pcall(set, 'autocommit', 1)
  144. pcall(set, 'max_allowed_packet', 67108864)
  145. end
  146.  
  147. local tables = {}
  148. do -- Tables and schema settings
  149. local t_auths = get('db.tables.auths')
  150. if type(t_auths) == 'table' then
  151. tables.auths = t_auths
  152. else
  153. tables.auths = {}
  154. tables.auths.name = get('db.tables.auths.name')
  155. tables.auths.schema = {}
  156. local S = tables.auths.schema
  157. S.userid = get('db.tables.auths.schema.userid')
  158. S.username = get('db.tables.auths.schema.username')
  159. S.password = get('db.tables.auths.schema.password')
  160. S.privs = get('db.tables.auths.schema.privs')
  161. S.lastlogin = get('db.tables.auths.schema.lastlogin')
  162. S.userid_type = get('db.tables.auths.schema.userid_type')
  163. S.username_type = get('db.tables.auths.schema.username_type')
  164. S.password_type = get('db.tables.auths.schema.password_type')
  165. S.privs_type = get('db.tables.auths.schema.privs_type')
  166. S.lastlogin_type = get('db.tables.auths.schema.lastlogin_type')
  167. end
  168.  
  169. do -- Default values
  170. tables.auths.name = tables.auths.name or 'auths'
  171. tables.auths.schema = tables.auths.schema or {}
  172. local S = tables.auths.schema
  173. S.userid = S.userid or 'userid'
  174. S.username = S.username or 'username'
  175. S.password = S.password or 'password'
  176. S.privs = S.privs or 'privs'
  177. S.lastlogin = S.lastlogin or 'lastlogin'
  178.  
  179. S.userid_type = S.userid_type or 'INT'
  180. S.username_type = S.username_type or 'VARCHAR(32)'
  181. S.password_type = S.password_type or 'VARCHAR(512)'
  182. S.privs_type = S.privs_type or 'VARCHAR(512)'
  183. S.lastlogin_type = S.lastlogin_type or 'BIGINT'
  184. -- Note lastlogin doesn't use the TIMESTAMP type, which is 32-bit and therefore
  185. -- subject to the year 2038 problem.
  186. end
  187. end
  188.  
  189. local auth_table_created
  190. do -- Auth table existence check and setup
  191. conn:query("SHOW TABLES LIKE '" .. tables.auths.name .. "'")
  192. local res = conn:store_result()
  193. local exists = (res:row_count() ~= 0)
  194. res:free()
  195. if not exists then
  196. -- Auth table doesn't exist, create it
  197. local S = tables.auths.schema
  198. conn:query('CREATE TABLE ' .. tables.auths.name .. ' (' ..
  199. S.userid .. ' ' .. S.userid_type .. ' NOT NULL AUTO_INCREMENT,' ..
  200. S.username .. ' ' .. S.username_type .. ' NOT NULL,' ..
  201. S.password .. ' ' .. S.password_type .. ' NOT NULL,' ..
  202. S.privs .. ' ' .. S.privs_type .. ' NOT NULL,' ..
  203. S.lastlogin .. ' ' .. S.lastlogin_type .. ',' ..
  204. 'PRIMARY KEY (' .. S.userid .. '),' ..
  205. 'UNIQUE (' .. S.username .. ')' ..
  206. ')')
  207. minetest.log('action', modname .. " created table '" .. dbname .. "." .. tables.auths.name ..
  208. "'")
  209. auth_table_created = true
  210. end
  211. end
  212.  
  213. local S = tables.auths.schema
  214. local get_auth_stmt = conn:prepare('SELECT ' .. S.password .. ',' .. S.privs .. ',' ..
  215. S.lastlogin .. ' FROM ' .. tables.auths.name .. ' WHERE ' .. S.username .. '=?')
  216. thismod.get_auth_stmt = get_auth_stmt
  217. local get_auth_params = get_auth_stmt:bind_params({S.username_type})
  218. thismod.get_auth_params = get_auth_params
  219. local get_auth_results = get_auth_stmt:bind_result({S.password_type, S.privs_type,
  220. S.lastlogin_type})
  221. thismod.get_auth_results = get_auth_results
  222.  
  223. local create_auth_stmt = conn:prepare('INSERT INTO ' .. tables.auths.name .. '(' .. S.username ..
  224. ',' .. S.password .. ',' .. S.privs .. ',' .. S.lastlogin .. ') VALUES (?,?,?,?)')
  225. thismod.create_auth_stmt = create_auth_stmt
  226. local create_auth_params = create_auth_stmt:bind_params({S.username_type, S.password_type,
  227. S.privs_type, S.lastlogin_type})
  228. thismod.create_auth_params = create_auth_params
  229.  
  230. local delete_auth_stmt = conn:prepare('DELETE FROM ' .. tables.auths.name .. ' WHERE ' ..
  231. S.username .. '=?')
  232. thismod.delete_auth_stmt = delete_auth_stmt
  233. local delete_auth_params = delete_auth_stmt:bind_params({S.username_type})
  234. thismod.delete_auth_params = delete_auth_params
  235.  
  236. local set_password_stmt = conn:prepare('UPDATE ' .. tables.auths.name .. ' SET ' .. S.password ..
  237. '=? WHERE ' .. S.username .. '=?')
  238. thismod.set_password_stmt = set_password_stmt
  239. local set_password_params = set_password_stmt:bind_params({S.password_type, S.username_type})
  240. thismod.set_password_params = set_password_params
  241.  
  242. local set_privileges_stmt = conn:prepare('UPDATE ' .. tables.auths.name .. ' SET ' .. S.privs ..
  243. '=? WHERE ' .. S.username .. '=?')
  244. thismod.set_privileges_stmt = set_privileges_stmt
  245. local set_privileges_params = set_privileges_stmt:bind_params({S.privs_type, S.username_type})
  246. thismod.set_privileges_params = set_privileges_params
  247.  
  248. local record_login_stmt = conn:prepare('UPDATE ' .. tables.auths.name .. ' SET ' ..
  249. S.lastlogin .. '=? WHERE ' .. S.username .. '=?')
  250. thismod.record_login_stmt = record_login_stmt
  251. local record_login_params = record_login_stmt:bind_params({S.lastlogin_type, S.username_type})
  252. thismod.record_login_params = record_login_params
  253.  
  254. local enumerate_auths_query = 'SELECT ' .. S.username .. ',' .. S.password .. ',' .. S.privs ..
  255. ',' .. S.lastlogin .. ' FROM ' .. tables.auths.name
  256. thismod.enumerate_auths_query = enumerate_auths_query
  257.  
  258. if auth_table_created and get('import_auth_txt_on_table_create') ~= 'false' then
  259. if not thismod.import_auth_txt then
  260. dofile(modpath .. '/auth_txt_import.lua')
  261. end
  262. thismod.import_auth_txt()
  263. end
  264.  
  265. thismod.auth_handler = {
  266. get_auth = function(name)
  267. assert(type(name) == 'string')
  268. get_auth_params:set(1, name)
  269. local success, msg = pcall(get_auth_stmt.exec, get_auth_stmt)
  270. if not success then
  271. minetest.log('error', modname .. ": get_auth(" .. name .. ") failed: " .. msg)
  272. return nil
  273. end
  274. get_auth_stmt:store_result()
  275. if not get_auth_stmt:fetch() then
  276. -- No such auth row exists
  277. return nil
  278. end
  279. while get_auth_stmt:fetch() do
  280. minetest.log('warning', modname .. ": get_auth(" .. name .. "): multiples lines were" ..
  281. " returned")
  282. end
  283. local password, privs_str, lastlogin = get_auth_results:get(1), get_auth_results:get(2),
  284. get_auth_results:get(3)
  285. local admin = (name == minetest.setting_get("name"))
  286. local privs
  287. if singleplayer or admin then
  288. privs = {}
  289. -- If admin, grant all privs, if singleplayer, grant all privs w/ give_to_singleplayer
  290. for priv, def in pairs(core.registered_privileges) do
  291. if (singleplayer and def.give_to_singleplayer) or admin then
  292. privs[priv] = true
  293. end
  294. end
  295. if admin and not thismod.admin_get_auth_called then
  296. thismod.admin_get_auth_called = true
  297. thismod.auth_handler.set_privileges(name, privs)
  298. end
  299. else
  300. privs = minetest.string_to_privs(privs_str)
  301. end
  302. return {
  303. password = password,
  304. privileges = privs,
  305. last_login = tonumber(lastlogin)
  306. }
  307. end,
  308. create_auth = function(name, password, reason)
  309. assert(type(name) == 'string')
  310. assert(type(password) == 'string')
  311. minetest.log('info', modname .. " creating player '"..name.."'" .. (reason or ""))
  312. create_auth_params:set(1, name)
  313. create_auth_params:set(2, password)
  314. create_auth_params:set(3, minetest.setting_get("default_privs"))
  315. create_auth_params:set(4, math.floor(os.time()))
  316. local success, msg = pcall(create_auth_stmt.exec, create_auth_stmt)
  317. if not success then
  318. minetest.log('error', modname .. ": create_auth(" .. name .. ") failed: " .. msg)
  319. return false
  320. end
  321. if create_auth_stmt:affected_rows() ~= 1 then
  322. minetest.log('error', modname .. ": create_auth(" .. name .. ") failed: affected row" ..
  323. " count is " .. create_auth_stmt:affected_rows() .. ", expected 1")
  324. return false
  325. end
  326. return true
  327. end,
  328. delete_auth = function(name)
  329. assert(type(name) == 'string')
  330. minetest.log('info', modname .. " deleting player '"..name.."'")
  331. delete_auth_params:set(1, name)
  332. local success, msg = pcall(delete_auth_stmt.exec, delete_auth_stmt)
  333. if not success then
  334. minetest.log('error', modname .. ": delete_auth(" .. name .. ") failed: " .. msg)
  335. return false
  336. end
  337. if delete_auth_stmt:affected_rows() ~= 1 then
  338. minetest.log('error', modname .. ": delete_auth(" .. name .. ") failed: affected row" ..
  339. " count is " .. delete_auth_stmt:affected_rows() .. ", expected 1")
  340. return false
  341. end
  342. return true
  343. end,
  344. set_password = function(name, password)
  345. assert(type(name) == 'string')
  346. assert(type(password) == 'string')
  347. if not thismod.auth_handler.get_auth(name) then
  348. return thismod.auth_handler.create_auth(name, password, " because set_password was requested")
  349. else
  350. minetest.log('info', modname .. " setting password of player '" .. name .. "'")
  351. set_password_params:set(1, password)
  352. set_password_params:set(2, name)
  353. local success, msg = pcall(set_password_stmt.exec, set_password_stmt)
  354. if not success then
  355. minetest.log('error', modname .. ": set_password(" .. name .. ") failed: " .. msg)
  356. return false
  357. end
  358. if set_password_stmt:affected_rows() ~= 1 then
  359. minetest.log('error', modname .. ": set_password(" .. name .. ") failed: affected row" ..
  360. " count is " .. set_password_stmt:affected_rows() .. ", expected 1")
  361. return false
  362. end
  363. return true
  364. end
  365. end,
  366. set_privileges = function(name, privileges)
  367. assert(type(name) == 'string')
  368. assert(type(privileges) == 'table')
  369. set_privileges_params:set(1, minetest.privs_to_string(privileges))
  370. set_privileges_params:set(2, name)
  371. local success, msg = pcall(set_privileges_stmt.exec, set_privileges_stmt)
  372. if not success then
  373. minetest.log('error', modname .. ": set_privileges(" .. name .. ") failed: " .. msg)
  374. return false
  375. end
  376. minetest.notify_authentication_modified(name)
  377. if set_privileges_stmt:affected_rows() ~= 1 then
  378. minetest.log('error', modname .. ": set_privileges(" .. name .. ") failed: affected row" ..
  379. " count is " .. set_privileges_stmt:affected_rows() .. ", expected 1")
  380. return false
  381. end
  382. return true
  383. end,
  384. reload = function()
  385. return true
  386. end,
  387. record_login = function(name)
  388. assert(type(name) == 'string')
  389. record_login_params:set(1, math.floor(os.time()))
  390. record_login_params:set(2, name)
  391. local success, msg = pcall(record_login_stmt.exec, record_login_stmt)
  392. if not success then
  393. minetest.log('error', modname .. ": record_login(" .. name .. ") failed: " .. msg)
  394. return false
  395. end
  396. if record_login_stmt:affected_rows() ~= 1 then
  397. minetest.log('error', modname .. ": record_login(" .. name .. ") failed: affected row" ..
  398. " count is " .. record_login_stmt:affected_rows() .. ", expected 1")
  399. return false
  400. end
  401. return true
  402. end,
  403. enumerate_auths = function()
  404. conn:query(enumerate_auths_query)
  405. local res = conn:store_result()
  406. return function()
  407. local row = res:fetch('n')
  408. if not row then
  409. return nil
  410. end
  411. local username, password, privs_str, lastlogin = unpack(row)
  412. return username, {
  413. password = password,
  414. privileges = minetest.string_to_privs(privs_str),
  415. last_login = tonumber(lastlogin)
  416. }
  417. end
  418. end
  419. }
  420. end
  421.  
  422. minetest.register_authentication_handler(thismod.auth_handler)
  423. minetest.log('action', modname .. ": Registered auth handler")
  424.  
  425. local function ping()
  426. if thismod.conn then
  427. if not thismod.conn:ping() then
  428. minetest.log('error', modname .. ": failed to ping database")
  429. end
  430. end
  431. minetest.after(1800, ping)
  432. end
  433. minetest.after(10, ping)
  434.  
  435. minetest.register_on_shutdown(function()
  436. if thismod.conn then
  437. thismod.get_auth_stmt:free_result()
  438. thismod.conn:close()
  439. thismod.conn = nil
  440. end
  441. end)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement