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}).
|
||||
|
||||
-spec list_users(chain_name(), authenticator_id(), map()) -> {ok, [user_info()]} | {error, term()}.
|
||||
list_users(ChainName, AuthenticatorID, Params) ->
|
||||
call({list_users, ChainName, AuthenticatorID, Params}).
|
||||
list_users(ChainName, AuthenticatorID, FuzzyParams) ->
|
||||
call({list_users, ChainName, AuthenticatorID, FuzzyParams}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% 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(Reply, State);
|
||||
|
||||
handle_call({list_users, ChainName, AuthenticatorID, PageParams}, _From, State) ->
|
||||
Reply = call_authenticator(ChainName, AuthenticatorID, list_users, [PageParams]),
|
||||
handle_call({list_users, ChainName, AuthenticatorID, FuzzyParams}, _From, State) ->
|
||||
Reply = call_authenticator(ChainName, AuthenticatorID, list_users, [FuzzyParams]),
|
||||
reply(Reply, State);
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
|
|
|
@ -380,7 +380,13 @@ schema("/authentication/:id/users") ->
|
|||
parameters => [
|
||||
param_auth_id(),
|
||||
{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 => #{
|
||||
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}) ->
|
||||
add_user(?GLOBAL, AuthenticatorID, UserInfo);
|
||||
authenticator_users(get, #{bindings := #{id := AuthenticatorID}, query_string := PageParams}) ->
|
||||
list_users(?GLOBAL, AuthenticatorID, PageParams).
|
||||
authenticator_users(get, #{bindings := #{id := AuthenticatorID}, query_string := QueryString}) ->
|
||||
list_users(?GLOBAL, AuthenticatorID, QueryString).
|
||||
|
||||
authenticator_user(put, #{bindings := #{id := AuthenticatorID,
|
||||
user_id := UserID}, body := UserInfo}) ->
|
||||
|
@ -840,13 +846,9 @@ delete_user(ChainName, AuthenticatorID, UserID) ->
|
|||
serialize_error({user_error, Reason})
|
||||
end.
|
||||
|
||||
list_users(ChainName, AuthenticatorID, PageParams) ->
|
||||
case emqx_authentication:list_users(ChainName, AuthenticatorID, PageParams) of
|
||||
{ok, Users} ->
|
||||
{200, Users};
|
||||
{error, Reason} ->
|
||||
serialize_error(Reason)
|
||||
end.
|
||||
list_users(ChainName, AuthenticatorID, QueryString) ->
|
||||
Response = emqx_authentication:list_users(ChainName, AuthenticatorID, QueryString),
|
||||
emqx_mgmt_util:generate_response(Response).
|
||||
|
||||
update_config(Path, ConfigRequest) ->
|
||||
emqx_conf:update(Path, ConfigRequest, #{rawconf_with_defaults => true,
|
||||
|
|
|
@ -43,7 +43,9 @@
|
|||
, 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_group() :: binary().
|
||||
|
@ -63,7 +65,10 @@
|
|||
-boot_mnesia({mnesia, [boot]}).
|
||||
|
||||
-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
|
||||
|
@ -219,8 +224,42 @@ lookup_user(UserID, #{user_group := UserGroup}) ->
|
|||
{error, not_found}
|
||||
end.
|
||||
|
||||
list_users(PageParams, #{user_group := UserGroup}) ->
|
||||
{ok, emqx_mgmt_api:paginate(?TAB, group_match_spec(UserGroup), PageParams, ?FORMAT_FUN)}.
|
||||
list_users(QueryString, #{user_group := UserGroup}) ->
|
||||
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
|
||||
|
@ -352,6 +391,14 @@ to_binary(L) when is_list(L) ->
|
|||
format_user_info(#user_info{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) ->
|
||||
ets:fun2ms(
|
||||
fun(#user_info{user_id = {Group, _}} = User) when Group =:= UserGroup ->
|
||||
|
|
Loading…
Reference in New Issue