24
1
mirror of https://github.com/processone/ejabberd.git synced 2024-06-04 21:27:16 +02:00

Make Stylistic Changes in order to conform to guidelines:

1. Remove trailing whitespace
2. Remove Macros
3. Handle all erroneous response codes the same way
4. Add specs
Also don't return nonces anymore when the http response is negative.
This commit is contained in:
Konstantinos Kallas 2017-06-09 18:53:54 +03:00
parent 53d47483c8
commit 167edacb5f

View File

@ -1,7 +1,7 @@
-module (ejabberd_acme).
-export([ scenario/3
, scenario0/0
, scenario0/1
, directory/1
, get_account/3
, new_account/4
@ -14,12 +14,15 @@
-include_lib("public_key/include/public_key.hrl").
-define(REQUEST_TIMEOUT, 5000). % 5 seconds.
-define(DIRURL, "directory").
-define(REGURL, "/acme/reg/").
-define(DEFAULT_KEY_FILE, "private_key_temporary").
-type nonce() :: string().
-type url() :: string().
-type proplist() :: [{_, _}].
-type jws() :: map().
-spec directory(url()) ->
{ok, map(), nonce()} | {error, _}.
directory(DirURL) ->
Options = [],
HttpOptions = [{timeout, ?REQUEST_TIMEOUT}],
@ -27,66 +30,63 @@ directory(DirURL) ->
{ok, {{_, Code, _}, Head, Body}} when Code >= 200, Code =< 299 ->
%% Decode the json string
{Directories} = jiffy:decode(Body),
StrDirectories = [{bitstring_to_list(X), bitstring_to_list(Y)} ||
StrDirectories = [{bitstring_to_list(X), bitstring_to_list(Y)} ||
{X,Y} <- Directories],
% Find and save the replay nonce
Nonce = get_nonce(Head),
%% Return Map of Directories
NewDirs = maps:from_list(StrDirectories),
{ok, NewDirs, Nonce};
{ok, {{_, Code, _}, Head, _Body}} ->
{ok, {{_, Code, _}, _Head, _Body}} ->
?ERROR_MSG("Got unexpected status code from <~s>: ~B",
[DirURL, Code]),
Nonce = get_nonce(Head),
{error, unexpected_code, Nonce};
{error, unexpected_code};
{error, Reason} ->
?ERROR_MSG("Error requesting directory from <~s>: ~p",
[DirURL, Reason]),
{error, Reason}
end.
-spec new_account(url(), jose_jwk:key(), proplist(), nonce()) ->
{ok, {url(), proplist()}, nonce()} | {error, _}.
new_account(NewAccURl, PrivateKey, Req, Nonce) ->
%% Make the request body
ReqBody = jiffy:encode({[{ <<"resource">>, <<"new-reg">>}] ++ Req}),
{_, SignedBody} = sign_json_jose(PrivateKey, ReqBody, NewAccURl, Nonce),
{_, SignedBody} = sign_json_jose(PrivateKey, ReqBody, Nonce),
%% Encode the Signed body with jiffy
FinalBody = jiffy:encode(SignedBody),
Options = [],
HttpOptions = [{timeout, ?REQUEST_TIMEOUT}],
case httpc:request(post,
case httpc:request(post,
{NewAccURl, [], "application/jose+json", FinalBody}, HttpOptions, Options) of
{ok, {{_, Code, _}, Head, Body}} when Code >= 200, Code =< 299 ->
%% Decode the json string
{Return} = jiffy:decode(Body),
TOSUrl = get_TOS(Head),
TOSUrl = get_tos(Head),
% Find and save the replay nonce
NewNonce = get_nonce(Head),
{ok, {TOSUrl, Return}, NewNonce};
{ok, {{_, 409 = Code, _}, Head, Body}} ->
?ERROR_MSG("Got status code: ~B from <~s>, Body: ~s",
[NewAccURl, Code, Body]),
NewNonce = get_nonce(Head),
{error, key_in_use, NewNonce};
{ok, {{_, Code, _}, Head, Body}} ->
{ok, {{_, Code, _}, _Head, Body}} ->
?ERROR_MSG("Got unexpected status code from <~s>: ~B, Body: ~s",
[NewAccURl, Code, Body]),
NewNonce = get_nonce(Head),
{error, unexpected_code, NewNonce};
{error, unexpected_code};
{error, Reason} ->
?ERROR_MSG("Error requesting directory from <~s>: ~p",
[NewAccURl, Reason]),
{error, Reason, Nonce}
{error, Reason}
end.
-spec update_account(url(), jose_jwk:key(), proplist(), nonce()) ->
{ok, proplist(), nonce()} | {error, _}.
update_account(AccURl, PrivateKey, Req, Nonce) ->
%% Make the request body
ReqBody = jiffy:encode({[{ <<"resource">>, <<"reg">>}] ++ Req}),
{_, SignedBody} = sign_json_jose(PrivateKey, ReqBody, AccURl, Nonce),
{_, SignedBody} = sign_json_jose(PrivateKey, ReqBody, Nonce),
%% Encode the Signed body with jiffy
FinalBody = jiffy:encode(SignedBody),
Options = [],
HttpOptions = [{timeout, ?REQUEST_TIMEOUT}],
case httpc:request(post,
case httpc:request(post,
{AccURl, [], "application/jose+json", FinalBody}, HttpOptions, Options) of
{ok, {{_, Code, _}, Head, Body}} when Code >= 200, Code =< 299 ->
%% Decode the json string
@ -94,79 +94,78 @@ update_account(AccURl, PrivateKey, Req, Nonce) ->
% Find and save the replay nonce
NewNonce = get_nonce(Head),
{ok, Return, NewNonce};
{ok, {{_, Code, _}, Head, Body}} ->
{ok, {{_, Code, _}, _Head, Body}} ->
?ERROR_MSG("Got unexpected status code from <~s>: ~B, Body: ~s",
[AccURl, Code, Body]),
NewNonce = get_nonce(Head),
{error, unexpected_code, NewNonce};
{error, unexpected_code};
{error, Reason} ->
?ERROR_MSG("Error requesting directory from <~s>: ~p",
[AccURl, Reason]),
{error, Reason, Nonce}
{error, Reason}
end.
-spec get_account(url(), jose_jwk:key(), nonce()) ->
{ok, {url(), proplist()}, nonce()} | {error, _}.
get_account(AccURl, PrivateKey, Nonce) ->
%% Make the request body
ReqBody = jiffy:encode({[
{ <<"resource">>, <<"reg">>}
]}),
ReqBody = jiffy:encode({[{<<"resource">>, <<"reg">>}]}),
%% Jose Sign
{_, SignedBody} = sign_json_jose(PrivateKey, ReqBody, AccURl, Nonce),
{_, SignedBody} = sign_json_jose(PrivateKey, ReqBody, Nonce),
%% Encode the Signed body with jiffy
FinalBody = jiffy:encode(SignedBody),
Options = [],
HttpOptions = [{timeout, ?REQUEST_TIMEOUT}],
case httpc:request(post,
case httpc:request(post,
{AccURl, [], "application/jose+json", FinalBody}, HttpOptions, Options) of
{ok, {{_, Code, _}, Head, Body}} when Code >= 200, Code =< 299 ->
%% Decode the json string
{Return} = jiffy:decode(Body),
TOSUrl = get_TOS(Head),
TOSUrl = get_tos(Head),
% Find and save the replay nonce
NewNonce = get_nonce(Head),
{ok, {TOSUrl, Return}, NewNonce};
{ok, {{_, Code, _}, Head, Body}} ->
{ok, {{_, Code, _}, _Head, Body}} ->
?ERROR_MSG("Got unexpected status code from <~s>: ~B, Head: ~s",
[AccURl, Code, Body]),
NewNonce = get_nonce(Head),
{error, unexpected_code, NewNonce};
{error, unexpected_code};
{error, Reason} ->
?ERROR_MSG("Error requesting directory from <~s>: ~p",
[AccURl, Reason]),
{error, Reason, Nonce}
{error, Reason}
end.
%%
%% Useful funs
%%
-spec get_nonce(proplist()) -> nonce() | 'none'.
get_nonce(Head) ->
{"replay-nonce", Nonce} = proplists:lookup("replay-nonce", Head),
Nonce.
case proplists:lookup("replay-nonce", Head) of
{"replay-nonce", Nonce} -> Nonce;
none -> none
end.
%% Very bad way to extract this
%% TODO: Find a better way
get_TOS(Head) ->
-spec get_tos(proplist()) -> url() | 'none'.
get_tos(Head) ->
try
[{_, Link}] = [{K, V} || {K, V} <- Head,
[{_, Link}] = [{K, V} || {K, V} <- Head,
K =:= "link" andalso lists:suffix("\"terms-of-service\"", V)],
[Link1, _] = string:tokens(Link, ";"),
Link2 = string:strip(Link1, left, $<),
string:strip(Link2, right, $>)
catch
_:_ ->
no_tos
none
end.
sign_json_jose(Key, Json, Url, Nonce) ->
-spec sign_json_jose(jose_jwk:key(), string(), nonce()) -> jws().
sign_json_jose(Key, Json, Nonce) ->
% Generate a public key
PubKey = jose_jwk:to_public(Key),
{_, BinaryPubKey} = jose_jwk:to_binary(PubKey),
PubKeyJson = jiffy:decode(BinaryPubKey),
% Jws object containing the algorithm
%% TODO: Dont hardcode the alg
JwsObj = jose_jws:from(
@ -185,17 +184,16 @@ sign_json_jose(Key, Json, Url, Nonce) ->
%% A typical acme workflow
scenario(CAUrl, AccId, PrivateKey) ->
DirURL = CAUrl ++ "/" ++ ?DIRURL,
DirURL = CAUrl ++ "/directory",
{ok, Dirs, Nonce0} = directory(DirURL),
AccURL = CAUrl ++ ?REGURL ++ AccId,
AccURL = CAUrl ++ "/acme/reg/" ++ AccId,
{ok, {_TOS, Account}, Nonce1} = get_account(AccURL, PrivateKey, Nonce0).
new_user_scenario(CAUrl) ->
PrivateKey = generate_key(),
DirURL = CAUrl ++ "/" ++ ?DIRURL,
DirURL = CAUrl ++ "/directory",
{ok, Dirs, Nonce0} = directory(DirURL),
#{"new-reg" := NewAccURL} = Dirs,
@ -203,7 +201,7 @@ new_user_scenario(CAUrl) ->
{ok, {TOS, Account}, Nonce1} = new_account(NewAccURL, PrivateKey, Req0, Nonce0),
{_, AccId} = proplists:lookup(<<"id">>, Account),
AccURL = CAUrl ++ ?REGURL ++ integer_to_list(AccId),
AccURL = CAUrl ++ "/acme/reg/" ++ integer_to_list(AccId),
Req1 = [{ <<"agreement">>, list_to_bitstring(TOS)}],
{ok, Account1, Nonce2} = update_account(AccURL, PrivateKey, Req1, Nonce1),
@ -213,7 +211,7 @@ generate_key() ->
jose_jwk:generate_key({ec, secp256r1}).
%% Just a test
scenario0() ->
PrivateKey = jose_jwk:from_file(?DEFAULT_KEY_FILE),
scenario0(KeyFile) ->
PrivateKey = jose_jwk:from_file(KeyFile),
% scenario("http://localhost:4000", "2", PrivateKey).
new_user_scenario("http://localhost:4000").