Compare commits
95 Commits
8a2b861ab8
...
main
Author | SHA1 | Date | |
---|---|---|---|
61c65010e4 | |||
dde9993e07 | |||
c3d3fefd08 | |||
7aad0ba0e1 | |||
254a44ee82 | |||
de8efb1b5d | |||
bb0d27b755 | |||
e6ed55bcc7 | |||
cba8b4089c | |||
8c3feb5233 | |||
6aeedb89a6 | |||
310af0a489 | |||
93f9e3e0c7 | |||
44a98f1741 | |||
fbc308d265 | |||
30dd8e8070 | |||
3035d7563f | |||
1fe688e287 | |||
2114e16af9 | |||
729a6a211d | |||
f17190bbc9 | |||
f9ae7618c9 | |||
b4d9af64d0 | |||
9ce61897d4 | |||
75a45e18e3 | |||
0f748992a8 | |||
7a79961839 | |||
c06a8f2265 | |||
4199e6e793 | |||
20a8b68314 | |||
6eca8bd3a5 | |||
c305bcd995 | |||
7585850fa3 | |||
70c05cf42c | |||
9ed504bb5f | |||
282f209217 | |||
510bc2b2a9 | |||
654c5394cd | |||
f3c4004500 | |||
23839bfcbe | |||
7c580e25ea | |||
1f24cd18b1 | |||
b52af66471 | |||
745de810e4 | |||
8eeaad61d5 | |||
8623e9cdd9 | |||
f0233a640d | |||
c9e6e5d229 | |||
d147fe682c | |||
41c7bc33a0 | |||
ad59627743 | |||
1a143b8995 | |||
0aa280381e | |||
0fefa83215 | |||
697906c953 | |||
2128066f14 | |||
a7761faf62 | |||
0510472f3c | |||
ca1af05b1e | |||
97f0f5529a | |||
908b5c6906 | |||
a2473fccdb | |||
0acd6acb11 | |||
f02d18674a | |||
a58ca7b94c | |||
22743c248c | |||
1f180d0627 | |||
60afced577 | |||
974c395331 | |||
c296d2612e | |||
c918c2da39 | |||
eb55a5d783 | |||
f63ab77fc1 | |||
d0ea038cdb | |||
583bca0a38 | |||
8a27a2ad06 | |||
b576c57f18 | |||
00cb10d9f5 | |||
14b8fc2cb6 | |||
0f2a13c4e3 | |||
f0615dfdc2 | |||
8068b1dcd0 | |||
4767b0ea01 | |||
8d2d7e2455 | |||
c8868c6732 | |||
ea5b2af9e8 | |||
975a70f69f | |||
d8f6c626ef | |||
f5c104b3de | |||
c85af16b55 | |||
d06c9c24c9 | |||
627dc1ac68 | |||
7fcae7b276 | |||
268e92d12a | |||
f6d3aef757 |
3
ReadMe.md
Normal file
3
ReadMe.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# ComputerCraft Programs & APIs
|
||||||
|
|
||||||
|
ComputerCraft Lua programs and APIs
|
45
install.lua
Normal file
45
install.lua
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
local args = { ... }
|
||||||
|
|
||||||
|
local target_branch = "main"
|
||||||
|
local bin_path = "/bin/"
|
||||||
|
local lib_path = "/lib/"
|
||||||
|
local startup_path = "/startup/"
|
||||||
|
|
||||||
|
|
||||||
|
function download(remote_path, local_path)
|
||||||
|
io.write("Downloading " .. remote_path .. " to " .. local_path .. "\n")
|
||||||
|
response = http.get("https://gitea.layla.gg/layla/computercraft/raw/branch/" .. target_branch .. "/" .. remote_path, nil)
|
||||||
|
if response.getResponseCode() == 200 then
|
||||||
|
file = fs.open(local_path, "w")
|
||||||
|
file.write(response.readAll())
|
||||||
|
file.close()
|
||||||
|
else
|
||||||
|
io.write("Failed to update " .. remote_path .. "\n")
|
||||||
|
io.write(response)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Startup
|
||||||
|
download("utils/lsh.lua", startup_path .. "lsh.lua")
|
||||||
|
|
||||||
|
-- Lib
|
||||||
|
download("lib/auth.lua", lib_path .. "auth.lua")
|
||||||
|
download("lib/lns.lua", lib_path .. "lns.lua")
|
||||||
|
download("lib/lum.lua", lib_path .. "lum.lua")
|
||||||
|
|
||||||
|
-- Bin
|
||||||
|
download("lum.lua", bin_path .. "lum.lua")
|
||||||
|
|
||||||
|
-- Run lsh
|
||||||
|
ok = shell.run(startup_path .. "lsh.lua")
|
||||||
|
if not ok then
|
||||||
|
io.write("Failed to run lsh!\n")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Packages
|
||||||
|
lum.install("dig")
|
||||||
|
lum.install("auth")
|
||||||
|
|
||||||
|
io.write("Done!\n")
|
202
lib/auth.lua
Normal file
202
lib/auth.lua
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
-- Desc: Authentication library
|
||||||
|
-- Auth: Layla Manley
|
||||||
|
-- Link: https://gitea.layla.gg/layla/computercraft
|
||||||
|
settings.define("auth.token", {
|
||||||
|
description = "Authentication token",
|
||||||
|
type = "number",
|
||||||
|
default = -1
|
||||||
|
})
|
||||||
|
|
||||||
|
settings.define("auth.server", {
|
||||||
|
description = "Authentication server",
|
||||||
|
type = "string",
|
||||||
|
default = "auth.box"
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
function login(username, password)
|
||||||
|
local data = {
|
||||||
|
["action"] = "login",
|
||||||
|
["username"] = username,
|
||||||
|
["password"] = password
|
||||||
|
}
|
||||||
|
|
||||||
|
local auth_server_id = lns.lookup(settings.get("auth.server"))
|
||||||
|
rednet.send(auth_server_id, data, "auth")
|
||||||
|
|
||||||
|
local token = nil
|
||||||
|
while true do
|
||||||
|
id, msg = rednet.receive("auth")
|
||||||
|
if id == auth_server_id then
|
||||||
|
if msg == "invalid request" then
|
||||||
|
io.write("Invalid request\n")
|
||||||
|
return false
|
||||||
|
elseif msg == "user not found" then
|
||||||
|
io.write("User not found\n")
|
||||||
|
return false
|
||||||
|
elseif msg == "invalid password" then
|
||||||
|
io.write("Invalid password\n")
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
token = msg
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
settings.set("auth.username", username)
|
||||||
|
settings.set("auth.token", token)
|
||||||
|
settings.save()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function register(username, password)
|
||||||
|
local data = {
|
||||||
|
["action"] = "register",
|
||||||
|
["username"] = username,
|
||||||
|
["password"] = password
|
||||||
|
}
|
||||||
|
|
||||||
|
local auth_server_id = lns.lookup(settings.get("auth.server"))
|
||||||
|
rednet.send(auth_server_id, data, "auth")
|
||||||
|
|
||||||
|
while true do
|
||||||
|
id, msg = rednet.receive("auth")
|
||||||
|
if id == auth_server_id then
|
||||||
|
if msg == "invalid request" then
|
||||||
|
io.write("Invalid request\n")
|
||||||
|
return false
|
||||||
|
elseif msg == "user already exists" then
|
||||||
|
io.write("User already exists\n")
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
io.write("Registered user " .. username .. "\n")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function logout()
|
||||||
|
settings.set("auth.token", -1)
|
||||||
|
settings.save()
|
||||||
|
io.write("Logged out\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_token()
|
||||||
|
return settings.get("auth.token")
|
||||||
|
end
|
||||||
|
|
||||||
|
function check_user_in_group(username, group)
|
||||||
|
local data = {
|
||||||
|
["action"] = "check_group",
|
||||||
|
["username"] = username,
|
||||||
|
["group"] = group
|
||||||
|
}
|
||||||
|
|
||||||
|
local auth_server_id = lns.lookup(settings.get("auth.server"))
|
||||||
|
rednet.send(auth_server_id, data, "auth")
|
||||||
|
|
||||||
|
while true do
|
||||||
|
id, msg = rednet.receive("auth")
|
||||||
|
if id == auth_server_id then
|
||||||
|
if msg == "invalid request" then
|
||||||
|
io.write("Invalid request\n")
|
||||||
|
return false
|
||||||
|
elseif msg == true then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function trueKeysToString(tbl)
|
||||||
|
local result = ""
|
||||||
|
local first = true
|
||||||
|
|
||||||
|
for key, value in pairs(tbl) do
|
||||||
|
if value == true then
|
||||||
|
if not first then
|
||||||
|
result = result .. ", "
|
||||||
|
else
|
||||||
|
first = false
|
||||||
|
end
|
||||||
|
result = result .. key
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function list_to_string(list)
|
||||||
|
local str = ""
|
||||||
|
for i, v in ipairs(list) do
|
||||||
|
str = str .. v .. ", "
|
||||||
|
end
|
||||||
|
return str:sub(1, -3)
|
||||||
|
end
|
||||||
|
|
||||||
|
function list_user_groups(username)
|
||||||
|
local data = {
|
||||||
|
["action"] = "list_groups",
|
||||||
|
["username"] = username
|
||||||
|
}
|
||||||
|
|
||||||
|
local auth_server_id = lns.lookup(settings.get("auth.server"))
|
||||||
|
rednet.send(auth_server_id, data, "auth")
|
||||||
|
|
||||||
|
while true do
|
||||||
|
id, msg = rednet.receive("auth")
|
||||||
|
if id == auth_server_id then
|
||||||
|
if msg == "invalid request" then
|
||||||
|
io.write("Invalid request\n")
|
||||||
|
return
|
||||||
|
elseif msg == "user not found" then
|
||||||
|
io.write("User not found\n")
|
||||||
|
return
|
||||||
|
elseif msg == "invalid token" then
|
||||||
|
io.write("Invalid token\n")
|
||||||
|
return
|
||||||
|
else
|
||||||
|
return msg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function add_user_to_group(username, group)
|
||||||
|
local data = {
|
||||||
|
["action"] = "add_group",
|
||||||
|
["username"] = username,
|
||||||
|
["group"] = group,
|
||||||
|
["token"] = get_token()
|
||||||
|
}
|
||||||
|
|
||||||
|
local auth_server_id = lns.lookup(settings.get("auth.server"))
|
||||||
|
rednet.send(auth_server_id, data, "auth")
|
||||||
|
|
||||||
|
while true do
|
||||||
|
id, msg = rednet.receive("auth")
|
||||||
|
if id == auth_server_id then
|
||||||
|
if msg == "invalid request" then
|
||||||
|
io.write("Invalid request\n")
|
||||||
|
return false
|
||||||
|
elseif msg == "user not found" then
|
||||||
|
io.write("User not found\n")
|
||||||
|
return false
|
||||||
|
elseif msg == "invalid token" then
|
||||||
|
io.write("Invalid token\n")
|
||||||
|
return false
|
||||||
|
elseif msg == "invalid privileges" then
|
||||||
|
io.write("Invalid privileges\n")
|
||||||
|
return false
|
||||||
|
elseif msg == "ok" then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
22
lib/ccttp.lua
Normal file
22
lib/ccttp.lua
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
-- Desc: CCTTP Networking Library
|
||||||
|
-- Auth: Layla Manley
|
||||||
|
-- Link: https://gitea.layla.gg/layla/computercraft
|
||||||
|
|
||||||
|
Response = {
|
||||||
|
status = 0,
|
||||||
|
body = "",
|
||||||
|
headers = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Response:new(o)
|
||||||
|
o = o or {}
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
function get(url, headers)
|
||||||
|
headers = headers or {}
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
212
lib/json.lua
Normal file
212
lib/json.lua
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
-- Desc: JSON encoder/decoder
|
||||||
|
-- Auth: ElvishJerricco
|
||||||
|
-- Link: http://www.computercraft.info/forums2/index.php?/topic/5854-json-api-v201-for-computercraft/
|
||||||
|
------------------------------------------------------------------ utils
|
||||||
|
local controls = {["\n"]="\\n", ["\r"]="\\r", ["\t"]="\\t", ["\b"]="\\b", ["\f"]="\\f", ["\""]="\\\"", ["\\"]="\\\\"}
|
||||||
|
|
||||||
|
local function isArray(t)
|
||||||
|
local max = 0
|
||||||
|
for k,v in pairs(t) do
|
||||||
|
if type(k) ~= "number" then
|
||||||
|
return false
|
||||||
|
elseif k > max then
|
||||||
|
max = k
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return max == #t
|
||||||
|
end
|
||||||
|
|
||||||
|
local whites = {['\n']=true; ['\r']=true; ['\t']=true; [' ']=true; [',']=true; [':']=true}
|
||||||
|
function removeWhite(str)
|
||||||
|
while whites[str:sub(1, 1)] do
|
||||||
|
str = str:sub(2)
|
||||||
|
end
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------ encoding
|
||||||
|
|
||||||
|
local function encodeCommon(val, pretty, tabLevel, tTracking)
|
||||||
|
local str = ""
|
||||||
|
|
||||||
|
-- Tabbing util
|
||||||
|
local function tab(s)
|
||||||
|
str = str .. ("\t"):rep(tabLevel) .. s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function arrEncoding(val, bracket, closeBracket, iterator, loopFunc)
|
||||||
|
str = str .. bracket
|
||||||
|
if pretty then
|
||||||
|
str = str .. "\n"
|
||||||
|
tabLevel = tabLevel + 1
|
||||||
|
end
|
||||||
|
for k,v in iterator(val) do
|
||||||
|
tab("")
|
||||||
|
loopFunc(k,v)
|
||||||
|
str = str .. ","
|
||||||
|
if pretty then str = str .. "\n" end
|
||||||
|
end
|
||||||
|
if pretty then
|
||||||
|
tabLevel = tabLevel - 1
|
||||||
|
end
|
||||||
|
if str:sub(-2) == ",\n" then
|
||||||
|
str = str:sub(1, -3) .. "\n"
|
||||||
|
elseif str:sub(-1) == "," then
|
||||||
|
str = str:sub(1, -2)
|
||||||
|
end
|
||||||
|
tab(closeBracket)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Table encoding
|
||||||
|
if type(val) == "table" then
|
||||||
|
assert(not tTracking[val], "Cannot encode a table holding itself recursively")
|
||||||
|
tTracking[val] = true
|
||||||
|
if isArray(val) then
|
||||||
|
arrEncoding(val, "[", "]", ipairs, function(k,v)
|
||||||
|
str = str .. encodeCommon(v, pretty, tabLevel, tTracking)
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
arrEncoding(val, "{", "}", pairs, function(k,v)
|
||||||
|
assert(type(k) == "string", "JSON object keys must be strings", 2)
|
||||||
|
str = str .. encodeCommon(k, pretty, tabLevel, tTracking)
|
||||||
|
str = str .. (pretty and ": " or ":") .. encodeCommon(v, pretty, tabLevel, tTracking)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
-- String encoding
|
||||||
|
elseif type(val) == "string" then
|
||||||
|
str = '"' .. val:gsub("[%c\"\\]", controls) .. '"'
|
||||||
|
-- Number encoding
|
||||||
|
elseif type(val) == "number" or type(val) == "boolean" then
|
||||||
|
str = tostring(val)
|
||||||
|
else
|
||||||
|
error("JSON only supports arrays, objects, numbers, booleans, and strings", 2)
|
||||||
|
end
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
function encode(val)
|
||||||
|
return encodeCommon(val, false, 0, {})
|
||||||
|
end
|
||||||
|
|
||||||
|
function encodePretty(val)
|
||||||
|
return encodeCommon(val, true, 0, {})
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------------ decoding
|
||||||
|
|
||||||
|
local decodeControls = {}
|
||||||
|
for k,v in pairs(controls) do
|
||||||
|
decodeControls[v] = k
|
||||||
|
end
|
||||||
|
|
||||||
|
function parseBoolean(str)
|
||||||
|
if str:sub(1, 4) == "true" then
|
||||||
|
return true, removeWhite(str:sub(5))
|
||||||
|
else
|
||||||
|
return false, removeWhite(str:sub(6))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function parseNull(str)
|
||||||
|
return nil, removeWhite(str:sub(5))
|
||||||
|
end
|
||||||
|
|
||||||
|
local numChars = {['e']=true; ['E']=true; ['+']=true; ['-']=true; ['.']=true}
|
||||||
|
function parseNumber(str)
|
||||||
|
local i = 1
|
||||||
|
while numChars[str:sub(i, i)] or tonumber(str:sub(i, i)) do
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
local val = tonumber(str:sub(1, i - 1))
|
||||||
|
str = removeWhite(str:sub(i))
|
||||||
|
return val, str
|
||||||
|
end
|
||||||
|
|
||||||
|
function parseString(str)
|
||||||
|
str = str:sub(2)
|
||||||
|
local s = ""
|
||||||
|
while str:sub(1,1) ~= "\"" do
|
||||||
|
local next = str:sub(1,1)
|
||||||
|
str = str:sub(2)
|
||||||
|
assert(next ~= "\n", "Unclosed string")
|
||||||
|
|
||||||
|
if next == "\\" then
|
||||||
|
local escape = str:sub(1,1)
|
||||||
|
str = str:sub(2)
|
||||||
|
|
||||||
|
next = assert(decodeControls[next..escape], "Invalid escape character")
|
||||||
|
end
|
||||||
|
|
||||||
|
s = s .. next
|
||||||
|
end
|
||||||
|
return s, removeWhite(str:sub(2))
|
||||||
|
end
|
||||||
|
|
||||||
|
function parseArray(str)
|
||||||
|
str = removeWhite(str:sub(2))
|
||||||
|
|
||||||
|
local val = {}
|
||||||
|
local i = 1
|
||||||
|
while str:sub(1, 1) ~= "]" do
|
||||||
|
local v = nil
|
||||||
|
v, str = parseValue(str)
|
||||||
|
val[i] = v
|
||||||
|
i = i + 1
|
||||||
|
str = removeWhite(str)
|
||||||
|
end
|
||||||
|
str = removeWhite(str:sub(2))
|
||||||
|
return val, str
|
||||||
|
end
|
||||||
|
|
||||||
|
function parseObject(str)
|
||||||
|
str = removeWhite(str:sub(2))
|
||||||
|
|
||||||
|
local val = {}
|
||||||
|
while str:sub(1, 1) ~= "}" do
|
||||||
|
local k, v = nil, nil
|
||||||
|
k, v, str = parseMember(str)
|
||||||
|
val[k] = v
|
||||||
|
str = removeWhite(str)
|
||||||
|
end
|
||||||
|
str = removeWhite(str:sub(2))
|
||||||
|
return val, str
|
||||||
|
end
|
||||||
|
|
||||||
|
function parseMember(str)
|
||||||
|
local k = nil
|
||||||
|
k, str = parseValue(str)
|
||||||
|
local val = nil
|
||||||
|
val, str = parseValue(str)
|
||||||
|
return k, val, str
|
||||||
|
end
|
||||||
|
|
||||||
|
function parseValue(str)
|
||||||
|
local fchar = str:sub(1, 1)
|
||||||
|
if fchar == "{" then
|
||||||
|
return parseObject(str)
|
||||||
|
elseif fchar == "[" then
|
||||||
|
return parseArray(str)
|
||||||
|
elseif tonumber(fchar) ~= nil or numChars[fchar] then
|
||||||
|
return parseNumber(str)
|
||||||
|
elseif str:sub(1, 4) == "true" or str:sub(1, 5) == "false" then
|
||||||
|
return parseBoolean(str)
|
||||||
|
elseif fchar == "\"" then
|
||||||
|
return parseString(str)
|
||||||
|
elseif str:sub(1, 4) == "null" then
|
||||||
|
return parseNull(str)
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function decode(str)
|
||||||
|
str = removeWhite(str)
|
||||||
|
t = parseValue(str)
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
function decodeFromFile(path)
|
||||||
|
local file = assert(fs.open(path, "r"))
|
||||||
|
local decoded = decode(file.readAll())
|
||||||
|
file.close()
|
||||||
|
return decoded
|
||||||
|
end
|
84
lib/lns.lua
Normal file
84
lib/lns.lua
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
-- Desc: LNS Client
|
||||||
|
-- Auth: Layla Manley
|
||||||
|
-- Link: https://gitea.layla.gg/layla/computercraft
|
||||||
|
local cache = {}
|
||||||
|
local cache_file = "/var/cache/lns_cache.db"
|
||||||
|
|
||||||
|
settings.define("lns_client.allow_dynamic_lns_hosts", {
|
||||||
|
description = "Allow dynamic LNS hosts",
|
||||||
|
type = "boolean",
|
||||||
|
default = false
|
||||||
|
})
|
||||||
|
|
||||||
|
settings.define("lns_client.lns_server", {
|
||||||
|
description = "LNS Server Rednet ID",
|
||||||
|
type = "number",
|
||||||
|
default = 8
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
function _load_cache()
|
||||||
|
if fs.exists(cache_file) then
|
||||||
|
db = fs.open(cache_file, "r")
|
||||||
|
db_contents = db.readAll()
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
cache = textutils.unserialize(db_contents)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _save_cache()
|
||||||
|
db = fs.open(cache_file, "w")
|
||||||
|
db.write(textutils.serialize(cache))
|
||||||
|
db.close()
|
||||||
|
end
|
||||||
|
|
||||||
|
function server()
|
||||||
|
local lns_server_id = settings.get("lns_client.lns_server")
|
||||||
|
local allow_dynamic_lns_hosts = settings.get("lns_client.allow_dynamic_lns_hosts")
|
||||||
|
|
||||||
|
if allow_dynamic_lns_hosts then
|
||||||
|
lns_server_id = rednet.lookup("lns", lns_server)
|
||||||
|
if lns_server_id == nil then
|
||||||
|
io.write("LNS Server not found!\n")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return lns_server_id
|
||||||
|
end
|
||||||
|
|
||||||
|
function lookup(remote_hostname)
|
||||||
|
|
||||||
|
_load_cache()
|
||||||
|
if cache[remote_hostname] ~= nil then
|
||||||
|
return cache[remote_hostname]
|
||||||
|
end
|
||||||
|
|
||||||
|
local data = {
|
||||||
|
["action"] = "lookup",
|
||||||
|
["hostname"] = remote_hostname
|
||||||
|
}
|
||||||
|
|
||||||
|
local lns_server_id = server()
|
||||||
|
rednet.send(lns_server_id, data, "lns")
|
||||||
|
|
||||||
|
while true do
|
||||||
|
id, msg = rednet.receive("lns")
|
||||||
|
if id == lns_server_id then
|
||||||
|
if msg == nil then
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
cache[remote_hostname] = msg
|
||||||
|
_save_cache()
|
||||||
|
return msg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function clear_cache()
|
||||||
|
cache = {}
|
||||||
|
_save_cache()
|
||||||
|
print("Cleared cache!")
|
||||||
|
end
|
66
lib/lum.lua
Normal file
66
lib/lum.lua
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
-- Desc: Lum Package Manager
|
||||||
|
-- Auth: Layla Manley
|
||||||
|
-- Link: https://gitea.layla.gg/layla/computercraft
|
||||||
|
local target_branch = "main"
|
||||||
|
local bin_path = "/bin/"
|
||||||
|
local lib_path = "/lib/"
|
||||||
|
local startup_path = "/startup/"
|
||||||
|
|
||||||
|
settings.define("lum.target_branch", {
|
||||||
|
description = "Target branch for updates",
|
||||||
|
type = "string",
|
||||||
|
default = "main"
|
||||||
|
})
|
||||||
|
|
||||||
|
settings.define("lum.log_level", {
|
||||||
|
description = "Log Level",
|
||||||
|
type = "string",
|
||||||
|
default = "debug"
|
||||||
|
})
|
||||||
|
|
||||||
|
function download_lua(remote_path, local_path)
|
||||||
|
local url = "https://gitea.layla.gg/layla/computercraft/raw/branch/" .. settings.get("lum.target_branch") .. "/" .. remote_path
|
||||||
|
if settings.get("lum.log_level") == "debug" then
|
||||||
|
io.write("Downloading " .. url .. " to " .. local_path .. "\n")
|
||||||
|
end
|
||||||
|
local response = http.get(url, nil)
|
||||||
|
if response.getResponseCode() == 200 then
|
||||||
|
file = fs.open(local_path, "w")
|
||||||
|
file.write(response.readAll())
|
||||||
|
file.close()
|
||||||
|
else
|
||||||
|
io.write("Failed to download " .. remote_path .. ".lua\n")
|
||||||
|
io.write(response)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function install(package)
|
||||||
|
io.write("Installing " .. package .. "\n")
|
||||||
|
|
||||||
|
local dir = bin_path
|
||||||
|
local package_dir = "packages/"
|
||||||
|
--if package starts with lib then remove lib
|
||||||
|
if string.sub(package, 1, 3) == "lib" then
|
||||||
|
package = string.sub(package, 4)
|
||||||
|
dir = lib_path
|
||||||
|
package_dir = "lib/"
|
||||||
|
end
|
||||||
|
|
||||||
|
download_lua(package_dir .. package .. ".lua", dir .. package .. ".lua")
|
||||||
|
if dir == lib_path then
|
||||||
|
os.loadAPI(lib_path .. package .. ".lua")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function remove(package)
|
||||||
|
io.write("Removing " .. package .. "\n")
|
||||||
|
|
||||||
|
local dir = bin_path
|
||||||
|
--if package starts with lib then remove lib
|
||||||
|
if string.sub(package, 1, 3) == "lib" then
|
||||||
|
package = string.sub(package, 5)
|
||||||
|
dir = lib_path
|
||||||
|
end
|
||||||
|
|
||||||
|
fs.delete(dir .. package .. ".lua")
|
||||||
|
end
|
195
lib/sha256.lua
Normal file
195
lib/sha256.lua
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
-- Desc: SHA-256 hashing algorithm
|
||||||
|
-- Auth: GravityScore
|
||||||
|
-- Link: http://www.computercraft.info/forums2/index.php?/topic/8169-sha-256-in-pure-lua/
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
--
|
||||||
|
-- Adaptation of the Secure Hashing Algorithm (SHA-244/256)
|
||||||
|
-- Found Here: http://lua-users.org/wiki/SecureHashAlgorithm
|
||||||
|
--
|
||||||
|
-- Using an adapted version of the bit library
|
||||||
|
-- Found Here: https://bitbucket.org/Boolsheet/bslf/src/1ee664885805/bit.lua
|
||||||
|
--
|
||||||
|
|
||||||
|
local MOD = 2^32
|
||||||
|
local MODM = MOD-1
|
||||||
|
|
||||||
|
local function memoize(f)
|
||||||
|
local mt = {}
|
||||||
|
local t = setmetatable({}, mt)
|
||||||
|
function mt:__index(k)
|
||||||
|
local v = f(k)
|
||||||
|
t[k] = v
|
||||||
|
return v
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
local function make_bitop_uncached(t, m)
|
||||||
|
local function bitop(a, b)
|
||||||
|
local res,p = 0,1
|
||||||
|
while a ~= 0 and b ~= 0 do
|
||||||
|
local am, bm = a % m, b % m
|
||||||
|
res = res + t[am][bm] * p
|
||||||
|
a = (a - am) / m
|
||||||
|
b = (b - bm) / m
|
||||||
|
p = p*m
|
||||||
|
end
|
||||||
|
res = res + (a + b) * p
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
return bitop
|
||||||
|
end
|
||||||
|
|
||||||
|
local function make_bitop(t)
|
||||||
|
local op1 = make_bitop_uncached(t,2^1)
|
||||||
|
local op2 = memoize(function(a) return memoize(function(b) return op1(a, b) end) end)
|
||||||
|
return make_bitop_uncached(op2, 2 ^ (t.n or 1))
|
||||||
|
end
|
||||||
|
|
||||||
|
local bxor1 = make_bitop({[0] = {[0] = 0,[1] = 1}, [1] = {[0] = 1, [1] = 0}, n = 4})
|
||||||
|
|
||||||
|
local function bxor(a, b, c, ...)
|
||||||
|
local z = nil
|
||||||
|
if b then
|
||||||
|
a = a % MOD
|
||||||
|
b = b % MOD
|
||||||
|
z = bxor1(a, b)
|
||||||
|
if c then z = bxor(z, c, ...) end
|
||||||
|
return z
|
||||||
|
elseif a then return a % MOD
|
||||||
|
else return 0 end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function band(a, b, c, ...)
|
||||||
|
local z
|
||||||
|
if b then
|
||||||
|
a = a % MOD
|
||||||
|
b = b % MOD
|
||||||
|
z = ((a + b) - bxor1(a,b)) / 2
|
||||||
|
if c then z = bit32_band(z, c, ...) end
|
||||||
|
return z
|
||||||
|
elseif a then return a % MOD
|
||||||
|
else return MODM end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bnot(x) return (-1 - x) % MOD end
|
||||||
|
|
||||||
|
local function rshift1(a, disp)
|
||||||
|
if disp < 0 then return lshift(a,-disp) end
|
||||||
|
return math.floor(a % 2 ^ 32 / 2 ^ disp)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rshift(x, disp)
|
||||||
|
if disp > 31 or disp < -31 then return 0 end
|
||||||
|
return rshift1(x % MOD, disp)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function lshift(a, disp)
|
||||||
|
if disp < 0 then return rshift(a,-disp) end
|
||||||
|
return (a * 2 ^ disp) % 2 ^ 32
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rrotate(x, disp)
|
||||||
|
x = x % MOD
|
||||||
|
disp = disp % 32
|
||||||
|
local low = band(x, 2 ^ disp - 1)
|
||||||
|
return rshift(x, disp) + lshift(low, 32 - disp)
|
||||||
|
end
|
||||||
|
|
||||||
|
local k = {
|
||||||
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
||||||
|
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||||
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||||
|
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||||
|
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||||||
|
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||||
|
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||||
|
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||||
|
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||||
|
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||||
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
||||||
|
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||||
|
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
||||||
|
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||||
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||||
|
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function str2hexa(s)
|
||||||
|
return (string.gsub(s, ".", function(c) return string.format("%02x", string.byte(c)) end))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function num2s(l, n)
|
||||||
|
local s = ""
|
||||||
|
for i = 1, n do
|
||||||
|
local rem = l % 256
|
||||||
|
s = string.char(rem) .. s
|
||||||
|
l = (l - rem) / 256
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function s232num(s, i)
|
||||||
|
local n = 0
|
||||||
|
for i = i, i + 3 do n = n*256 + string.byte(s, i) end
|
||||||
|
return n
|
||||||
|
end
|
||||||
|
|
||||||
|
local function preproc(msg, len)
|
||||||
|
local extra = 64 - ((len + 9) % 64)
|
||||||
|
len = num2s(8 * len, 8)
|
||||||
|
msg = msg .. "\128" .. string.rep("\0", extra) .. len
|
||||||
|
assert(#msg % 64 == 0)
|
||||||
|
return msg
|
||||||
|
end
|
||||||
|
|
||||||
|
local function initH256(H)
|
||||||
|
H[1] = 0x6a09e667
|
||||||
|
H[2] = 0xbb67ae85
|
||||||
|
H[3] = 0x3c6ef372
|
||||||
|
H[4] = 0xa54ff53a
|
||||||
|
H[5] = 0x510e527f
|
||||||
|
H[6] = 0x9b05688c
|
||||||
|
H[7] = 0x1f83d9ab
|
||||||
|
H[8] = 0x5be0cd19
|
||||||
|
return H
|
||||||
|
end
|
||||||
|
|
||||||
|
local function digestblock(msg, i, H)
|
||||||
|
local w = {}
|
||||||
|
for j = 1, 16 do w[j] = s232num(msg, i + (j - 1)*4) end
|
||||||
|
for j = 17, 64 do
|
||||||
|
local v = w[j - 15]
|
||||||
|
local s0 = bxor(rrotate(v, 7), rrotate(v, 18), rshift(v, 3))
|
||||||
|
v = w[j - 2]
|
||||||
|
w[j] = w[j - 16] + s0 + w[j - 7] + bxor(rrotate(v, 17), rrotate(v, 19), rshift(v, 10))
|
||||||
|
end
|
||||||
|
|
||||||
|
local a, b, c, d, e, f, g, h = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8]
|
||||||
|
for i = 1, 64 do
|
||||||
|
local s0 = bxor(rrotate(a, 2), rrotate(a, 13), rrotate(a, 22))
|
||||||
|
local maj = bxor(band(a, b), band(a, c), band(b, c))
|
||||||
|
local t2 = s0 + maj
|
||||||
|
local s1 = bxor(rrotate(e, 6), rrotate(e, 11), rrotate(e, 25))
|
||||||
|
local ch = bxor (band(e, f), band(bnot(e), g))
|
||||||
|
local t1 = h + s1 + ch + k[i] + w[i]
|
||||||
|
h, g, f, e, d, c, b, a = g, f, e, d + t1, c, b, a, t1 + t2
|
||||||
|
end
|
||||||
|
|
||||||
|
H[1] = band(H[1] + a)
|
||||||
|
H[2] = band(H[2] + b)
|
||||||
|
H[3] = band(H[3] + c)
|
||||||
|
H[4] = band(H[4] + d)
|
||||||
|
H[5] = band(H[5] + e)
|
||||||
|
H[6] = band(H[6] + f)
|
||||||
|
H[7] = band(H[7] + g)
|
||||||
|
H[8] = band(H[8] + h)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sha256(msg)
|
||||||
|
msg = preproc(msg, #msg)
|
||||||
|
local H = initH256({})
|
||||||
|
for i = 1, #msg, 64 do digestblock(msg, i, H) end
|
||||||
|
return str2hexa(num2s(H[1], 4) .. num2s(H[2], 4) .. num2s(H[3], 4) .. num2s(H[4], 4) ..
|
||||||
|
num2s(H[5], 4) .. num2s(H[6], 4) .. num2s(H[7], 4) .. num2s(H[8], 4))
|
||||||
|
end
|
@ -1,67 +0,0 @@
|
|||||||
|
|
||||||
modem_location = "right"
|
|
||||||
|
|
||||||
|
|
||||||
rednet.host("lns", "default")
|
|
||||||
rednet.open(modem_location)
|
|
||||||
|
|
||||||
local data = {}
|
|
||||||
|
|
||||||
function save_data()
|
|
||||||
db = fs.open("lns.db", "w")
|
|
||||||
db.write(textutils.serialize(data))
|
|
||||||
db.close()
|
|
||||||
end
|
|
||||||
|
|
||||||
function load_data()
|
|
||||||
if fs.exists("lns.db") then
|
|
||||||
db = fs.open("lns.db", "r")
|
|
||||||
db_contents = db.readAll()
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
data = textutils.unserialize(db_contents)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function split (inputstr, sep)
|
|
||||||
if sep == nil then
|
|
||||||
sep = "%s"
|
|
||||||
end
|
|
||||||
local t={}
|
|
||||||
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
|
|
||||||
table.insert(t, str)
|
|
||||||
end
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
|
|
||||||
load_data()
|
|
||||||
|
|
||||||
while true do
|
|
||||||
client_id, msg = rednet.receive()
|
|
||||||
request = msg
|
|
||||||
|
|
||||||
if request.action == nil or request.hostname == nil then
|
|
||||||
rednet.send(client_id, "invalid request")
|
|
||||||
end
|
|
||||||
|
|
||||||
if request.action == "lookup" then
|
|
||||||
|
|
||||||
-- check if hostname in data
|
|
||||||
if request.hostname == nil then
|
|
||||||
rednet.send(client_id, "not found")
|
|
||||||
else
|
|
||||||
rednet.send(client_id, data[request.hostname])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if request.action == "reload" then
|
|
||||||
load_data()
|
|
||||||
rednet.send(client_id, "ok")
|
|
||||||
end
|
|
||||||
|
|
||||||
if request.action == "register" then
|
|
||||||
data[request.hostname] = client_id
|
|
||||||
save_data()
|
|
||||||
rednet.send(client_id, "ok")
|
|
||||||
end
|
|
||||||
end
|
|
28
lum.lua
Normal file
28
lum.lua
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
local args = { ... }
|
||||||
|
|
||||||
|
local action = args[1]
|
||||||
|
|
||||||
|
if action == "install" then
|
||||||
|
package = args[2]
|
||||||
|
|
||||||
|
if package == nil then
|
||||||
|
print("Usage: lum install <package>")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
lum.install(package)
|
||||||
|
io.write("Done!\n")
|
||||||
|
elseif action == "remove" then
|
||||||
|
package = args[2]
|
||||||
|
if package == nil then
|
||||||
|
print("Usage: lum remove <package>")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
lum.remove(package)
|
||||||
|
io.write("Done!\n")
|
||||||
|
elseif action == "version" then
|
||||||
|
print("0.1.0")
|
||||||
|
else
|
||||||
|
io.write("Usage: lum <install:remove:version>\n")
|
||||||
|
end
|
33
lupdate.lua
33
lupdate.lua
@ -1,33 +0,0 @@
|
|||||||
local args = { ... }
|
|
||||||
|
|
||||||
local target_branch = "main"
|
|
||||||
local bin_path = "/bin/"
|
|
||||||
|
|
||||||
local program_name = args[1]
|
|
||||||
if program_name == nil then
|
|
||||||
print("Usage: lupdate <program_name>")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
response = http.get("https://gitea.layla.gg/layla/computercraft/raw/branch/" .. target_branch .. "/" .. program_name .. ".lua", nil)
|
|
||||||
|
|
||||||
io.write("Updating " .. program_name .. ".lua\n")
|
|
||||||
|
|
||||||
if response.getResponseCode() == 200 then
|
|
||||||
|
|
||||||
file = fs.open(bin_path .. program_name .. ".lua", "w")
|
|
||||||
file.write(response.readAll())
|
|
||||||
file.close()
|
|
||||||
|
|
||||||
-- check if program is in path
|
|
||||||
if not shell.resolveProgram(program_name) then
|
|
||||||
io.write("Program not in path, adding to path\n")
|
|
||||||
shell.setPath(shell.path() .. ":" .. bin_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
io.write("Updated " .. program_name .. ".lua\n")
|
|
||||||
else
|
|
||||||
io.write("Failed to update " .. program_name .. ".lua\n")
|
|
||||||
io.write(response)
|
|
||||||
end
|
|
||||||
|
|
75
packages/auth.lua
Normal file
75
packages/auth.lua
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
args = { ... }
|
||||||
|
|
||||||
|
if args[1] == nil then
|
||||||
|
io.write("Usage: auth <action>\n")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local action = args[1]
|
||||||
|
|
||||||
|
if action == "login" then
|
||||||
|
io.write("Username: ")
|
||||||
|
local username = io.read()
|
||||||
|
io.write("Password: ")
|
||||||
|
local password = read("*")
|
||||||
|
|
||||||
|
if login(username, password) then
|
||||||
|
io.write("Logged in as " .. username .. "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif action == "logout" then
|
||||||
|
auth.logout()
|
||||||
|
return
|
||||||
|
|
||||||
|
elseif action == "register" then
|
||||||
|
io.write("Username: ")
|
||||||
|
local username = io.read()
|
||||||
|
io.write("Password: ")
|
||||||
|
local password = read("*")
|
||||||
|
io.write("Confirm Password: ")
|
||||||
|
local confirm_password = read("*")
|
||||||
|
|
||||||
|
if password ~= confirm_password then
|
||||||
|
io.write("Passwords do not match\n")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
auth.register(username, password)
|
||||||
|
elseif action == "group" then
|
||||||
|
local subaction = args[2]
|
||||||
|
if subaction == nil then
|
||||||
|
io.write("Usage: auth group <list:check:add>\n")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if subaction == "list" then
|
||||||
|
local result = auth.list_user_groups(settings.get("auth.username"))
|
||||||
|
io.write("Groups: " .. auth.trueKeysToString(result) .. "\n")
|
||||||
|
elseif subaction == "add" then
|
||||||
|
local target_user = args[3]
|
||||||
|
local group = args[4]
|
||||||
|
if target_user == nil or group == nil then
|
||||||
|
io.write("Usage: auth group add <user> <group>\n")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local result = auth.add_user_to_group(target_user, group)
|
||||||
|
elseif subaction == "check" then
|
||||||
|
local target_user = args[3]
|
||||||
|
local group = args[4]
|
||||||
|
if target_user == nil or group == nil then
|
||||||
|
io.write("Usage: auth group check <user> <group>\n")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local result = auth.check_user_in_group(target_user, group)
|
||||||
|
if result then
|
||||||
|
io.write("User " .. target_user .. " is in group " .. group .. "\n")
|
||||||
|
else
|
||||||
|
io.write("User " .. target_user .. " is not in group " .. group .. "\n")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
io.write("Usage: auth group <list:check:add>\n")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
233
packages/auth_server.lua
Normal file
233
packages/auth_server.lua
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
|
||||||
|
settings.define("auth_server.salt", {
|
||||||
|
description = "Salt for hashing passwords",
|
||||||
|
type = "string",
|
||||||
|
default = "3m&LmNm7"
|
||||||
|
})
|
||||||
|
|
||||||
|
local data = {
|
||||||
|
users = {}
|
||||||
|
}
|
||||||
|
-- Schema:
|
||||||
|
-- data = {
|
||||||
|
-- users = {
|
||||||
|
-- ["username"] = {
|
||||||
|
-- token = "token",
|
||||||
|
-- password = "hashed password",
|
||||||
|
-- groups = {
|
||||||
|
-- ["group"] = true
|
||||||
|
-- },
|
||||||
|
-- },
|
||||||
|
-- }
|
||||||
|
|
||||||
|
function save_data()
|
||||||
|
db = fs.open("auth.db", "w")
|
||||||
|
db.write(textutils.serialize(data))
|
||||||
|
db.close()
|
||||||
|
end
|
||||||
|
|
||||||
|
function load_data()
|
||||||
|
if fs.exists("auth.db") then
|
||||||
|
db = fs.open("auth.db", "r")
|
||||||
|
db_contents = db.readAll()
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
data = textutils.unserialize(db_contents)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function generate_token(user)
|
||||||
|
local token = sha256.sha256(user .. os.time() .. math.random())
|
||||||
|
data.users[user].token = token
|
||||||
|
|
||||||
|
return token
|
||||||
|
end
|
||||||
|
|
||||||
|
function convert_password(password)
|
||||||
|
local salt = settings.get("auth_server.salt")
|
||||||
|
return sha256.sha256(password .. salt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function log(str)
|
||||||
|
io.write("[" .. os.time() .. "] " .. str .. "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
function countElements(table)
|
||||||
|
local count = 0
|
||||||
|
for key, value in pairs(table) do
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
return count
|
||||||
|
end
|
||||||
|
|
||||||
|
load_data()
|
||||||
|
math.randomseed(os.time())
|
||||||
|
|
||||||
|
while true do
|
||||||
|
client_id, msg = rednet.receive("auth")
|
||||||
|
request = msg
|
||||||
|
|
||||||
|
if request.action == nil then
|
||||||
|
rednet.send(client_id, "invalid request", "auth")
|
||||||
|
end
|
||||||
|
|
||||||
|
if request.action == "register" then
|
||||||
|
if request.username == nil or request.password == nil then
|
||||||
|
rednet.send(client_id, "invalid request", "auth")
|
||||||
|
end
|
||||||
|
|
||||||
|
if data.users[request.username] ~= nil then
|
||||||
|
rednet.send(client_id, "user already exists", "auth")
|
||||||
|
end
|
||||||
|
|
||||||
|
data.users[request.username] = {
|
||||||
|
password = convert_password(request.password)
|
||||||
|
}
|
||||||
|
|
||||||
|
local user_count = countElements(data.users)
|
||||||
|
if user_count == 1 then
|
||||||
|
log("Promoting " .. request.username .. " to admin")
|
||||||
|
data.users[request.username].groups = {
|
||||||
|
["admin"] = true
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
save_data()
|
||||||
|
|
||||||
|
rednet.send(client_id, "ok", "auth")
|
||||||
|
log(request.username .. " registered")
|
||||||
|
end
|
||||||
|
|
||||||
|
if request.action == "login" then
|
||||||
|
if request.username == nil or request.password == nil then
|
||||||
|
rednet.send(client_id, "invalid request", "auth")
|
||||||
|
|
||||||
|
elseif data.users[request.username] == nil then
|
||||||
|
rednet.send(client_id, "user not found", "auth")
|
||||||
|
|
||||||
|
log(request.username .. " failed log in attempt")
|
||||||
|
elseif convert_password(request.password) == data.users[request.username].password then
|
||||||
|
local token = generate_token(request.username)
|
||||||
|
|
||||||
|
rednet.send(client_id, token, "auth")
|
||||||
|
|
||||||
|
log(request.username .. " logged in")
|
||||||
|
else
|
||||||
|
rednet.send(client_id, "invalid password", "auth")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
if request.action == "token" then
|
||||||
|
if request.token == nil or request.token == "" or request.token == -1 then
|
||||||
|
rednet.send(client_id, "invalid request", "auth")
|
||||||
|
else
|
||||||
|
|
||||||
|
local found = false
|
||||||
|
for user, userdata in pairs(data.users) do
|
||||||
|
if userdata.token == request.token then
|
||||||
|
rednet.send(client_id, user, "auth")
|
||||||
|
found = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not found then
|
||||||
|
rednet.send(client_id, "invalid token", "auth")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if request.action == "profile" then
|
||||||
|
if request.username == nil then
|
||||||
|
rednet.send(client_id, "invalid request", "auth")
|
||||||
|
end
|
||||||
|
|
||||||
|
local profile = {}
|
||||||
|
|
||||||
|
if data.users[request.username].profile_data ~= nil then
|
||||||
|
profile = data.users[request.username].profile_data
|
||||||
|
else
|
||||||
|
profile.display_name = request.username
|
||||||
|
end
|
||||||
|
|
||||||
|
profile.username = request.username
|
||||||
|
|
||||||
|
rednet.send(client_id, data.users[request.username], "auth")
|
||||||
|
end
|
||||||
|
|
||||||
|
if request.action == "update_profile" then
|
||||||
|
if request.username == nil or request.token == nil then
|
||||||
|
rednet.send(client_id, "invalid request", "auth")
|
||||||
|
end
|
||||||
|
|
||||||
|
if data.users[request.username].token == request.token then
|
||||||
|
if request.profile_data ~= nil then
|
||||||
|
data.users[request.username].profile_data = request.profile_data
|
||||||
|
end
|
||||||
|
|
||||||
|
save_data()
|
||||||
|
|
||||||
|
rednet.send(client_id, "ok")
|
||||||
|
log(request.username .. " updated their profile", "auth")
|
||||||
|
else
|
||||||
|
rednet.send(client_id, "invalid token", "auth")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if request.action == "check_group" then
|
||||||
|
if request.username == nil or request.group == nil then
|
||||||
|
rednet.send(client_id, "invalid request", "auth")
|
||||||
|
end
|
||||||
|
|
||||||
|
if data.users[request.username].groups == nil or data.users[request.username].groups[request.group] == nil or data.users[request.username].groups[request.group] == false then
|
||||||
|
rednet.send(client_id, false, "auth")
|
||||||
|
else
|
||||||
|
rednet.send(client_id, true, "auth")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if request.action == "list_groups" then
|
||||||
|
if request.username == nil then
|
||||||
|
rednet.send(client_id, "invalid request", "auth")
|
||||||
|
end
|
||||||
|
|
||||||
|
if data.users[request.username].groups == nil then
|
||||||
|
rednet.send(client_id, {}, "auth")
|
||||||
|
else
|
||||||
|
rednet.send(client_id, data.users[request.username].groups, "auth")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Only if in group admin
|
||||||
|
if request.action == "add_group" then
|
||||||
|
if request.username == nil or request.group == nil or request.token == nil then
|
||||||
|
rednet.send(client_id, "invalid request", "auth")
|
||||||
|
else
|
||||||
|
local found = false
|
||||||
|
local acting_user = nil
|
||||||
|
for user, userdata in pairs(data.users) do
|
||||||
|
if userdata.token == request.token then
|
||||||
|
acting_user = user
|
||||||
|
found = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not found then
|
||||||
|
rednet.send(client_id, "invalid token", "auth")
|
||||||
|
elseif data.users[acting_user]["groups"] == nil or data.users[acting_user].groups["admin"] == nil then
|
||||||
|
rednet.send(client_id, "invalid privileges", "auth")
|
||||||
|
else
|
||||||
|
if data.users[request.username].groups == nil then
|
||||||
|
data.users[request.username].groups = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
data.users[request.username].groups[request.group] = true
|
||||||
|
|
||||||
|
save_data()
|
||||||
|
|
||||||
|
rednet.send(client_id, "ok", "auth")
|
||||||
|
log(request.username .. " added to group " .. request.group, "auth")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
13
packages/dig.lua
Normal file
13
packages/dig.lua
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
local args = { ... }
|
||||||
|
|
||||||
|
if args[1] == nil then
|
||||||
|
print("Usage: dig <hostname>")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
result = lns.lookup(args[1])
|
||||||
|
if result == nil then
|
||||||
|
io.write("Not found!\n")
|
||||||
|
else
|
||||||
|
io.write(result .. "\n")
|
||||||
|
end
|
184
packages/lns_server.lua
Normal file
184
packages/lns_server.lua
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
|
||||||
|
settings.define("lns_server.modem_location", {
|
||||||
|
description = "Modem location",
|
||||||
|
type = "string",
|
||||||
|
default = "right"
|
||||||
|
})
|
||||||
|
modem_location = settings.get("lns_server.modem_location")
|
||||||
|
|
||||||
|
settings.define("lns_server.rednet_hostname", {
|
||||||
|
description = "Rednet hostname",
|
||||||
|
type = "string",
|
||||||
|
default = "test"
|
||||||
|
})
|
||||||
|
|
||||||
|
settings.define("lns_server.require_auth", {
|
||||||
|
description = "Require authentication",
|
||||||
|
type = "boolean",
|
||||||
|
default = false
|
||||||
|
})
|
||||||
|
require_auth = settings.get("lns_server.require_auth")
|
||||||
|
|
||||||
|
settings.define("lns_server.auth_group", {
|
||||||
|
description = "Authentication group",
|
||||||
|
type = "string",
|
||||||
|
default = "admin"
|
||||||
|
})
|
||||||
|
local auth_group = settings.get("lns_server.auth_group")
|
||||||
|
|
||||||
|
settings.define("lns_server.auth_server", {
|
||||||
|
description = "Authentication server",
|
||||||
|
type = "string",
|
||||||
|
default = "auth.box"
|
||||||
|
})
|
||||||
|
auth_server = settings.get("lns_server.auth_server")
|
||||||
|
|
||||||
|
settings.define("lns_server.log_level", {
|
||||||
|
description = "Log Level",
|
||||||
|
type = "string",
|
||||||
|
default = "info"
|
||||||
|
})
|
||||||
|
log_level = settings.get("lns_server.log_level")
|
||||||
|
|
||||||
|
rednet.host("lns", settings.get("lns_server.rednet_hostname"))
|
||||||
|
rednet.open(modem_location)
|
||||||
|
|
||||||
|
local data = {}
|
||||||
|
|
||||||
|
|
||||||
|
function log(str)
|
||||||
|
io.write("[" .. os.time() .. "] " .. str .. "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
function log_debug(str)
|
||||||
|
if log_level == "debug" then
|
||||||
|
log(str)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function save_data()
|
||||||
|
db = fs.open("lns.db", "w")
|
||||||
|
db.write(textutils.serialize(data))
|
||||||
|
db.close()
|
||||||
|
end
|
||||||
|
|
||||||
|
function load_data()
|
||||||
|
if fs.exists("lns.db") then
|
||||||
|
db = fs.open("lns.db", "r")
|
||||||
|
db_contents = db.readAll()
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
data = textutils.unserialize(db_contents)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function split (inputstr, sep)
|
||||||
|
if sep == nil then
|
||||||
|
sep = "%s"
|
||||||
|
end
|
||||||
|
local t={}
|
||||||
|
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
|
||||||
|
table.insert(t, str)
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
function check_user_in_group(username, group)
|
||||||
|
local package = {
|
||||||
|
["action"] = "check_group",
|
||||||
|
["username"] = username,
|
||||||
|
["group"] = group
|
||||||
|
}
|
||||||
|
|
||||||
|
local authID = data[auth_server]
|
||||||
|
rednet.send(authID, package, "auth")
|
||||||
|
|
||||||
|
while true do
|
||||||
|
id, msg = rednet.receive("auth")
|
||||||
|
if id == authID then
|
||||||
|
if msg == "invalid request" then
|
||||||
|
io.write("Invalid request\n")
|
||||||
|
return false
|
||||||
|
elseif msg == true then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
load_data()
|
||||||
|
|
||||||
|
while true do
|
||||||
|
client_id, msg = rednet.receive("lns")
|
||||||
|
request = msg
|
||||||
|
|
||||||
|
if request.action == nil or request.hostname == nil then
|
||||||
|
rednet.send(client_id, "invalid request", "lns")
|
||||||
|
end
|
||||||
|
|
||||||
|
if request.action == "lookup" then
|
||||||
|
|
||||||
|
-- check if hostname in data
|
||||||
|
if request.hostname == nil then
|
||||||
|
rednet.send(client_id, nil, "lns")
|
||||||
|
log("Sent nil to " .. client_id)
|
||||||
|
else
|
||||||
|
rednet.send(client_id, data[request.hostname], "lns")
|
||||||
|
log("Sent " .. request.hostname .. " to " .. client_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if request.action == "reload" then
|
||||||
|
load_data()
|
||||||
|
rednet.send(client_id, "ok", "lns")
|
||||||
|
log("Reloaded data")
|
||||||
|
end
|
||||||
|
|
||||||
|
if request.action == "register" then
|
||||||
|
|
||||||
|
local auth_passed = false
|
||||||
|
if require_auth then
|
||||||
|
local authID = data[auth_server]
|
||||||
|
if request.token == nil then
|
||||||
|
rednet.send(client_id, "invalid auth", "lns")
|
||||||
|
elseif authID ~= nil then
|
||||||
|
rednet.send(authID, {
|
||||||
|
["action"] = "token",
|
||||||
|
["token"] = request.token
|
||||||
|
}, "auth")
|
||||||
|
|
||||||
|
local responseServerID = nil
|
||||||
|
while responseServerID ~= authID do
|
||||||
|
responseServerID, auth_response = rednet.receive("auth")
|
||||||
|
end
|
||||||
|
|
||||||
|
local errorPos, errorEnd = string.find(auth_response, "invalid")
|
||||||
|
if errorPos then
|
||||||
|
log("Error: " .. auth_response)
|
||||||
|
else
|
||||||
|
if check_user_in_group(auth_response, auth_group) then
|
||||||
|
auth_passed = true
|
||||||
|
else
|
||||||
|
rednet.send(client_id, "invalid auth", "lns")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log("Error: Auth server not found to " .. client_id)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
auth_passed = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if auth_passed then
|
||||||
|
data[request.hostname] = client_id
|
||||||
|
log("Registered " .. request.hostname .. " as " .. client_id)
|
||||||
|
save_data()
|
||||||
|
rednet.send(client_id, "ok", "lns")
|
||||||
|
else
|
||||||
|
rednet.send(client_id, "auth required", "lns")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -5,15 +5,6 @@ hostname = io.read()
|
|||||||
io.write("Enter the modem location: ")
|
io.write("Enter the modem location: ")
|
||||||
modem_location = io.read()
|
modem_location = io.read()
|
||||||
|
|
||||||
io.write("Use default LNS Server? (y/n): ")
|
|
||||||
use_default = io.read()
|
|
||||||
|
|
||||||
lns_server = "default"
|
|
||||||
if use_default == "n" then
|
|
||||||
io.write("Enter the LNS Server Rednet Hostname: ")
|
|
||||||
lns_server = io.read()
|
|
||||||
end
|
|
||||||
|
|
||||||
rednet.open(modem_location)
|
rednet.open(modem_location)
|
||||||
receiverID = rednet.lookup("lns", lns_server)
|
receiverID = rednet.lookup("lns", lns_server)
|
||||||
current_time = os.time()
|
current_time = os.time()
|
||||||
@ -21,13 +12,14 @@ current_time = os.time()
|
|||||||
|
|
||||||
data = {
|
data = {
|
||||||
["action"] = "register",
|
["action"] = "register",
|
||||||
["hostname"] = hostname
|
["hostname"] = hostname,
|
||||||
|
["token"] = settings.get("auth.token", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
rednet.send(receiverID, data)
|
rednet.send(receiverID, data, "lns")
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
id, msg = rednet.receive()
|
id, msg = rednet.receive("lns")
|
||||||
if id == receiverID then
|
if id == receiverID then
|
||||||
if msg == "ok" then
|
if msg == "ok" then
|
||||||
print("Registered with LNS Server Successfully!")
|
print("Registered with LNS Server Successfully!")
|
12
utils/lsh.lua
Normal file
12
utils/lsh.lua
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
local bin_path = "/bin/"
|
||||||
|
local lib_path = "/lib/"
|
||||||
|
|
||||||
|
shell.setPath(shell.path() .. ":" .. bin_path)
|
||||||
|
|
||||||
|
-- Load libraries
|
||||||
|
for _, file in ipairs(fs.list(lib_path)) do
|
||||||
|
if not fs.isDir(lib_path .. file) then
|
||||||
|
os.loadAPI(lib_path .. file)
|
||||||
|
end
|
||||||
|
end
|
Reference in New Issue
Block a user