local json = require("cjson.safe") local id = require("ssso_identity") local nginx = require("ssso_nginx") 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 req_data:matches(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 req_data:matches(r) then for _, a in ipairs(site.a) do if a[1] == "C" then nginx.add_cookie(a[2], auth:format(a[3])) elseif a[1] == "H" then nginx.add_header(a[2], auth:format(a[3])) end end return nginx.forward_request(req_data) end end end for _, r in ipairs(auth.ko) do if req_data:matches(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 parse_known_sites(user, denied_handler, allowed_handler) local f, site, go_on for _, known in ipairs(known_sites) do f = io.open(known, "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 denied_handler(pat) end end if go_on then if pat.public then allowed_handler(pat) else for _, allowed in ipairs(pat.allow or {}) do if allowed == "*" or allowed == user then allowed_handler(pat) break end end end end end end end end local class__profile = {} setmetatable(class__profile, {__index = id.class__identity}) function class__profile:build(delegate_identity, ok_list, ko_list) local profile = { delegate = delegate_identity, ok = ok_list, ko = ko_list, } setmetatable(profile, {__index = self}) return profile end function class__profile:build_from_lists(user, password, name, email, ok_list, ko_list) local delegate_identity = id.class__identity:build(user, password, name, email) return self:build(delegate_identity, ok_list, ko_list) end function class__profile:build_from_conf(user, password, name, email) local ok_list = {} local ko_list = {} local delegate_identity = id.class__identity:build(user, password, name, email) parse_known_sites(user, function (ko_pat) for _, re in ipairs(ko_pat.lua_regex) do table.insert(ko_list, re) end end, function (ok_pat) local a_type local ok = { r = ok_pat.lua_regex or {}, a = {}, } for _, action in ipairs(ok_pat.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 table.insert(ok_list, ok) end) return self:build(delegate_identity, ok_list, ko_list) end function class__profile:email() return self.delegate:email() end function class__profile:name() return self.delegate:name() end function class__profile:user() return self.delegate:user() end function class__profile:format(template) return self.delegate:format(template) end function class__profile:serialize() local ser_s = "" for _, site in ipairs(self.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(self.ko or {}) do ser_s = ser_s .. r .. "\030" end return ser_s .. "\026" .. self.delegate:serialize() end function class__profile:deserialize(ser) local ok_list = {} local ko_list = {} ser = ser:gsub("^(.-)\026", function (ser_sites) ser_sites = ser_sites: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) ser_sites = ser_sites:gsub("(.-)\030", function (ko) table.insert(ko_list, ko) return "" end) return "" end) local delegate_identity = id.class__identity:deserialize(ser) return self:build(delegate_identity, ok_list, ko_list) end function class__profile:authorized_links() local links = {} parse_known_sites(self:user(), function (_) end, function (ok_pat) for link, label in pairs(ok_pat.portal or {}) do table.insert(links, {link = link, label = label}) end end) return links end return { class__profile = class__profile, handle_request = handle_request, load_sites = load_sites, }