| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- %%--------------------------------------------------------------------
- %% Copyright (c) 2020-2024 EMQ Technologies Co., Ltd. All Rights Reserved.
- %%
- %% 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.
- %%--------------------------------------------------------------------
- -module(emqx_authz_mongodb_SUITE).
- -compile(nowarn_export_all).
- -compile(export_all).
- -include_lib("emqx_auth/include/emqx_authz.hrl").
- -include_lib("emqx_connector/include/emqx_connector.hrl").
- -include_lib("eunit/include/eunit.hrl").
- -include_lib("common_test/include/ct.hrl").
- -include_lib("emqx/include/emqx_placeholder.hrl").
- -define(MONGO_HOST, "mongo").
- -define(MONGO_CLIENT, 'emqx_authz_mongo_SUITE_client').
- all() ->
- emqx_authz_test_lib:all_with_table_case(?MODULE, t_run_case, cases()).
- groups() ->
- emqx_authz_test_lib:table_groups(t_run_case, cases()).
- init_per_suite(Config) ->
- case emqx_common_test_helpers:is_tcp_server_available(?MONGO_HOST, ?MONGO_DEFAULT_PORT) of
- true ->
- Apps = emqx_cth_suite:start(
- [
- emqx,
- {emqx_conf,
- "authorization.no_match = deny, authorization.cache.enable = false"},
- emqx_auth,
- emqx_auth_mongodb
- ],
- #{work_dir => ?config(priv_dir, Config)}
- ),
- [{suite_apps, Apps} | Config];
- false ->
- {skip, no_mongo}
- end.
- end_per_suite(Config) ->
- ok = emqx_authz_test_lib:restore_authorizers(),
- emqx_cth_suite:stop(?config(suite_apps, Config)).
- init_per_group(Group, Config) ->
- [{test_case, emqx_authz_test_lib:get_case(Group, cases())} | Config].
- end_per_group(_Group, _Config) ->
- ok.
- init_per_testcase(_TestCase, Config) ->
- {ok, _} = mc_worker_api:connect(mongo_config()),
- ok = emqx_authz_test_lib:reset_authorizers(),
- Config.
- end_per_testcase(_TestCase, _Config) ->
- _ = emqx_authz:set_feature_available(rich_actions, true),
- ok = reset_samples(),
- ok = mc_worker_api:disconnect(?MONGO_CLIENT).
- %%------------------------------------------------------------------------------
- %% Testcases
- %%------------------------------------------------------------------------------
- t_run_case(Config) ->
- Case = ?config(test_case, Config),
- ok = setup_source_data(Case),
- ok = setup_authz_source(Case),
- ok = emqx_authz_test_lib:run_checks(Case).
- %%------------------------------------------------------------------------------
- %% Cases
- %%------------------------------------------------------------------------------
- cases() ->
- [
- #{
- name => base_publish,
- records => [
- #{
- <<"username">> => <<"username">>,
- <<"action">> => <<"publish">>,
- <<"topic">> => <<"a">>,
- <<"permission">> => <<"allow">>
- },
- #{
- <<"username">> => <<"username">>,
- <<"action">> => <<"subscribe">>,
- <<"topic">> => <<"b">>,
- <<"permission">> => <<"allow">>
- },
- #{
- <<"username">> => <<"username">>,
- <<"action">> => <<"all">>,
- <<"topics">> => [<<"c">>, <<"d">>],
- <<"permission">> => <<"allow">>
- }
- ],
- filter => #{<<"username">> => <<"${username}">>},
- checks => [
- {allow, ?AUTHZ_PUBLISH, <<"a">>},
- {deny, ?AUTHZ_SUBSCRIBE, <<"a">>},
- {deny, ?AUTHZ_PUBLISH, <<"b">>},
- {allow, ?AUTHZ_SUBSCRIBE, <<"b">>},
- {allow, ?AUTHZ_PUBLISH, <<"c">>},
- {allow, ?AUTHZ_SUBSCRIBE, <<"c">>},
- {allow, ?AUTHZ_PUBLISH, <<"d">>},
- {allow, ?AUTHZ_SUBSCRIBE, <<"d">>}
- ]
- },
- #{
- name => filter_works,
- records => [
- #{
- <<"action">> => <<"publish">>,
- <<"topic">> => <<"a">>,
- <<"permission">> => <<"allow">>
- }
- ],
- filter => #{<<"username">> => <<"${username}">>},
- checks => [
- {deny, ?AUTHZ_PUBLISH, <<"a">>}
- ]
- },
- #{
- name => invalid_rich_rules,
- features => [rich_actions],
- records => [
- #{
- <<"action">> => <<"publish">>,
- <<"topic">> => <<"a">>,
- <<"permission">> => <<"allow">>,
- <<"qos">> => <<"1,2,3">>
- },
- #{
- <<"action">> => <<"publish">>,
- <<"topic">> => <<"a">>,
- <<"permission">> => <<"allow">>,
- <<"retain">> => <<"yes">>
- }
- ],
- filter => #{},
- checks => [
- {deny, ?AUTHZ_PUBLISH, <<"a">>}
- ]
- },
- #{
- name => invalid_rules,
- records => [
- #{
- <<"action">> => <<"publis">>,
- <<"topic">> => <<"a">>,
- <<"permission">> => <<"allow">>
- }
- ],
- filter => #{},
- checks => [
- {deny, ?AUTHZ_PUBLISH, <<"a">>}
- ]
- },
- #{
- name => rule_by_clientid_cn_dn_peerhost,
- records => [
- #{
- <<"cn">> => <<"cn">>,
- <<"dn">> => <<"dn">>,
- <<"clientid">> => <<"clientid">>,
- <<"peerhost">> => <<"127.0.0.1">>,
- <<"action">> => <<"publish">>,
- <<"topic">> => <<"a">>,
- <<"permission">> => <<"allow">>
- }
- ],
- client_info => #{
- cn => <<"cn">>,
- dn => <<"dn">>
- },
- filter => #{
- <<"cn">> => <<"${cert_common_name}">>,
- <<"dn">> => <<"${cert_subject}">>,
- <<"clientid">> => <<"${clientid}">>,
- <<"peerhost">> => <<"${peerhost}">>
- },
- checks => [
- {allow, ?AUTHZ_PUBLISH, <<"a">>}
- ]
- },
- #{
- name => topics_literal_wildcard_variable,
- records => [
- #{
- <<"username">> => <<"username">>,
- <<"action">> => <<"publish">>,
- <<"permission">> => <<"allow">>,
- <<"topics">> => [
- <<"t/${username}">>,
- <<"t/${clientid}">>,
- <<"t1/#">>,
- <<"t2/+">>,
- <<"eq t3/${username}">>
- ]
- }
- ],
- filter => #{<<"username">> => <<"${username}">>},
- checks => [
- {allow, ?AUTHZ_PUBLISH, <<"t/username">>},
- {allow, ?AUTHZ_PUBLISH, <<"t/clientid">>},
- {allow, ?AUTHZ_PUBLISH, <<"t1/a/b">>},
- {allow, ?AUTHZ_PUBLISH, <<"t2/a">>},
- {allow, ?AUTHZ_PUBLISH, <<"t3/${username}">>},
- {deny, ?AUTHZ_PUBLISH, <<"t3/username">>}
- ]
- },
- #{
- name => qos_retain_in_query_result,
- features => [rich_actions],
- records => [
- #{
- <<"username">> => <<"username">>,
- <<"action">> => <<"publish">>,
- <<"permission">> => <<"allow">>,
- <<"topic">> => <<"a">>,
- <<"qos">> => 1,
- <<"retain">> => true
- },
- #{
- <<"username">> => <<"username">>,
- <<"action">> => <<"publish">>,
- <<"permission">> => <<"allow">>,
- <<"topic">> => <<"b">>,
- <<"qos">> => <<"1">>,
- <<"retain">> => <<"true">>
- },
- #{
- <<"username">> => <<"username">>,
- <<"action">> => <<"publish">>,
- <<"permission">> => <<"allow">>,
- <<"topic">> => <<"c">>,
- <<"qos">> => <<"1,2">>,
- <<"retain">> => 1
- },
- #{
- <<"username">> => <<"username">>,
- <<"action">> => <<"publish">>,
- <<"permission">> => <<"allow">>,
- <<"topic">> => <<"d">>,
- <<"qos">> => [1, 2],
- <<"retain">> => <<"1">>
- },
- #{
- <<"username">> => <<"username">>,
- <<"action">> => <<"publish">>,
- <<"permission">> => <<"allow">>,
- <<"topic">> => <<"e">>,
- <<"qos">> => [1, 2],
- <<"retain">> => <<"all">>
- },
- #{
- <<"username">> => <<"username">>,
- <<"action">> => <<"publish">>,
- <<"permission">> => <<"allow">>,
- <<"topic">> => <<"f">>,
- <<"qos">> => null,
- <<"retain">> => null
- }
- ],
- filter => #{<<"username">> => <<"${username}">>},
- checks => [
- {allow, ?AUTHZ_PUBLISH(1, true), <<"a">>},
- {deny, ?AUTHZ_PUBLISH(1, false), <<"a">>},
- {allow, ?AUTHZ_PUBLISH(1, true), <<"b">>},
- {deny, ?AUTHZ_PUBLISH(1, false), <<"b">>},
- {deny, ?AUTHZ_PUBLISH(2, false), <<"b">>},
- {allow, ?AUTHZ_PUBLISH(2, true), <<"c">>},
- {deny, ?AUTHZ_PUBLISH(2, false), <<"c">>},
- {deny, ?AUTHZ_PUBLISH(0, true), <<"c">>},
- {allow, ?AUTHZ_PUBLISH(2, true), <<"d">>},
- {deny, ?AUTHZ_PUBLISH(0, true), <<"d">>},
- {allow, ?AUTHZ_PUBLISH(1, false), <<"e">>},
- {allow, ?AUTHZ_PUBLISH(1, true), <<"e">>},
- {deny, ?AUTHZ_PUBLISH(0, false), <<"e">>},
- {allow, ?AUTHZ_PUBLISH, <<"f">>},
- {deny, ?AUTHZ_SUBSCRIBE, <<"f">>}
- ]
- },
- #{
- name => nonbin_values_in_client_info,
- records => [
- #{
- <<"username">> => <<"username">>,
- <<"clientid">> => <<"clientid">>,
- <<"action">> => <<"publish">>,
- <<"topic">> => <<"a">>,
- <<"permission">> => <<"allow">>
- }
- ],
- client_info => #{
- username => "username",
- clientid => clientid
- },
- filter => #{<<"username">> => <<"${username}">>, <<"clientid">> => <<"${clientid}">>},
- checks => [
- {allow, ?AUTHZ_PUBLISH, <<"a">>}
- ]
- },
- #{
- name => invalid_query,
- records => [
- #{
- <<"action">> => <<"publish">>,
- <<"topic">> => <<"a">>,
- <<"permission">> => <<"allow">>
- }
- ],
- filter => #{<<"$in">> => #{<<"a">> => 1}},
- checks => [
- {deny, ?AUTHZ_PUBLISH, <<"a">>}
- ]
- },
- #{
- name => complex_query,
- records => [
- #{
- <<"a">> => #{<<"u">> => <<"clientid">>, <<"c">> => [<<"cn">>, <<"dn">>]},
- <<"action">> => <<"publish">>,
- <<"topic">> => <<"a">>,
- <<"permission">> => <<"allow">>
- }
- ],
- client_info => #{
- cn => <<"cn">>,
- dn => <<"dn">>
- },
- filter => #{
- <<"a">> => #{
- <<"u">> => <<"${clientid}">>,
- <<"c">> => [<<"${cert_common_name}">>, <<"${cert_subject}">>]
- }
- },
- checks => [
- {allow, ?AUTHZ_PUBLISH, <<"a">>}
- ]
- }
- ].
- %%------------------------------------------------------------------------------
- %% Helpers
- %%------------------------------------------------------------------------------
- reset_samples() ->
- {true, _} = mc_worker_api:delete(?MONGO_CLIENT, <<"acl">>, #{}),
- ok.
- setup_source_data(#{records := Records}) ->
- {{true, _}, _} = mc_worker_api:insert(?MONGO_CLIENT, <<"acl">>, Records),
- ok.
- setup_authz_source(#{filter := Filter}) ->
- setup_config(
- #{
- <<"filter">> => Filter
- }
- ).
- setup_config(SpecialParams) ->
- emqx_authz_test_lib:setup_config(
- raw_mongo_authz_config(),
- SpecialParams
- ).
- raw_mongo_authz_config() ->
- #{
- <<"type">> => <<"mongodb">>,
- <<"enable">> => <<"true">>,
- <<"mongo_type">> => <<"single">>,
- <<"database">> => <<"mqtt">>,
- <<"collection">> => <<"acl">>,
- <<"server">> => mongo_server(),
- <<"auth_source">> => mongo_authsource(),
- <<"username">> => mongo_username(),
- <<"password">> => mongo_password(),
- <<"filter">> => #{<<"username">> => <<"${username}">>}
- }.
- mongo_server() ->
- iolist_to_binary(io_lib:format("~s", [?MONGO_HOST])).
- mongo_config() ->
- [
- {database, <<"mqtt">>},
- {host, ?MONGO_HOST},
- {port, ?MONGO_DEFAULT_PORT},
- {auth_source, mongo_authsource()},
- {login, mongo_username()},
- {password, mongo_password()},
- {register, ?MONGO_CLIENT}
- ].
- mongo_authsource() ->
- iolist_to_binary(os:getenv("MONGO_AUTHSOURCE", "admin")).
- mongo_username() ->
- iolist_to_binary(os:getenv("MONGO_USERNAME", "")).
- mongo_password() ->
- iolist_to_binary(os:getenv("MONGO_PASSWORD", "")).
- start_apps(Apps) ->
- lists:foreach(fun application:ensure_all_started/1, Apps).
- stop_apps(Apps) ->
- lists:foreach(fun application:stop/1, Apps).
|