Skip to content
This repository has been archived by the owner on Nov 17, 2020. It is now read-only.

Commit

Permalink
HTTP provider for trust store
Browse files Browse the repository at this point in the history
Fixes #54

Provider will list certificates as JSON, requiring list of objects
with `id` and `url` fields.
JSON root is an object with a single `certificates` field, containing
a list of certificate objects. (some web services require json root to
be object)
Certificates are loaded as PEM encoded files.
  • Loading branch information
Daniil Fedotov committed Feb 7, 2017
1 parent 85c38f1 commit 542e958
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 2 deletions.
5 changes: 3 additions & 2 deletions src/rabbit_trust_store_certificate_provider.erl
Expand Up @@ -3,13 +3,14 @@
-include_lib("public_key/include/public_key.hrl").

-callback list_certs(Config)
-> no_change | {ok, [{CertId, Attributes}]}
-> no_change | {ok, [{CertId, Attributes}], ProviderState}
when Config :: list(),
ProviderState :: term(),
CertId :: term(),
Attributes :: list().

-callback list_certs(Config, ProviderState)
-> no_change | {ok, [{CertId, Attributes}]}
-> no_change | {ok, [{CertId, Attributes}], ProviderState}
when Config :: list(),
ProviderState :: term(),
CertId :: term(),
Expand Down
92 changes: 92 additions & 0 deletions src/rabbit_trust_store_http_provider.erl
@@ -0,0 +1,92 @@
-module(rabbit_trust_store_http_provider).

-include_lib("public_key/include/public_key.hrl").

-behaviour(rabbit_trust_store_certificate_provider).

-export([list_certs/1, list_certs/2, load_cert/3]).

-record(http_state,{
url :: string(),
http_options :: list(),
headers :: httpc:headers()
}).

list_certs(Config) ->
init(),
State = init_state(Config),
list_certs(Config, State).

list_certs(_, #http_state{url = Url,
http_options = HttpOptions,
headers = Headers} = State) ->
Res = httpc:request(get, {Url, Headers}, HttpOptions, [{body_format, binary}]),
case Res of
{ok, {{_, 200, _}, RespHeaders, Body}} ->
Certs = decode_cert_list(Body),
NewState = new_state(RespHeaders, State),
{ok, Certs, NewState};
{ok, {{_,304, _}, _, _}} -> no_change;
{ok, {{_,Code,_}, _, Body}} -> {error, {http_error, Code, Body}};
{error, Reason} -> {error, Reason}
end.

load_cert(_, #{url := CertUrl}, Config) ->
#http_state{url = BaseUrl,
http_options = HttpOptions,
headers = Headers} = init_state(Config),
Url = join_url(BaseUrl, CertUrl),
Res = httpc:request(get,
{Url, Headers},
HttpOptions,
[{body_format, binary}, {full_result, false}]),
case Res of
{ok, {200, Body}} ->
[{'Certificate', Cert, not_encrypted}] = public_key:pem_decode(Body),
{ok, Cert};
{ok, {Code, Body}} -> {error, {http_error, Code, Body}};
{error, Reason} -> {error, Reason}
end.

join_url(BaseUrl, CertUrl) ->
string:strip(rabbit_data_coercion:to_list(BaseUrl), right, $/)
++ "/" ++
string:strip(rabbit_data_coercion:to_list(CertUrl), left, $/).

init() ->
inets:start(),
ssl:start().

init_state(Config) ->
Url = proplists:get_value(url, Config),
Headers = proplists:get_value(http_headers, Config, []),
HttpOptions = case proplists:get_value(ssl_opts, Config) of
undefined -> [];
SslOpts -> [{ssl, SslOpts}]
end,
#http_state{url = Url, http_options = HttpOptions, headers = Headers}.

decode_cert_list(Body) ->
#{<<"certificates">> := Certs} = rabbit_json:decode(Body),
[ {CertId, #{url => Url}} || #{<<"id">> := CertId, <<"url">> := Url} <- Certs ].

new_state(RespHeaders, #http_state{headers = Headers} = State) ->
LastModified = proplists:get_value("Last-Modified",
RespHeaders,
proplists:get_value("last-modified",
RespHeaders,
undefined)),
case LastModified of
undefined -> State;
Value ->
NewHeaders = lists:ukeymerge(1, Headers,
[{"If-Modified-Since", Value}]),
State#http_state{headers = NewHeaders}
end.







0 comments on commit 542e958

Please sign in to comment.