grim/prosody_mod_auth_jetbrains_hub

Initial revision

2019-10-19, Gary Kramlich
18e22b4ce5f4
Initial revision
-- Prosody IM
-- Copyright (C) 2008-2013 Matthew Wild
-- Copyright (C) 2008-2013 Waqas Hussain
-- Copyright (C) 2014 Kim Alvefur
-- Copyright (C) 2019 Gary Kramlich
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
local new_sasl = require "util.sasl".new;
local base64 = require "util.encodings".base64.encode;
local have_async, async = pcall(require, "util.async");
local log = module._log;
local host = module.host;
local hub_url = module:get_option_string("jetbrains_hub_url", ""):gsub("$host", host);
if hub_url == "" then error("jetbrains_hub_url required") end
local hub_scopes = module:get_option_string("jetbrains_hub_scopes", "");
if hub_scopes == "" then error("jetbrains_hub_scopes required") end
local hub_client_id = module:get_option_string("jetbrains_hub_client_id", "");
if hub_client_id == "" then error("jetbrains_hub_client_id required") end
local hub_client_secret = module:get_option_string("jetbrains_hub_client_secret", "");
if hub_client_secret == "" then error("jetbrains_hub_client_secret required") end
local provider = {};
-- globals required by socket.http
if rawget(_G, "PROXY") == nil then
rawset(_G, "PROXY", false)
end
if rawget(_G, "base_parsed") == nil then
rawset(_G, "base_parsed", false)
end
if not have_async then -- FINE! Set your globals then
prosody.unlock_globals()
require "ltn12"
require "socket"
require "socket.http"
require "ssl.https"
prosody.lock_globals()
end
local function async_http_auth(url, username, password)
module:log("debug", "async_http_auth()");
local http = require "net.http";
local wait, done = async.waiter();
local content, code, request, response;
local body = {
{ name = "grant_type", value = "password" };
{ name = "username", value = username };
{ name = "password", value = password };
{ name = "scope", value = hub_scopes };
}
local ex = {
headers = { Authorization = "Basic "..base64(hub_client_id..":"..hub_client_secret); };
body = http.formencode(body);
}
local function cb(content_, code_, request_, response_)
content, code, request, response = content_, code_, request_, response_;
done();
end
http.request(url, ex, cb);
wait();
if code >= 200 and code <= 299 then
module:log("debug", "HTTP auth provider confirmed valid password");
return true;
else
module:log("debug", "HTTP auth provider returned status code %d", code);
end
return nil, "Auth failed. Invalid username or password.";
end
local function sync_http_auth(url,username, password)
module:log("debug", "sync_http_auth()");
require "ltn12";
local http = require "socket.http";
local https = require "ssl.https";
local request;
if string.sub(url, 1, string.len('https')) == 'https' then
request = https.request;
else
request = http.request;
end
local body = {
{ name = "grant_type", value = "password" };
{ name = "username", value = username };
{ name = "password", value = password };
{ name = "scope", value = hub_scopes };
}
local _, code, headers, status = request{
url = url,
headers = { Authorization = "Basic "..base64(hub_client_id..":"..hub_client_secret); },
body = http.formencode(body)
};
if type(code) == "number" and code >= 200 and code <= 299 then
module:log("debug", "HTTP auth provider confirmed valid password");
return true;
else
module:log("debug", "HTTP auth provider returned status code: "..code);
end
return nil, "Auth failed. Invalid username or password.";
end
function provider.test_password(username, password)
local url = hub_url .. "/api/rest/oauth2/token"
log("debug", "Testing password for user %s at host %s with URL %s", username, host, url);
if (have_async) then
return async_http_auth(url, username, password);
else
return sync_http_auth(url, username, password);
end
end
function provider.users()
return function()
return nil;
end
end
function provider.set_password(username, password)
return nil, "Changing passwords not supported";
end
function provider.user_exists(username)
return true;
end
function provider.create_user(username, password)
return nil, "User creation not supported";
end
function provider.delete_user(username)
return nil , "User deletion not supported";
end
function provider.get_sasl_handler()
return new_sasl(host, {
plain_test = function(sasl, username, password, realm)
return provider.test_password(username, password), true;
end
});
end
module:provides("auth", provider);