Skip to content

Commit ddfd9ab

Browse files
authored
OPS-413: Add blacklisting by user id in token for phony api keys (#38)
* added blacklisting by user id in token for phony api keys * fixed format
1 parent 7f6572d commit ddfd9ab

File tree

5 files changed

+58
-18
lines changed

5 files changed

+58
-18
lines changed

rebar.config

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@
5151
{plugins, [
5252
{rebar3_lint, "1.0.1"},
5353
{erlfmt, "1.0.0"},
54-
{covertool, "2.0.4"}
54+
{covertool, "2.0.4"},
55+
{rebar3_thrift_compiler, {git, "https://github.com/valitydev/rebar3-thrift-compiler.git", {tag, "0.4"}}}
5556
]}.
5657

5758
%% Linter config.

src/tk_blacklist.erl

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
%% API
66

77
-export([is_blacklisted/2]).
8+
-export([is_user_blacklisted/2]).
89

910
%% Supervisor callbacks
1011

@@ -23,6 +24,7 @@
2324
%%
2425

2526
-define(TAB, ?MODULE).
27+
-define(USER_TAB, user_blacklist).
2628

2729
%%
2830

@@ -34,50 +36,58 @@ child_spec(Options) ->
3436
type => supervisor
3537
}.
3638

37-
-spec is_blacklisted(tk_token:token_id(), tk_authdata:authority_id()) -> boolean().
39+
-spec is_blacklisted(tk_token:token_id(), tk_token:authority_id()) -> boolean().
3840
is_blacklisted(TokenID, AuthorityID) ->
39-
check_entry({AuthorityID, TokenID}).
41+
check_entry(?TAB, {AuthorityID, TokenID}).
42+
43+
-spec is_user_blacklisted(binary(), tk_token:authority_id()) -> boolean().
44+
is_user_blacklisted(UserID, AuthorityID) ->
45+
check_entry(?USER_TAB, {AuthorityID, UserID}).
4046

4147
%%
4248

