More integration tests

[#158782152]
[#158782156]
This commit is contained in:
Michael Klishin 2018-07-18 18:15:50 +03:00
parent 8cc9e4f628
commit 821f54c92a
4 changed files with 110 additions and 32 deletions

View File

@ -65,7 +65,9 @@ check_vhost_access(#auth_user{impl = DecodedToken},
VHost, _Sock) ->
with_decoded_token(DecodedToken,
fun() ->
Scopes = get_scopes(DecodedToken),
Scopes = get_scopes(DecodedToken),
ScopeString = rabbit_oauth2_scope:concat_scopes(Scopes, ","),
rabbit_log:debug("Matching virtual host '~s' against the following scopes: ~s", [VHost, ScopeString]),
rabbit_oauth2_scope:vhost_access(VHost, Scopes)
end).

View File

@ -16,7 +16,7 @@
-module(rabbit_oauth2_scope).
-export([vhost_access/2, resource_access/3, topic_access/4]).
-export([vhost_access/2, resource_access/3, topic_access/4, concat_scopes/2]).
-include_lib("rabbit_common/include/rabbit.hrl").
@ -25,20 +25,22 @@
%% API functions --------------------------------------------------------------
-spec vhost_access(binary(), [binary()]) -> boolean().
vhost_access(VHost, Scopes) ->
PermissionScopes = get_scope_permissions(Scopes),
lists:any(
fun({ScopeVHost, _, _, _}) ->
wildcard:match(VHost, ScopeVHost)
fun({VHostPattern, _, _, _}) ->
wildcard:match(VHost, VHostPattern)
end,
get_scope_permissions(Scopes)).
PermissionScopes).
-spec resource_access(rabbit_types:r(atom()), permission(), [binary()]) -> boolean().
resource_access(#resource{virtual_host = VHost, name = Name},
Permission, Scopes) ->
rabbit_log:info("VHost: '~s', scopes: ~p", [VHost, Scopes]),
lists:any(
fun({ScopeVHost, ScopeName, _, ScopePermission}) ->
wildcard:match(VHost, ScopeVHost) andalso
wildcard:match(Name, ScopeName) andalso
Permission =:= ScopePermission
fun({VHostPattern, NamePattern, _, ScopeGrantedPermission}) ->
wildcard:match(VHost, VHostPattern) andalso
wildcard:match(Name, NamePattern) andalso
Permission =:= ScopeGrantedPermission
end,
get_scope_permissions(Scopes)).
@ -47,12 +49,12 @@ topic_access(#resource{virtual_host = VHost, name = ExchangeName},
#{routing_key := RoutingKey},
Scopes) ->
lists:any(
fun({ScopeVHost, ScopeExchangeName, ScopeRoutingKey, ScopePermission}) ->
is_binary(ScopeRoutingKey) andalso
wildcard:match(VHost, ScopeVHost) andalso
wildcard:match(ExchangeName, ScopeExchangeName) andalso
wildcard:match(RoutingKey, ScopeRoutingKey) andalso
Permission =:= ScopePermission
fun({VHostPattern, ExchangeNamePattern, RoutingKeyPattern, ScopeGrantedPermission}) ->
is_binary(RoutingKeyPattern) andalso
wildcard:match(VHost, VHostPattern) andalso
wildcard:match(ExchangeName, ExchangeNamePattern) andalso
wildcard:match(RoutingKey, RoutingKeyPattern) andalso
Permission =:= ScopeGrantedPermission
end,
get_scope_permissions(Scopes)).
@ -69,6 +71,10 @@ get_scope_permissions(Scopes) when is_list(Scopes) ->
end,
Scopes).
-spec concat_scopes([binary()], string()) -> string().
concat_scopes(Scopes, Separator) when is_list(Scopes) ->
lists:concat(lists:join(Separator, lists:map(fun rabbit_data_coercion:to_list/1, Scopes))).
-spec parse_permission_pattern(binary()) -> {rabbit_types:r(pattern), permission()}.
parse_permission_pattern(<<"read:", ResourcePatternBin/binary>>) ->
Permission = read,
@ -92,4 +98,3 @@ parse_resource_pattern(Pattern, Permission) ->
{VhostPattern, NamePattern, RoutingKeyPattern, Permission};
_Other -> ignore
end.

View File

@ -8,13 +8,6 @@
%% API
%%
expirable_token() ->
TokenPayload = fixture_token(),
TokenPayload#{<<"exp">> := os:system_time(seconds) + timer:seconds(?EXPIRATION_TIME)}.
wait_for_token_to_expire() ->
timer:sleep(?EXPIRATION_TIME).
sign_token_hs(Token, #{<<"kid">> := TokenKey} = Jwk) ->
sign_token_hs(Token, Jwk, TokenKey).
@ -48,8 +41,29 @@ fixture_jwk() ->
<<"use">> => <<"sig">>,
<<"value">> => <<"tokenkey">>}.
full_permission_scopes() ->
[<<"rabbitmq.configure:*/*">>,
<<"rabbitmq.write:*/*">>,
<<"rabbitmq.read:*/*">>].
expirable_token() ->
TokenPayload = fixture_token(),
TokenPayload#{<<"exp">> := os:system_time(seconds) + timer:seconds(?EXPIRATION_TIME)}.
wait_for_token_to_expire() ->
timer:sleep(?EXPIRATION_TIME).
expired_token() ->
expired_token_with_scopes(full_permission_scopes()).
expired_token_with_scopes(Scopes) ->
token_with_scopes_and_expiration(Scopes, os:system_time(seconds) - timer:seconds(10)).
fixture_token_with_scopes(Scopes) ->
#{<<"exp">> => os:system_time(seconds) + 3000,
token_with_scopes_and_expiration(Scopes, os:system_time(seconds) + timer:seconds(10)).
token_with_scopes_and_expiration(Scopes, Expiration) ->
#{<<"exp">> => Expiration,
<<"kid">> => <<"token-key">>,
<<"iss">> => <<"unit_test">>,
<<"foo">> => <<"bar">>,
@ -67,10 +81,5 @@ fixture_token(ExtraScopes) ->
<<"rabbitmq.read:vhost/bar/%23%2Ffoo">>] ++ ExtraScopes,
fixture_token_with_scopes(Scopes).
full_permission_scopes() ->
[<<"rabbitmq.configure:*/*">>,
<<"rabbitmq.write:*/*">>,
<<"rabbitmq.read:*/*">>].
fixture_token_with_full_permissions() ->
fixture_token_with_scopes(full_permission_scopes()).

