refactor: do not destory resource when update authn/authz resource

This commit is contained in:
JimMoen 2022-05-09 13:41:07 +08:00
parent a5ddc5390f
commit 87af77ec35
17 changed files with 351 additions and 235 deletions

View File

@ -20,6 +20,8 @@
-include_lib("emqx_authn.hrl"). -include_lib("emqx_authn.hrl").
-export([ -export([
create_resource/3,
update_resource/3,
check_password_from_selected_map/3, check_password_from_selected_map/3,
parse_deep/1, parse_deep/1,
parse_str/1, parse_str/1,
@ -47,6 +49,27 @@
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
create_resource(ResourceId, Module, Config) ->
{ok, _Data} = emqx_resource:create_local(
ResourceId,
?RESOURCE_GROUP,
Module,
Config,
#{}
).
update_resource(Module, Config, ResourceId) ->
%% recreate before maybe stop
%% resource will auto start during recreate
Result = emqx_resource:recreate_local(ResourceId, Module, Config),
case Config of
#{enable := true} ->
Result;
#{enable := false} ->
ok = emqx_resource:stop(ResourceId),
Result
end.
check_password_from_selected_map(_Algorithm, _Selected, undefined) -> check_password_from_selected_map(_Algorithm, _Selected, undefined) ->
{error, bad_username_or_password}; {error, bad_username_or_password};
check_password_from_selected_map( check_password_from_selected_map(

View File

@ -158,45 +158,24 @@ refs() ->
create(_AuthenticatorID, Config) -> create(_AuthenticatorID, Config) ->
create(Config). create(Config).
create( create(Config0) ->
#{
method := Method,
url := RawUrl,
headers := Headers,
request_timeout := RequestTimeout
} = Config
) ->
{BaseUrl0, Path, Query} = parse_url(RawUrl),
{ok, BaseUrl} = emqx_http_lib:uri_parse(BaseUrl0),
ResourceId = emqx_authn_utils:make_resource_id(?MODULE), ResourceId = emqx_authn_utils:make_resource_id(?MODULE),
State = #{ {Config, State} = parse_config(Config0),
method => Method, {ok, _Data} = emqx_authn_utils:create_resource(
path => Path,
headers => ensure_header_name_type(Headers),
base_path_templete => emqx_authn_utils:parse_str(Path),
base_query_template => emqx_authn_utils:parse_deep(
cow_qs:parse_qs(to_bin(Query))
),
body_template => emqx_authn_utils:parse_deep(maps:get(body, Config, #{})),
request_timeout => RequestTimeout,
resource_id => ResourceId
},
{ok, _Data} = emqx_resource:create_local(
ResourceId, ResourceId,
?RESOURCE_GROUP,
emqx_connector_http, emqx_connector_http,
Config#{ Config
base_url => BaseUrl,
pool_type => random
},
#{}
), ),
{ok, State}. {ok, State#{resource_id => ResourceId}}.
update(Config, State) -> update(Config0, #{resource_id := ResourceId} = _State) ->
{ok, NewState} = create(Config), {Config, NState} = parse_config(Config0),
ok = destroy(State), case emqx_authn_utils:update_resource(emqx_connector_http, Config, ResourceId) of
{ok, NewState}. {error, Reason} ->
error({load_config_error, Reason});
{ok, _} ->
{ok, NState#{resource_id => ResourceId}}
end.
authenticate(#{auth_method := _}, _) -> authenticate(#{auth_method := _}, _) ->
ignore; ignore;
@ -325,6 +304,29 @@ parse_url(Url) ->
throw({invalid_url, Url}) throw({invalid_url, Url})
end. end.
parse_config(
#{
method := Method,
url := RawUrl,
headers := Headers,
request_timeout := RequestTimeout
} = Config
) ->
{BaseUrl0, Path, Query} = parse_url(RawUrl),
{ok, BaseUrl} = emqx_http_lib:uri_parse(BaseUrl0),
State = #{
method => Method,
path => Path,
headers => ensure_header_name_type(Headers),
base_path_templete => emqx_authn_utils:parse_str(Path),
base_query_template => emqx_authn_utils:parse_deep(
cow_qs:parse_qs(to_bin(Query))
),
body_template => emqx_authn_utils:parse_deep(maps:get(body, Config, #{})),
request_timeout => RequestTimeout
},
{Config#{base_url => BaseUrl, pool_type => random}, State}.
generate_request(Credential, #{ generate_request(Credential, #{
method := Method, method := Method,
headers := Headers0, headers := Headers0,

View File

@ -126,39 +126,28 @@ refs() ->
create(_AuthenticatorID, Config) -> create(_AuthenticatorID, Config) ->
create(Config). create(Config).
create(#{filter := Filter} = Config) -> create(Config0) ->
FilterTemplate = emqx_authn_utils:parse_deep(Filter), ResourceId = emqx_authn_utils:make_resource_id(?MODULE),
State = maps:with( {Config, State} = parse_config(Config0),
[ {ok, _Data} = emqx_authn_utils:create_resource(
collection, ResourceId,
password_hash_field, emqx_connector_mongo,
salt_field,
is_superuser_field,
password_hash_algorithm,
salt_position
],
Config Config
), ),
#{password_hash_algorithm := Algorithm} = State, {ok, State#{resource_id => ResourceId}}.
ok = emqx_authn_password_hashing:init(Algorithm),
ResourceId = emqx_authn_utils:make_resource_id(?MODULE),
NState = State#{
filter_template => FilterTemplate,
resource_id => ResourceId
},
{ok, _Data} = emqx_resource:create_local(
ResourceId,
?RESOURCE_GROUP,
emqx_connector_mongo,
Config,
#{}
),
{ok, NState}.
update(Config, State) -> update(Config0, #{resource_id := ResourceId} = _State) ->
{ok, NewState} = create(Config), {Config, NState} = parse_config(Config0),
ok = destroy(State), case emqx_authn_utils:update_resource(emqx_connector_mongo, Config, ResourceId) of
{ok, NewState}. {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 := _}, _) -> authenticate(#{auth_method := _}, _) ->
ignore; ignore;
@ -201,14 +190,26 @@ authenticate(
end end
end. end.
destroy(#{resource_id := ResourceId}) ->
_ = emqx_resource:remove_local(ResourceId),
ok.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
parse_config(#{filter := Filter} = Config) ->
FilterTemplate = emqx_authn_utils:parse_deep(Filter),
State = maps:with(
[
collection,
password_hash_field,
salt_field,
is_superuser_field,
password_hash_algorithm,
salt_position
],
Config
),
ok = emqx_authn_password_hashing:init(maps:get(password_hash_algorithm, State)),
{Config, State#{filter_template => FilterTemplate}}.
check_password(undefined, _Selected, _State) -> check_password(undefined, _Selected, _State) ->
{error, bad_username_or_password}; {error, bad_username_or_password};
check_password( check_password(

View File

@ -83,35 +83,24 @@ refs() ->
create(_AuthenticatorID, Config) -> create(_AuthenticatorID, Config) ->
create(Config). create(Config).
create( create(Config0) ->
#{
password_hash_algorithm := Algorithm,
query := Query0,
query_timeout := QueryTimeout
} = Config
) ->
ok = emqx_authn_password_hashing:init(Algorithm),
{PrepareSql, TmplToken} = emqx_authn_utils:parse_sql(Query0, '?'),
ResourceId = emqx_authn_utils:make_resource_id(?MODULE), ResourceId = emqx_authn_utils:make_resource_id(?MODULE),
State = #{ {Config, State} = parse_config(Config0),
password_hash_algorithm => Algorithm, {ok, _Data} = emqx_authn_utils:create_resource(ResourceId, emqx_connector_mysql, Config),
tmpl_token => TmplToken, {ok, State#{resource_id => ResourceId}}.
query_timeout => QueryTimeout,
resource_id => ResourceId
},
{ok, _Data} = emqx_resource:create_local(
ResourceId,
?RESOURCE_GROUP,
emqx_connector_mysql,
Config#{prepare_statement => #{?PREPARE_KEY => PrepareSql}},
#{}
),
{ok, State}.
update(Config, State) -> update(Config0, #{resource_id := ResourceId} = _State) ->
{ok, NewState} = create(Config), {Config, NState} = parse_config(Config0),
ok = destroy(State), case emqx_authn_utils:update_resource(emqx_connector_mysql, Config, ResourceId) of
{ok, NewState}. {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 := _}, _) -> authenticate(#{auth_method := _}, _) ->
ignore; ignore;
@ -152,6 +141,18 @@ authenticate(
ignore ignore
end. end.
destroy(#{resource_id := ResourceId}) -> parse_config(
_ = emqx_resource:remove_local(ResourceId), #{
ok. password_hash_algorithm := Algorithm,
query := Query0,
query_timeout := QueryTimeout
} = Config
) ->
ok = emqx_authn_password_hashing:init(Algorithm),
{PrepareSql, TmplToken} = emqx_authn_utils:parse_sql(Query0, '?'),
State = #{
password_hash_algorithm => Algorithm,
tmpl_token => TmplToken,
query_timeout => QueryTimeout
},
{Config#{prepare_statement => #{?PREPARE_KEY => PrepareSql}}, State}.

View File

@ -82,33 +82,28 @@ refs() ->
create(_AuthenticatorID, Config) -> create(_AuthenticatorID, Config) ->
create(Config). create(Config).
create( create(Config0) ->
#{
query := Query0,
password_hash_algorithm := Algorithm
} = Config
) ->
ok = emqx_authn_password_hashing:init(Algorithm),
{Query, PlaceHolders} = emqx_authn_utils:parse_sql(Query0, '$n'),
ResourceId = emqx_authn_utils:make_resource_id(?MODULE), ResourceId = emqx_authn_utils:make_resource_id(?MODULE),
State = #{ {Config, State} = parse_config(Config0, ResourceId),
placeholders => PlaceHolders, {ok, _Data} = emqx_authn_utils:create_resource(
password_hash_algorithm => Algorithm,
resource_id => ResourceId
},
{ok, _Data} = emqx_resource:create_local(
ResourceId, ResourceId,
?RESOURCE_GROUP,
emqx_connector_pgsql, emqx_connector_pgsql,
Config#{prepare_statement => #{ResourceId => Query}}, Config
#{}
), ),
{ok, State}. {ok, State#{resource_id => ResourceId}}.
update(Config, State) -> update(Config0, #{resource_id := ResourceId} = _State) ->
{ok, NewState} = create(Config), {Config, NState} = parse_config(Config0, ResourceId),
ok = destroy(State), case emqx_authn_utils:update_resource(emqx_connector_pgsql, Config, ResourceId) of
{ok, NewState}. {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 := _}, _) -> authenticate(#{auth_method := _}, _) ->
ignore; ignore;
@ -147,6 +142,17 @@ authenticate(
ignore ignore
end. end.
destroy(#{resource_id := ResourceId}) -> parse_config(
_ = emqx_resource:remove_local(ResourceId), #{
ok. 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}.

View File

@ -96,51 +96,33 @@ refs() ->
create(_AuthenticatorID, Config) -> create(_AuthenticatorID, Config) ->
create(Config). create(Config).
create( create(Config0) ->
#{ ResourceId = emqx_authn_utils:make_resource_id(?MODULE),
cmd := Cmd, case parse_config(Config0) of
password_hash_algorithm := Algorithm {error, _} = Res ->
} = Config Res;
) -> {Config, State} ->
ok = emqx_authn_password_hashing:init(Algorithm), {ok, _Data} = emqx_authn_utils:create_resource(
try ResourceId,
NCmd = parse_cmd(Cmd), emqx_connector_redis,
ok = emqx_authn_utils:ensure_apps_started(Algorithm),
State = maps:with(
[password_hash_algorithm, salt_position],
Config Config
), ),
ResourceId = emqx_authn_utils:make_resource_id(?MODULE), {ok, State#{resource_id => ResourceId}}
NState = State#{
cmd => NCmd,
resource_id => ResourceId
},
{ok, _Data} = emqx_resource:create_local(
ResourceId,
?RESOURCE_GROUP,
emqx_connector_redis,
Config,
#{}
),
{ok, NState}
catch
error:{unsupported_cmd, _Cmd} ->
{error, {unsupported_cmd, Cmd}};
error:missing_password_hash ->
{error, missing_password_hash};
error:{unsupported_fields, Fields} ->
{error, {unsupported_fields, Fields}}
end. end.
update(Config, State) -> update(Config0, #{resource_id := ResourceId} = _State) ->
case create(Config) of {Config, NState} = parse_config(Config0),
{ok, NewState} -> case emqx_authn_utils:update_resource(emqx_connector_redis, Config, ResourceId) of
ok = destroy(State),
{ok, NewState};
{error, Reason} -> {error, Reason} ->
{error, Reason} error({load_config_error, Reason});
{ok, _} ->
{ok, NState#{resource_id => ResourceId}}
end. end.
destroy(#{resource_id := ResourceId}) ->
_ = emqx_resource:remove_local(ResourceId),
ok.
authenticate(#{auth_method := _}, _) -> authenticate(#{auth_method := _}, _) ->
ignore; ignore;
authenticate( authenticate(
@ -190,14 +172,31 @@ authenticate(
ignore ignore
end. end.
destroy(#{resource_id := ResourceId}) ->
_ = emqx_resource:remove_local(ResourceId),
ok.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
parse_config(
#{
cmd := Cmd,
password_hash_algorithm := Algorithm
} = Config
) ->
try
NCmd = parse_cmd(Cmd),
ok = emqx_authn_password_hashing:init(Algorithm),
ok = emqx_authn_utils:ensure_apps_started(Algorithm),
State = maps:with([password_hash_algorithm, salt_position], Config),
{Config, State#{cmd => NCmd}}
catch
error:{unsupported_cmd, _Cmd} ->
{error, {unsupported_cmd, Cmd}};
error:missing_password_hash ->
{error, missing_password_hash};
error:{unsupported_fields, Fields} ->
{error, {unsupported_fields, Fields}}
end.
%% Only support HGET and HMGET %% Only support HGET and HMGET
parse_cmd(Cmd) -> parse_cmd(Cmd) ->
case string:tokens(Cmd, " ") of case string:tokens(Cmd, " ") of

View File

@ -63,16 +63,20 @@
%% Initialize authz backend. %% Initialize authz backend.
%% Populate the passed configuration map with necessary data, %% Populate the passed configuration map with necessary data,
%% like `ResourceID`s %% like `ResourceID`s
-callback init(source()) -> source(). -callback create(source()) -> source().
%% Get authz text description. %% Update authz backend.
-callback description() -> string(). %% Change configuration, or simply enable/disable
-callback update(source()) -> source().
%% Destroy authz backend. %% Destroy authz backend.
%% Make cleanup of all allocated data. %% Make cleanup of all allocated data.
%% An authz backend will not be used after `destroy`. %% An authz backend will not be used after `destroy`.
-callback destroy(source()) -> ok. -callback destroy(source()) -> ok.
%% Get authz text description.
-callback description() -> string().
%% Authorize client action. %% Authorize client action.
-callback authorize( -callback authorize(
emqx_types:clientinfo(), emqx_types:clientinfo(),
@ -81,6 +85,10 @@
source() source()
) -> match_result(). ) -> match_result().
-optional_callbacks([
update/1
]).
-spec register_metrics() -> ok. -spec register_metrics() -> ok.
register_metrics() -> register_metrics() ->
lists:foreach(fun emqx_metrics:ensure/1, ?METRICS). lists:foreach(fun emqx_metrics:ensure/1, ?METRICS).
@ -90,7 +98,7 @@ init() ->
emqx_conf:add_handler(?CONF_KEY_PATH, ?MODULE), emqx_conf:add_handler(?CONF_KEY_PATH, ?MODULE),
Sources = emqx_conf:get(?CONF_KEY_PATH, []), Sources = emqx_conf:get(?CONF_KEY_PATH, []),
ok = check_dup_types(Sources), ok = check_dup_types(Sources),
NSources = init_sources(Sources), NSources = create_sources(Sources),
ok = emqx_hooks:add('client.authorize', {?MODULE, authorize, [NSources]}, -1). ok = emqx_hooks:add('client.authorize', {?MODULE, authorize, [NSources]}, -1).
deinit() -> deinit() ->
@ -170,7 +178,7 @@ do_post_config_update({?CMD_MOVE, _Type, _Where} = Cmd, _Sources) ->
InitedSources = lookup(), InitedSources = lookup(),
do_move(Cmd, InitedSources); do_move(Cmd, InitedSources);
do_post_config_update({?CMD_PREPEND, RawNewSource}, Sources) -> do_post_config_update({?CMD_PREPEND, RawNewSource}, Sources) ->
InitedNewSource = init_source(get_source_by_type(type(RawNewSource), Sources)), InitedNewSource = create_source(get_source_by_type(type(RawNewSource), Sources)),
%% create metrics %% create metrics
TypeName = type(RawNewSource), TypeName = type(RawNewSource),
ok = emqx_metrics_worker:create_metrics( ok = emqx_metrics_worker:create_metrics(
@ -181,14 +189,13 @@ do_post_config_update({?CMD_PREPEND, RawNewSource}, Sources) ->
), ),
[InitedNewSource] ++ lookup(); [InitedNewSource] ++ lookup();
do_post_config_update({?CMD_APPEND, RawNewSource}, Sources) -> do_post_config_update({?CMD_APPEND, RawNewSource}, Sources) ->
InitedNewSource = init_source(get_source_by_type(type(RawNewSource), Sources)), InitedNewSource = create_source(get_source_by_type(type(RawNewSource), Sources)),
lookup() ++ [InitedNewSource]; lookup() ++ [InitedNewSource];
do_post_config_update({{?CMD_REPLACE, Type}, RawNewSource}, Sources) -> do_post_config_update({{?CMD_REPLACE, Type}, RawNewSource}, Sources) ->
OldSources = lookup(), OldSources = lookup(),
{OldSource, Front, Rear} = take(Type, OldSources), {OldSource, Front, Rear} = take(Type, OldSources),
NewSource = get_source_by_type(type(RawNewSource), Sources), NewSource = get_source_by_type(type(RawNewSource), Sources),
ok = ensure_resource_deleted(OldSource), InitedSources = update_source(type(RawNewSource), OldSource, NewSource),
InitedSources = init_source(NewSource),
Front ++ [InitedSources] ++ Rear; Front ++ [InitedSources] ++ Rear;
do_post_config_update({{?CMD_DELETE, Type}, _RawNewSource}, _Sources) -> do_post_config_update({{?CMD_DELETE, Type}, _RawNewSource}, _Sources) ->
OldInitedSources = lookup(), OldInitedSources = lookup(),
@ -203,7 +210,7 @@ do_post_config_update({?CMD_REPLACE, _RawNewSources}, Sources) ->
OldInitedSources = lookup(), OldInitedSources = lookup(),
lists:foreach(fun ensure_resource_deleted/1, OldInitedSources), lists:foreach(fun ensure_resource_deleted/1, OldInitedSources),
lists:foreach(fun clear_certs/1, OldInitedSources), lists:foreach(fun clear_certs/1, OldInitedSources),
init_sources(Sources). create_sources(Sources).
%% @doc do source move %% @doc do source move
do_move({?CMD_MOVE, Type, ?CMD_MOVE_FRONT}, Sources) -> do_move({?CMD_MOVE, Type, ?CMD_MOVE_FRONT}, Sources) ->
@ -251,20 +258,22 @@ check_dup_types([Source | Sources], Checked) ->
check_dup_types(Sources, [Type | Checked]) check_dup_types(Sources, [Type | Checked])
end. end.
init_sources(Sources) -> create_sources(Sources) ->
{_Enabled, Disabled} = lists:partition(fun(#{enable := Enable}) -> Enable end, Sources), {_Enabled, Disabled} = lists:partition(fun(#{enable := Enable}) -> Enable end, Sources),
case Disabled =/= [] of case Disabled =/= [] of
true -> ?SLOG(info, #{msg => "disabled_sources_ignored", sources => Disabled}); true -> ?SLOG(info, #{msg => "disabled_sources_ignored", sources => Disabled});
false -> ok false -> ok
end, end,
ok = lists:foreach(fun init_metrics/1, Sources), ok = lists:foreach(fun init_metrics/1, Sources),
lists:map(fun init_source/1, Sources). lists:map(fun create_source/1, Sources).
init_source(#{enable := false} = Source) -> create_source(#{type := Type} = Source) ->
Source;
init_source(#{type := Type} = Source) ->
Module = authz_module(Type), Module = authz_module(Type),
Module:init(Source). Module:create(Source).
update_source(Type, OldSource, NewSource) ->
Module = authz_module(Type),
Module:update(maps:merge(OldSource, NewSource)).
init_metrics(Source) -> init_metrics(Source) ->
TypeName = type(Source), TypeName = type(Source),

View File

@ -317,6 +317,7 @@ lookup_from_local_node(Type) ->
end; end;
_ -> _ ->
Metrics = emqx_metrics_worker:get_metrics(authz_metrics, Type), Metrics = emqx_metrics_worker:get_metrics(authz_metrics, Type),
%% for authz file/authz mnesia
{ok, {NodeId, connected, Metrics, #{}}} {ok, {NodeId, connected, Metrics, #{}}}
catch catch
_:Reason -> {error, {NodeId, list_to_binary(io_lib:format("~p", [Reason]))}} _:Reason -> {error, {NodeId, list_to_binary(io_lib:format("~p", [Reason]))}}

View File

@ -29,7 +29,8 @@
%% APIs %% APIs
-export([ -export([
description/0, description/0,
init/1, create/1,
update/1,
destroy/1, destroy/1,
authorize/4 authorize/4
]). ]).
@ -37,7 +38,7 @@
description() -> description() ->
"AuthZ with static rules". "AuthZ with static rules".
init(#{path := Path} = Source) -> create(#{path := Path} = Source) ->
Rules = Rules =
case file:consult(Path) of case file:consult(Path) of
{ok, Terms} -> {ok, Terms} ->
@ -55,6 +56,9 @@ init(#{path := Path} = Source) ->
end, end,
Source#{annotations => #{rules => Rules}}. Source#{annotations => #{rules => Rules}}.
update(#{path := _Path} = Source) ->
create(Source).
destroy(_Source) -> ok. destroy(_Source) -> ok.
authorize(Client, PubSub, Topic, #{annotations := #{rules := Rules}}) -> authorize(Client, PubSub, Topic, #{annotations := #{rules := Rules}}) ->

View File

@ -26,7 +26,8 @@
%% AuthZ Callbacks %% AuthZ Callbacks
-export([ -export([
description/0, description/0,
init/1, create/1,
update/1,
destroy/1, destroy/1,
authorize/4, authorize/4,
parse_url/1 parse_url/1
@ -50,10 +51,18 @@
description() -> description() ->
"AuthZ with http". "AuthZ with http".
init(Config) -> create(Config) ->
NConfig = parse_config(Config), NConfig = parse_config(Config),
{ok, Id} = emqx_authz_utils:create_resource(emqx_connector_http, NConfig), ResourceId = emqx_authn_utils:make_resource_id(?MODULE),
NConfig#{annotations => #{id => Id}}. {ok, _Data} = emqx_authz_utils:create_resource(ResourceId, emqx_connector_http, NConfig),
NConfig#{annotations => #{id => ResourceId}}.
update(Config) ->
NConfig = parse_config(Config),
case emqx_authz_utils:update_resource(emqx_connector_http, NConfig) of
{error, Reason} -> error({load_config_error, Reason});
{ok, Id} -> NConfig#{annotations => #{id => Id}}
end.
destroy(#{annotations := #{id := Id}}) -> destroy(#{annotations := #{id := Id}}) ->
ok = emqx_resource:remove_local(Id). ok = emqx_resource:remove_local(Id).

View File

@ -28,7 +28,8 @@
%% APIs %% APIs
-export([ -export([
description/0, description/0,
init/1, create/1,
update/1,
destroy/1, destroy/1,
authorize/4 authorize/4
]). ]).
@ -46,7 +47,10 @@
description() -> description() ->
"AuthZ with JWT". "AuthZ with JWT".
init(#{acl_claim_name := _AclClaimName} = Source) -> create(#{acl_claim_name := _AclClaimName} = Source) ->
Source.
update(#{acl_claim_name := _AclClaimName} = Source) ->
Source. Source.
destroy(_Source) -> ok. destroy(_Source) -> ok.

View File

@ -46,7 +46,8 @@
%% AuthZ Callbacks %% AuthZ Callbacks
-export([ -export([
description/0, description/0,
init/1, create/1,
update/1,
destroy/1, destroy/1,
authorize/4 authorize/4
]). ]).
@ -88,7 +89,9 @@ mnesia(boot) ->
description() -> description() ->
"AuthZ with Mnesia". "AuthZ with Mnesia".
init(Source) -> Source. create(Source) -> Source.
update(Source) -> Source.
destroy(_Source) -> ok. destroy(_Source) -> ok.

View File

@ -26,7 +26,8 @@
%% AuthZ Callbacks %% AuthZ Callbacks
-export([ -export([
description/0, description/0,
init/1, create/1,
update/1,
destroy/1, destroy/1,
authorize/4 authorize/4
]). ]).
@ -45,15 +46,20 @@
description() -> description() ->
"AuthZ with MongoDB". "AuthZ with MongoDB".
init(#{filter := Filter} = Source) -> create(#{filter := Filter} = Source) ->
{ok, Id} = emqx_authz_utils:create_resource(emqx_connector_mongo, Source), ResourceId = emqx_authz_utils:make_resource_id(?MODULE),
Source#{ {ok, _Data} = emqx_authz_utils:create_resource(ResourceId, emqx_connector_mongo, Source),
annotations => #{id => Id}, FilterTemp = emqx_authz_utils:parse_deep(Filter, ?PLACEHOLDERS),
filter_template => emqx_authz_utils:parse_deep( Source#{annotations => #{id => ResourceId}, filter_template => FilterTemp}.
Filter,
?PLACEHOLDERS update(#{filter := Filter} = Source) ->
) FilterTemp = emqx_authz_utils:parse_deep(Filter, ?PLACEHOLDERS),
}. case emqx_authz_utils:update_resource(emqx_connector_mongo, Source) of
{error, Reason} ->
error({load_config_error, Reason});
{ok, Id} ->
Source#{annotations => #{id => Id}, filter_template => FilterTemp}
end.
destroy(#{annotations := #{id := Id}}) -> destroy(#{annotations := #{id := Id}}) ->
ok = emqx_resource:remove_local(Id). ok = emqx_resource:remove_local(Id).

View File

@ -28,7 +28,8 @@
%% AuthZ Callbacks %% AuthZ Callbacks
-export([ -export([
description/0, description/0,
init/1, create/1,
update/1,
destroy/1, destroy/1,
authorize/4 authorize/4
]). ]).
@ -49,11 +50,22 @@
description() -> description() ->
"AuthZ with Mysql". "AuthZ with Mysql".
init(#{query := SQL} = Source0) -> create(#{query := SQL} = Source0) ->
{PrepareSQL, TmplToken} = emqx_authz_utils:parse_sql(SQL, '?', ?PLACEHOLDERS),
ResourceId = emqx_authz_utils:make_resource_id(?MODULE),
Source = Source0#{prepare_statement => #{?PREPARE_KEY => PrepareSQL}},
{ok, _Data} = emqx_authz_utils:create_resource(ResourceId, emqx_connector_mysql, Source),
Source#{annotations => #{id => ResourceId, tmpl_oken => TmplToken}}.
update(#{query := SQL} = Source0) ->
{PrepareSQL, TmplToken} = emqx_authz_utils:parse_sql(SQL, '?', ?PLACEHOLDERS), {PrepareSQL, TmplToken} = emqx_authz_utils:parse_sql(SQL, '?', ?PLACEHOLDERS),
Source = Source0#{prepare_statement => #{?PREPARE_KEY => PrepareSQL}}, Source = Source0#{prepare_statement => #{?PREPARE_KEY => PrepareSQL}},
{ok, Id} = emqx_authz_utils:create_resource(emqx_connector_mysql, Source), case emqx_authz_utils:update_resource(emqx_connector_mysql, Source) of
Source#{annotations => #{id => Id, tmpl_oken => TmplToken}}. {error, Reason} ->
error({load_config_error, Reason});
{ok, Id} ->
Source#{annotations => #{id => Id, tmpl_oken => TmplToken}}
end.
destroy(#{annotations := #{id := Id}}) -> destroy(#{annotations := #{id := Id}}) ->
ok = emqx_resource:remove_local(Id). ok = emqx_resource:remove_local(Id).

View File

@ -26,7 +26,8 @@
%% AuthZ Callbacks %% AuthZ Callbacks
-export([ -export([
description/0, description/0,
init/1, create/1,
update/1,
destroy/1, destroy/1,
authorize/4 authorize/4
]). ]).
@ -47,27 +48,29 @@
description() -> description() ->
"AuthZ with PostgreSQL". "AuthZ with PostgreSQL".
init(#{query := SQL0} = Source) -> create(#{query := SQL0} = Source) ->
{SQL, PlaceHolders} = emqx_authz_utils:parse_sql( {SQL, PlaceHolders} = emqx_authz_utils:parse_sql(SQL0, '$n', ?PLACEHOLDERS),
SQL0,
'$n',
?PLACEHOLDERS
),
ResourceID = emqx_authz_utils:make_resource_id(emqx_connector_pgsql), ResourceID = emqx_authz_utils:make_resource_id(emqx_connector_pgsql),
{ok, _Data} = emqx_resource:create_local( {ok, _Data} = emqx_authz_utils:create_resource(
ResourceID, ResourceID,
?RESOURCE_GROUP,
emqx_connector_pgsql, emqx_connector_pgsql,
Source#{prepare_statement => #{ResourceID => SQL}}, Source#{prepare_statement => #{ResourceID => SQL}}
#{}
), ),
Source#{ Source#{annotations => #{id => ResourceID, placeholders => PlaceHolders}}.
annotations =>
#{ update(#{query := SQL0, annotations := #{id := ResourceID}} = Source) ->
id => ResourceID, {SQL, PlaceHolders} = emqx_authz_utils:parse_sql(SQL0, '$n', ?PLACEHOLDERS),
placeholders => PlaceHolders case
} emqx_authz_utils:update_resource(
}. emqx_connector_pgsql,
Source#{prepare_statement => #{ResourceID => SQL}}
)
of
{error, Reason} ->
error({load_config_error, Reason});
{ok, Id} ->
Source#{annotations => #{id => Id, placeholders => PlaceHolders}}
end.
destroy(#{annotations := #{id := Id}}) -> destroy(#{annotations := #{id := Id}}) ->
ok = emqx_resource:remove_local(Id). ok = emqx_resource:remove_local(Id).

View File

@ -26,7 +26,8 @@
%% AuthZ Callbacks %% AuthZ Callbacks
-export([ -export([
description/0, description/0,
init/1, create/1,
update/1,
destroy/1, destroy/1,
authorize/4 authorize/4
]). ]).
@ -47,14 +48,22 @@
description() -> description() ->
"AuthZ with Redis". "AuthZ with Redis".
init(#{cmd := CmdStr} = Source) -> create(#{cmd := CmdStr} = Source) ->
Cmd = tokens(CmdStr),
ResourceId = emqx_authz_utils:make_resource_id(?MODULE),
CmdTemplate = emqx_authz_utils:parse_deep(Cmd, ?PLACEHOLDERS),
{ok, _Data} = emqx_authz_utils:create_resource(ResourceId, emqx_connector_redis, Source),
Source#{annotations => #{id => ResourceId}, cmd_template => CmdTemplate}.
update(#{cmd := CmdStr} = Source) ->
Cmd = tokens(CmdStr), Cmd = tokens(CmdStr),
CmdTemplate = emqx_authz_utils:parse_deep(Cmd, ?PLACEHOLDERS), CmdTemplate = emqx_authz_utils:parse_deep(Cmd, ?PLACEHOLDERS),
{ok, Id} = emqx_authz_utils:create_resource(emqx_connector_redis, Source), case emqx_authz_utils:update_resource(emqx_connector_redis, Source) of
Source#{ {error, Reason} ->
annotations => #{id => Id}, error({load_config_error, Reason});
cmd_template => CmdTemplate {ok, Id} ->
}. Source#{annotations => #{id => Id}, cmd_template => CmdTemplate}
end.
destroy(#{annotations := #{id := Id}}) -> destroy(#{annotations := #{id := Id}}) ->
ok = emqx_resource:remove_local(Id). ok = emqx_resource:remove_local(Id).

View File

@ -23,6 +23,8 @@
cleanup_resources/0, cleanup_resources/0,
make_resource_id/1, make_resource_id/1,
create_resource/2, create_resource/2,
create_resource/3,
update_resource/2,
update_config/2, update_config/2,
parse_deep/2, parse_deep/2,
parse_str/2, parse_str/2,
@ -37,15 +39,37 @@
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
create_resource(Module, Config) -> create_resource(Module, Config) ->
ResourceID = make_resource_id(Module), ResourceId = make_resource_id(Module),
create_resource(ResourceId, Module, Config).
create_resource(ResourceId, Module, Config) ->
{ok, _Data} = emqx_resource:create_local( {ok, _Data} = emqx_resource:create_local(
ResourceID, ResourceId,
?RESOURCE_GROUP, ?RESOURCE_GROUP,
Module, Module,
Config, Config,
#{} #{}
), ).
{ok, ResourceID}.
update_resource(Module, #{annotations := #{id := ResourceId}} = Source) ->
Result =
case
emqx_resource:recreate_local(
ResourceId,
Module,
Source
)
of
{ok, _} -> {ok, ResourceId};
{error, Reason} -> {error, Reason}
end,
case Source of
#{enable := true} ->
Result;
#{enable := false} ->
ok = emqx_resource:stop(ResourceId),
Result
end.
cleanup_resources() -> cleanup_resources() ->
lists:foreach( lists:foreach(