mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-26 17:38:45 +01: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:
parent
53d47483c8
commit
167edacb5f
@ -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").
|
||||
|
Loading…
Reference in New Issue
Block a user