summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJay Doane <jay.s.doane@gmail.com>2017-08-08 10:59:13 -0700
committerGitHub <noreply@github.com>2017-08-08 10:59:13 -0700
commitd4f4aeea3643079439e5aa704dd9322bb1eeab4c (patch)
tree2c3f3b46445ea0c31b403d82be221a5d08701edb
parentbb1744ea78b36059f9291921a77490774b2fdd55 (diff)
parent3d6c294eec8363575ac82c256a9a6b82d31d1673 (diff)
downloadcouchdb-d4f4aeea3643079439e5aa704dd9322bb1eeab4c.tar.gz
Merge pull request #10 from cloudant/move-key-cache
Move key cache to epep
-rw-r--r--src/jwks.erl162
-rw-r--r--src/jwtf.app.src3
-rw-r--r--src/jwtf_app.erl26
-rw-r--r--src/jwtf_sup.erl60
4 files changed, 0 insertions, 251 deletions
diff --git a/src/jwks.erl b/src/jwks.erl
deleted file mode 100644
index 458a4cf3e..000000000
--- a/src/jwks.erl
+++ /dev/null
@@ -1,162 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
-% @doc
-% This module fetches and parses JSON Web Key Sets (JWKS).
-
--module(jwks).
-
--export([
- get_key/3,
- get_keyset/1
-]).
-
--include_lib("public_key/include/public_key.hrl").
-
-get_key(Url, Kty, Kid) ->
- case lookup(Url, Kty, Kid) of
- {ok, Key} ->
- {ok, Key};
- {error, not_found} ->
- case update_cache(Url) of
- ok ->
- lookup(Url, Kty, Kid);
- {error, Reason} ->
- {error, Reason}
- end
- end.
-
-
-lookup(Url, Kty, Kid) ->
- case ets_lru:lookup_d(jwks_cache_lru, {Url, Kty, Kid}) of
- {ok, Key} ->
- {ok, Key};
- not_found ->
- {error, not_found}
- end.
-
-
-update_cache(Url) ->
- case get_keyset(Url) of
- {ok, KeySet} ->
- [ets_lru:insert(jwks_cache_lru, {Url, Kty, Kid}, Key)
- || {{Kty, Kid}, Key} <- KeySet],
- ok;
- {error, Reason} ->
- {error, Reason}
- end.
-
-
-get_keyset(Url) ->
- ReqHeaders = [],
- case ibrowse:send_req(Url, ReqHeaders, get) of
- {ok, "200", _RespHeaders, RespBody} ->
- {ok, parse_keyset(RespBody)};
- {ok, Code, _RespHeaders, _RespBody} ->
- couch_log:warning("get_keyset failed with code ~p", [Code]),
- {error, {service_unavailable, <<"JWKS service unavailable">>}};
- {error, Reason} ->
- couch_log:warning("get_keyset failed with reason ~p", [Reason]),
- {error, {service_unavailable, <<"JWKS service unavailable">>}}
- end.
-
-
-parse_keyset(Body) ->
- {Props} = jiffy:decode(Body),
- Keys = proplists:get_value(<<"keys">>, Props),
- lists:flatmap(fun parse_key/1, Keys).
-
-
-parse_key({Props}) ->
- Alg = proplists:get_value(<<"alg">>, Props),
- Kty = proplists:get_value(<<"kty">>, Props),
- Kid = proplists:get_value(<<"kid">>, Props),
- case {Alg, Kty} of
- {<<"RS256">>, <<"RSA">>} ->
- E = proplists:get_value(<<"e">>, Props),
- N = proplists:get_value(<<"n">>, Props),
- [{{Kty, Kid}, #'RSAPublicKey'{
- modulus = decode_number(N),
- publicExponent = decode_number(E)}}];
- {<<"ES256">>, <<"EC">>} ->
- Crv = proplists:get_value(<<"crv">>, Props),
- case Crv of
- <<"P-256">> ->
- X = proplists:get_value(<<"x">>, Props),
- Y = proplists:get_value(<<"y">>, Props),
- Point = <<4:8,
- (b64url:decode(X))/binary,
- (b64url:decode(Y))/binary>>,
- [{{Kty, Kid}, {
- #'ECPoint'{point = Point},
- {namedCurve,{1,2,840,10045,3,1,7}}
- }}];
- _ ->
- []
- end;
- _ ->
- []
- end.
-
-
-decode_number(Base64) ->
- crypto:bytes_to_integer(b64url:decode(Base64)).
-
-
--ifdef(TEST).
--include_lib("eunit/include/eunit.hrl").
-
-jwks_test() ->
- application:ensure_all_started(ibrowse),
- application:ensure_all_started(ssl),
- ?assertMatch({ok, _}, get_keyset("https://iam.eu-gb.bluemix.net/oidc/keys")).
-
-rs_test() ->
- Ejson = {[
- {<<"kty">>, <<"RSA">>},
- {<<"n">>, <<"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx"
- "4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMs"
- "tn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2"
- "QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbI"
- "SD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqb"
- "w0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw">>},
- {<<"e">>, <<"AQAB">>},
- {<<"alg">>, <<"RS256">>},
- {<<"kid">>, <<"2011-04-29">>}
- ]},
- ?assertMatch([{{<<"RSA">>, <<"2011-04-29">>}, {'RSAPublicKey', _, 65537}}],
- parse_key(Ejson)).
-
-
-ec_test() ->
- PrivateKey = #'ECPrivateKey'{
- version = 1,
- parameters = {namedCurve,{1,2,840,10045,3,1,7}},
- privateKey = b64url:decode("870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"),
- publicKey = <<4:8,
- (b64url:decode("MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4"))/binary,
- (b64url:decode("4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"))/binary>>},
- Ejson = {[
- {<<"kty">>, <<"EC">>},
- {<<"crv">>, <<"P-256">>},
- {<<"x">>, <<"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4">>},
- {<<"y">>, <<"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM">>},
- {<<"alg">>, <<"ES256">>},
- {<<"kid">>, <<"1">>}
- ]},
- ?assertMatch([{_Key, _Value}], parse_key(Ejson)),
- [{_, ECPublicKey}] = parse_key(Ejson),
- Msg = <<"foo">>,
- Sig = public_key:sign(Msg, sha256, PrivateKey),
- ?assert(public_key:verify(Msg, sha256, Sig, ECPublicKey)).
-
--endif.
diff --git a/src/jwtf.app.src b/src/jwtf.app.src
index 2ff221309..304bb9e0a 100644
--- a/src/jwtf.app.src
+++ b/src/jwtf.app.src
@@ -14,14 +14,11 @@
{description, "JSON Web Token Functions"},
{vsn, git},
{registered, []},
- {mod, { jwtf_app, []}},
{applications, [
kernel,
stdlib,
b64url,
crypto,
- ets_lru,
- ibrowse,
jiffy,
public_key
]},
diff --git a/src/jwtf_app.erl b/src/jwtf_app.erl
deleted file mode 100644
index 92a26d558..000000000
--- a/src/jwtf_app.erl
+++ /dev/null
@@ -1,26 +0,0 @@
-%%%-------------------------------------------------------------------
-%% @doc jwtf public API
-%% @end
-%%%-------------------------------------------------------------------
-
--module(jwtf_app).
-
--behaviour(application).
-
-%% Application callbacks
--export([start/2, stop/1]).
-
-%%====================================================================
-%% API
-%%====================================================================
-
-start(_StartType, _StartArgs) ->
- jwtf_sup:start_link().
-
-%%--------------------------------------------------------------------
-stop(_State) ->
- ok.
-
-%%====================================================================
-%% Internal functions
-%%====================================================================
diff --git a/src/jwtf_sup.erl b/src/jwtf_sup.erl
deleted file mode 100644
index 7cf56e84f..000000000
--- a/src/jwtf_sup.erl
+++ /dev/null
@@ -1,60 +0,0 @@
-%%%-------------------------------------------------------------------
-%% @doc epep top level supervisor.
-%% @end
-%%%-------------------------------------------------------------------
-
--module(jwtf_sup).
-
--behaviour(supervisor).
-
-%% API
--export([start_link/0]).
-
-%% Supervisor callbacks
--export([init/1]).
-
--define(SERVER, ?MODULE).
-
-%%====================================================================
-%% API functions
-%%====================================================================
-
-start_link() ->
- supervisor:start_link({local, ?SERVER}, ?MODULE, []).
-
-%%====================================================================
-%% Supervisor callbacks
-%%====================================================================
-
-%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}
-init([]) ->
- Children = [
- {jwks_cache_lru,
- {ets_lru, start_link, [jwks_cache_lru, lru_opts()]},
- permanent, 5000, worker, [ets_lru]}
- ],
- {ok, { {one_for_all, 5, 10}, Children} }.
-
-%%====================================================================
-%% Internal functions
-%%====================================================================
-
-lru_opts() ->
- case config:get_integer("jwtf_cache", "max_objects", 50) of
- MxObjs when MxObjs > 0 ->
- [{max_objects, MxObjs}];
- _ ->
- []
- end ++
- case config:get_integer("jwtf_cache", "max_size", 0) of
- MxSize when MxSize > 0 ->
- [{max_size, MxSize}];
- _ ->
- []
- end ++
- case config:get_integer("jwtf_cache", "max_lifetime", 0) of
- MxLT when MxLT > 0 ->
- [{max_lifetime, MxLT}];
- _ ->
- []
- end.