grim/prosody_mod_auth_jetbrains_hub
Clone
Summary
Browse
Changes
Graph
Update my copyright and the readme for hub_groups being required now
draft
default
tip
2021-02-11, Gary Kramlich
58723e5d2735
Update my copyright and the readme for hub_groups being required now
-- Prosody IM
-- Copyright (C) 2008-2013 Matthew Wild
-- Copyright (C) 2008-2013 Waqas Hussain
-- Copyright (C) 2014 Kim Alvefur
-- Copyright (C) 2019-2021 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
json_decode
=
require
"util.json"
.
decode
local
formencode
=
require
"util.http"
.
formencode
local
have_async
,
async
=
pcall
(
require
,
"util.async"
);
local
collect
=
require
"util.array"
.
collect
local
log
=
module
.
_log
;
local
host
=
module
.
host
;
-- Options
local
hub_url
=
module
:
get_option_string
(
"jetbrains_hub_url"
):
gsub
(
"$host"
,
host
);
if
not
hub_url
then
error
(
"jetbrains_hub_url required"
)
end
local
hub_scopes
=
module
:
get_option_string
(
"jetbrains_hub_scopes"
);
if
not
hub_scopes
then
error
(
"jetbrains_hub_scopes required"
)
end
local
hub_client_id
=
module
:
get_option_string
(
"jetbrains_hub_client_id"
);
if
not
hub_client_id
then
error
(
"jetbrains_hub_client_id required"
)
end
local
hub_client_secret
=
module
:
get_option_string
(
"jetbrains_hub_client_secret"
);
if
not
hub_client_secret
then
error
(
"jetbrains_hub_client_secret required"
)
end
local
hub_groups
=
module
:
get_option_set
(
"jetbrains_hub_groups"
,
{});
if
hub_groups
:
empty
()
then
error
(
"jetbrains_hub_groups must specify at least one group"
)
end
-- we only need to update the group part of the query once on load so do it
-- right away
local
group_query
=
collect
(
hub_groups
):
map
(
function
(
item
)
return
string.format
(
"in:%q"
,
item
)
end
):
concat
(
" or "
)
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
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
;
}
if
body
and
#
body
>
0
then
ex
[
"body"
]
=
formencode
(
body
);
end
local
function
cb
(
content_
,
code_
,
response_
,
request_
)
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
,
sink
=
ltn12
.
sink
.
table
(
t
),
};
if
body
and
#
body
>
0
then
request
[
'body'
]
=
formencode
(
body
)
end
return
{
code
=
code
,
content
=
table.concat
(
t
),
}
end
end
local
function
check_group
(
username
,
token
)
local
headers
=
{
Authorization
=
"Bearer "
..
token
;
};
local
params
=
{
{
name
=
"$top"
,
value
=
"1"
};
{
name
=
"query"
,
value
=
string.format
(
"login:%s and (%s)"
,
username
,
group_query
)};
{
name
=
"fields"
,
value
=
"login"
};
}
local
url
=
string.format
(
'%s/api/rest/users?%s'
,
hub_url
,
formencode
(
params
))
local
resp
=
http_request
(
url
,
headers
,
nil
)
if
resp
.
code
==
200
then
local
json_payload
=
json_decode
(
resp
.
content
);
if
json_payload
.
total
~=
1
then
module
:
log
(
"warn"
,
"user %q authenticated but is not in any approved groups"
,
username
)
return
nil
,
"Permission denied"
end
if
json_payload
.
users
[
1
][
'login'
]
~=
username
then
module
:
log
(
"warn"
,
"user %q authenticated is in an approved group but the returned username was different"
,
username
)
return
nil
,
"Permission denied"
end
return
true
;
end
return
nil
,
"Failed to check access permissions"
end
function
provider
.
test_password
(
username
,
password
)
module
:
log
(
"debug"
,
"testing password for jbhub auth"
)
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
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"
);
-- a group is required to we need to do that check now.
local
json_payload
=
json_decode
(
resp
.
content
);
return
check_group
(
username
,
json_payload
.
access_token
)
else
module
:
log
(
"debug"
,
"HTTP auth provider returned status code %d"
,
resp
.
code
);
end
return
nil
,
"Auth failed. Invalid username or password."
;
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
);