diff --git a/src/emqx_access_control.erl b/src/emqx_access_control.erl index 7127c8d33..847f801ec 100644 --- a/src/emqx_access_control.erl +++ b/src/emqx_access_control.erl @@ -28,52 +28,57 @@ %% APIs %%-------------------------------------------------------------------- --spec(authenticate(emqx_types:credentials()) - -> {ok, emqx_types:credentials()} | {error, term()}). -authenticate(Credentials) -> - case emqx_hooks:run_fold('client.authenticate', [], init_auth_result(Credentials)) of - #{auth_result := success, anonymous := true} = NewCredentials -> +-spec(authenticate(emqx_types:client()) + -> {ok, #{auth_result := emqx_types:auth_result(), + anonymous := boolean}} | {error, term()}). +authenticate(Client = #{zone := Zone}) -> + case emqx_hooks:run_fold('client.authenticate', + [Client], default_auth_result(Zone)) of + Result = #{auth_result := success, anonymous := true} -> emqx_metrics:inc('auth.mqtt.anonymous'), - {ok, NewCredentials}; - #{auth_result := success} = NewCredentials -> - {ok, NewCredentials}; - NewCredentials -> - {error, maps:get(auth_result, NewCredentials, unknown_error)} - end. - -%% @doc Check ACL --spec(check_acl(emqx_types:credentials(), emqx_types:pubsub(), emqx_types:topic()) - -> allow | deny). -check_acl(Credentials, PubSub, Topic) -> - case emqx_acl_cache:is_enabled() of - false -> - do_check_acl(Credentials, PubSub, Topic); - true -> - case emqx_acl_cache:get_acl_cache(PubSub, Topic) of - not_found -> - AclResult = do_check_acl(Credentials, PubSub, Topic), - emqx_acl_cache:put_acl_cache(PubSub, Topic, AclResult), - AclResult; - AclResult -> AclResult - end + {ok, Result}; + Result = #{auth_result := success} -> + {ok, Result}; + Result -> + {error, maps:get(auth_result, Result, unknown_error)} end. -do_check_acl(#{zone := Zone} = Credentials, PubSub, Topic) -> - case emqx_hooks:run_fold('client.check_acl', [Credentials, PubSub, Topic], - emqx_zone:get_env(Zone, acl_nomatch, deny)) of - allow -> allow; - _ -> deny +%% @doc Check ACL +-spec(check_acl(emqx_types:cient(), emqx_types:pubsub(), emqx_types:topic()) + -> allow | deny). +check_acl(Client, PubSub, Topic) -> + case emqx_acl_cache:is_enabled() of + true -> + check_acl_cache(Client, PubSub, Topic); + false -> + do_check_acl(Client, PubSub, Topic) + end. + +check_acl_cache(Client, PubSub, Topic) -> + case emqx_acl_cache:get_acl_cache(PubSub, Topic) of + not_found -> + AclResult = do_check_acl(Client, PubSub, Topic), + emqx_acl_cache:put_acl_cache(PubSub, Topic, AclResult), + AclResult; + AclResult -> AclResult + end. + +do_check_acl(#{zone := Zone} = Client, PubSub, Topic) -> + Default = emqx_zone:get_env(Zone, acl_nomatch, deny), + case emqx_hooks:run_fold('client.check_acl', [Client, PubSub, Topic], Default) of + allow -> allow; + _Other -> deny end. -spec(reload_acl() -> ok | {error, term()}). reload_acl() -> - emqx_acl_cache:is_enabled() andalso - emqx_acl_cache:empty_acl_cache(), + emqx_acl_cache:is_enabled() + andalso emqx_acl_cache:empty_acl_cache(), emqx_mod_acl_internal:reload_acl(). -init_auth_result(Credentials) -> - case emqx_zone:get_env(maps:get(zone, Credentials, undefined), allow_anonymous, false) of - true -> Credentials#{auth_result => success, anonymous => true}; - false -> Credentials#{auth_result => not_authorized, anonymous => false} +default_auth_result(Zone) -> + case emqx_zone:get_env(Zone, allow_anonymous, false) of + true -> #{auth_result => success, anonymous => true}; + false -> #{auth_result => not_authorized, anonymous => false} end. diff --git a/src/emqx_access_rule.erl b/src/emqx_access_rule.erl index d56710a21..b9d166030 100644 --- a/src/emqx_access_rule.erl +++ b/src/emqx_access_rule.erl @@ -40,10 +40,6 @@ -define(ALLOW_DENY(A), ((A =:= allow) orelse (A =:= deny))). -define(PUBSUB(A), ((A =:= subscribe) orelse (A =:= publish) orelse (A =:= pubsub))). -%%-------------------------------------------------------------------- -%% APIs -%%-------------------------------------------------------------------- - %% @doc Compile Access Rule. compile({A, all}) when ?ALLOW_DENY(A) -> {A, all}; @@ -92,21 +88,21 @@ bin(B) when is_binary(B) -> %% @doc Match access rule -spec(match(emqx_types:credentials(), emqx_types:topic(), rule()) -> {matched, allow} | {matched, deny} | nomatch). -match(_Credentials, _Topic, {AllowDeny, all}) when ?ALLOW_DENY(AllowDeny) -> +match(_Client, _Topic, {AllowDeny, all}) when ?ALLOW_DENY(AllowDeny) -> {matched, AllowDeny}; -match(Credentials, Topic, {AllowDeny, Who, _PubSub, TopicFilters}) +match(Client, Topic, {AllowDeny, Who, _PubSub, TopicFilters}) when ?ALLOW_DENY(AllowDeny) -> - case match_who(Credentials, Who) - andalso match_topics(Credentials, Topic, TopicFilters) of + case match_who(Client, Who) + andalso match_topics(Client, Topic, TopicFilters) of true -> {matched, AllowDeny}; false -> nomatch end. -match_who(_Credentials, all) -> +match_who(_Client, all) -> true; -match_who(_Credentials, {user, all}) -> +match_who(_Client, {user, all}) -> true; -match_who(_Credentials, {client, all}) -> +match_who(_Client, {client, all}) -> true; match_who(#{client_id := ClientId}, {client, ClientId}) -> true; @@ -116,44 +112,44 @@ match_who(#{peername := undefined}, {ipaddr, _Tup}) -> false; match_who(#{peername := {IP, _}}, {ipaddr, CIDR}) -> esockd_cidr:match(IP, CIDR); -match_who(Credentials, {'and', Conds}) when is_list(Conds) -> +match_who(Client, {'and', Conds}) when is_list(Conds) -> lists:foldl(fun(Who, Allow) -> - match_who(Credentials, Who) andalso Allow + match_who(Client, Who) andalso Allow end, true, Conds); -match_who(Credentials, {'or', Conds}) when is_list(Conds) -> +match_who(Client, {'or', Conds}) when is_list(Conds) -> lists:foldl(fun(Who, Allow) -> - match_who(Credentials, Who) orelse Allow + match_who(Client, Who) orelse Allow end, false, Conds); -match_who(_Credentials, _Who) -> +match_who(_Client, _Who) -> false. -match_topics(_Credentials, _Topic, []) -> +match_topics(_Client, _Topic, []) -> false; -match_topics(Credentials, Topic, [{pattern, PatternFilter}|Filters]) -> - TopicFilter = feed_var(Credentials, PatternFilter), +match_topics(Client, Topic, [{pattern, PatternFilter}|Filters]) -> + TopicFilter = feed_var(Client, PatternFilter), match_topic(emqx_topic:words(Topic), TopicFilter) - orelse match_topics(Credentials, Topic, Filters); -match_topics(Credentials, Topic, [TopicFilter|Filters]) -> + orelse match_topics(Client, Topic, Filters); +match_topics(Client, Topic, [TopicFilter|Filters]) -> match_topic(emqx_topic:words(Topic), TopicFilter) - orelse match_topics(Credentials, Topic, Filters). + orelse match_topics(Client, Topic, Filters). match_topic(Topic, {eq, TopicFilter}) -> Topic == TopicFilter; match_topic(Topic, TopicFilter) -> emqx_topic:match(Topic, TopicFilter). -feed_var(Credentials, Pattern) -> - feed_var(Credentials, Pattern, []). -feed_var(_Credentials, [], Acc) -> +feed_var(Client, Pattern) -> + feed_var(Client, Pattern, []). +feed_var(_Client, [], Acc) -> lists:reverse(Acc); -feed_var(Credentials = #{client_id := undefined}, [<<"%c">>|Words], Acc) -> - feed_var(Credentials, Words, [<<"%c">>|Acc]); -feed_var(Credentials = #{client_id := ClientId}, [<<"%c">>|Words], Acc) -> - feed_var(Credentials, Words, [ClientId |Acc]); -feed_var(Credentials = #{username := undefined}, [<<"%u">>|Words], Acc) -> - feed_var(Credentials, Words, [<<"%u">>|Acc]); -feed_var(Credentials = #{username := Username}, [<<"%u">>|Words], Acc) -> - feed_var(Credentials, Words, [Username|Acc]); -feed_var(Credentials, [W|Words], Acc) -> - feed_var(Credentials, Words, [W|Acc]). +feed_var(Client = #{client_id := undefined}, [<<"%c">>|Words], Acc) -> + feed_var(Client, Words, [<<"%c">>|Acc]); +feed_var(Client = #{client_id := ClientId}, [<<"%c">>|Words], Acc) -> + feed_var(Client, Words, [ClientId |Acc]); +feed_var(Client = #{username := undefined}, [<<"%u">>|Words], Acc) -> + feed_var(Client, Words, [<<"%u">>|Acc]); +feed_var(Client = #{username := Username}, [<<"%u">>|Words], Acc) -> + feed_var(Client, Words, [Username|Acc]); +feed_var(Client, [W|Words], Acc) -> + feed_var(Client, Words, [W|Acc]).