119 lines
3.8 KiB
Erlang
119 lines
3.8 KiB
Erlang
%%--------------------------------------------------------------------
|
|
%% Copyright (c) 2021-2023 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_authn_postgresql).
|
|
|
|
-include_lib("emqx_auth/include/emqx_authn.hrl").
|
|
-include_lib("emqx/include/logger.hrl").
|
|
-include_lib("epgsql/include/epgsql.hrl").
|
|
|
|
-behaviour(emqx_authn_provider).
|
|
|
|
-export([
|
|
create/2,
|
|
update/2,
|
|
authenticate/2,
|
|
destroy/1
|
|
]).
|
|
|
|
-ifdef(TEST).
|
|
-compile(export_all).
|
|
-compile(nowarn_export_all).
|
|
-endif.
|
|
|
|
%%------------------------------------------------------------------------------
|
|
%% APIs
|
|
%%------------------------------------------------------------------------------
|
|
|
|
create(_AuthenticatorID, Config) ->
|
|
create(Config).
|
|
|
|
create(Config0) ->
|
|
ResourceId = emqx_authn_utils:make_resource_id(?MODULE),
|
|
{Config, State} = parse_config(Config0, ResourceId),
|
|
{ok, _Data} = emqx_authn_utils:create_resource(
|
|
ResourceId,
|
|
emqx_postgresql,
|
|
Config
|
|
),
|
|
{ok, State#{resource_id => ResourceId}}.
|
|
|
|
update(Config0, #{resource_id := ResourceId} = _State) ->
|
|
{Config, NState} = parse_config(Config0, ResourceId),
|
|
case emqx_authn_utils:update_resource(emqx_postgresql, Config, ResourceId) of
|
|
{error, Reason} ->
|
|
error({load_config_error, Reason});
|
|
{ok, _} ->
|
|
{ok, NState#{resource_id => ResourceId}}
|
|
end.
|
|
|
|
destroy(#{resource_id := ResourceId}) ->
|
|
_ = emqx_resource:remove_local(ResourceId),
|
|
ok.
|
|
|
|
authenticate(#{auth_method := _}, _) ->
|
|
ignore;
|
|
authenticate(#{password := undefined}, _) ->
|
|
{error, bad_username_or_password};
|
|
authenticate(
|
|
#{password := Password} = Credential,
|
|
#{
|
|
placeholders := PlaceHolders,
|
|
resource_id := ResourceId,
|
|
password_hash_algorithm := Algorithm
|
|
}
|
|
) ->
|
|
Params = emqx_authn_utils:render_sql_params(PlaceHolders, Credential),
|
|
case emqx_resource:simple_sync_query(ResourceId, {prepared_query, ResourceId, Params}) of
|
|
{ok, _Columns, []} ->
|
|
ignore;
|
|
{ok, Columns, [Row | _]} ->
|
|
NColumns = [Name || #column{name = Name} <- Columns],
|
|
Selected = maps:from_list(lists:zip(NColumns, erlang:tuple_to_list(Row))),
|
|
case
|
|
emqx_authn_utils:check_password_from_selected_map(
|
|
Algorithm, Selected, Password
|
|
)
|
|
of
|
|
ok ->
|
|
{ok, emqx_authn_utils:is_superuser(Selected)};
|
|
{error, Reason} ->
|
|
{error, Reason}
|
|
end;
|
|
{error, Reason} ->
|
|
?TRACE_AUTHN_PROVIDER(error, "postgresql_query_failed", #{
|
|
resource => ResourceId,
|
|
params => Params,
|
|
reason => Reason
|
|
}),
|
|
ignore
|
|
end.
|
|
|
|
parse_config(
|
|
#{
|
|
query := Query0,
|
|
password_hash_algorithm := Algorithm
|
|
} = Config,
|
|
ResourceId
|
|
) ->
|
|
ok = emqx_authn_password_hashing:init(Algorithm),
|
|
{Query, PlaceHolders} = emqx_authn_utils:parse_sql(Query0, '$n'),
|
|
State = #{
|
|
placeholders => PlaceHolders,
|
|
password_hash_algorithm => Algorithm
|
|
},
|
|
{Config#{prepare_statement => #{ResourceId => Query}}, State}.
|