From d8db671abb33babf6a0a026e68a048762ce8ba63 Mon Sep 17 00:00:00 2001 From: Ilya Averyanov Date: Mon, 11 Apr 2022 23:00:31 +0300 Subject: [PATCH] feat(emqx_auth_jwt): use JWT for ACL checks --- CHANGES-4.3.md | 2 + apps/emqx_auth_jwt/README.md | 32 ++++ apps/emqx_auth_jwt/etc/emqx_auth_jwt.conf | 5 + apps/emqx_auth_jwt/priv/emqx_auth_jwt.schema | 10 + .../emqx_auth_jwt/src/emqx_auth_jwt.appup.src | 22 +-- apps/emqx_auth_jwt/src/emqx_auth_jwt.erl | 40 +++- apps/emqx_auth_jwt/src/emqx_auth_jwt_app.erl | 18 +- apps/emqx_auth_jwt/src/emqx_auth_jwt_svr.erl | 18 +- .../test/emqx_auth_jwt_SUITE.erl | 174 +++++++++++++++--- src/emqx.appup.src | 54 ++++-- src/emqx_access_rule.erl | 3 +- 11 files changed, 301 insertions(+), 77 deletions(-) diff --git a/CHANGES-4.3.md b/CHANGES-4.3.md index 29ea968a8..400b94531 100644 --- a/CHANGES-4.3.md +++ b/CHANGES-4.3.md @@ -32,6 +32,8 @@ File format: scenarios, especially when the emqx-bridge-mqtt plugin is configured as shared subscription. [#7462] * Add some compression functions to rule-engine: gzip, gunzip, zip, unzip, zip_compress, zip_uncompress +* Add support for JWT authorization [#7596] + Now MQTT clients may be authorized with respect to a specific claim containing publish/subscribe topic whitelists. ### Bug fixes diff --git a/apps/emqx_auth_jwt/README.md b/apps/emqx_auth_jwt/README.md index 7af41f490..954171692 100644 --- a/apps/emqx_auth_jwt/README.md +++ b/apps/emqx_auth_jwt/README.md @@ -46,6 +46,11 @@ auth.jwt.verify_claims = off ## - %u: username ## - %c: clientid # auth.jwt.verify_claims.username = %u + +## Name of the claim containg ACL rules +## +## Value: String +#auth.jwt.acl_claim_name = acl ``` Load the Plugin @@ -62,6 +67,33 @@ Example mosquitto_pub -t 'pub' -m 'hello' -i test -u test -P eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiYm9iIiwiYWdlIjoyOX0.bIV_ZQ8D5nQi0LT8AVkpM4Pd6wmlbpR9S8nOLJAsA8o ``` +ACL +--- +JWT may contain lists of topics allowed for subscribing/publishing (ACL rules): + +Payload example: +```json +{ + "sub": "emqx", + "name": "John Doe", + "iat": 1516239022, + "exp": 1516239122, + "acl": { + "sub": [ + "a/b", + "c/+", + "%u/%c" + ], + "pub": [ + "a/b", + "c/+", + "%u/%c" + ] + } +} +``` + + Algorithms ---------- diff --git a/apps/emqx_auth_jwt/etc/emqx_auth_jwt.conf b/apps/emqx_auth_jwt/etc/emqx_auth_jwt.conf index 748902f9f..372b342d1 100644 --- a/apps/emqx_auth_jwt/etc/emqx_auth_jwt.conf +++ b/apps/emqx_auth_jwt/etc/emqx_auth_jwt.conf @@ -47,3 +47,8 @@ auth.jwt.verify_claims = off ## For example, to verify that the username in the JWT payload is the same ## as the client (MQTT protocol) username #auth.jwt.verify_claims.username = %u + +## Name of the claim containg ACL rules +## +## Value: String +#auth.jwt.acl_claim_name = acl diff --git a/apps/emqx_auth_jwt/priv/emqx_auth_jwt.schema b/apps/emqx_auth_jwt/priv/emqx_auth_jwt.schema index 3d8de3678..ff676c3ff 100644 --- a/apps/emqx_auth_jwt/priv/emqx_auth_jwt.schema +++ b/apps/emqx_auth_jwt/priv/emqx_auth_jwt.schema @@ -47,3 +47,13 @@ end, [], cuttlefish_variable:filter_by_prefix("auth.jwt.verify_claims", Conf)) end end}. + +{mapping, "auth.jwt.acl_claim_name", "emqx_auth_jwt.acl_claim_name", [ + {default, "acl"}, + {datatype, string} +]}. + +{translation, "emqx_auth_jwt.acl_claim_name", fun(Conf) -> + list_to_binary(cuttlefish:conf_get("auth.jwt.acl_claim_name", Conf)) +end}. + diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt.appup.src b/apps/emqx_auth_jwt/src/emqx_auth_jwt.appup.src index 684b4fa93..9af4560bf 100644 --- a/apps/emqx_auth_jwt/src/emqx_auth_jwt.appup.src +++ b/apps/emqx_auth_jwt/src/emqx_auth_jwt.appup.src @@ -1,15 +1,9 @@ -%% -*-: erlang -*- +%% -*- mode: erlang -*- +%% Unless you know what you are doing, DO NOT edit manually!! {VSN, - [ - {<<"4\\.3\\.[0-1]">>, [ - {load_module, emqx_auth_jwt_svr, brutal_purge, soft_purge, []} - ]}, - {<<".*">>, []} - ], - [ - {<<"4\\.3\\.[0-1]">>, [ - {load_module, emqx_auth_jwt_svr, brutal_purge, soft_purge, []} - ]}, - {<<".*">>, []} - ] -}. + [{<<"4\\.3\\.[0-1]">>, + [{restart_application, emqx_auth_jwt}]}, + {<<".*">>,[]}], + [{<<"4\\.3\\.[0-1]">>, + [{restart_application, emqx_auth_jwt}]}, + {<<".*">>,[]}]}. diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt.erl b/apps/emqx_auth_jwt/src/emqx_auth_jwt.erl index 487bc837f..46451d1bb 100644 --- a/apps/emqx_auth_jwt/src/emqx_auth_jwt.erl +++ b/apps/emqx_auth_jwt/src/emqx_auth_jwt.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% Copyright (c) 2020-2022 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. @@ -22,7 +22,8 @@ -logger_header("[JWT]"). -export([ register_metrics/0 - , check/3 + , check_auth/3 + , check_acl/5 , description/0 ]). @@ -46,8 +47,7 @@ register_metrics() -> %% Authentication callbacks %%-------------------------------------------------------------------- -check(ClientInfo, AuthResult, #{from := From, - checklists := Checklists}) -> +check_auth(ClientInfo, AuthResult, #{from := From, checklists := Checklists}) -> case maps:find(From, ClientInfo) of error -> ok = emqx_metrics:inc(?AUTH_METRICS(ignore)); @@ -67,12 +67,38 @@ check(ClientInfo, AuthResult, #{from := From, end end. +check_acl(ClientInfo = #{jwt_claims := Claims}, + PubSub, + Topic, + _NoMatchAction, + #{acl_claim_name := AclClaimName}) -> + Deadline = erlang:system_time(second), + case Claims of + #{AclClaimName := Acl, <<"exp">> := Exp} + when is_integer(Exp) andalso Exp >= Deadline -> + verify_acl(ClientInfo, Acl, PubSub, Topic); + _ -> ignore + end. + description() -> "Authentication with JWT". %%------------------------------------------------------------------------------ %% Verify Claims %%-------------------------------------------------------------------- +verify_acl(ClientInfo, #{<<"sub">> := SubTopics}, subscribe, Topic) when is_list(SubTopics) -> + verify_acl(ClientInfo, SubTopics, Topic); +verify_acl(ClientInfo, #{<<"pub">> := PubTopics}, publish, Topic) when is_list(PubTopics) -> + verify_acl(ClientInfo, PubTopics, Topic); +verify_acl(_ClientInfo, _Acl, _PubSub, _Topic) -> {stop, deny}. + +verify_acl(_ClientInfo, [], _Topic) -> {stop, deny}; +verify_acl(ClientInfo, [AclTopic | AclTopics], Topic) -> + case match_topic(ClientInfo, AclTopic, Topic) of + true -> {stop, allow}; + false -> verify_acl(ClientInfo, AclTopics, Topic) + end. + verify_claims(Checklists, Claims, ClientInfo) -> case do_verify_claims(feedvar(Checklists, ClientInfo), Claims) of {error, Reason} -> @@ -96,3 +122,9 @@ feedvar(Checklists, #{username := Username, clientid := ClientId}) -> ({K, <<"%c">>}) -> {K, ClientId}; ({K, Expected}) -> {K, Expected} end, Checklists). + +match_topic(ClientInfo, AclTopic, Topic) -> + AclTopicWords = emqx_topic:words(AclTopic), + TopicWords = emqx_topic:words(Topic), + AclTopicRendered = emqx_access_rule:feed_var(ClientInfo, AclTopicWords), + emqx_topic:match(TopicWords, AclTopicRendered). diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt_app.erl b/apps/emqx_auth_jwt/src/emqx_auth_jwt_app.erl index b38730af5..a1e9fc619 100644 --- a/apps/emqx_auth_jwt/src/emqx_auth_jwt_app.erl +++ b/apps/emqx_auth_jwt/src/emqx_auth_jwt_app.erl @@ -31,15 +31,20 @@ start(_Type, _Args) -> {ok, Sup} = supervisor:start_link({local, ?MODULE}, ?MODULE, []), - {ok, Pid} = start_auth_server(jwks_svr_options()), + {ok, _} = start_auth_server(jwks_svr_options()), ok = emqx_auth_jwt:register_metrics(), + AuthEnv = auth_env(), + _ = emqx:hook('client.authenticate', {emqx_auth_jwt, check_auth, [AuthEnv]}), + + AclEnv = acl_env(), + _ = emqx:hook('client.check_acl', {emqx_auth_jwt, check_acl, [AclEnv]}), - _ = emqx:hook('client.authenticate', {emqx_auth_jwt, check, [AuthEnv]}), - {ok, Sup, AuthEnv}. + {ok, Sup}. -stop(AuthEnv) -> - emqx:unhook('client.authenticate', {emqx_auth_jwt, check, [AuthEnv]}). +stop(_State) -> + emqx:unhook('client.authenticate', {emqx_auth_jwt, check_auth}), + emqx:unhook('client.check_acl', {emqx_auth_jwt, check_acl}). %%-------------------------------------------------------------------- %% Dummy supervisor @@ -68,6 +73,9 @@ auth_env() -> , checklists => Checklists }. +acl_env() -> + #{acl_claim_name => env(acl_claim_name, <<"acl">>)}. + jwks_svr_options() -> [{K, V} || {K, V} <- [{secret, env(secret, undefined)}, diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt_svr.erl b/apps/emqx_auth_jwt/src/emqx_auth_jwt_svr.erl index 5cc98ef9e..05e45cec3 100644 --- a/apps/emqx_auth_jwt/src/emqx_auth_jwt_svr.erl +++ b/apps/emqx_auth_jwt/src/emqx_auth_jwt_svr.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% Copyright (c) 2020-2022 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. @@ -73,8 +73,7 @@ init([Options]) -> ok = jose:json_module(jiffy), _ = ets:new(?TAB, [set, protected, named_table]), {Static, Remote} = do_init_jwks(Options), - true = ets:insert(?TAB, {static, Static}), - true = ets:insert(?TAB, {remote, Remote}), + true = ets:insert(?TAB, [{static, Static}, {remote, Remote}]), Intv = proplists:get_value(interval, Options, ?INTERVAL), {ok, reset_timer( #state{ @@ -181,7 +180,7 @@ do_verify(JwsCompacted) -> end catch Class : Reason : Stk -> - ?LOG(error, "Handle JWK crashed: ~p, ~p, stacktrace: ~p~n", + ?LOG(error, "verify JWK crashed: ~p, ~p, stacktrace: ~p~n", [Class, Reason, Stk]), {error, invalid_signature} end. @@ -219,11 +218,12 @@ check_claims(Claims) -> do_check_claim([], Claims) -> Claims; do_check_claim([{K, F}|More], Claims) -> - case maps:take(K, Claims) of - error -> do_check_claim(More, Claims); - {V, NClaims} -> + case Claims of + #{K := V} -> case F(V) of - true -> do_check_claim(More, NClaims); + true -> do_check_claim(More, Claims); _ -> {false, K} - end + end; + _ -> + do_check_claim(More, Claims) end. diff --git a/apps/emqx_auth_jwt/test/emqx_auth_jwt_SUITE.erl b/apps/emqx_auth_jwt/test/emqx_auth_jwt_SUITE.erl index d4f562b6f..e28c7348b 100644 --- a/apps/emqx_auth_jwt/test/emqx_auth_jwt_SUITE.erl +++ b/apps/emqx_auth_jwt/test/emqx_auth_jwt_SUITE.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% Copyright (c) 2020-2022 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. @@ -20,22 +20,13 @@ -compile(nowarn_export_all). -include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/emqx_mqtt.hrl"). -include_lib("eunit/include/eunit.hrl"). -include_lib("common_test/include/ct.hrl"). -define(APP, emqx_auth_jwt). -all() -> - [{group, emqx_auth_jwt}]. - -groups() -> - [{emqx_auth_jwt, [sequence], [ t_check_auth - , t_check_claims - , t_check_claims_clientid - , t_check_claims_username - , t_check_claims_kid_in_header - ]} - ]. +all() -> emqx_ct:all(?MODULE). init_per_suite(Config) -> emqx_ct_helpers:start_apps([emqx_auth_jwt], fun set_special_configs/1), @@ -44,6 +35,14 @@ init_per_suite(Config) -> end_per_suite(_Config) -> emqx_ct_helpers:stop_apps([emqx_auth_jwt]). +init_per_testcase(TestCase, Config) -> + ?MODULE:TestCase(init, Config), + emqx_ct_helpers:start_apps([emqx_auth_jwt], fun set_special_configs/1), + Config. + +end_per_testcase(_Case, _Config) -> + emqx_ct_helpers:stop_apps([emqx_auth_jwt]). + set_special_configs(emqx) -> application:set_env(emqx, allow_anonymous, false), application:set_env(emqx, acl_nomatch, deny), @@ -78,7 +77,9 @@ sign(Payload, Alg, Key) -> %% Testcases %%------------------------------------------------------------------------------ -t_check_auth(_) -> +t_check_auth(init, _Config) -> + application:unset_env(emqx_auth_jwt, verify_claims). +t_check_auth(_Config) -> Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external}, Jwt = sign([{clientid, <<"client1">>}, {username, <<"plain">>}, @@ -102,10 +103,9 @@ t_check_auth(_) -> ?assertEqual({error, invalid_signature}, Result2), ?assertMatch({error, _}, emqx_access_control:authenticate(Plain#{password => <<"asd">>})). -t_check_claims(_) -> - application:set_env(emqx_auth_jwt, verify_claims, [{sub, <<"value">>}]), - application:stop(emqx_auth_jwt), application:start(emqx_auth_jwt), - +t_check_claims(init, _Config) -> + application:set_env(emqx_auth_jwt, verify_claims, [{sub, <<"value">>}]). +t_check_claims(_Config) -> Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external}, Jwt = sign([{client_id, <<"client1">>}, {username, <<"plain">>}, @@ -120,9 +120,9 @@ t_check_claims(_) -> ct:pal("Auth result for the invalid jwt: ~p~n", [Result2]), ?assertEqual({error, invalid_signature}, Result2). -t_check_claims_clientid(_) -> - application:set_env(emqx_auth_jwt, verify_claims, [{clientid, <<"%c">>}]), - application:stop(emqx_auth_jwt), application:start(emqx_auth_jwt), +t_check_claims_clientid(init, _Config) -> + application:set_env(emqx_auth_jwt, verify_claims, [{clientid, <<"%c">>}]). +t_check_claims_clientid(_Config) -> Plain = #{clientid => <<"client23">>, username => <<"plain">>, zone => external}, Jwt = sign([{clientid, <<"client23">>}, {username, <<"plain">>}, @@ -136,10 +136,9 @@ t_check_claims_clientid(_) -> ct:pal("Auth result for the invalid jwt: ~p~n", [Result2]), ?assertEqual({error, invalid_signature}, Result2). -t_check_claims_username(_) -> - application:set_env(emqx_auth_jwt, verify_claims, [{username, <<"%u">>}]), - application:stop(emqx_auth_jwt), application:start(emqx_auth_jwt), - +t_check_claims_username(init, _Config) -> + application:set_env(emqx_auth_jwt, verify_claims, [{username, <<"%u">>}]). +t_check_claims_username(_Config) -> Plain = #{clientid => <<"client23">>, username => <<"plain">>, zone => external}, Jwt = sign([{client_id, <<"client23">>}, {username, <<"plain">>}, @@ -153,8 +152,9 @@ t_check_claims_username(_) -> ct:pal("Auth result for the invalid jwt: ~p~n", [Result3]), ?assertEqual({error, invalid_signature}, Result3). -t_check_claims_kid_in_header(_) -> - application:set_env(emqx_auth_jwt, verify_claims, []), +t_check_claims_kid_in_header(init, _Config) -> + application:set_env(emqx_auth_jwt, verify_claims, []). +t_check_claims_kid_in_header(_Config) -> Plain = #{clientid => <<"client23">>, username => <<"plain">>, zone => external}, Jwt = sign([{clientid, <<"client23">>}, {username, <<"plain">>}, @@ -164,3 +164,125 @@ t_check_claims_kid_in_header(_) -> Result0 = emqx_access_control:authenticate(Plain#{password => Jwt}), ct:pal("Auth result: ~p~n", [Result0]), ?assertMatch({ok, #{auth_result := success, jwt_claims := _}}, Result0). + +t_check_jwt_acl(init, _Config) -> + application:set_env(emqx_auth_jwt, verify_claims, [{sub, <<"value">>}]). +t_check_jwt_acl(_Config) -> + Jwt = sign([{client_id, <<"client1">>}, + {username, <<"plain">>}, + {sub, value}, + {acl, [{sub, [<<"a/b">>]}, + {pub, [<<"c/d">>]}]}, + {exp, os:system_time(seconds) + 10}], + <<"HS256">>, + <<"emqxsecret">>), + + {ok, C} = emqtt:start_link( + [{clean_start, true}, + {proto_ver, v5}, + {client_id, <<"client1">>}, + {password, Jwt}]), + {ok, _} = emqtt:connect(C), + + ?assertMatch( + {ok, #{}, [0]}, + emqtt:subscribe(C, <<"a/b">>, 0)), + + ?assertMatch( + ok, + emqtt:publish(C, <<"c/d">>, <<"hi">>, 0)), + + ?assertMatch( + {ok, #{}, [?RC_NOT_AUTHORIZED]}, + emqtt:subscribe(C, <<"c/d">>, 0)), + + ok = emqtt:publish(C, <<"a/b">>, <<"hi">>, 0), + + receive + {publish, #{topic := <<"a/b">>}} -> + ?assert(false, "Publish to `a/b` should not be allowed") + after 100 -> ok + end, + + ok = emqtt:disconnect(C). + +t_check_jwt_acl_no_recs(init, _Config) -> + application:set_env(emqx_auth_jwt, verify_claims, [{sub, <<"value">>}]). +t_check_jwt_acl_no_recs(_Config) -> + Jwt = sign([{client_id, <<"client1">>}, + {username, <<"plain">>}, + {sub, value}, + {acl, []}, + {exp, os:system_time(seconds) + 10}], + <<"HS256">>, + <<"emqxsecret">>), + + {ok, C} = emqtt:start_link( + [{clean_start, true}, + {proto_ver, v5}, + {client_id, <<"client1">>}, + {password, Jwt}]), + {ok, _} = emqtt:connect(C), + + ?assertMatch( + {ok, #{}, [?RC_NOT_AUTHORIZED]}, + emqtt:subscribe(C, <<"a/b">>, 0)), + + ok = emqtt:disconnect(C). + +t_check_jwt_acl_no_acl_claim(init, _Config) -> + application:set_env(emqx_auth_jwt, verify_claims, [{sub, <<"value">>}]). +t_check_jwt_acl_no_acl_claim(_Config) -> + Jwt = sign([{client_id, <<"client1">>}, + {username, <<"plain">>}, + {sub, value}, + {exp, os:system_time(seconds) + 10}], + <<"HS256">>, + <<"emqxsecret">>), + + {ok, C} = emqtt:start_link( + [{clean_start, true}, + {proto_ver, v5}, + {client_id, <<"client1">>}, + {password, Jwt}]), + {ok, _} = emqtt:connect(C), + + ?assertMatch( + {ok, #{}, [?RC_NOT_AUTHORIZED]}, + emqtt:subscribe(C, <<"a/b">>, 0)), + + ok = emqtt:disconnect(C). + +t_check_jwt_acl_expire(init, _Config) -> + application:set_env(emqx_auth_jwt, verify_claims, [{sub, <<"value">>}]). +t_check_jwt_acl_expire(_Config) -> + Jwt = sign([{client_id, <<"client1">>}, + {username, <<"plain">>}, + {sub, value}, + {acl, [{sub, [<<"a/b">>]}]}, + {exp, os:system_time(seconds) + 1}], + <<"HS256">>, + <<"emqxsecret">>), + + {ok, C} = emqtt:start_link( + [{clean_start, true}, + {proto_ver, v5}, + {client_id, <<"client1">>}, + {password, Jwt}]), + {ok, _} = emqtt:connect(C), + + ?assertMatch( + {ok, #{}, [0]}, + emqtt:subscribe(C, <<"a/b">>, 0)), + + ?assertMatch( + {ok, #{}, [0]}, + emqtt:unsubscribe(C, <<"a/b">>)), + + timer:sleep(2000), + + ?assertMatch( + {ok, #{}, [?RC_NOT_AUTHORIZED]}, + emqtt:subscribe(C, <<"a/b">>, 0)), + + ok = emqtt:disconnect(C). diff --git a/src/emqx.appup.src b/src/emqx.appup.src index 1ef499276..347634ba9 100644 --- a/src/emqx.appup.src +++ b/src/emqx.appup.src @@ -2,7 +2,8 @@ %% Unless you know what you are doing, DO NOT edit manually!! {VSN, [{"4.3.14", - [{load_module,emqx,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_sys,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, @@ -10,7 +11,8 @@ {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, {"4.3.13", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, @@ -27,7 +29,8 @@ {load_module,emqx_misc,brutal_purge,soft_purge,[]}, {load_module,emqx_connection,brutal_purge,soft_purge,[]}]}, {"4.3.12", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_frame,brutal_purge,soft_purge,[]}, @@ -52,7 +55,8 @@ {load_module,emqx_message,brutal_purge,soft_purge,[]}, {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.11", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, @@ -79,7 +83,8 @@ {load_module,emqx_message,brutal_purge,soft_purge,[]}, {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.10", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, @@ -106,7 +111,8 @@ {load_module,emqx_message,brutal_purge,soft_purge,[]}, {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.9", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, @@ -137,7 +143,8 @@ {load_module,emqx_message,brutal_purge,soft_purge,[]}, {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.8", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, @@ -168,7 +175,8 @@ {load_module,emqx_message,brutal_purge,soft_purge,[]}, {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.7", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, @@ -200,7 +208,8 @@ {load_module,emqx_message,brutal_purge,soft_purge,[]}, {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.6", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, @@ -444,7 +453,8 @@ {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {<<".*">>,[]}], [{"4.3.14", - [{load_module,emqx,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_sys,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, @@ -452,7 +462,8 @@ {load_module,emqx_app,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}]}, {"4.3.13", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, @@ -469,7 +480,8 @@ {load_module,emqx_cm,brutal_purge,soft_purge,[]}, {load_module,emqx_connection,brutal_purge,soft_purge,[]}]}, {"4.3.12", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_frame,brutal_purge,soft_purge,[]}, @@ -493,7 +505,8 @@ {load_module,emqx_message,brutal_purge,soft_purge,[]}, {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.11", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, @@ -519,7 +532,8 @@ {load_module,emqx_message,brutal_purge,soft_purge,[]}, {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.10", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, @@ -545,7 +559,8 @@ {load_module,emqx_message,brutal_purge,soft_purge,[]}, {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.9", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, @@ -575,7 +590,8 @@ {load_module,emqx_message,brutal_purge,soft_purge,[]}, {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.8", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, @@ -605,7 +621,8 @@ {load_module,emqx_message,brutal_purge,soft_purge,[]}, {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.7", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, @@ -636,7 +653,8 @@ {load_module,emqx_message,brutal_purge,soft_purge,[]}, {load_module,emqx_limiter,brutal_purge,soft_purge,[]}]}, {"4.3.6", - [{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, + [{load_module,emqx_access_rule,brutal_purge,soft_purge,[]}, + {load_module,emqx_shared_sub,brutal_purge,soft_purge,[]}, {load_module,emqx_hooks,brutal_purge,soft_purge,[]}, {load_module,emqx_plugins,brutal_purge,soft_purge,[]}, {load_module,emqx_pmon,brutal_purge,soft_purge,[]}, diff --git a/src/emqx_access_rule.erl b/src/emqx_access_rule.erl index 49831d614..da2a39a22 100644 --- a/src/emqx_access_rule.erl +++ b/src/emqx_access_rule.erl @@ -1,5 +1,5 @@ %%-------------------------------------------------------------------- -%% Copyright (c) 2017-2021 EMQ Technologies Co., Ltd. All Rights Reserved. +%% Copyright (c) 2017-2022 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. @@ -19,6 +19,7 @@ %% APIs -export([ match/3 , compile/1 + , feed_var/2 ]). -export_type([rule/0]).