mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-28 17:01:18 +00:00
4fa4bf3c78
* ENH - centralize phone configuration defaults * FIX - enforce fixed phone permissions
313 lines
8.7 KiB
Lua
313 lines
8.7 KiB
Lua
local registered_callbacks = {}
|
|
local migration_callbacks = {}
|
|
local event_handlers = {}
|
|
|
|
Bridge = {
|
|
Callbacks = {
|
|
Register = function(name, callback)
|
|
assert(type(name) == "string" and type(callback) == "function")
|
|
assert(registered_callbacks[name] == nil, "duplicate callback: " .. name)
|
|
registered_callbacks[name] = callback
|
|
end,
|
|
},
|
|
Database = {
|
|
AfterMigration = function(name, callback)
|
|
assert(name == "sky_phone")
|
|
migration_callbacks[#migration_callbacks + 1] = callback
|
|
end,
|
|
Query = function()
|
|
return {}
|
|
end,
|
|
Transaction = function()
|
|
return true
|
|
end,
|
|
},
|
|
Debug = function()
|
|
end,
|
|
Framework = {
|
|
HasPermission = function()
|
|
return true
|
|
end,
|
|
},
|
|
Inventory = {
|
|
GetResourceName = function()
|
|
return "test_inventory"
|
|
end,
|
|
RegisterUsableItem = function(_, callback)
|
|
assert(type(callback) == "function")
|
|
return true
|
|
end,
|
|
},
|
|
}
|
|
|
|
Config = {
|
|
AdminPanel = {
|
|
Command = "phoneadmin",
|
|
Enabled = true,
|
|
},
|
|
Phone = {
|
|
DevelopmentCommand = false,
|
|
DeviceName = "Test Phone",
|
|
Item = "phone",
|
|
Unique = true,
|
|
},
|
|
Sim = {
|
|
Enabled = true,
|
|
NumberGroups = { 3, 4 },
|
|
NumberLength = 7,
|
|
NumberPrefix = "",
|
|
},
|
|
Server = {
|
|
PasscodePepper = "test-pepper",
|
|
},
|
|
Security = {
|
|
AttemptsPerMinute = 5,
|
|
LockSeconds = 60,
|
|
MaximumAttempts = 5,
|
|
},
|
|
Mail = {
|
|
AuthAttemptsPerMinute = 5,
|
|
Domain = "test.local",
|
|
LocalPartMinLength = 3,
|
|
LocalPartMaxLength = 32,
|
|
PasswordMinLength = 8,
|
|
PasswordMaxLength = 72,
|
|
},
|
|
}
|
|
|
|
json = {
|
|
encode = function()
|
|
return "{}"
|
|
end,
|
|
decode = function()
|
|
return {}
|
|
end,
|
|
}
|
|
|
|
function AddEventHandler(name, callback)
|
|
assert(type(callback) == "function")
|
|
event_handlers[name] = event_handlers[name] or {}
|
|
event_handlers[name][#event_handlers[name] + 1] = callback
|
|
end
|
|
|
|
function RegisterCommand(name, callback, restricted)
|
|
assert(name == "phoneadmin")
|
|
assert(type(callback) == "function")
|
|
assert(restricted == false)
|
|
end
|
|
|
|
function TriggerClientEvent()
|
|
end
|
|
|
|
function TriggerEvent()
|
|
end
|
|
|
|
function GetCurrentResourceName()
|
|
return "sky_phone"
|
|
end
|
|
|
|
function GetGameTimer()
|
|
return 1
|
|
end
|
|
|
|
function GetPlayers()
|
|
return {}
|
|
end
|
|
|
|
SkyPhoneImei = {}
|
|
SkyPhoneSimNumber = {}
|
|
|
|
local module_paths = {
|
|
"sky_phone/source/server/phone_security.lua",
|
|
"sky_phone/source/server/phone_accounts.lua",
|
|
"sky_phone/source/server/phone_persistence.lua",
|
|
"sky_phone/source/server/phone.lua",
|
|
}
|
|
|
|
for _, path in ipairs(module_paths) do
|
|
assert(loadfile(path))()
|
|
end
|
|
|
|
for _, callback in ipairs(migration_callbacks) do
|
|
callback()
|
|
end
|
|
|
|
assert(type(SkyPhoneSecurity) == "table")
|
|
assert(type(SkyPhoneSecurity.Load) == "function")
|
|
assert(type(SkyPhoneSecurity.Status) == "function")
|
|
assert(type(SkyPhoneAccounts) == "table")
|
|
assert(type(SkyPhoneAccounts.List) == "function")
|
|
|
|
for _, method in ipairs({
|
|
"EnsureDevice",
|
|
"FindDeviceSlots",
|
|
"LoadDevice",
|
|
"RefreshSource",
|
|
"GetEquippedPhoneNumber",
|
|
"GetSourceFromNumber",
|
|
"FormatNumber",
|
|
"RequireDeviceSession",
|
|
"RequireSession",
|
|
"AllowOperation",
|
|
"RequireAccount",
|
|
"NotifyAccount",
|
|
"NotifyAccountDevices",
|
|
"RefreshAccount",
|
|
"RefreshDevice",
|
|
"OpenDeviceForCall",
|
|
}) do
|
|
assert(type(SkyPhone[method]) == "function", "missing SkyPhone API: " .. method)
|
|
end
|
|
|
|
for _, callback_name in ipairs({
|
|
"sky_phone:security:unlock",
|
|
"sky_phone:security:set-passcode",
|
|
"sky_phone:security:change-passcode",
|
|
"sky_phone:security:disable-passcode",
|
|
"sky_phone:device:save",
|
|
"sky_phone:notifications:save",
|
|
"sky_phone:device:factory-reset",
|
|
"sky_phone:account:login",
|
|
"sky_phone:mail:login",
|
|
"sky_phone:account:register",
|
|
"sky_phone:mail:register",
|
|
"sky_phone:account:logout",
|
|
"sky_phone:mail:logout",
|
|
"sky_phone:account:devices",
|
|
"sky_phone:account:remove-device",
|
|
"sky_phone:device:open-request",
|
|
"sky_phone:device:development-open",
|
|
"sky_phone:device:close",
|
|
"sky_phone:device:equipped-number",
|
|
"sky_phone:device:notification-open",
|
|
}) do
|
|
assert(type(registered_callbacks[callback_name]) == "function", "missing callback: " .. callback_name)
|
|
end
|
|
|
|
for _, callback_name in ipairs({
|
|
"sky_phone:security:unlock",
|
|
"sky_phone:device:save",
|
|
"sky_phone:device:factory-reset",
|
|
"sky_phone:account:devices",
|
|
}) do
|
|
local response = registered_callbacks[callback_name](1, {})
|
|
assert(response.success == false and response.error == "device_not_open", "callback not bound to core: " .. callback_name)
|
|
end
|
|
|
|
local phone_item = {
|
|
name = "phone",
|
|
slot = 4,
|
|
amount = 1,
|
|
metadata = { imei = "123456789012345" },
|
|
}
|
|
local opened_event
|
|
local device_error
|
|
local hide_phone_during_prepare = false
|
|
|
|
Bridge.Framework.GetIdentifier = function(source)
|
|
assert(source == 1)
|
|
return "license:test-player"
|
|
end
|
|
Bridge.Framework.GetFirstname = function()
|
|
return "Test"
|
|
end
|
|
Bridge.Framework.GetLastname = function()
|
|
return "Player"
|
|
end
|
|
Bridge.Inventory.GetSlot = function(source, slot)
|
|
assert(source == 1 and slot == phone_item.slot)
|
|
return phone_item
|
|
end
|
|
Bridge.Inventory.GetSlotsWithItem = function(source, item_name)
|
|
assert(source == 1 and item_name == Config.Phone.Item)
|
|
if hide_phone_during_prepare == true then
|
|
return {}
|
|
end
|
|
return { phone_item }
|
|
end
|
|
Bridge.Inventory.SetSlotMetadata = function()
|
|
error("existing phone metadata must not be rewritten")
|
|
end
|
|
Bridge.Database.Query = function(query)
|
|
if query:find("FROM `sky_phone_devices` d", 1, true) then
|
|
return {
|
|
{
|
|
imei = phone_item.metadata.imei,
|
|
device_name = Config.Phone.DeviceName,
|
|
account_id = nil,
|
|
sim_id = nil,
|
|
},
|
|
}
|
|
end
|
|
return {}
|
|
end
|
|
SkyPhoneImei.IsValid = function(imei)
|
|
return imei == phone_item.metadata.imei
|
|
end
|
|
SkyPhoneSim = {
|
|
PrepareDevice = function(source, slot, imei)
|
|
assert(source == 1 and slot == phone_item and imei == phone_item.metadata.imei)
|
|
if hide_phone_during_prepare == "next" then
|
|
hide_phone_during_prepare = true
|
|
end
|
|
return true
|
|
end,
|
|
}
|
|
SkyPhoneNotes = {
|
|
List = function()
|
|
return {}
|
|
end,
|
|
}
|
|
SkyPhoneMemos = {
|
|
List = function()
|
|
return {}
|
|
end,
|
|
}
|
|
SkyPhoneCompanies = {
|
|
ClearCallAvailability = function()
|
|
end,
|
|
}
|
|
TriggerClientEvent = function(event_name, source, payload)
|
|
if event_name == "sky_phone:device:open" then
|
|
opened_event = { source = source, payload = payload }
|
|
elseif event_name == "sky_phone:device:error" then
|
|
device_error = { source = source, error = payload }
|
|
end
|
|
end
|
|
|
|
for _, callback in ipairs(event_handlers.onServerResourceStart or {}) do
|
|
callback("sky_phone")
|
|
end
|
|
|
|
local no_sim_open = registered_callbacks["sky_phone:device:open-request"](1, {})
|
|
assert(no_sim_open.success == true, "a phone item without a SIM must still open")
|
|
assert(opened_event and opened_event.source == 1, "no-SIM open must reach the client")
|
|
assert(opened_event.payload.device.imei == phone_item.metadata.imei)
|
|
assert(opened_event.payload.device.sim == nil, "no-SIM bootstrap must keep device.sim nullable")
|
|
|
|
opened_event = nil
|
|
device_error = nil
|
|
hide_phone_during_prepare = "next"
|
|
local lost_phone_open = registered_callbacks["sky_phone:device:open-request"](1, {})
|
|
assert(lost_phone_open.success == false, "bootstrap ownership loss must fail the open request")
|
|
assert(lost_phone_open.error == "device_not_owned", "bootstrap ownership loss must return its error code")
|
|
assert(opened_event == nil, "bootstrap ownership loss must not open the NUI")
|
|
assert(device_error and device_error.error == "device_not_owned", "bootstrap ownership loss must notify the client")
|
|
|
|
local manifest_file = assert(io.open("sky_phone/fxmanifest.lua", "rb"))
|
|
local manifest = manifest_file:read("*a")
|
|
manifest_file:close()
|
|
|
|
local security_position = assert(manifest:find("source/server/phone_security.lua", 1, true))
|
|
local accounts_position = assert(manifest:find("source/server/phone_accounts.lua", 1, true))
|
|
local persistence_position = assert(manifest:find("source/server/phone_persistence.lua", 1, true))
|
|
local phone_position = assert(manifest:find("source/server/phone.lua", 1, true))
|
|
local migration_position = assert(manifest:find("source/server/db_migrate.lua", 1, true))
|
|
|
|
assert(security_position < phone_position)
|
|
assert(accounts_position < phone_position)
|
|
assert(persistence_position < phone_position)
|
|
assert(phone_position < migration_position)
|
|
|
|
print("server phone modules: ok")
|