feat(authn): authn mnesia rows fuzzy searching by `clientid` or `username`
This commit is contained in:
parent
16ec8fe289
commit
593e1a3efb
|
@ -378,8 +378,8 @@ lookup_user(ChainName, AuthenticatorID, UserID) ->
|
||||||
call({lookup_user, ChainName, AuthenticatorID, UserID}).
|
call({lookup_user, ChainName, AuthenticatorID, UserID}).
|
||||||
|
|
||||||
-spec list_users(chain_name(), authenticator_id(), map()) -> {ok, [user_info()]} | {error, term()}.
|
-spec list_users(chain_name(), authenticator_id(), map()) -> {ok, [user_info()]} | {error, term()}.
|
||||||
list_users(ChainName, AuthenticatorID, Params) ->
|
list_users(ChainName, AuthenticatorID, FuzzyParams) ->
|
||||||
call({list_users, ChainName, AuthenticatorID, Params}).
|
call({list_users, ChainName, AuthenticatorID, FuzzyParams}).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
|
@ -476,8 +476,8 @@ handle_call({lookup_user, ChainName, AuthenticatorID, UserID}, _From, State) ->
|
||||||
Reply = call_authenticator(ChainName, AuthenticatorID, lookup_user, [UserID]),
|
Reply = call_authenticator(ChainName, AuthenticatorID, lookup_user, [UserID]),
|
||||||
reply(Reply, State);
|
reply(Reply, State);
|
||||||
|
|
||||||
handle_call({list_users, ChainName, AuthenticatorID, PageParams}, _From, State) ->
|
handle_call({list_users, ChainName, AuthenticatorID, FuzzyParams}, _From, State) ->
|
||||||
Reply = call_authenticator(ChainName, AuthenticatorID, list_users, [PageParams]),
|
Reply = call_authenticator(ChainName, AuthenticatorID, list_users, [FuzzyParams]),
|
||||||
reply(Reply, State);
|
reply(Reply, State);
|
||||||
|
|
||||||
handle_call(Req, _From, State) ->
|
handle_call(Req, _From, State) ->
|
||||||
|
|
|
@ -380,7 +380,13 @@ schema("/authentication/:id/users") ->
|
||||||
parameters => [
|
parameters => [
|
||||||
param_auth_id(),
|
param_auth_id(),
|
||||||
{page, mk(integer(), #{in => query, desc => <<"Page Index">>, required => false})},
|
{page, mk(integer(), #{in => query, desc => <<"Page Index">>, required => false})},
|
||||||
{limit, mk(integer(), #{in => query, desc => <<"Page Limit">>, required => false})}
|
{limit, mk(integer(), #{in => query, desc => <<"Page Limit">>, required => false})},
|
||||||
|
{like_username, mk(binary(), #{ in => query
|
||||||
|
, desc => <<"Fuzzy search username">>
|
||||||
|
, required => false})},
|
||||||
|
{like_clientid, mk(binary(), #{ in => query
|
||||||
|
, desc => <<"Fuzzy search clientid">>
|
||||||
|
, required => false})}
|
||||||
],
|
],
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => emqx_dashboard_swagger:schema_with_example(
|
200 => emqx_dashboard_swagger:schema_with_example(
|
||||||
|
@ -638,8 +644,8 @@ listener_authenticator_import_users(post, #{bindings := #{listener_id := _, id :
|
||||||
|
|
||||||
authenticator_users(post, #{bindings := #{id := AuthenticatorID}, body := UserInfo}) ->
|
authenticator_users(post, #{bindings := #{id := AuthenticatorID}, body := UserInfo}) ->
|
||||||
add_user(?GLOBAL, AuthenticatorID, UserInfo);
|
add_user(?GLOBAL, AuthenticatorID, UserInfo);
|
||||||
authenticator_users(get, #{bindings := #{id := AuthenticatorID}, query_string := PageParams}) ->
|
authenticator_users(get, #{bindings := #{id := AuthenticatorID}, query_string := QueryString}) ->
|
||||||
list_users(?GLOBAL, AuthenticatorID, PageParams).
|
list_users(?GLOBAL, AuthenticatorID, QueryString).
|
||||||
|
|
||||||
authenticator_user(put, #{bindings := #{id := AuthenticatorID,
|
authenticator_user(put, #{bindings := #{id := AuthenticatorID,
|
||||||
user_id := UserID}, body := UserInfo}) ->
|
user_id := UserID}, body := UserInfo}) ->
|
||||||
|
@ -840,13 +846,9 @@ delete_user(ChainName, AuthenticatorID, UserID) ->
|
||||||
serialize_error({user_error, Reason})
|
serialize_error({user_error, Reason})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
list_users(ChainName, AuthenticatorID, PageParams) ->
|
list_users(ChainName, AuthenticatorID, QueryString) ->
|
||||||
case emqx_authentication:list_users(ChainName, AuthenticatorID, PageParams) of
|
Response = emqx_authentication:list_users(ChainName, AuthenticatorID, QueryString),
|
||||||
{ok, Users} ->
|
emqx_mgmt_util:generate_response(Response).
|
||||||
{200, Users};
|
|
||||||
{error, Reason} ->
|
|
||||||
serialize_error(Reason)
|
|
||||||
end.
|
|
||||||
|
|
||||||
update_config(Path, ConfigRequest) ->
|
update_config(Path, ConfigRequest) ->
|
||||||
emqx_conf:update(Path, ConfigRequest, #{rawconf_with_defaults => true,
|
emqx_conf:update(Path, ConfigRequest, #{rawconf_with_defaults => true,
|
||||||
|
|
|
@ -43,7 +43,9 @@
|
||||||
, list_users/2
|
, list_users/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([format_user_info/1]).
|
-export([ query/4
|
||||||
|
, format_user_info/1
|
||||||
|
, group_match_spec/1]).
|
||||||
|
|
||||||
-type user_id_type() :: clientid | username.
|
-type user_id_type() :: clientid | username.
|
||||||
-type user_group() :: binary().
|
-type user_group() :: binary().
|
||||||
|
@ -63,7 +65,10 @@
|
||||||
-boot_mnesia({mnesia, [boot]}).
|
-boot_mnesia({mnesia, [boot]}).
|
||||||
|
|
||||||
-define(TAB, ?MODULE).
|
-define(TAB, ?MODULE).
|
||||||
-define(FORMAT_FUN, {?MODULE, format_user_info}).
|
-define(AUTHN_QSCHEMA, [ {<<"like_username">>, binary}
|
||||||
|
, {<<"like_clientid">>, binary}
|
||||||
|
, {<<"user_group">>, binary}]).
|
||||||
|
-define(QUERY_FUN, {?MODULE, query}).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Mnesia bootstrap
|
%% Mnesia bootstrap
|
||||||
|
@ -219,8 +224,42 @@ lookup_user(UserID, #{user_group := UserGroup}) ->
|
||||||
{error, not_found}
|
{error, not_found}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
list_users(PageParams, #{user_group := UserGroup}) ->
|
list_users(QueryString, #{user_group := UserGroup}) ->
|
||||||
{ok, emqx_mgmt_api:paginate(?TAB, group_match_spec(UserGroup), PageParams, ?FORMAT_FUN)}.
|
NQueryString = QueryString#{<<"user_group">> => UserGroup},
|
||||||
|
emqx_mgmt_api:node_query(node(), NQueryString, ?TAB, ?AUTHN_QSCHEMA, ?QUERY_FUN).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Query Functions
|
||||||
|
|
||||||
|
query(Tab, {QString, []}, Continuation, Limit) ->
|
||||||
|
Ms = ms_from_qstring(QString),
|
||||||
|
emqx_mgmt_api:select_table_with_count(Tab, Ms, Continuation, Limit,
|
||||||
|
fun format_user_info/1);
|
||||||
|
|
||||||
|
query(Tab, {QString, FuzzyQString}, Continuation, Limit) ->
|
||||||
|
Ms = ms_from_qstring(QString),
|
||||||
|
FuzzyFilterFun = fuzzy_filter_fun(FuzzyQString),
|
||||||
|
emqx_mgmt_api:select_table_with_count(Tab, {Ms, FuzzyFilterFun}, Continuation, Limit,
|
||||||
|
fun format_user_info/1).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Match funcs
|
||||||
|
|
||||||
|
%% Fuzzy username funcs
|
||||||
|
fuzzy_filter_fun(Fuzzy) ->
|
||||||
|
fun(MsRaws) when is_list(MsRaws) ->
|
||||||
|
lists:filter( fun(E) -> run_fuzzy_filter(E, Fuzzy) end
|
||||||
|
, MsRaws)
|
||||||
|
end.
|
||||||
|
|
||||||
|
run_fuzzy_filter(_, []) ->
|
||||||
|
true;
|
||||||
|
run_fuzzy_filter( E = #user_info{user_id = {_, UserID}}
|
||||||
|
, [{username, like, UsernameSubStr} | Fuzzy]) ->
|
||||||
|
binary:match(UserID, UsernameSubStr) /= nomatch andalso run_fuzzy_filter(E, Fuzzy);
|
||||||
|
run_fuzzy_filter( E = #user_info{user_id = {_, UserID}}
|
||||||
|
, [{clientid, like, ClientIDSubStr} | Fuzzy]) ->
|
||||||
|
binary:match(UserID, ClientIDSubStr) /= nomatch andalso run_fuzzy_filter(E, Fuzzy).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
|
@ -352,6 +391,14 @@ to_binary(L) when is_list(L) ->
|
||||||
format_user_info(#user_info{user_id = {_, UserID}, is_superuser = IsSuperuser}) ->
|
format_user_info(#user_info{user_id = {_, UserID}, is_superuser = IsSuperuser}) ->
|
||||||
#{user_id => UserID, is_superuser => IsSuperuser}.
|
#{user_id => UserID, is_superuser => IsSuperuser}.
|
||||||
|
|
||||||
|
ms_from_qstring(QString) ->
|
||||||
|
[Ms] = lists:foldl(fun({user_group, '=:=', UserGroup}, AccIn) ->
|
||||||
|
[group_match_spec(UserGroup) | AccIn];
|
||||||
|
(_, AccIn) ->
|
||||||
|
AccIn
|
||||||
|
end, [], QString),
|
||||||
|
Ms.
|
||||||
|
|
||||||
group_match_spec(UserGroup) ->
|
group_match_spec(UserGroup) ->
|
||||||
ets:fun2ms(
|
ets:fun2ms(
|
||||||
fun(#user_info{user_id = {Group, _}} = User) when Group =:= UserGroup ->
|
fun(#user_info{user_id = {Group, _}} = User) when Group =:= UserGroup ->
|
||||||
|
|
Loading…
Reference in New Issue