simple-sso/src/ssso_sites.lua

216 lines
5.5 KiB
Lua

local json = require("cjson.safe")
local nginx = require("ssso_nginx")
local prof = require("ssso_profile")
local known_private_re = {}
local known_sites = {}
local function load_sites(prefix)
local f, site
local ls = assert(io.popen("/bin/ls -f1Nb \"" .. prefix .. "/sites\"/*.json", "r"), "popen is required")
for name in ls:lines() do
f = assert(io.open(name, "r"), "File " .. name .. " cannot be read")
site = assert(json.decode(f:read("*all")))
f:close()
table.insert(known_sites, name)
for _, pat in ipairs(site.patterns) do
if not pat.public then
for _, r in ipairs(pat.lua_regex) do
table.insert(known_private_re, r)
end
end
end
end
ls:close()
end
local function is_known_private(req_data)
for _, r in ipairs(known_private_re) do
if nginx.matches(req_data, r) then
return true
end
end
return false
end
local function handle_request(req_data, auth)
if auth then
for _, site in ipairs(auth.ok) do
for _, r in ipairs(site.r) do
if nginx.matches(req_data, r) then
for _, a in ipairs(site.a) do
if a[1] == "C" then
nginx.add_cookie(a[2], prof.format(a[3], auth))
elseif a[1] == "H" then
nginx.add_header(a[2], prof.format(a[3], auth))
end
end
return nginx.forward_request(req_data)
end
end
end
for _, r in ipairs(auth.ko) do
if nginx.matches(req_data, r) then
return nginx.redirect_to_login(req_data, 403)
end
end
return nginx.forward_request(req_data)
elseif is_known_private(req_data) then
return nginx.redirect_to_login(req_data, 401)
else
return nginx.forward_request(req_data)
end
end
local function format_pattern(pattern)
local a_type
local ok = {
r = pattern.lua_regex or {},
a = {},
}
for _, action in ipairs(pattern.actions or {}) do
if action.type == "header" then
a_type = "H"
elseif action.type == "cookie" then
a_type = "C"
else
a_type = nil
end
if a_type then
table.insert(ok.a, {a_type, action.name, action.value})
end
end
return ok
end
local function with_sites(profile, user)
local f, site, go_on
local ok_list = {}
local ko_list = {}
for _, name in ipairs(known_sites) do
f = io.open(name, "r")
if f then
site = json.decode(f:read("*all"))
f:close()
for _, pat in ipairs(site.patterns) do
go_on = true
for _, denied in ipairs(pat.deny or {}) do
if denied == user then
go_on = false
for _, re in ipairs(pat.lua_regex) do
table.insert(ko_list, re)
end
break
end
end
if go_on then
if pat.public then
local ok = format_pattern(pat)
table.insert(ok_list, ok)
else
for _, allowed in ipairs(pat.allow or {}) do
if allowed == "*" or allowed == user then
local ok = format_pattern(pat)
table.insert(ok_list, ok)
break
end
end
end
end
end
end
end
profile["ok"] = ok_list
profile["ko"] = ko_list
return profile
end
local function serialize(profile)
local ser_s = ""
for _, site in ipairs(profile.ok or {}) do
for _, r in ipairs(site.r) do
ser_s = ser_s .. r .. "\029"
end
for _, a in ipairs(site.a) do
ser_s = ser_s .. a[1] .. a[2] .. "=" .. a[3] .. "\028"
end
ser_s = ser_s .. "\031"
end
for _, r in ipairs(profile.ko or {}) do
ser_s = ser_s .. r .. "\030"
end
return ser_s
end
local function deserialize_update(ser, profile)
if not ser or ser == "" then
return profile
end
local ok_list = {}
local ko_list = {}
local remainder = ser:gsub("(.-)\031", function (ser_ok)
local ok = {
r = {},
a = {},
}
ser_ok = ser_ok:gsub("(.-)\029", function(r) table.insert(ok.r, r); return "" end)
ser_ok:gsub("(.)([^=]-)=(.-)\028", function(t, n, v) table.insert(ok.a, {t, n, v}) end)
table.insert(ok_list, ok)
return ""
end)
remainder = remainder:gsub("(.-)\030", function (ko)
table.insert(ko_list, ko)
return ""
end)
profile.ok = ok_list
profile.ko = ko_list
return profile, remainder
end
local function authorized_links(user)
local links = {}
local f, site, go_on
for _, name in ipairs(known_sites) do
f = io.open(name, "r")
if f then
site = json.decode(f:read("*all"))
f:close()
for _, pat in ipairs(site.patterns) do
go_on = true
for _, denied in ipairs(pat.deny or {}) do
if denied == user then
go_on = false
break
end
end
if go_on then
if pat.public then
for link, label in pairs(pat.portal or {}) do
table.insert(links, {link = link, label = label})
end
else
for _, allowed in ipairs(pat.allow or {}) do
if allowed == "*" or allowed == user then
for link, label in pairs(pat.portal or {}) do
table.insert(links, {link = link, label = label})
end
break
end
end
end
end
end
end
end
return links
end
return {
authorized_links = authorized_links,
deserialize_update = deserialize_update, -- TODO: test
handle_request = handle_request,
load_sites = load_sites,
serialize = serialize, -- TODO: test
with_sites = with_sites,
}