-- comfy-auth.lua -- Copyright (c) 2014, Thomas Jost -- -- Permission to use, copy, modify, and/or distribute this software for any -- purpose with or without fee is hereby granted, provided that the above -- copyright notice and this permission notice appear in all copies. -- -- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -- PERFORMANCE OF THIS SOFTWARE. local mime = require("mime") local md5 = require("md5") -- {{{ Basic auth support -- inspired by Yoo, but much simpler (https://github.com/stbuehler/yoo) function readAuthFile(filename) local users = {} local line local user, realm, pw for line in io.lines(filename) do if line:byte(1) ~= "#" then user, realm, pw = string.match(line, "^([^:]*):([^:]*):(.*)$") if user and realm then users[user .. ":" .. realm] = pw end end end return users end Auth = {} Auth.__index = Auth function Auth.create() local authfile = lighty.req_env["COMFY_AUTHFILE"] local users = {} if lighty.stat(authfile) then users = readAuthFile(authfile) else print("comfy: Authfile '"..authfile.."' not found") end local auth = {} setmetatable(auth, Auth) auth.realm = lighty.req_env["COMFY_REALM"] auth.users = users return auth end function Auth:send_auth_headers() if self.sent_header then return end self.sent_header = true lighty.header["WWW-Authenticate"] = "Basic realm=\"" .. self.realm .. "\"" end function Auth:basic_check(s) local user, pass, hash, pw s = (mime.unb64(s)) user, pass = string.match(s, "([^:]*):(.*)") if not user or not pass then return false end for user, hash in pairs(self.users) do if not hash then hash = "(nil)" end end hash = self.users[user .. ":" .. self.realm] if not hash then return false end pw = md5.sumhexa(user .. ":" .. self.realm .. ":" .. pass) if pw == hash then self.user = user return true else return false end end function Auth:check() local res = self.checked if res ~= nil then return res end res = false s = lighty.request["Authorization"] if s and string.lower(string.sub(s, 1, 6)) == "basic " then res = self:basic_check(string.sub(s, 7)) end self.checked = res return res end function Auth:enforce() if not self:check() then self:send_auth_headers() return false end return true end -- }}} -- {{{ Client SSL certificate check with fallback to basic auth if lighty.req_env["REMOTE_USER"] then --print("comfy: Remote user/SSL: " .. lighty.req_env["REMOTE_USER"]) else local auth = Auth.create() if auth:enforce() then --print("comfy: Remote user/basic: " .. auth.user) lighty.req_env["REMOTE_USER"] = auth.user else --print("comfy: 401") return 401 end end -- }}}