View File

@ -20,6 +20,7 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("amqp_client/include/amqp_client.hrl").
-include_lib("eunit/include/eunit.hrl").
-import(rabbit_ct_client_helpers, [close_connection/1, close_channel/1,
open_unmanaged_connection/4, open_unmanaged_connection/5,
@ -28,7 +29,8 @@
all() ->
[
{group, happy_path}
{group, happy_path},
{group, unhappy_path}
].
groups() ->
@ -36,6 +38,10 @@ groups() ->
{happy_path, [], [
test_successful_connection_with_a_full_permission_token_and_all_defaults,
test_successful_connection_with_a_full_permission_token_and_explicitly_configured_vhost
]},
{unhappy_path, [], [
test_failed_connection_with_expired_token,
test_failed_connection_with_a_non_token
]}
].
@ -57,6 +63,37 @@ init_per_suite(Config) ->
end_per_suite(Config) ->
rabbit_ct_helpers:run_teardown_steps(Config, rabbit_ct_broker_helpers:teardown_steps()).
init_per_group(_Group, Config) ->
%% The broker is managed by {init,end}_per_testcase().
rabbit_ct_broker_helpers:add_vhost(Config, <<"avhost">>),
Config.
end_per_group(_Group, Config) ->
%% The broker is managed by {init,end}_per_testcase().
rabbit_ct_broker_helpers:delete_vhost(Config, <<"avhost">>),
Config.
init_per_testcase(test_successful_connection_with_a_full_permission_token_and_explicitly_configured_vhost = Testcase, Config) ->
rabbit_ct_broker_helpers:add_vhost(Config, <<"avhost">>),
rabbit_ct_helpers:testcase_started(Config, Testcase),
Config;
init_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:testcase_started(Config, Testcase),
Config.
end_per_testcase(test_successful_connection_with_a_full_permission_token_and_explicitly_configured_vhost = Testcase, Config) ->
rabbit_ct_broker_helpers:add_vhost(Config, <<"avhost">>),
rabbit_ct_helpers:testcase_started(Config, Testcase),
Config;
end_per_testcase(Testcase, Config) ->
rabbit_ct_broker_helpers:delete_vhost(Config, <<"avhost">>),
rabbit_ct_helpers:testcase_finished(Config, Testcase),
Config.
preconfigure_node(Config) ->
ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env,
[rabbit, auth_backends, [rabbit_auth_backend_uaa]]),
@ -78,6 +115,16 @@ generate_valid_token(Config, Scopes) ->
end,
?UTIL_MOD:sign_token_hs(?UTIL_MOD:fixture_token_with_scopes(Scopes), Jwk).
generate_expired_token(Config) ->
generate_expired_token(Config, ?UTIL_MOD:full_permission_scopes()).
generate_expired_token(Config, Scopes) ->
Jwk = case rabbit_ct_helpers:get_config(Config, fixture_jwk) of
undefined -> ?UTIL_MOD:fixture_jwk();
Value -> Value
end,
?UTIL_MOD:sign_token_hs(?UTIL_MOD:expired_token_with_scopes(Scopes), Jwk).
preconfigure_token(Config) ->
Token = generate_valid_token(Config),
@ -97,9 +144,24 @@ test_successful_connection_with_a_full_permission_token_and_all_defaults(Config)
close_connection_and_channel(Conn, Ch).
test_successful_connection_with_a_full_permission_token_and_explicitly_configured_vhost(Config) ->
{_Algo, Token} = rabbit_ct_helpers:get_config(Config, fixture_jwt),
Conn = open_unmanaged_connection(Config, 0, <<"username">>, Token),
{_Algo, Token} = generate_valid_token(Config, [<<"rabbitmq.configure:avhost/*">>,
<<"rabbitmq.write:avhost/*">>,
<<"rabbitmq.read:avhost/*">>]),
Conn = open_unmanaged_connection(Config, 0, <<"avhost">>, <<"username">>, Token),
{ok, Ch} = amqp_connection:open_channel(Conn),
#'queue.declare_ok'{queue = _} =
amqp_channel:call(Ch, #'queue.declare'{exclusive = true}),
close_connection_and_channel(Conn, Ch).
test_failed_connection_with_expired_token(Config) ->
{_Algo, Token} = generate_expired_token(Config, [<<"rabbitmq.configure:avhost/*">>,
<<"rabbitmq.write:avhost/*">>,
<<"rabbitmq.read:avhost/*">>]),
?assertEqual({error, not_allowed},
open_unmanaged_connection(Config, 0, <<"avhost">>, <<"username">>, Token)).
test_failed_connection_with_a_non_token(Config) ->
?assertMatch({error, {auth_failure, _}},
open_unmanaged_connection(Config, 0, <<"avhost">>, <<"username">>, <<"a-non-token-value">>)).