4349
-spec init(options()) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
4450
init(Options) ->
45-
_ = init_tab(),
51+
_ = init_tab(?TAB),
52+
_ = init_tab(?USER_TAB),
4653
_ = load_blacklist_conf(maps:get(path, Options, undefined)),
4754
{ok, {#{}, []}}.
4855

49-
init_tab() ->
50-
ets:new(?TAB, [set, protected, named_table, {read_concurrency, true}]).
56+
init_tab(Name) ->
57+
ets:new(Name, [set, protected, named_table, {read_concurrency, true}]).
5158

5259
-define(ENTRIES_KEY, "entries").
60+
-define(USER_ENTRIES_KEY, "user_entries").
5361

5462
load_blacklist_conf(undefined) ->
5563
_ = logger:warning("No token blacklist file specified! Blacklisting functionality will be disabled."),
5664
ok;
5765
load_blacklist_conf(Filename) ->
5866
[Mappings] = yamerl_constr:file(Filename),
5967
Entries = process_entries(proplists:get_value(?ENTRIES_KEY, Mappings)),
60-
put_entires(Entries).
68+
put_entires(?TAB, Entries),
69+
UserEntries = process_entries(proplists:get_value(?USER_ENTRIES_KEY, Mappings)),
70+
put_entires(?USER_TAB, UserEntries).
6171

6272
process_entries(Entries) ->
6373
lists:foldl(
64-
fun({AuthorityID, TokenIDs}, Acc) ->
65-
Acc ++ [make_ets_entry(AuthorityID, ID) || ID <- TokenIDs]
74+
fun({AuthorityID, IDs}, Acc) ->
75+
Acc ++ [make_ets_entry(AuthorityID, ID) || ID <- IDs]
6676
end,
6777
[],
6878
Entries
6979
).
7080

71-
make_ets_entry(AuthorityID, TokenID) ->
72-
{{list_to_binary(AuthorityID), list_to_binary(TokenID)}, true}.
81+
make_ets_entry(AuthorityID, ID) ->
82+
{{list_to_binary(AuthorityID), list_to_binary(ID)}, true}.
7383

7484
%%
7585

76-
put_entires(Entries) ->
77-
ets:insert_new(?TAB, Entries).
86+
put_entires(Name, Entries) ->
87+
ets:insert_new(Name, Entries).
7888

79-
check_entry(Key) ->
80-
case ets:lookup(?TAB, Key) of
89+
check_entry(Name, Key) ->
90+
case ets:lookup(Name, Key) of
8191
[_Entry] -> true;
8292
[] -> false
8393
end.

src/tk_context_extractor_phony_api_key.erl

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,33 @@
2020
%% API functions
2121

2222
-spec extract_context(tk_token:token_data(), opts()) -> tk_context_extractor:extracted_context() | undefined.
23-
extract_context(#{id := TokenID, payload := Payload}, Opts) ->
23+
extract_context(#{id := TokenID, payload := Payload} = TokenData, Opts) ->
2424
case extract_party_data(Payload) of
2525
{ok, PartyID} ->
26-
create_context_and_metadata(TokenID, PartyID, Opts);
26+
case check_blacklist(PartyID, TokenData) of
27+
ok ->
28+
create_context_and_metadata(TokenID, PartyID, Opts);
29+
{error, blacklisted} ->
30+
_ = logger:warning("phony_api_key context was extract, but it blacklisted for user id: ~p", [
31+
PartyID
32+
]),
33+
undefined
34+
end;
2735
{error, Reason} ->
2836
_ = logger:warning("Could not extract phony_api_key context, reason: ~p", [Reason]),
2937
undefined
3038
end.
3139

3240
%%
3341

42+
check_blacklist(PartyID, #{authority_id := AuthorityID}) ->
43+
case tk_blacklist:is_user_blacklisted(PartyID, AuthorityID) of
44+
false ->
45+
ok;
46+
true ->
47+
{error, blacklisted}
48+
end.
49+
3450
create_context_and_metadata(TokenID, PartyID, Opts) ->
3551
{
3652
create_context(TokenID, PartyID),

test/token_keeper_SUITE.erl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
-export([authenticate_user_session_token_w_resource_access/1]).
3030
-export([authenticate_blacklisted_jti_fail/1]).
3131
-export([authenticate_non_blacklisted_jti_ok/1]).
32+
-export([authenticate_blacklisted_user_fail/1]).
3233
-export([authenticate_ephemeral_claim_token_ok/1]).
3334
-export([issue_ephemeral_token_ok/1]).
3435
-export([authenticate_offline_token_not_found_fail/1]).
@@ -118,7 +119,8 @@ groups() ->
118119
]},
119120
{blacklist, [parallel], [
120121
authenticate_blacklisted_jti_fail,
121-
authenticate_non_blacklisted_jti_ok
122+
authenticate_non_blacklisted_jti_ok,
123+
authenticate_blacklisted_user_fail
122124
]}
123125
].
124126

@@ -482,6 +484,14 @@ authenticate_non_blacklisted_jti_ok(C) ->
482484
Token = issue_token_with(Claims, get_filename("keys/secondary/private.pem", C)),
483485
?assertMatch(#token_keeper_AuthData{}, call_authenticate(Token, ?TOKEN_SOURCE_CONTEXT, C)).
484486

487+
-spec authenticate_blacklisted_user_fail(config()) -> _.
488+
authenticate_blacklisted_user_fail(C) ->
489+
JTI = unique_id(),
490+
SubjectID = <<"PARTYID">>,
491+
Claims = get_phony_api_key_claims(JTI, SubjectID),
492+
Token = issue_token_with(Claims, get_filename("keys/local/private.pem", C)),
493+
?assertThrow(#token_keeper_AuthDataNotFound{}, call_authenticate(Token, ?TOKEN_SOURCE_CONTEXT, C)).
494+
485495
-spec authenticate_ephemeral_claim_token_ok(config()) -> _.
486496
authenticate_ephemeral_claim_token_ok(C) ->
487497
JTI = unique_id(),
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
entries:
22
blacklisting_authority:
33
- "MYCOOLKEY"
4+
user_entries:
5+
blacklisting_authority:
6+
- "PARTYID"

0 commit comments

Comments
 (0)