grim/prosody_mod_auth_jetbrains_hub

297bf63a92af
Parents 18e22b4ce5f4
Children 04429328022c
overhaul all the things and add group checking support
--- a/mod_auth_jetbrains_hub.lua Sat Oct 19 02:04:03 2019 -0500
+++ b/mod_auth_jetbrains_hub.lua Mon Oct 21 20:38:02 2019 -0500
@@ -10,11 +10,13 @@
local new_sasl = require "util.sasl".new;
local base64 = require "util.encodings".base64.encode;
+local json_decode = require "util.json".decode
local have_async, async = pcall(require, "util.async");
local log = module._log;
local host = module.host;
+-- Options
local hub_url = module:get_option_string("jetbrains_hub_url", ""):gsub("$host", host);
if hub_url == "" then error("jetbrains_hub_url required") end
@@ -27,6 +29,12 @@
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 hub_groups = module:get_option_set("jetbrains_hub_groups", {});
+
+-- we only need to update the group part of the query once on load so do it
+-- right away
+local group_query = get_group_query()
+
local provider = {};
-- globals required by socket.http
@@ -36,86 +44,140 @@
if rawget(_G, "base_parsed") == nil then
rawset(_G, "base_parsed", false)
end
-if not have_async then -- FINE! Set your globals then
+
+local http_request = nil
+
+if have_async then
+ http_request = function(url, headers, body)
+ local http = require "net.http";
+ local wait, done = async.waiter();
+ local code, response;
+
+ local ex = {
+ headers = headers;
+ body = http.formencode(body);
+ }
+
+ local function cb(content_, code_, request_, response_)
+ code, response = code_, response_.body;
+ done();
+ end
+ http.request(url, ex, cb);
+ wait();
+
+ return {
+ code = code;
+ content = response;
+ }
+ end
+else
prosody.unlock_globals()
require "ltn12"
require "socket"
require "socket.http"
require "ssl.https"
prosody.lock_globals()
+
+ http_request = function(url, headers, body)
+ 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 t = {}
+ local _, code, _ = request{
+ url = url,
+ headers = headers,
+ body = http.formencode(body),
+ sink = ltn12.sink.table(t),
+ };
+
+ return {
+ code = code,
+ content = table.concat(t),
+ }
+ end
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 function get_group_query()
+ -- if there's no groups, just return an empty string
+ if #hub_groups == 0 then
+ return ""
+ end
+
+ -- we have at least one group, so output the start of our query
+ local query = " and ("
+
+ for i, v in ipairs(hub_groups) do
+ -- add the group making sure to quote it as spaces are allowed and will
+ -- fail if they're not quoted.
+ query = query .. "in:\"" .. v .. "\""
+
+ -- if we have additional groups add our "or" separator between the
+ -- groups.
+ if i < #hub_groups then
+ query = query .. " or "
+ end
+ end
+
+ -- finally add our closing paren and return the query
+ return query .. ")"
+end
+
+local function check_group(username, token)
+ local headers = { Authorization = "Bearer "..token; };
+ local body = {
+ { name = "$top", value = "1" };
+ { name = "query", value = "login:"..username..group_query };
+ { name = "fields", value = "type" };
+ }
+
+ local resp = http_request(hub_url .. "/api/rest/users", headers, body)
+
+ if resp["code"] == 200 then
+ local json_payload = json_decode(response.body);
+
+ if json_payload["total"] ~= 1 then
+ return nil, "Permission denied";
+ end
+
+ return true;
+ end
+
+ return nil, "Failed to check access permissions"
+end
+
+function provider.test_password(username, password)
+ local headers = { Authorization = "Basic "..base64(hub_client_id..":"..hub_client_secret); };
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
+ local resp = http_request(hub_url.."/api/rest/oauth2/token", headers, body);
+
+ if resp["code"] >= 200 and resp["code"] <= 299 then
module:log("debug", "HTTP auth provider confirmed valid password");
+
+ if #hub_groups > 0 then
+ local json_payload = json_decode(resp["content"]);
+
+ return async_group_check(username, json_payload["access_token"])
+ end
+
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;
+ module:log("debug", "HTTP auth provider returned status code %d", resp["code"]);
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;