monitor/resource/sv_admins.lua
2025-04-16 22:30:27 +07:00

132 lines
4.0 KiB
Lua

-- Prevent running in monitor mode
if not TX_SERVER_MODE then return end
if TX_LUACOMHOST == "invalid" or TX_LUACOMTOKEN == "invalid" then
txPrint('^1API Host or Pipe Token ConVars not found. Do not start this resource if not using txAdmin.')
return
end
if TX_LUACOMTOKEN == "removed" then
txPrint('^1Please do not restart the monitor resource.')
return
end
-- =============================================
-- Lua Admin Manager
-- =============================================
-- Variables & Consts
local failedAuths = {}
local attemptCooldown = 15000
-- Handle auth failures
local function handleAuthFail(src, reason)
local srcString = tostring(src)
TX_ADMINS[srcString] = nil
failedAuths[srcString] = GetGameTimer()
reason = reason or "unknown"
debugPrint("Auth rejected #"..srcString.." ("..reason..")")
TriggerClientEvent('txcl:setAdmin', src, false, false, reason)
TriggerEvent('txAdmin:events:adminAuth', {
netid = src,
isAdmin = false,
})
end
-- Handle menu auth requests
RegisterNetEvent('txsv:checkIfAdmin', function()
local src = source
local srcString = tostring(source)
debugPrint('Handling authentication request from player #'..srcString)
-- Rate Limiter
if type(failedAuths[srcString]) == 'number' and failedAuths[srcString] + attemptCooldown > GetGameTimer() then
return handleAuthFail(source, "too many auth attempts")
end
-- Prepping http request
local url = "http://"..TX_LUACOMHOST.."/auth/self"
local headers = {
['Content-Type'] = 'application/json',
['X-TxAdmin-Token'] = TX_LUACOMTOKEN,
['X-TxAdmin-Identifiers'] = table.concat(GetPlayerIdentifiers(src), ',')
}
-- Making http request
PerformHttpRequest(url, function(httpCode, data, resultHeaders)
-- Validating response
local resp = json.decode(data)
if not resp then
return handleAuthFail(src, "invalid response")
end
if resp.logout ~= nil and resp.logout then
return handleAuthFail(src, resp.reason or 'unknown reject reason')
end
if type(resp.name) ~= "string" then
return handleAuthFail(src, "invalid response")
end
if type(resp.permissions) ~= 'table' then
resp.permissions = {}
end
-- Setting up admin
local adminTag = "[#"..src.."] "..resp.name
debugPrint(("^2Authenticated admin ^5%s^2 with permissions: %s"):format(
src,
adminTag,
json.encode(resp.permissions)
))
TX_ADMINS[srcString] = {
tag = adminTag,
username = resp.name,
perms = resp.permissions,
bucket = 0
}
sendInitialPlayerlist(src)
TriggerClientEvent('txcl:setAdmin', src, resp.name, resp.permissions)
TriggerEvent('txAdmin:events:adminAuth', {
netid = src,
isAdmin = true,
username = resp.name,
})
end, 'GET', '', headers)
end)
-- Remove admin from table when disconnected
AddEventHandler('playerDropped', function()
TX_ADMINS[tostring(source)] = nil
end)
-- Handle updated admin list
AddEventHandler('txAdmin:events:adminsUpdated', function(onlineAdminIDs)
debugPrint('^3Admins list updated. Online admins: ' .. json.encode(onlineAdminIDs))
-- Collect old and new admin IDs as key to prevent duplicate
local refreshAdminIds = {}
for id, _ in pairs(TX_ADMINS) do
refreshAdminIds[id] = true
end
for _, id in pairs(onlineAdminIDs) do
refreshAdminIds[tostring(id)] = true
end
debugPrint('^3Forcing clients to re-auth')
-- Resetting all admin permissions and rate limiter
TX_ADMINS = {}
failedAuths = {}
-- Informing clients that they need to reauth
for id, _ in pairs(refreshAdminIds) do
TriggerClientEvent('txcl:reAuth', tonumber(id))
end
-- Broadcasting the invalidation of all admins
TriggerEvent('txAdmin:events:adminAuth', {
netid = -1,
isAdmin = false,
})
end)