Просмотр исходного кода

feat(authn): authn mnesia rows fuzzy searching by `clientid` or `username`

JimMoen 4 лет назад
Родитель
Сommit
593e1a3efb

+ 4 - 4
apps/emqx/src/emqx_authentication.erl

@@ -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) ->

+ 12 - 10
apps/emqx_authn/src/emqx_authn_api.erl

@@ -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,

+ 51 - 4
apps/emqx_authn/src/simple_authn/emqx_authn_mnesia.erl

@@ -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 ->