Prevent misuse of RequestAccess

This commit is contained in:
boatbomber 2023-07-04 19:21:32 -07:00
parent 91cba1b0e9
commit dcfdf7fabd
2 changed files with 29 additions and 6 deletions

View file

@ -314,9 +314,16 @@ function App:releaseSyncLock()
Log.trace("Could not release sync lock because it is owned by {}", lock.Value) Log.trace("Could not release sync lock because it is owned by {}", lock.Value)
end end
function App:requestPermission(source: string, name: string, apis: {string}, initialState: {[string]: boolean?}): {[string]: boolean} function App:requestPermission(plugin: Plugin, source: string, name: string, apis: {string}, initialState: {[string]: boolean?}): {[string]: boolean}
local responseEvent = Instance.new("BindableEvent") local responseEvent = Instance.new("BindableEvent")
Log.info("The third-party plugin '{}' is requesting permission to use the API!", name)
local unloadProtection = plugin.Unloading:Connect(function()
Log.warn("Cancelling API permission request for '{}' because the third-party plugin has been removed.", name)
responseEvent:Fire(initialState)
end)
self:setState(function(state) self:setState(function(state)
state.popups[source .. " Permissions"] = { state.popups[source .. " Permissions"] = {
name = name, name = name,
@ -338,6 +345,7 @@ function App:requestPermission(source: string, name: string, apis: {string}, ini
local response = responseEvent.Event:Wait() local response = responseEvent.Event:Wait()
responseEvent:Destroy() responseEvent:Destroy()
unloadProtection:Disconnect()
self:setState(function(state) self:setState(function(state)
state.popups[source .. " Permissions"] = nil state.popups[source .. " Permissions"] = nil

View file

@ -23,7 +23,9 @@ function API.new(app)
local Rojo = {} local Rojo = {}
Rojo._rateLimit = {} Rojo._rateLimit = {}
Rojo._sourceToPlugin = {}
Rojo._permissions = Settings:get("apiPermissions") or {} Rojo._permissions = Settings:get("apiPermissions") or {}
Rojo._activePermissionRequests = {}
Rojo._changedEvent = Instance.new("BindableEvent") Rojo._changedEvent = Instance.new("BindableEvent")
Rojo._apiDescriptions = {} Rojo._apiDescriptions = {}
@ -55,7 +57,7 @@ function API.new(app)
local traceback = string.split(debug.traceback(), "\n") local traceback = string.split(debug.traceback(), "\n")
local topLevel = traceback[#traceback - 1] local topLevel = traceback[#traceback - 1]
local localPlugin = string.match(topLevel, "(user_.-)%.") local localPlugin = string.match(topLevel, "user_.-%.%w+")
if localPlugin then if localPlugin then
return localPlugin return localPlugin
end end
@ -188,15 +190,26 @@ function API.new(app)
end end
Rojo._apiDescriptions.RequestAccess = "Used to gain access to Rojo API members" Rojo._apiDescriptions.RequestAccess = "Used to gain access to Rojo API members"
function Rojo:RequestAccess(apis: {string}): {[string]: boolean} function Rojo:RequestAccess(plugin: Plugin, apis: {string}): {[string]: boolean}
assert(type(apis) == "table", "Rojo:RequestAccess expects an array of valid API names") assert(type(apis) == "table", "Rojo:RequestAccess expects an array of valid API names as the second argument")
assert(typeof(plugin) == "Instance" and plugin:IsA("Plugin"), "Rojo:RequestAccess expects a Plugin as the first argument")
local source, name = Rojo:_getCallerSource(), Rojo:_getCallerName()
Rojo._sourceToPlugin[source] = plugin
if Rojo:_checkRateLimit("RequestAccess") then if Rojo:_checkRateLimit("RequestAccess") then
-- Because this opens a popup, we dont want to let users get spammed by it -- Because this opens a popup, we dont want to let users get spammed by it
return {} return {}
end end
local source, name = Rojo:_getCallerSource(), Rojo:_getCallerName() if Rojo._activePermissionRequests[source] then
-- If a request is already active, exit
error(
"Rojo:RequestAccess cannot be called in multiple threads at once. Please call it once and wait for the response before calling it again.",
2
)
end
Rojo._activePermissionRequests[source] = true
if Rojo._permissions[source] == nil then if Rojo._permissions[source] == nil then
Rojo._permissions[source] = {} Rojo._permissions[source] = {}
@ -226,10 +239,11 @@ function API.new(app)
for _, api in sanitizedApis do for _, api in sanitizedApis do
response[api] = true response[api] = true
end end
Rojo._activePermissionRequests[source] = nil
return response return response
end end
local response = app:requestPermission(source, name, sanitizedApis, Rojo._permissions[source]) local response = app:requestPermission(plugin, source, name, sanitizedApis, Rojo._permissions[source])
for api, granted in response do for api, granted in response do
Log.warn(string.format( Log.warn(string.format(
@ -240,6 +254,7 @@ function API.new(app)
end end
Settings:set("apiPermissions", Rojo._permissions) Settings:set("apiPermissions", Rojo._permissions)
Rojo._activePermissionRequests[source] = nil
return response return response
end end