emqx_authn_postgresql.erl 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2021-2024 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_authn_postgresql).
  17. -include_lib("emqx_auth/include/emqx_authn.hrl").
  18. -include_lib("emqx/include/logger.hrl").
  19. -include_lib("epgsql/include/epgsql.hrl").
  20. -behaviour(emqx_authn_provider).
  21. -export([
  22. create/2,
  23. update/2,
  24. authenticate/2,
  25. destroy/1
  26. ]).
  27. -ifdef(TEST).
  28. -compile(export_all).
  29. -compile(nowarn_export_all).
  30. -endif.
  31. %%------------------------------------------------------------------------------
  32. %% APIs
  33. %%------------------------------------------------------------------------------
  34. create(_AuthenticatorID, Config) ->
  35. create(Config).
  36. create(Config0) ->
  37. ResourceId = emqx_authn_utils:make_resource_id(?MODULE),
  38. {Config, State} = parse_config(Config0, ResourceId),
  39. {ok, _Data} = emqx_authn_utils:create_resource(
  40. ResourceId,
  41. emqx_postgresql,
  42. Config
  43. ),
  44. {ok, State#{resource_id => ResourceId}}.
  45. update(Config0, #{resource_id := ResourceId} = _State) ->
  46. {Config, NState} = parse_config(Config0, ResourceId),
  47. case emqx_authn_utils:update_resource(emqx_postgresql, Config, ResourceId) of
  48. {error, Reason} ->
  49. error({load_config_error, Reason});
  50. {ok, _} ->
  51. {ok, NState#{resource_id => ResourceId}}
  52. end.
  53. destroy(#{resource_id := ResourceId}) ->
  54. _ = emqx_resource:remove_local(ResourceId),
  55. ok.
  56. authenticate(#{auth_method := _}, _) ->
  57. ignore;
  58. authenticate(#{password := undefined}, _) ->
  59. {error, bad_username_or_password};
  60. authenticate(
  61. #{password := Password} = Credential,
  62. #{
  63. placeholders := PlaceHolders,
  64. resource_id := ResourceId,
  65. password_hash_algorithm := Algorithm
  66. }
  67. ) ->
  68. Params = emqx_authn_utils:render_sql_params(PlaceHolders, Credential),
  69. case emqx_resource:simple_sync_query(ResourceId, {prepared_query, ResourceId, Params}) of
  70. {ok, _Columns, []} ->
  71. ignore;
  72. {ok, Columns, [Row | _]} ->
  73. NColumns = [Name || #column{name = Name} <- Columns],
  74. Selected = maps:from_list(lists:zip(NColumns, erlang:tuple_to_list(Row))),
  75. case
  76. emqx_authn_utils:check_password_from_selected_map(
  77. Algorithm, Selected, Password
  78. )
  79. of
  80. ok ->
  81. {ok, emqx_authn_utils:is_superuser(Selected)};
  82. {error, Reason} ->
  83. {error, Reason}
  84. end;
  85. {error, Reason} ->
  86. ?TRACE_AUTHN_PROVIDER(error, "postgresql_query_failed", #{
  87. resource => ResourceId,
  88. params => Params,
  89. reason => Reason
  90. }),
  91. ignore
  92. end.
  93. parse_config(
  94. #{
  95. query := Query0,
  96. password_hash_algorithm := Algorithm
  97. } = Config,
  98. ResourceId
  99. ) ->
  100. ok = emqx_authn_password_hashing:init(Algorithm),
  101. {Query, PlaceHolders} = emqx_authn_utils:parse_sql(Query0, '$n'),
  102. State = #{
  103. placeholders => PlaceHolders,
  104. password_hash_algorithm => Algorithm
  105. },
  106. {Config#{prepare_statement => #{ResourceId => Query}}, State}.