diff --git a/com/Access/GetMasterAccess.lua b/com/Access/GetMasterAccess.lua new file mode 100644 index 0000000..6a4145f --- /dev/null +++ b/com/Access/GetMasterAccess.lua @@ -0,0 +1,77 @@ +local session = require("internal.session") +local log = require("internal.log") +local jwt = require("internal.crypt.jwt") +local bc = require("internal.crypt.bcrypt") +local db = require("internal.database.sqlite").connect("db/root.db", {log = true}) +local sha256 = require("internal.crypt.sha256") + +log.info("Someone at "..session.request.address.." trying to get master access") + +local function close_db() + if db then + db:close() + db = nil + end +end + +local params = session.request.params.get() + +local function check_missing(arr, p) + local is_missing = {} + local ok = true + for _, key in ipairs(arr) do + if p[key] == nil then + table.insert(is_missing, key) + ok = false + end + end + return ok, is_missing +end + +local ok, mp = check_missing({"master_secret", "master_name", "my_key"}, params) +if not ok then + close_db() + session.response.send_error(-32602, "Missing params", mp) +end + +if type(params.master_secret) ~= "string" then + close_db() + session.response.send_error(-32050, "Access denied") +end + +if type(params.master_name) ~= "string" then + close_db() + session.response.send_error(-32050, "Access denied") +end + +local master, err = db:query_row("SELECT * FROM master_units WHERE master_name = ?", {params.master_name}) + +if not master then + log.event("DB query failed:", err) + close_db() + session.response.send_error(-32050, "Access denied") +end + +local ok = bc.compare(master.master_secret, params.master_secret) +if not ok then + log.warn("Login failed: wrong password") + close_db() + session.response.send_error(-32050, "Access denied") +end + +local token = jwt.encode({ + secret = require("_config").token(), + payload = { + session_uuid = session.id, + master_id = master.id, + key = sha256.sum(params.my_key) + }, + expires_in = 3600 +}) + +close_db() +session.response.send({ + token = token +}) + +-- G7HgOgl72o7t7u7r diff --git a/com/_Auth/DeleteUnit.lua b/com/_Auth/DeleteUnit.lua new file mode 100644 index 0000000..adaa1ab --- /dev/null +++ b/com/_Auth/DeleteUnit.lua @@ -0,0 +1,119 @@ +-- com/DeleteUnit.lua + +---@diagnostic disable: redefined-local +local db = require("internal.database.sqlite").connect("db/user-database.db", {log = true}) +local log = require("internal.log") +local session = require("internal.session") +local crypt = require("internal.crypt.bcrypt") +local jwt = require("internal.crypt.jwt") +local sha256 = require("internal.crypt.sha256") + +local params = session.request.params.get() +local token = session.request.headers.get("authorization") + +local function close_db() + if db then + db:close() + db = nil + end +end + +local function error_response(message, code, data) + session.response.error = { + code = code or nil, + message = message, + data = data or nil + } + close_db() +end + +if not token or type(token) ~= "string" then + return error_response("Access denied") +end + +local prefix = "Bearer " +if token:sub(1, #prefix) ~= prefix then + return error_response("Invalid Authorization scheme") +end + +local access_token = token:sub(#prefix + 1) + +local err, data = jwt.decode(access_token, { secret = require("_config").token() }) + +if err or not data then + session.response.error = { + message = err + } + return +end + +-- if data.session_uuid ~= session.id then +-- return error_response("Access denied") +-- end + +-- if data.key ~= sha256.sum(session.request.address .. session.id .. session.request.headers.get("user-agent", "noagent")) then +-- return error_response("Access denied") +-- end + +if not params then + return error_response("no params provided") +end + +if not (params.username and params.email and params.password) then + return error_response("no username/email/password provided") +end + +local existing, err = db:query( + "SELECT password FROM users WHERE email = ? AND username = ? AND deleted = 0 LIMIT 1", + { + params.email, + params.username + } +) + +if err ~= nil then + log.error("Password fetch failed: " .. tostring(err)) + return error_response("Database query failed: " .. tostring(err)) +end + +if not existing or #existing == 0 then + return error_response("Unit not found") +end + +local hashed_password = existing[1].password + +local ok = crypt.compare(hashed_password, params.password) +if not ok then + log.warn("Wrong password attempt for: " .. params.username) + return error_response("Invalid password") +end + +local ctx, err = db:exec( + [[ + UPDATE users + SET deleted = 1, + deleted_at = CURRENT_TIMESTAMP + WHERE email = ? AND username = ? AND deleted = 0 + ]], + { params.email, params.username } +) + +if err ~= nil then + log.error("Soft delete failed: " .. tostring(err)) + return error_response("Soft delete failed: " .. tostring(err)) +end + +local res, err = ctx:wait() +if err ~= nil then + log.error("Soft delete confirmation failed: " .. tostring(err)) + return error_response("Soft delete confirmation failed: " .. tostring(err)) +end + +session.response.result = { + rows_affected = res, + message = "Unit soft-deleted successfully" +} + +log.info("user " .. params.username .. " soft-deleted successfully") + +close_db() diff --git a/com/_Auth/GetAccess.lua b/com/_Auth/GetAccess.lua new file mode 100644 index 0000000..14da4bf --- /dev/null +++ b/com/_Auth/GetAccess.lua @@ -0,0 +1,76 @@ +-- com/GetAccess + +---@diagnostic disable: redefined-local +local db = require("internal.database.sqlite").connect("db/user-database.db", {log = true}) +local log = require("internal.log") +local session = require("internal.session") +local crypt = require("internal.crypt.bcrypt") +local jwt = require("internal.crypt.jwt") +local sha256 = require("internal.crypt.sha256") + +local params = session.request.params.get() +local secret = require("_config").token() + +local function close_db() + if db then + db:close() + db = nil + end +end + +local function error_response(message, code, data) + session.response.error = { + code = code or nil, + message = message, + data = data or nil + } + close_db() +end + +if not params then + return error_response("No params provided") +end + +if not (params.username and params.email and params.password) then + return error_response("Missing username, email or password") +end + +local unit, err = db:query( + "SELECT id, username, email, password, created_at FROM users WHERE email = ? AND username = ? AND deleted = 0 LIMIT 1", + { + params.email, + params.username + } +) + +if err then + log.error("DB query error: " .. tostring(err)) + return error_response("Database query failed") +end + +if not unit or #unit == 0 then + return error_response("Unit not found") +end + +unit = unit[1] + +local ok = crypt.compare(unit.password, params.password) +if not ok then + log.warn("Login failed: wrong password for " .. params.username) + return error_response("Invalid password") +end + +local token = jwt.encode({ + secret = secret, + payload = { session_uuid = session.id, + admin_user = params.username, + key = sha256.sum(session.request.address .. session.id .. session.request.headers.get("user-agent", "noagent")) + }, + expires_in = 3600 +}) + +session.response.result = { + access_token = token +} + +close_db() diff --git a/com/_Auth/PutNewUnit.lua b/com/_Auth/PutNewUnit.lua new file mode 100644 index 0000000..3a140f4 --- /dev/null +++ b/com/_Auth/PutNewUnit.lua @@ -0,0 +1,109 @@ +-- com/PutNewUnit.lua + +---@diagnostic disable: redefined-local +local db = require("internal.database.sqlite").connect("db/user-database.db", {log = true}) +local log = require("internal.log") +local session = require("internal.session") +local crypt = require("internal.crypt.bcrypt") +local jwt = require("internal.crypt.jwt") +local sha256 = require("internal.crypt.sha256") + +local params = session.request.params.get() +local token = session.request.headers.get("authorization") + +local function close_db() + if db then + db:close() + db = nil + end +end + +local function error_response(message, code, data) + session.response.error = { + code = code or nil, + message = message, + data = data or nil + } + close_db() +end + +if not token or type(token) ~= "string" then + return error_response("Access denied") +end + +local prefix = "Bearer " +if token:sub(1, #prefix) ~= prefix then + return error_response("Invalid Authorization scheme") +end + +local access_token = token:sub(#prefix + 1) + +local err, data = jwt.decode(access_token, { secret = require("_config").token() }) + +if err or not data then + session.response.error = { + message = err + } + return +end + +if data.session_uuid ~= session.id then + return error_response("Access denied") +end + +if data.key ~= sha256.sum(session.request.address .. session.id .. session.request.headers.get("user-agent", "noagent")) then + return error_response("Access denied") +end + +if not params then + return error_response("no params provided") +end + +if not (params.username and params.email and params.password) then + return error_response("no username/email/password provided") +end + +local hashPass = crypt.generate(params.password, crypt.DefaultCost) + +local existing, err = db:query("SELECT 1 FROM users WHERE deleted = 0 AND (email = ? OR username = ?) LIMIT 1", { + params.email, + params.username +}) + +if err ~= nil then + log.error("Email check failed: "..tostring(err)) + return error_response("Database check failed: "..tostring(err)) +end + +if existing and #existing > 0 then + return error_response("Unit already exists") +end + +local ctx, err = db:exec( + "INSERT INTO users (username, email, password, first_name, last_name, phone_number) VALUES (?, ?, ?, ?, ?, ?)", + { + params.username, + params.email, + hashPass, + params.first_name or "", + params.last_name or "", + params.phone_number or "" + } +) +if err ~= nil then + log.error("Insert failed: "..tostring(err)) + return error_response("Insert failed: "..tostring(err)) +end + +local res, err = ctx:wait() +if err ~= nil then + log.error("Insert confirmation failed: "..tostring(err)) + return error_response("Insert confirmation failed: "..tostring(err)) +end + +session.response.result = { + rows_affected = res, + message = "Unit created successfully" +} + +close_db() \ No newline at end of file