Original request was to allow ejabberd sysadmin to generate tokens for specific users. JIDs must not be passed as argument when requesting the tokens.
This commit is contained in:
parent
8bc3dc9c49
commit
673a654c47
|
@ -48,7 +48,7 @@
|
||||||
process/2,
|
process/2,
|
||||||
opt_type/1]).
|
opt_type/1]).
|
||||||
|
|
||||||
-export([oauth_issue_token/1, oauth_list_tokens/0, oauth_revoke_token/1, oauth_list_scopes/0]).
|
-export([oauth_issue_token/2, oauth_list_tokens/0, oauth_revoke_token/1, oauth_list_scopes/0]).
|
||||||
|
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
%% (as it has access to ejabberd command line).
|
%% (as it has access to ejabberd command line).
|
||||||
-record(oauth_token, {
|
-record(oauth_token, {
|
||||||
token = {<<"">>, <<"">>} :: {binary(), binary()},
|
token = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||||
us = {<<"">>, <<"">>} :: {binary(), binary()} | server_admin,
|
us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||||
scope = [] :: [binary()],
|
scope = [] :: [binary()],
|
||||||
expire :: integer()
|
expire :: integer()
|
||||||
}).
|
}).
|
||||||
|
@ -92,18 +92,18 @@ get_commands_spec() ->
|
||||||
#ejabberd_commands{name = oauth_issue_token, tags = [oauth],
|
#ejabberd_commands{name = oauth_issue_token, tags = [oauth],
|
||||||
desc = "Issue an oauth token. Available scopes are the ones usable by ejabberd admins",
|
desc = "Issue an oauth token. Available scopes are the ones usable by ejabberd admins",
|
||||||
module = ?MODULE, function = oauth_issue_token,
|
module = ?MODULE, function = oauth_issue_token,
|
||||||
args = [{scopes, string}],
|
args = [{jid, string},{scopes, string}],
|
||||||
policy = restricted,
|
policy = restricted,
|
||||||
args_example = ["connected_users_number;muc_online_rooms"],
|
args_example = ["user@server.com", "connected_users_number;muc_online_rooms"],
|
||||||
args_desc = ["List of scopes to allow, separated by ';'"],
|
args_desc = ["List of scopes to allow, separated by ';'"],
|
||||||
result = {result, {tuple, [{token, string}, {scopes, string}, {expires_in, string}]}}
|
result = {result, {tuple, [{token, string}, {scopes, string}, {expires_in, string}]}}
|
||||||
},
|
},
|
||||||
#ejabberd_commands{name = oauth_list_tokens, tags = [oauth],
|
#ejabberd_commands{name = oauth_list_tokens, tags = [oauth],
|
||||||
desc = "List oauth tokens, their scope, and how many seconds remain until expirity",
|
desc = "List oauth tokens, their user and scope, and how many seconds remain until expirity",
|
||||||
module = ?MODULE, function = oauth_list_tokens,
|
module = ?MODULE, function = oauth_list_tokens,
|
||||||
args = [],
|
args = [],
|
||||||
policy = restricted,
|
policy = restricted,
|
||||||
result = {tokens, {list, {token, {tuple, [{token, string}, {scope, string}, {expires_in, string}]}}}}
|
result = {tokens, {list, {token, {tuple, [{token, string}, {user, string}, {scope, string}, {expires_in, string}]}}}}
|
||||||
},
|
},
|
||||||
#ejabberd_commands{name = oauth_list_scopes, tags = [oauth],
|
#ejabberd_commands{name = oauth_list_scopes, tags = [oauth],
|
||||||
desc = "List scopes that can be granted to tokens generated through the command line",
|
desc = "List scopes that can be granted to tokens generated through the command line",
|
||||||
|
@ -122,25 +122,30 @@ get_commands_spec() ->
|
||||||
}
|
}
|
||||||
].
|
].
|
||||||
|
|
||||||
oauth_issue_token(ScopesString) ->
|
oauth_issue_token(Jid, ScopesString) ->
|
||||||
Scopes = [list_to_binary(Scope) || Scope <- string:tokens(ScopesString, ";")],
|
Scopes = [list_to_binary(Scope) || Scope <- string:tokens(ScopesString, ";")],
|
||||||
case oauth2:authorize_client_credentials(ejabberd_ctl, Scopes, none) of
|
case jid:from_string(list_to_binary(Jid)) of
|
||||||
{ok, {_AppCtx, Authorization}} ->
|
#jid{luser =Username, lserver = Server} ->
|
||||||
{ok, {_AppCtx2, Response}} = oauth2:issue_token(Authorization, none),
|
case oauth2:authorize_password({Username, Server}, Scopes, admin_generated) of
|
||||||
{ok, AccessToken} = oauth2_response:access_token(Response),
|
{ok, {Ctx,Authorization}} ->
|
||||||
{ok, Expires} = oauth2_response:expires_in(Response),
|
{ok, {_AppCtx2, Response}} = oauth2:issue_token(Authorization, Ctx),
|
||||||
{ok, VerifiedScope} = oauth2_response:scope(Response),
|
{ok, AccessToken} = oauth2_response:access_token(Response),
|
||||||
{AccessToken, VerifiedScope, integer_to_list(Expires) ++ " seconds"};
|
{ok, Expires} = oauth2_response:expires_in(Response),
|
||||||
{error, Error} ->
|
{ok, VerifiedScope} = oauth2_response:scope(Response),
|
||||||
{error, Error}
|
{AccessToken, VerifiedScope, integer_to_list(Expires) ++ " seconds"};
|
||||||
|
{error, Error} ->
|
||||||
|
{error, Error}
|
||||||
|
end;
|
||||||
|
error ->
|
||||||
|
{error, "Invalid JID: " ++ Jid}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
oauth_list_tokens() ->
|
oauth_list_tokens() ->
|
||||||
Tokens = mnesia:dirty_match_object(#oauth_token{us = server_admin, _ = '_'}),
|
Tokens = mnesia:dirty_match_object(#oauth_token{_ = '_'}),
|
||||||
{MegaSecs, Secs, _MiniSecs} = os:timestamp(),
|
{MegaSecs, Secs, _MiniSecs} = os:timestamp(),
|
||||||
TS = 1000000 * MegaSecs + Secs,
|
TS = 1000000 * MegaSecs + Secs,
|
||||||
[{Token, Scope, integer_to_list(Expires - TS) ++ " seconds"} ||
|
[{Token, jid:to_string(jid:make(U,S,<<>>)), Scope, integer_to_list(Expires - TS) ++ " seconds"} ||
|
||||||
#oauth_token{token=Token, scope=Scope, expire=Expires} <- Tokens].
|
#oauth_token{token=Token, scope=Scope, us= {U,S},expire=Expires} <- Tokens].
|
||||||
|
|
||||||
|
|
||||||
oauth_revoke_token(Token) ->
|
oauth_revoke_token(Token) ->
|
||||||
|
@ -203,7 +208,7 @@ get_client_identity(Client, Ctx) -> {ok, {Ctx, {client, Client}}}.
|
||||||
|
|
||||||
verify_redirection_uri(_, _, Ctx) -> {ok, Ctx}.
|
verify_redirection_uri(_, _, Ctx) -> {ok, Ctx}.
|
||||||
|
|
||||||
authenticate_user({User, Server}, {password, Password} = Ctx) ->
|
authenticate_user({User, Server}, Ctx) ->
|
||||||
case jid:make(User, Server, <<"">>) of
|
case jid:make(User, Server, <<"">>) of
|
||||||
#jid{} = JID ->
|
#jid{} = JID ->
|
||||||
Access =
|
Access =
|
||||||
|
@ -213,11 +218,16 @@ authenticate_user({User, Server}, {password, Password} = Ctx) ->
|
||||||
none),
|
none),
|
||||||
case acl:match_rule(JID#jid.lserver, Access, JID) of
|
case acl:match_rule(JID#jid.lserver, Access, JID) of
|
||||||
allow ->
|
allow ->
|
||||||
case ejabberd_auth:check_password(User, <<"">>, Server, Password) of
|
case Ctx of
|
||||||
true ->
|
{password, Password} ->
|
||||||
{ok, {Ctx, {user, User, Server}}};
|
case ejabberd_auth:check_password(User, <<"">>, Server, Password) of
|
||||||
false ->
|
true ->
|
||||||
{error, badpass}
|
{ok, {Ctx, {user, User, Server}}};
|
||||||
|
false ->
|
||||||
|
{error, badpass}
|
||||||
|
end;
|
||||||
|
admin_generated ->
|
||||||
|
{ok, {Ctx, {user, User, Server}}}
|
||||||
end;
|
end;
|
||||||
deny ->
|
deny ->
|
||||||
{error, badpass}
|
{error, badpass}
|
||||||
|
@ -272,17 +282,12 @@ associate_access_code(_AccessCode, _Context, AppContext) ->
|
||||||
{ok, AppContext}.
|
{ok, AppContext}.
|
||||||
|
|
||||||
associate_access_token(AccessToken, Context, AppContext) ->
|
associate_access_token(AccessToken, Context, AppContext) ->
|
||||||
%% Tokens generated using the API/WEB belongs to users and always include the user, server pair.
|
{user, User, Server} = proplists:get_value(<<"resource_owner">>, Context, <<"">>),
|
||||||
%% Tokens generated form command line aren't tied to an user, and instead belongs to the ejabberd sysadmin
|
|
||||||
US = case proplists:get_value(<<"resource_owner">>, Context, <<"">>) of
|
|
||||||
{user, User, Server} -> {jid:nodeprep(User), jid:nodeprep(Server)};
|
|
||||||
undefined -> server_admin
|
|
||||||
end,
|
|
||||||
Scope = proplists:get_value(<<"scope">>, Context, []),
|
Scope = proplists:get_value(<<"scope">>, Context, []),
|
||||||
Expire = proplists:get_value(<<"expiry_time">>, Context, 0),
|
Expire = proplists:get_value(<<"expiry_time">>, Context, 0),
|
||||||
R = #oauth_token{
|
R = #oauth_token{
|
||||||
token = AccessToken,
|
token = AccessToken,
|
||||||
us = US,
|
us = {jid:nodeprep(User), jid:nodeprep(Server)},
|
||||||
scope = Scope,
|
scope = Scope,
|
||||||
expire = Expire
|
expire = Expire
|
||||||
},
|
},
|
||||||
|
@ -320,10 +325,7 @@ check_token(Scope, Token) ->
|
||||||
case oauth2_priv_set:is_member(
|
case oauth2_priv_set:is_member(
|
||||||
Scope, oauth2_priv_set:new(TokenScope)) andalso
|
Scope, oauth2_priv_set:new(TokenScope)) andalso
|
||||||
Expire > TS of
|
Expire > TS of
|
||||||
true -> case US of
|
true -> {ok, user, US};
|
||||||
{LUser, LServer} -> {ok, user, {LUser, LServer}};
|
|
||||||
server_admin -> {ok, server_admin}
|
|
||||||
end;
|
|
||||||
false -> false
|
false -> false
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
|
|
|
@ -162,8 +162,6 @@ check_permissions2(#request{auth = HTTPAuth, headers = Headers}, Call, _)
|
||||||
case oauth_check_token(Call, Token) of
|
case oauth_check_token(Call, Token) of
|
||||||
{ok, user, {User, Server}} ->
|
{ok, user, {User, Server}} ->
|
||||||
{ok, {User, Server, {oauth, Token}, Admin}};
|
{ok, {User, Server, {oauth, Token}, Admin}};
|
||||||
{ok, server_admin} -> %% token whas generated using issue_token command line
|
|
||||||
{ok, admin};
|
|
||||||
false ->
|
false ->
|
||||||
false
|
false
|
||||||
end;
|
end;
|
||||||
|
|
Loading…
Reference in New Issue