Merge pull request #7903 from JimMoen/refactor-authn-authz-resource
refactor authn authz resource
This commit is contained in:
commit
41e5bda914
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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}.
|
||||
|
|
|
@ -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}.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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]))}}
|
||||
|
|
|
@ -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}}) ->
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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) ->
|
||||
|
|
Loading…
Reference in New Issue