emqx_acl_pgsql.erl 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
  3. %%
  4. %% Licensed under the Apache License, Version 2.0 (the "License");
  5. %% you may not use this file except in compliance with the License.
  6. %% You may obtain a copy of the License at
  7. %%
  8. %% http://www.apache.org/licenses/LICENSE-2.0
  9. %%
  10. %% Unless required by applicable law or agreed to in writing, software
  11. %% distributed under the License is distributed on an "AS IS" BASIS,
  12. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. %% See the License for the specific language governing permissions and
  14. %% limitations under the License.
  15. %%--------------------------------------------------------------------
  16. -module(emqx_acl_pgsql).
  17. -include("emqx_auth_pgsql.hrl").
  18. -include_lib("emqx/include/emqx.hrl").
  19. -include_lib("emqx/include/logger.hrl").
  20. %% ACL callbacks
  21. -export([ register_metrics/0
  22. , check_acl/5
  23. , description/0
  24. ]).
  25. -spec(register_metrics() -> ok).
  26. register_metrics() ->
  27. lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS).
  28. check_acl(ClientInfo, PubSub, Topic, NoMatchAction, #{pool := Pool} = State) ->
  29. case do_check_acl(Pool, ClientInfo, PubSub, Topic, NoMatchAction, State) of
  30. ok -> emqx_metrics:inc(?ACL_METRICS(ignore)), ok;
  31. {stop, allow} -> emqx_metrics:inc(?ACL_METRICS(allow)), {stop, allow};
  32. {stop, deny} -> emqx_metrics:inc(?ACL_METRICS(deny)), {stop, deny}
  33. end.
  34. do_check_acl(_Pool, #{username := <<$$, _/binary>>}, _PubSub, _Topic, _NoMatchAction, _State) ->
  35. ok;
  36. do_check_acl(Pool, ClientInfo, PubSub, Topic, _NoMatchAction, #{acl_query := {AclSql, AclParams}}) ->
  37. case emqx_auth_pgsql_cli:equery(Pool, AclSql, AclParams, ClientInfo) of
  38. {ok, _, []} -> ok;
  39. {ok, _, Rows} ->
  40. Rules = filter(PubSub, compile(Rows)),
  41. case match(ClientInfo, Topic, Rules) of
  42. {matched, allow} -> {stop, allow};
  43. {matched, deny} -> {stop, deny};
  44. nomatch -> ok
  45. end;
  46. {error, Reason} ->
  47. ?LOG(error, "[Postgres] do_check_acl error: ~p~n", [Reason]),
  48. ok
  49. end.
  50. match(_ClientInfo, _Topic, []) ->
  51. nomatch;
  52. match(ClientInfo, Topic, [Rule|Rules]) ->
  53. case emqx_access_rule:match(ClientInfo, Topic, Rule) of
  54. nomatch -> match(ClientInfo, Topic, Rules);
  55. {matched, AllowDeny} -> {matched, AllowDeny}
  56. end.
  57. filter(PubSub, Rules) ->
  58. [Term || Term = {_, _, Access, _} <- Rules,
  59. Access =:= PubSub orelse Access =:= pubsub].
  60. compile(Rows) ->
  61. compile(Rows, []).
  62. compile([], Acc) ->
  63. Acc;
  64. compile([{Allow, IpAddr, Username, ClientId, Access, Topic}|T], Acc) ->
  65. Who = who(IpAddr, Username, ClientId),
  66. Term = {allow(Allow), Who, access(Access), [topic(Topic)]},
  67. compile(T, [emqx_access_rule:compile(Term) | Acc]).
  68. who(_, <<"$all">>, _) ->
  69. all;
  70. who(null, null, null) ->
  71. throw(undefined_who);
  72. who(CIDR, Username, ClientId) ->
  73. Cols = [{ipaddr, b2l(CIDR)}, {user, Username}, {client, ClientId}],
  74. case [{C, V} || {C, V} <- Cols, not empty(V)] of
  75. [Who] -> Who;
  76. Conds -> {'and', Conds}
  77. end.
  78. allow(1) -> allow;
  79. allow(0) -> deny;
  80. allow(<<"1">>) -> allow;
  81. allow(<<"0">>) -> deny.
  82. access(1) -> subscribe;
  83. access(2) -> publish;
  84. access(3) -> pubsub;
  85. access(<<"1">>) -> subscribe;
  86. access(<<"2">>) -> publish;
  87. access(<<"3">>) -> pubsub.
  88. topic(<<"eq ", Topic/binary>>) ->
  89. {eq, Topic};
  90. topic(Topic) ->
  91. Topic.
  92. description() ->
  93. "ACL with Postgres".
  94. b2l(null) -> null;
  95. b2l(B) -> binary_to_list(B).
  96. empty(null) -> true;
  97. empty("") -> true;
  98. empty(<<>>) -> true;
  99. empty(_) -> false.