blob: dbf7f43f0b79db26c8006619cfde0b2024a94019 [file] [log] [blame]
--[[
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--
-- This is oauth.lua - an oauth providing script for ponymail
local JSON = require 'cjson'
local http = require 'socket.http'
local elastic = require 'lib/elastic'
local https = require 'ssl.https'
local user = require 'lib/user'
local cross = require 'lib/cross'
local config = require 'lib/config'
function handle(r)
r.content_type = "application/json"
local get = r:parseargs()
local post = r:parsebody()
local valid, json
local scheme = "https"
if r.port == 80 then
scheme = "http"
end
local oauth_domain = ""
if config.oauth_fields and config.oauth_fields[get.key] then
for k, v in pairs(config.oauth_fields[get.key]) do
r.args = r.args .. ("&%s=%s"):format(k,v)
end
if config.oauth_fields[get.key].oauth_token then
get.oauth_token = config.oauth_fields[get.key].oauth_token
end
end
-- Persona callback
if get.mode and get.mode == "persona" then
oauth_domain = "verifier.login.persona.org"
local result = https.request("https://verifier.login.persona.org/verify", ("assertion=%s&audience=%s://%s:%u/"):format(post.assertion, scheme, r.hostname, r.port))
r:err(("assertion=%s&audience=%s://ponymail:443/"):format(post.assertion, scheme))
r:err(result)
valid, json = pcall(function() return JSON.decode(result) end)
-- Google Auth callback
elseif get.oauth_token and get.oauth_token:match("^https://www.google") and get.id_token then
oauth_domain = "www.googleapis.com"
local result = https.request("https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=" .. r:escape(get.id_token))
valid, json = pcall(function() return JSON.decode(result) end)
-- GitHub Auth callback
elseif get.oauth_token and get.key == 'github' then
local result = https.request(get.oauth_token, r.args)
local token = result:match("(access_token=[a-f0-9]+)")
if token then
local result = https.request("https://api.github.com/user/emails?" .. token)
valid, json = pcall(function() return JSON.decode(result) end)
if valid and json then
json = json[1]
end
end
-- Generic callback (like ASF Oauth2)
elseif get.state and get.code and get.oauth_token then
oauth_domain = get.oauth_token:match("https?://(.-)/")
local result = https.request(get.oauth_token, r.args)
valid, json = pcall(function() return JSON.decode(result) end)
end
-- Did we get something useful from the backend?
if valid and json then
local eml = json.email
local fname = json.fullname or json.name or json.email
local admin = json.isMember
-- If we got an email and a name, log in the user and set cookie etc
if eml and fname then
local cid = json.uid or json.email
-- Does the user exist already?
local oaccount = user.get(r, cid)
local usr = {}
if oaccount then
usr.preferences = oaccount.preferences
else
usr.preferences = {}
end
usr.gauth = get.id_token
usr.fullname = fname
-- if the oauth provider can set admin status, do so if needed
local authority = false
for k, v in pairs(config.admin_oauth or {}) do
if r.strcmp_match(oauth_domain, v) then
authority = true
break
end
end
if authority then
usr.admin = admin
end
usr.email = eml
usr.uid = json.uid
usr.oauth_used = oauth_domain
user.update(r, cid, usr)
r:puts[[{"okay": true, "msg": "Logged in successfully!"}]]
-- didn't get email or name, bork!
else
r:puts[[{"okay": false, "msg": "Erroneous or missing response from backend!"}]]
end
-- Backend borked, let the user know
else
r:puts[[{"okay": false, "msg": "Invalid oauth response!"}]]
end
return cross.OK
end
cross.start(handle)