-- VID-12 Token validator
--
-- Validate a token against the requested path (and the client making the request) and deny access if validation fails.



local table = table
local require = require
local string = string
local os = os
local sha256 = require "lib.sha256"


local excluded_paths = {'resources/embed/'}


local function mint_token(path,expires,ip,secret)
    local mint = {path,expires,ip}
    local mintstr = table.concat(mint,':')
    -- print mintstr
    return sha256.hmac_sha256(secret,mintstr)
end


local function denyaccess(reason)
    ngx.status = 403
    ngx.header['X-Fail-Reason'] = reason
    ngx.say(reason)
    ngx.exit(403)
end


local function getPath(str,sep)
    -- From https://stackoverflow.com/questions/9102126/lua-return-directory-path-from-path
    sep=sep or'/'
    return str:match("(.*"..sep..")")
end

function table.contains(table, element)
  -- From https://stackoverflow.com/questions/2282444/how-to-check-if-a-table-contains-an-element-in-lua
  for _, value in pairs(table) do
    if value == element then
      return true
    end
  end
  return false
end


local function bakeTokenCookie(name,value,path,expires)
    -- Return a string like 
    -- foo=value; Path=/foo/bar/sed; Expires=Jan 1st 2011
    
    -- Calculate a cookie expiry string
    expiry = ngx.cookie_time(expires)
    
    -- name comes from something we used to reference a ngx variable, so strip the cookie_ prefix
    local t = {string.sub(name,8), "=", value, "; Path=/", path, "; Expires=", expiry}
    return table.concat(t,'')
end


-- Use same static values as were used to mint the origin token
source = 'qs'
vidpath = getPath(string.sub(ngx.var.uri,2))
ip = ngx.var.remote_addr
secret = ngx.var.secret
cookiename = sha256.sha256(vidpath)


if table.contains(excluded_paths,vidpath)
then
   -- We don't enforce tokens for this file
   ngx.exit(ngx.OK)
end



-- ngx.header['X-Tk-Debug'] = 'Validating for ip ' .. ip .. ' and path ' .. vidpath
local provided = ngx.var.arg_t
local expires = tonumber(ngx.var.arg_e)


-- Token may also exist in a cookie (and if it doesn't were gonna push it there)
-- calc the name as it's derived from the path
cookie_t_name = "cookie_vid_auth_" .. cookiename
cookie_e_name = "cookie_vid_auth_e_" .. cookiename


if provided == nil
then
    -- Check whether a token exists in cookies
    cookie_t = ngx.var[cookie_t_name]
    cookie_e = tonumber(ngx.var[cookie_e_name])
        
    if cookie_t == nil
    then
        ngx.header['X-tk-db1'] = 'Cookie t is null'
    end
    
    if cookie_t == nil or cookie_e == nil
    then
       -- Token not provided at all
       -- reject
       denyaccess('Missing Token')
    end
    
    -- Otherwise
    provided = cookie_t
    expires = cookie_e
    source = 'ck'
end


if provided == nil or expires == nil
then
    denyaccess('Missing Token')
end


local now = os.time()
-- ngx.header['X-Tk-Timings'] = 'Now: ' .. now .. ' Expiry: ' .. expires

if (now > expires)
then
    denyaccess('Token Expired')
    return
end

local nowtok = mint_token(vidpath,expires,ip,secret)


-- ngx.header['X-tk-vals'] = "Provided " .. provided .. " Calc " .. nowtok

if nowtok ~= provided
then
    denyaccess('Token Invalid')
else
    
    if source == 'qs'
    then
        -- Push into cookies
        ckstring = {bakeTokenCookie(cookie_t_name,provided,vidpath,expires),bakeTokenCookie(cookie_e_name,expires,vidpath,expires)}
        ngx.header["Set-Cookie"] = ckstring
    end
    
    
    ngx.exit(ngx.OK)
end
