update 2026-10-27 07:31:06

This commit is contained in:
action committed 2026-10-27 07:31:06 +08:00
1 parent 197c60d843
commit a4d6470641
4213 files changed
-1575003

No files matched your search

@@ -1,94 +0,0 @@
local source_root = assert(os.getenv("LINKEASE_APPS_SOURCE_ROOT"), "LINKEASE_APPS_SOURCE_ROOT is required")
package.path = source_root .. "/?.lua;" .. package.path
local apps = require "luci.model.linkease.apps"
local function equal(actual, expected, message)
if actual ~= expected then
error((message or "values differ") .. ": got " .. tostring(actual) .. ", want " .. tostring(expected), 2)
end
end
local function engine(options)
options = options or {}
local registrations = options.registrations or {
dockermanager = { id = "dockermanager", url = "/apps/dockermanager/" }
}
return apps.new({
lookup = function(id)
return registrations[id]
end,
entry_status = function()
return options.entry or {
available = true,
worker = "gateway",
proxy_supported = true,
proxy_enabled = true
}
end,
external_status = function(app)
if options.external then
return options.external[app.id]
end
return nil
end
})
end
local invalid = engine():resolve("../dockermanager")
equal(invalid.reason, "invalid_id")
equal(invalid.registered, false)
equal(invalid.available, false)
local missing = engine():resolve("unknown")
equal(missing.reason, "not_registered")
equal(missing.registered, false)
local routed = engine():resolve("dockermanager")
equal(routed.mode, "apps")
equal(routed.url, "/apps/dockermanager/")
equal(routed.worker, "gateway")
equal(routed.available, true)
equal(routed.proxy.supported, true)
equal(routed.proxy.enabled, true)
local full = engine({ entry = {
available = true,
worker = "linkeasefull",
proxy_supported = true,
proxy_enabled = true
} }):status("dockermanager")
equal(full.mode, "apps")
equal(full.worker, "linkeasefull")
local builtin = engine({
registrations = {
["openwrt-luci"] = {
id = "openwrt-luci", url = "/apps/openwrt-luci/", entry_supported = false
}
}
}):status("openwrt-luci")
equal(builtin.registered, true)
equal(builtin.available, false)
equal(builtin.mode, "unavailable")
equal(builtin.reason, "entry_unsupported_and_external_unavailable")
local external = engine({
entry = { available = false, worker = "stopped", proxy_supported = false, proxy_enabled = false },
external = { dockermanager = { available = true, url = "http://192.168.30.7:8192/apps/dockermanager/" } }
}):resolve("dockermanager")
equal(external.mode, "external")
equal(external.available, true)
equal(external.url, "http://192.168.30.7:8192/apps/dockermanager/")
equal(external.reason, "external_available")
local unavailable = engine({
entry = { available = false, worker = "stopped", proxy_supported = true, proxy_enabled = true },
external = { dockermanager = { available = false, reason = "external_disabled" } }
}):resolve("dockermanager")
equal(unavailable.mode, "unavailable")
equal(unavailable.available, false)
equal(unavailable.registered, true)
equal(unavailable.reason, "entry_unavailable_and_external_disabled")
print("linkease apps decision tests passed")
@@ -1,36 +0,0 @@
local source_root = assert(os.getenv("LINKEASE_APPS_SOURCE_ROOT"), "LINKEASE_APPS_SOURCE_ROOT is required")
package.path = source_root .. "/?.lua;" .. package.path
local compat_module = require "luci.model.linkease.apps_compat"
local output = {}
local decision = {
schemaVersion = 1, id = "dockermanager", registered = true, available = true,
mode = "apps", url = "/apps/dockermanager/", worker = "gateway",
proxy = { supported = true, enabled = true }, reason = "entry_available"
}
local http = {
status = function(code) output.status = code end,
prepare_content = function(value) output.content_type = value end,
write_json = function(value) output.body = value end,
redirect = function(value) output.redirect = value end,
urlencode = function(value) return (value:gsub("/", "%%2F")) end
}
local compat = compat_module.new({
http = http,
resolver = { resolve = function() return decision end, status = function() return decision end },
auth_url = "/auth"
})
compat:open("dockermanager")
assert(output.redirect == "/auth?return=%2Fapps%2Fdockermanager%2F")
compat:legacy_status("dockermanager", { running = true, external_port_enabled = false })
assert(output.status == 200)
assert(output.body.running == true)
assert(output.body.entry_url == "/apps/dockermanager/")
assert(output.body.proxy_prefix_supported == true)
assert(output.body.proxy_prefix_enabled == true)
assert(output.body.app_entry_running == true)
assert(output.body.access == decision)
print("linkease apps compatibility tests passed")
@@ -1,64 +0,0 @@
local source_root = assert(os.getenv("LINKEASE_APPS_SOURCE_ROOT"), "LINKEASE_APPS_SOURCE_ROOT is required")
package.path = source_root .. "/?.lua;" .. package.path
local apps_http = require "luci.model.linkease.apps_http"
local function equal(actual, expected, message)
if actual ~= expected then
error((message or "values differ") .. ": got " .. tostring(actual) .. ", want " .. tostring(expected), 2)
end
end
local function invoke(decision, action, id)
local output = { id = id }
local http = {
formvalue = function(name) if name == "id" then return output.id end end,
status = function(code) output.status = code end,
prepare_content = function(value) output.content_type = value end,
write_json = function(value) output.body = value end,
redirect = function(value) output.redirect = value end,
urlencode = function(value)
return (value:gsub("([^A-Za-z0-9._~-])", function(char)
return string.format("%%%02X", char:byte())
end))
end
}
local resolver = {
status = function() return decision end,
resolve = function() return decision end
}
local handler = apps_http.new({
http = http,
resolver = resolver,
auth_url = "/cgi-bin/luci/admin/services/linkease_auth/auth"
})
handler[action](handler)
return output
end
local valid = {
schemaVersion = 1, id = "dockermanager", registered = true, available = true,
mode = "apps", url = "/apps/dockermanager/", worker = "gateway",
reason = "entry_available", proxy = { supported = true, enabled = true }
}
local response = invoke(valid, "status", "dockermanager")
equal(response.status, 200)
equal(response.content_type, "application/json")
equal(response.body.schemaVersion, 1)
response = invoke(valid, "open", "dockermanager")
equal(response.status, nil)
equal(response.redirect, "/cgi-bin/luci/admin/services/linkease_auth/auth?return=%2Fapps%2Fdockermanager%2F")
response = invoke({ reason = "invalid_id", registered = false, available = false }, "status", "../bad")
equal(response.status, 400)
response = invoke({ reason = "not_registered", registered = false, available = false }, "status", "missing")
equal(response.status, 404)
response = invoke({ reason = "entry_unavailable_and_external_disabled", registered = true, available = false }, "open", "dockermanager")
equal(response.status, 503)
equal(response.body.available, false)
print("linkease apps HTTP boundary tests passed")
@@ -1,158 +0,0 @@
local source_root = assert(os.getenv("LINKEASE_APPS_SOURCE_ROOT"), "LINKEASE_APPS_SOURCE_ROOT is required")
package.path = source_root .. "/?.lua;" .. package.path
local function equal(actual, expected, message)
if actual ~= expected then
error((message or "values differ") .. ": got " .. tostring(actual) .. ", want " .. tostring(expected), 2)
end
end
local manifests = {
["fastnet.json"] = {
id = "fastnet",
standalone = { basePath = "/apps/fastnet/" },
desktop = {
mode = "iframe",
target = {
scheme = "http", hostMode = "request-host", path = "/",
port = { default = 3200, keys = { uci = "fastnet.@fastnet[0].port" } }
}
}
},
["kspeeder.json"] = {
id = "kspeeder",
standalone = {
basePath = "/apps/kspeeder/",
externalOpen = { enabled = true, path = "/" }
},
desktop = { mode = "module", entry = "desktop-entry.js" },
backend = {
type = "http", portFromUci = "istoreenhance.@istoreenhance[0].adminport",
defaultPort = 5003
}
},
["kai.json"] = {
id = "kai",
standalone = {
basePath = "/apps/kai/",
externalOpen = { enabled = true }
},
desktop = {
mode = "iframe",
target = {
scheme = "http", hostMode = "request-host", path = "/apps/kai/",
port = { default = 8197, keys = { uci = "kai.@kai[0].port" } }
}
},
backend = { type = "app-base", basePath = "/apps/kai/" }
},
["dockermanager.json"] = {
id = "dockermanager",
standalone = { basePath = "/apps/dockermanager/" },
desktop = { mode = "module", entry = "desktop-entry.js" },
backend = {
type = "http",
values = {
externalPortEnabled = {
default = false,
keys = { uci = "dockermanager.@dockermanager[0].external_port_enabled" }
},
port = {
default = 8192,
keys = { uci = "dockermanager.@dockermanager[0].port" }
}
}
}
},
["openwrt.json"] = {
id = "openwrt-luci",
standalone = { url = "/cgi-bin/luci/" },
desktop = { mode = "builtin", component = "LuciContainer" }
}
}
local function dependencies(filename, entry_available, uci_values)
uci_values = uci_values or {}
return {
registration_dirs = { "/registrations" },
fs = {
dir = function()
local emitted = false
return function()
if emitted then return nil end
emitted = true
return filename
end
end,
readfile = function(path)
if path == "/registrations/" .. filename then return "manifest" end
if path == "/state.json" then return "state" end
if path == "/etc/config/uhttpd" then return entry_available and "/apps=http://127.0.0.1:19290" or "" end
end
},
json = { parse = function(raw)
if raw == "manifest" then return manifests[filename] end
if raw == "state" then return { active = entry_available and "gateway" or "stopped" } end
end },
sys = { call = function() return 0 end },
http = { getenv = function(name) if name == "HTTP_HOST" then return "192.168.30.7:10000" end end },
read_uci = function(key)
if key == "fastnet.@fastnet[0].port" then return "3201" end
return uci_values[key]
end,
uci = {
get_list = function() return entry_available and { "/apps=http://127.0.0.1:19290" } or {} end,
get = function(_, config, section, option)
if config == "linkease_app_entry" and section == "main" and option == "state_file" then return "/state.json" end
end,
get_first = function(_, config, section_type, option)
return uci_values[config .. ".@" .. section_type .. "[0]." .. option]
end
}
}
end
package.loaded["luci.model.linkease.apps_openwrt"] = nil
local apps_openwrt = require "luci.model.linkease.apps_openwrt"
local fastnet = apps_openwrt.new(dependencies("fastnet.json", false)):resolve("fastnet")
equal(fastnet.available, true)
equal(fastnet.mode, "external")
equal(fastnet.url, "http://192.168.30.7:3201/")
local kspeeder = apps_openwrt.new(dependencies("kspeeder.json", false, {
["istoreenhance.@istoreenhance[0].adminport"] = "5004"
})):resolve("kspeeder")
equal(kspeeder.available, true)
equal(kspeeder.mode, "external")
equal(kspeeder.url, "http://192.168.30.7:5004/", "standalone external path must win")
local kai = apps_openwrt.new(dependencies("kai.json", false, {
["kai.@kai[0].port"] = "8198"
})):resolve("kai")
equal(kai.available, true)
equal(kai.mode, "external")
equal(kai.url, "http://192.168.30.7:8198/apps/kai/", "app-base external path must be preserved")
local kai_proxy = apps_openwrt.new(dependencies("kai.json", true)):resolve("kai")
equal(kai_proxy.available, true)
equal(kai_proxy.mode, "apps")
equal(kai_proxy.url, "/apps/kai/", "shared entry must keep the public KAI root")
local docker_disabled = apps_openwrt.new(dependencies("dockermanager.json", false)):resolve("dockermanager")
equal(docker_disabled.available, false)
equal(docker_disabled.reason, "entry_unavailable_and_external_disabled")
local docker_enabled = apps_openwrt.new(dependencies("dockermanager.json", false, {
["dockermanager.@dockermanager[0].external_port_enabled"] = "1",
["dockermanager.@dockermanager[0].port"] = "8193"
})):resolve("dockermanager")
equal(docker_enabled.available, true)
equal(docker_enabled.url, "http://192.168.30.7:8193/apps/dockermanager/")
local builtin = apps_openwrt.new(dependencies("openwrt.json", true)):resolve("openwrt-luci")
equal(builtin.registered, true)
equal(builtin.available, false)
equal(builtin.reason, "entry_unsupported_and_external_disabled")
print("linkease apps OpenWrt adapter tests passed")
@@ -1,177 +0,0 @@
local source_root = assert(os.getenv("LINKEASE_AUTH_SOURCE_ROOT"), "LINKEASE_AUTH_SOURCE_ROOT is required")
package.path = source_root .. "/?.lua;" .. package.path
local function equal(actual, expected, message)
if actual ~= expected then
error((message or "values differ") .. ": got " .. tostring(actual) .. ", want " .. tostring(expected), 2)
end
end
local function has_header(headers, pattern)
for _, header in ipairs(headers) do
if header[1] == "Set-Cookie" and header[2]:match(pattern) then return true end
end
return false
end
local function scenario(options)
options = options or {}
local headers = {}
local result = {}
local cookies = options.cookies or {}
local environment = options.environment or { HTTP_HOST = "192.168.30.7:10000" }
local forms = options.forms or {}
package.loaded["luci.model.linkease.auth"] = nil
local auth = require "luci.model.linkease.auth"
local bridge = auth.new({
http = {
getcookie = function(name) return cookies[name] end,
getenv = function(name) return environment[name] end,
formvalue = function(name) return forms[name] end,
header = function(name, value) headers[#headers + 1] = { name, value } end,
redirect = function(value) result.redirect = value end,
status = function(code, message) result.status = code; result.message = message end
},
ubus = function(object, method, request)
if object == "session" and method == "get" and request.ubus_rpc_session == options.valid_sid then
return { values = { username = "root" } }
end
return nil
end,
lan_ip = function() return "192.168.30.7" end,
build_url = function(...)
local arguments = {...}
local url = "/cgi-bin/luci/admin/services/linkease_auth/auth_finish"
if arguments[5] then url = url .. "/" .. arguments[5] end
return url
end
})
return bridge, result, headers
end
local bridge, result, headers = scenario({
cookies = { sysauth = "valid-session" },
valid_sid = "valid-session",
forms = { ["return"] = "/apps/dockermanager/" }
})
bridge:auth()
equal(result.redirect, "/apps/dockermanager/")
equal(has_header(headers, "linkease_openwrt_sid=valid%-session"), true)
equal(has_header(headers, "linkease_openwrt_sid=.*Path=/apps"), true)
bridge, result, headers = scenario({ forms = { ["return"] = "/apps/kaiplus/" } })
bridge:auth()
local state = result.redirect:match("/auth_finish/([A-Za-z0-9_-]+)$")
equal(type(state), "string", "auth begin must encode return intent into the protected path")
bridge, result = scenario({
cookies = { sysauth = "valid-session" },
valid_sid = "valid-session"
})
bridge:auth_finish(state)
equal(result.redirect, "/apps/kaiplus/")
local first_bridge, first_result = scenario({
forms = { ["return"] = "/apps/dockermanager/containers/abc?tab=logs&follow=1" }
})
first_bridge:auth()
local first_state = first_result.redirect:match("/auth_finish/([A-Za-z0-9_-]+)$")
equal(type(first_state), "string", "deep link state must be encoded")
local second_bridge, second_result = scenario({
forms = { ["return"] = "/apps/kaiplus/?conversation=second" }
})
second_bridge:auth()
local second_state = second_result.redirect:match("/auth_finish/([A-Za-z0-9_-]+)$")
equal(type(second_state), "string", "second tab state must be encoded")
bridge, result = scenario({
cookies = { sysauth = "valid-session", linkease_openwrt_pending_return = "%2Fapps%2Fwrong%2F" },
valid_sid = "valid-session"
})
bridge:auth_finish(first_state)
equal(result.redirect, "/apps/dockermanager/containers/abc?tab=logs&follow=1", "first tab must use its own path state")
bridge, result = scenario({
cookies = { sysauth = "valid-session", linkease_openwrt_pending_return = "%2Fapps%2Fwrong%2F" },
valid_sid = "valid-session"
})
bridge:auth_finish(second_state)
equal(result.redirect, "/apps/kaiplus/?conversation=second", "second tab must use its own path state")
bridge, result = scenario({
cookies = { sysauth = "valid-session" },
valid_sid = "valid-session"
})
bridge:auth_finish("not_base64url_@@")
equal(result.redirect, "/apps/", "malformed state must fail closed")
bridge, result = scenario({
cookies = { sysauth = "valid-session" },
valid_sid = "valid-session"
})
bridge:auth_finish(string.rep("A", 4097))
equal(result.redirect, "/apps/", "oversized state must fail closed")
bridge, result = scenario({
cookies = { sysauth = "valid-session" },
valid_sid = "valid-session",
forms = { ["return"] = "http://192.168.30.7:8192/apps/dockermanager/" }
})
bridge:auth()
equal(result.redirect, "http://192.168.30.7:8192/apps/dockermanager/")
bridge, result = scenario({
cookies = { sysauth = "valid-session" },
valid_sid = "valid-session",
forms = { ["return"] = "http://192.168.30.7:3200/" }
})
bridge:auth()
equal(result.redirect, "http://192.168.30.7:3200/", "same-device external app port must be allowed")
bridge, result = scenario({
cookies = { sysauth = "valid-session" },
valid_sid = "valid-session",
forms = { ["return"] = "http://192.168.30.7:10000/cgi-bin/luci/" }
})
bridge:auth()
equal(result.redirect, "/apps/", "same-authority non-app path must remain rejected")
bridge, result = scenario({
cookies = { sysauth = "valid-session" },
valid_sid = "valid-session",
forms = { ["return"] = "http://evil.example/apps/dockermanager/" }
})
bridge:auth()
equal(result.redirect, "/apps/")
bridge, result = scenario({
cookies = { sysauth = "valid-session" },
valid_sid = "valid-session",
forms = { ["return"] = "/apps/dockermanager/%0d%0aLocation:%20http://evil.example/" }
})
bridge:auth()
equal(result.redirect, "/apps/dockermanager/%0d%0aLocation:%20http://evil.example/")
bridge, result = scenario({
cookies = { sysauth = "valid-session" },
valid_sid = "valid-session",
forms = { ["return"] = "/apps/dockermanager/\r\nLocation: http://evil.example/" }
})
bridge:auth()
equal(result.redirect, "/apps/", "raw control characters must be rejected")
bridge, result = scenario({
cookies = { sysauth = "valid-session" },
valid_sid = "valid-session",
forms = { ["return"] = "http://192.168.30.7:8192@evil.example/apps/dockermanager/" }
})
bridge:auth()
equal(result.redirect, "/apps/")
bridge, result = scenario()
bridge:auth_finish()
equal(result.status, 403)
print("linkease auth bridge tests passed")