local FORMNAME = "fediauth-check" local FORMNAMEFEDI = "fediauth-check-fedi" -- time for fediauth code verification local fediauth_time = 300 -- playername => start_time local fediauth_sessions = {} local formspecfediadd = "size[9,10]" .. "label[1,7;Input your fediverse account handle]" .. "image[1.5,0.6;7,7;fediverse.png]" .. "field[1,9;4,1;fediverse_account_url;@nick@example.com;]" .. "button[5,8.7;3,1;submit;Send code]" local feditempstore = {} local failed_counter = {} local cubic_jail_entities = {} function fediauth.remove_lock_cube(playername) for _, obj in ipairs(cubic_jail_entities[playername]) do obj:remove() end cubic_jail_entities[playername] = nil minetest.log("action", "[fediauth] cubic jail removed for: '" .. playername .. "'") end minetest.register_entity("fediauth:checkmark", { initial_properties = { pointable = false, armor_groups = { immortal = 1 }, visual = "sprite", visual_size = {x = 0.5, y = 0.5}, textures = { "checkmark.png^[opacity:180" }, use_texture_alpha = true, static_save = false, glow = 5, }, on_detach = function(self, parent) self.object:remove() end }) minetest.register_entity("fediauth:stopper", { initial_properties = { pointable = false, physical = true, collide_with_objects = true, armor_group = { immortal = 1 }, collisionbox = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }, visual = "sprite", visual_size = {x = 1, y = 1 }, textures = { "ignore" }, use_texture_alpha = true, static_save = true, glow = 10, } }) function fediauth.verified_checkmark(player, verified) local tag = player:get_player_name() local props = player:get_properties() if verified then local obj = minetest.add_entity({x=0,y=2,z=0}, "fediauth:checkmark", nil) obj:set_attach(player, "Head", {x = 0, y = 12, z = 0}) player:set_properties({nametag = props.nametag .. " [FEDI]", nametag_color = "#00ff00" }) end end -- Code formspec on join for fediauth enabled players minetest.register_on_joinplayer(function(player) local playername = player:get_player_name() local player_pos = player:getpos() if fediauth.is_player_bypassed(playername) then return end if fediauth.is_player_enabled(playername) or minetest.settings:get_bool("fediauth.fedi_required", false) then minetest.log("action", "[fediauth] session start for player: '" .. playername .. "'") if minetest.settings:get_bool("fediauth.create_lock_jail_cube") then local cube = {} for x=-2,2 do for y=-2,2 do for z=-2,2 do if x ~= 0 then table.insert(cube, {player_pos.x + x, player_pos.y + y, player_pos.z + z}) elseif y ~= 0 and y ~= 1 then table.insert(cube, {player_pos.x + x, player_pos.y + y, player_pos.z + z}) elseif z ~= 0 then table.insert(cube, {player_pos.x + x, player_pos.y + y, player_pos.z + z}) end end end end local cube_entities = {} for _, i in ipairs(cube) do table.insert(cube_entities, minetest.add_entity({x=i[1], y=i[2], z=i[3]}, "fediauth:stopper", nil)) end cubic_jail_entities[playername] = cube_entities cube = nil minetest.log("action", "[fediauth] cubic jail created for: '" .. playername .. "'") end -- start fediauth session time fediauth_sessions[player:get_player_name()] = os.time() -- revoke important privs and re-grant again on code-verification fediauth.revoke_privs(playername) -- save password for prevent changes fediauth.save_passw(playername) -- if fedi only allowed if minetest.settings:get_bool("fediauth.fedi_required", false) then local existsfedi = fediauth.storage:get_string(playername .. "_fedi") if existsfedi == "" or not existsfedi then minetest.log("action", "[fediauth] request fedi account for player: '" .. playername .. "'") minetest.show_formspec(playername, FORMNAMEFEDI, formspecfediadd) return end end local secret_b32 = fediauth.get_player_secret_b32(playername) local codeseq = fediauth.give_code(secret_b32) local fedihandle = fediauth.storage:get_string(playername .. "_fedi"):split("@") if fediauth.is_home_instance(fedihandle[2]) then fediauth.send_code(codeseq[1], "@" .. fedihandle[1], playername) else fediauth.send_code(codeseq[1], "@" .. fedihandle[1] .. "@" .. fedihandle[2], playername) end -- send verification formspec local formspec = "size[10,2]" .. "label[1,0;Please check your fedi account and enter code]" .. "field[1,1.3;4,1;code;Code;]" .. "button_exit[5,1;3,1;submit;Verify]" minetest.show_formspec(playername, FORMNAME, formspec) end end) -- prevent flooding codes minetest.register_on_prejoinplayer(function(name, ip) if (failed_counter[name] or 0) >= 2 then return "Please try later, your attempts has expired" end end) local function attempts_cleanup(name) for k, v in pairs(failed_counter) do if v >= 2 then failed_counter[k] = nil end end minetest.after(120, attempts_cleanup) end minetest.after(120, attempts_cleanup) -- clear fediauth session on leave minetest.register_on_leaveplayer(function(player) local playername = player:get_player_name() fediauth_sessions[playername] = nil end) -- check sessions periodically and kick if timed out local function session_check() local now = os.time() for name, start_time in pairs(fediauth_sessions) do if (now - start_time) > fediauth_time then minetest.kick_player(name, "fediauth code validation timed out") fediauth_sessions[name] = nil end end minetest.after(5, session_check) end minetest.after(5, session_check) -- fediauth check minetest.register_on_player_receive_fields(function(player, formname, fields) if formname ~= FORMNAME and formname ~= FORMNAMEFEDI then return end local playername = player:get_player_name() local secret_b32 = fediauth.get_player_secret_b32(playername) -- check for new player or doesn't have fedi account if fields.fediverse_account_url then -- basic prevent mention spam and limit length if not string.starts(fields.fediverse_account_url, "@") or string.len(fields.fediverse_account_url) < 3 or string.len(fields.fediverse_account_url) > 100 then minetest.chat_send_player(playername, minetest.colorize("#ff0000", "Try again, your input is incorrect")) minetest.show_formspec(playername, FORMNAMEFEDI, formspecfediadd) return end local fedihandle = fields.fediverse_account_url:split("@") if #fedihandle ~= 2 then minetest.chat_send_player(playername, minetest.colorize("#ff0000", "Incorrect format")) minetest.show_formspec(playername, FORMNAMEFEDI, formspecfediadd) return end if fediauth.check_for_restricted_instance(fedihandle[2]) then minetest.chat_send_player(playername, minetest.colorize("#ff0000", fedihandle[2] .. " has restricted, try another...")) minetest.show_formspec(playername, FORMNAMEFEDI, formspecfediadd) return end local secret_b32 = fediauth.get_player_secret_b32(playername) local codeseq = fediauth.give_code(secret_b32) if fediauth.is_home_instance(fedihandle[2]) then fediauth.send_code(codeseq[1], "@" .. fedihandle[1], playername) else fediauth.send_code(codeseq[1], "@" .. fedihandle[1] .. "@" .. fedihandle[2], playername) end feditempstore[playername] = fields.fediverse_account_url local formspec = "size[9,10]" .. "label[1,7;Check code on " .. minetest.formspec_escape(fields.fediverse_account_url) .. "]" .. "field[1,9;4,1;code;Code;]" .. "button_exit[5,8.7;3,1;submit;Verify]" minetest.show_formspec(playername, FORMNAME, formspec) return end if fediauth.check_code(secret_b32, fields.code) then local fedi_account = fediauth.storage:get_string(playername .. "_fedi") -- if player without fediverse (for prevent write account handle if code incorrect) if fedi_account == "" and feditempstore[playername] then fediauth.storage:set_string(playername .. "_fedi", feditempstore[playername]) fedi_account = feditempstore[playername] feditempstore[playername] = nil end minetest.chat_send_player(playername, minetest.colorize("#00ff00", "fediauth code validation succeeded for " .. fedi_account)) fediauth_sessions[playername] = nil fediauth.regrant_privs(playername) fediauth.discard_passw(playername) fediauth.verified_checkmark(player, true) if minetest.settings:get_bool("fediauth.create_lock_jail_cube") then fediauth.remove_lock_cube(playername) end else minetest.kick_player(playername, "fediauth code validation failed") fediauth.regrant_privs(playername) fediauth.discard_passw(playername) if minetest.settings:get_bool("fediauth.create_lock_jail_cube") then fediauth.remove_lock_cube(playername) end failed_counter[playername] = (failed_counter[playername] or 0) + 1 end end)