Merge pull request #7903 from JimMoen/refactor-authn-authz-resource

refactor authn authz resource
This commit is contained in:
JimMoen 2022-05-12 15:57:41 +08:00 committed by GitHub
commit 41e5bda914
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 367 additions and 236 deletions

View File

@ -20,6 +20,8 @@
-include_lib("emqx_authn.hrl").
-export([
create_resource/3,
update_resource/3,
check_password_from_selected_map/3,
parse_deep/1,
parse_str/1,
@ -47,6 +49,27 @@
%% 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) ->
{error, bad_username_or_password};
check_password_from_selected_map(

View File

@ -158,45 +158,24 @@ refs() ->
create(_AuthenticatorID, Config) ->
create(Config).
create(
#{
method := Method,
url := RawUrl,
headers := Headers,
request_timeout := RequestTimeout
} = Config
) ->
{BaseUrl0, Path, Query} = parse_url(RawUrl),
{ok, BaseUrl} = emqx_http_lib:uri_parse(BaseUrl0),
create(Config0) ->
ResourceId = emqx_authn_utils:make_resource_id(?MODULE),
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,
resource_id => ResourceId
},
{ok, _Data} = emqx_resource:create_local(
{Config, State} = parse_config(Config0),
{ok, _Data} = emqx_authn_utils:create_resource(
ResourceId,
?RESOURCE_GROUP,
emqx_connector_http,
Config#{
base_url => BaseUrl,
pool_type => random
},
#{}
Config
),
{ok, State}.
{ok, State#{resource_id => ResourceId}}.
update(Config, State) ->
{ok, NewState} = create(Config),
ok = destroy(State),
{ok, NewState}.
update(Config0, #{resource_id := ResourceId} = _State) ->
{Config, NState} = parse_config(Config0),
case emqx_authn_utils:update_resource(emqx_connector_http, Config, ResourceId) of
{error, Reason} ->
error({load_config_error, Reason});
{ok, _} ->
{ok, NState#{resource_id => ResourceId}}
end.
authenticate(#{auth_method := _}, _) ->
ignore;
@ -325,6 +304,29 @@ parse_url(Url) ->
throw({invalid_url, Url})
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, #{
method := Method,
headers := Headers0,

View File

@ -126,39 +126,28 @@ refs() ->
create(_AuthenticatorID, Config) ->
create(Config).
create(#{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
],
create(Config0) ->
ResourceId = emqx_authn_utils:make_resource_id(?MODULE),
{Config, State} = parse_config(Config0),
{ok, _Data} = emqx_authn_utils:create_resource(
ResourceId,
emqx_connector_mongo,
Config
),
#{password_hash_algorithm := Algorithm} = State,
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}.
{ok, State#{resource_id => ResourceId}}.
update(Config, State) ->
{ok, NewState} = create(Config),
ok = destroy(State),
{ok, NewState}.
update(Config0, #{resource_id := ResourceId} = _State) ->
{Config, NState} = parse_config(Config0),
case emqx_authn_utils:update_resource(emqx_connector_mongo, 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;
@ -201,14 +190,26 @@ authenticate(
end
end.
destroy(#{resource_id := ResourceId}) ->
_ = emqx_resource:remove_local(ResourceId),
ok.
%%------------------------------------------------------------------------------
%% 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) ->
{error, bad_username_or_password};
check_password(

View File

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

View File

@ -96,51 +96,33 @@ refs() ->
create(_AuthenticatorID, Config) ->
create(Config).
create(
#{
cmd := Cmd,
password_hash_algorithm := Algorithm
} = Config
) ->
ok = emqx_authn_password_hashing:init(Algorithm),
try
NCmd = parse_cmd(Cmd),
ok = emqx_authn_utils:ensure_apps_started(Algorithm),
State = maps:with(
[password_hash_algorithm, salt_position],
Config
),
ResourceId = emqx_authn_utils:make_resource_id(?MODULE),
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}}
create(Config0) ->
ResourceId = emqx_authn_utils:make_resource_id(?MODULE),
case parse_config(Config0) of
{error, _} = Res ->
Res;
{Config, State} ->
{ok, _Data} = emqx_authn_utils:create_resource(
ResourceId,
emqx_connector_redis,
Config
),
{ok, State#{resource_id => ResourceId}}
end.
update(Config, State) ->
case create(Config) of
{ok, NewState} ->
ok = destroy(State),
{ok, NewState};
update(Config0, #{resource_id := ResourceId} = _State) ->
{Config, NState} = parse_config(Config0),
case emqx_authn_utils:update_resource(emqx_connector_redis, Config, ResourceId) of
{error, Reason} ->
{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(
@ -190,14 +172,31 @@ authenticate(
ignore
end.
destroy(#{resource_id := ResourceId}) ->
_ = emqx_resource:remove_local(ResourceId),
ok.
%%------------------------------------------------------------------------------
%% 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
parse_cmd(Cmd) ->
case string:tokens(Cmd, " ") of

View File

@ -107,6 +107,9 @@ t_create_invalid(_Config) ->
},
AuthConfig#{
cmd => <<"HMGET mqtt_user:${username} salt is_superuser">>
},
AuthConfig#{
cmd => <<"HGETALL mqtt_user:${username} salt is_superuser">>
}
],
lists:foreach(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -28,7 +28,8 @@
%% AuthZ Callbacks
-export([
description/0,
init/1,
create/1,
update/1,
destroy/1,
authorize/4
]).
@ -49,11 +50,22 @@
description() ->
"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),
Source = Source0#{prepare_statement => #{?PREPARE_KEY => PrepareSQL}},
{ok, Id} = emqx_authz_utils:create_resource(emqx_connector_mysql, Source),
Source#{annotations => #{id => Id, tmpl_oken => TmplToken}}.
case emqx_authz_utils:update_resource(emqx_connector_mysql, Source) of
{error, Reason} ->
error({load_config_error, Reason});
{ok, Id} ->
Source#{annotations => #{id => Id, tmpl_oken => TmplToken}}
end.
destroy(#{annotations := #{id := Id}}) ->
ok = emqx_resource:remove_local(Id).

View File

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

View File

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

View File

@ -23,6 +23,8 @@
cleanup_resources/0,
make_resource_id/1,
create_resource/2,
create_resource/3,
update_resource/2,
update_config/2,
parse_deep/2,
parse_str/2,
@ -37,15 +39,37 @@
%%------------------------------------------------------------------------------
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(
ResourceID,
ResourceId,
?RESOURCE_GROUP,
Module,
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() ->
lists:foreach(

View File

@ -68,7 +68,7 @@ init_per_suite(Config) ->
emqx_config:erase(gateway),
init_gateway_conf(),
meck:new(emqx_authz_file, [non_strict, passthrough, no_history, no_link]),
meck:expect(emqx_authz_file, init, fun(S) -> S end),
meck:expect(emqx_authz_file, create, fun(S) -> S end),
emqx_mgmt_api_test_util:init_suite([emqx_conf, emqx_authz, emqx_gateway]),
application:ensure_all_started(cowboy),
emqx_gateway_auth_ct:start(),

View File

@ -56,7 +56,9 @@
create_dry_run/2,
create_dry_run_local/2,
%% this will do create_dry_run, stop the old instance and start a new one
recreate/3,
recreate/4,
recreate_local/3,
recreate_local/4,
%% remove the config and stop the instance
remove/1,
@ -200,11 +202,21 @@ create_dry_run(ResourceType, Config) ->
create_dry_run_local(ResourceType, Config) ->
emqx_resource_manager:create_dry_run(ResourceType, Config).
-spec recreate(instance_id(), resource_type(), resource_config()) ->
{ok, resource_data()} | {error, Reason :: term()}.
recreate(InstId, ResourceType, Config) ->
recreate(InstId, ResourceType, Config, #{}).
-spec recreate(instance_id(), resource_type(), resource_config(), create_opts()) ->
{ok, resource_data()} | {error, Reason :: term()}.
recreate(InstId, ResourceType, Config, Opts) ->
wrap_rpc(emqx_resource_proto_v1:recreate(InstId, ResourceType, Config, Opts)).
-spec recreate_local(instance_id(), resource_type(), resource_config()) ->
{ok, resource_data()} | {error, Reason :: term()}.
recreate_local(InstId, ResourceType, Config) ->
recreate_local(InstId, ResourceType, Config, #{}).
-spec recreate_local(instance_id(), resource_type(), resource_config(), create_opts()) ->
{ok, resource_data()} | {error, Reason :: term()}.
recreate_local(InstId, ResourceType, Config, Opts) ->