feat: mnesia auth/acl support multiple condition queries
This commit is contained in:
parent
810ca65011
commit
73faf08059
|
@ -96,18 +96,24 @@
|
|||
, delete/2
|
||||
]).
|
||||
|
||||
-define(CLIENTID_SCHEMA, [{<<"clientid">>, binary}, {<<"_like_clientid">>, binary}] ++ ?COMMON_SCHEMA).
|
||||
-define(USERNAME_SCHEMA, [{<<"username">>, binary}, {<<"_like_username">>, binary}] ++ ?COMMON_SCHEMA).
|
||||
-define(COMMON_SCHEMA, [{<<"topic">>, binary}, {<<"action">>, atom}, {<<"access">>, atom}]).
|
||||
|
||||
list_clientid(_Bindings, Params) ->
|
||||
Table = emqx_acl_mnesia_db:login_acl_table(clientid),
|
||||
return({ok, emqx_auth_mnesia_api:paginate_qh(Table, count(Table), Params, fun emqx_acl_mnesia_db:comparing/2, fun format/1)}).
|
||||
{_, Params1 = {_Qs, _Fuzzy}} = emqx_mgmt_api:params2qs(Params, ?CLIENTID_SCHEMA),
|
||||
Table = emqx_acl_mnesia_db:login_acl_table(clientid, Params1),
|
||||
return({ok, paginate_qh(Table, count(Table), Params, fun emqx_acl_mnesia_db:comparing/2, fun format/1)}).
|
||||
|
||||
list_username(_Bindings, Params) ->
|
||||
Table = emqx_acl_mnesia_db:login_acl_table(username),
|
||||
return({ok, emqx_auth_mnesia_api:paginate_qh(Table, count(Table), Params, fun emqx_acl_mnesia_db:comparing/2, fun format/1)}).
|
||||
{_, Params1 = {_Qs, _Fuzzy}} = emqx_mgmt_api:params2qs(Params, ?USERNAME_SCHEMA),
|
||||
Table = emqx_acl_mnesia_db:login_acl_table(username, Params1),
|
||||
return({ok, paginate_qh(Table, count(Table), Params, fun emqx_acl_mnesia_db:comparing/2, fun format/1)}).
|
||||
|
||||
list_all(_Bindings, Params) ->
|
||||
Table = emqx_acl_mnesia_db:login_acl_table(all),
|
||||
return({ok, emqx_auth_mnesia_api:paginate_qh(Table, count(Table), Params, fun emqx_acl_mnesia_db:comparing/2, fun format/1)}).
|
||||
|
||||
{_, Params1 = {_Qs, _Fuzzy}} = emqx_mgmt_api:params2qs(Params, ?COMMON_SCHEMA),
|
||||
Table = emqx_acl_mnesia_db:login_acl_table(all, Params1),
|
||||
return({ok, paginate_qh(Table, count(Table), Params, fun emqx_acl_mnesia_db:comparing/2, fun format/1)}).
|
||||
|
||||
lookup(#{clientid := Clientid}, _Params) ->
|
||||
return({ok, format(emqx_acl_mnesia_db:lookup_acl({clientid, urldecode(Clientid)}))});
|
||||
|
@ -170,7 +176,11 @@ delete(#{topic := Topic}, _) ->
|
|||
%%------------------------------------------------------------------------------
|
||||
|
||||
count(QH) ->
|
||||
qlc:fold(fun(_, Count) -> Count + 1 end, 0, QH).
|
||||
Count = qlc:fold(fun(_, Sum) -> Sum + 1 end, 0, QH),
|
||||
case is_integer(Count) of
|
||||
true -> Count;
|
||||
false -> 0
|
||||
end.
|
||||
|
||||
format({{clientid, Clientid}, Topic, Action, Access, _CreatedAt}) ->
|
||||
#{clientid => Clientid, topic => Topic, action => Action, access => Access};
|
||||
|
@ -222,3 +232,27 @@ format_msg(Message) when is_tuple(Message) ->
|
|||
|
||||
urldecode(S) ->
|
||||
emqx_http_lib:uri_decode(S).
|
||||
|
||||
paginate_qh(Qh, Count, Params, ComparingFun, RowFun) ->
|
||||
Page = page(Params),
|
||||
Limit = limit(Params),
|
||||
Cursor = qlc:cursor(Qh),
|
||||
case Page > 1 of
|
||||
true ->
|
||||
_ = qlc:next_answers(Cursor, (Page - 1) * Limit),
|
||||
ok;
|
||||
false -> ok
|
||||
end,
|
||||
Rows = qlc:next_answers(Cursor, Limit),
|
||||
qlc:delete_cursor(Cursor),
|
||||
#{meta => #{page => Page, limit => Limit, count => Count},
|
||||
data => [RowFun(Row) || Row <- lists:sort(ComparingFun, Rows)]}.
|
||||
|
||||
page(Params) ->
|
||||
binary_to_integer(proplists:get_value(<<"_page">>, Params, <<"1">>)).
|
||||
|
||||
limit(Params) ->
|
||||
case proplists:get_value(<<"_limit">>, Params) of
|
||||
undefined -> 50;
|
||||
Size -> binary_to_integer(Size)
|
||||
end.
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
, remove_acl/2
|
||||
, merge_acl_records/3
|
||||
, login_acl_table/1
|
||||
, login_acl_table/2
|
||||
, is_migration_started/0
|
||||
]).
|
||||
|
||||
|
@ -124,7 +125,7 @@ all_acls_export() ->
|
|||
|
||||
{atomic, Records} = mnesia:transaction(
|
||||
fun() ->
|
||||
QH = acl_table(MatchSpecNew, MatchSpecOld, fun mnesia:table/2, fun lookup_mnesia/2),
|
||||
QH = acl_table(MatchSpecNew, MatchSpecOld, {#{}, #{}}, fun mnesia:table/2, fun lookup_mnesia/2),
|
||||
qlc:eval(QH)
|
||||
end),
|
||||
Records.
|
||||
|
@ -132,9 +133,15 @@ all_acls_export() ->
|
|||
%% @doc QLC table of logins matching spec
|
||||
-spec(login_acl_table(acl_target_type()) -> qlc:query_handle()).
|
||||
login_acl_table(AclTargetType) ->
|
||||
MatchSpecNew = login_match_spec_new(AclTargetType),
|
||||
MatchSpecOld = login_match_spec_old(AclTargetType),
|
||||
acl_table(MatchSpecNew, MatchSpecOld, fun ets:table/2, fun lookup_ets/2).
|
||||
login_acl_table(AclTargetType, {[], []}).
|
||||
|
||||
login_acl_table(AclTargetType, {Qs, Fuzzy}) ->
|
||||
ToMap = fun({Type, Symbol, Val}, Acc) -> Acc#{{Type, Symbol} => Val} end,
|
||||
Qs1 = lists:foldl(ToMap, #{}, Qs),
|
||||
Fuzzy1 = lists:foldl(ToMap, #{}, Fuzzy),
|
||||
MatchSpecNew = login_match_spec_new(AclTargetType, Qs1),
|
||||
MatchSpecOld = login_match_spec_old(AclTargetType, Qs1),
|
||||
acl_table(MatchSpecNew, MatchSpecOld, {Qs1, Fuzzy1}, fun ets:table/2, fun lookup_ets/2).
|
||||
|
||||
%% @doc Combine old `emqx_acl` ACL records with a new `emqx_acl2` ACL record for a given login
|
||||
-spec(merge_acl_records(acl_target(), [#?ACL_TABLE{}], [#?ACL_TABLE2{}]) -> #?ACL_TABLE2{}).
|
||||
|
@ -223,27 +230,39 @@ comparing({_, _, _, _, CreatedAt1},
|
|||
{_, _, _, _, CreatedAt2}) ->
|
||||
CreatedAt1 >= CreatedAt2.
|
||||
|
||||
login_match_spec_old(all) ->
|
||||
login_match_spec_old(Type) -> login_match_spec_old(Type, #{}).
|
||||
|
||||
login_match_spec_old(all, _) ->
|
||||
ets:fun2ms(fun(#?ACL_TABLE{filter = {all, _}} = Record) ->
|
||||
Record
|
||||
end);
|
||||
|
||||
login_match_spec_old(Type) when (Type =:= username) or (Type =:= clientid) ->
|
||||
ets:fun2ms(fun(#?ACL_TABLE{filter = {{RecordType, _}, _}} = Record)
|
||||
when RecordType =:= Type -> Record
|
||||
end).
|
||||
login_match_spec_old(Type, Params) when (Type =:= username) orelse (Type =:= clientid) ->
|
||||
case maps:get({Type, '=:='}, Params, undefined) of
|
||||
undefined ->
|
||||
ets:fun2ms(fun(#?ACL_TABLE{filter = {{RType, _}, _}} = Rec) when RType =:= Type -> Rec end);
|
||||
Val ->
|
||||
ets:fun2ms(fun(#?ACL_TABLE{filter = {{RType, RVal}, _}} = Rec)
|
||||
when RType =:= Type andalso RVal =:= Val -> Rec end)
|
||||
end.
|
||||
|
||||
login_match_spec_new(all) ->
|
||||
login_match_spec_new(Type) -> login_match_spec_new(Type, #{}).
|
||||
|
||||
login_match_spec_new(all, _) ->
|
||||
ets:fun2ms(fun(#?ACL_TABLE2{who = all} = Record) ->
|
||||
Record
|
||||
end);
|
||||
|
||||
login_match_spec_new(Type) when (Type =:= username) or (Type =:= clientid) ->
|
||||
ets:fun2ms(fun(#?ACL_TABLE2{who = {RecordType, _}} = Record)
|
||||
when RecordType =:= Type -> Record
|
||||
end).
|
||||
login_match_spec_new(Type, Params) when (Type =:= username) orelse (Type =:= clientid) ->
|
||||
case maps:get({Type, '=:='}, Params, undefined) of
|
||||
undefined ->
|
||||
ets:fun2ms(fun(#?ACL_TABLE2{who = {RType, _}} = Rec) when RType =:= Type -> Rec end);
|
||||
Val ->
|
||||
ets:fun2ms(fun(#?ACL_TABLE2{who = {RType, RVal}} = Rec)
|
||||
when RType =:= Type andalso RVal =:= Val -> Rec end)
|
||||
end.
|
||||
|
||||
acl_table(MatchSpecNew, MatchSpecOld, TableFun, LookupFun) ->
|
||||
acl_table(MatchSpecNew, MatchSpecOld, Params, TableFun, LookupFun) ->
|
||||
TraverseFun =
|
||||
fun() ->
|
||||
CursorNew =
|
||||
|
@ -252,7 +271,7 @@ acl_table(MatchSpecNew, MatchSpecOld, TableFun, LookupFun) ->
|
|||
CursorOld =
|
||||
qlc:cursor(
|
||||
TableFun(?ACL_TABLE, [{traverse, {select, MatchSpecOld}}])),
|
||||
traverse_new(CursorNew, CursorOld, #{}, LookupFun)
|
||||
traverse_new(CursorNew, CursorOld, Params, #{}, LookupFun)
|
||||
end,
|
||||
|
||||
qlc:table(TraverseFun, []).
|
||||
|
@ -265,12 +284,12 @@ acl_table(MatchSpecNew, MatchSpecOld, TableFun, LookupFun) ->
|
|||
% After migration, number of such logins is zero, so traversing starts working in
|
||||
% constant memory.
|
||||
|
||||
traverse_new(CursorNew, CursorOld, FoundKeys, LookupFun) ->
|
||||
traverse_new(CursorNew, CursorOld, Params, FoundKeys, LookupFun) ->
|
||||
Acls = qlc:next_answers(CursorNew, 1),
|
||||
case Acls of
|
||||
[] ->
|
||||
qlc:delete_cursor(CursorNew),
|
||||
traverse_old(CursorOld, FoundKeys);
|
||||
traverse_old(CursorOld, Params, FoundKeys);
|
||||
[#?ACL_TABLE2{who = Login, rules = Rules} = Acl] ->
|
||||
Keys = lists:usort([{Login, Topic} || {_, _, Topic, _} <- Rules]),
|
||||
OldRecs = lists:flatmap(fun(Key) -> LookupFun(?ACL_TABLE, Key) end, Keys),
|
||||
|
@ -281,27 +300,57 @@ traverse_new(CursorNew, CursorOld, FoundKeys, LookupFun) ->
|
|||
OldRecs),
|
||||
case acl_to_list(MergedAcl) of
|
||||
[] ->
|
||||
traverse_new(CursorNew, CursorOld, NewFoundKeys, LookupFun);
|
||||
traverse_new(CursorNew, CursorOld, Params, NewFoundKeys, LookupFun);
|
||||
List ->
|
||||
List ++ fun() -> traverse_new(CursorNew, CursorOld, NewFoundKeys, LookupFun) end
|
||||
filter_params(List, Params) ++
|
||||
fun() -> traverse_new(CursorNew, CursorOld, Params, NewFoundKeys, LookupFun) end
|
||||
end
|
||||
end.
|
||||
|
||||
traverse_old(CursorOld, FoundKeys) ->
|
||||
filter_params(List, {Qs, Fuzzy}) ->
|
||||
case maps:size(Qs) =:= 0 andalso maps:size(Fuzzy) =:= 0 of
|
||||
false ->
|
||||
Topic = maps:get({topic, '=:='}, Qs, undefined),
|
||||
Action = maps:get({action, '=:='}, Qs, undefined),
|
||||
Access = maps:get({access, '=:='}, Qs, undefined),
|
||||
lists:filter(fun({Target, Topic0, Action0, Access0, _CreatedAt}) ->
|
||||
CheckList = [{Topic, Topic0}, {Action, Action0}, {Access, Access0}],
|
||||
case lists:all(fun is_match/1, CheckList) of
|
||||
true ->
|
||||
case Target of
|
||||
{Type, Login} ->
|
||||
case maps:get({Type, 'like'}, Fuzzy, <<>>) of
|
||||
<<>> -> true;
|
||||
LikeSchema -> binary:match(Login, LikeSchema) =/= nomatch
|
||||
end;
|
||||
all -> true
|
||||
end;
|
||||
false -> false
|
||||
end
|
||||
end, List);
|
||||
true -> List
|
||||
end.
|
||||
|
||||
is_match({Schema, Val}) ->
|
||||
Schema =:= undefined orelse Schema =:= Val.
|
||||
|
||||
traverse_old(CursorOld, Params, FoundKeys) ->
|
||||
OldAcls = qlc:next_answers(CursorOld),
|
||||
case OldAcls of
|
||||
[] ->
|
||||
qlc:delete_cursor(CursorOld),
|
||||
[];
|
||||
_ ->
|
||||
Records = [ {Login, Topic, Action, Access, CreatedAt}
|
||||
Records = [{Login, Topic, Action, Access, CreatedAt}
|
||||
|| #?ACL_TABLE{filter = {Login, Topic}, action = LegacyAction, access = Access, created_at = CreatedAt} <- OldAcls,
|
||||
{_, Action, _, _} <- normalize_rule({Access, LegacyAction, Topic, CreatedAt}),
|
||||
not maps:is_key({Login, Topic}, FoundKeys)
|
||||
],
|
||||
case Records of
|
||||
[] -> traverse_old(CursorOld, FoundKeys);
|
||||
List -> List ++ fun() -> traverse_old(CursorOld, FoundKeys) end
|
||||
[] -> traverse_old(CursorOld, Params, FoundKeys);
|
||||
List ->
|
||||
filter_params(List, Params)
|
||||
++ fun() -> traverse_old(CursorOld, Params, FoundKeys) end
|
||||
end
|
||||
end.
|
||||
|
||||
|
|
|
@ -18,18 +18,20 @@
|
|||
|
||||
-include_lib("stdlib/include/qlc.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
-include("emqx_auth_mnesia.hrl").
|
||||
|
||||
-define(TABLE, emqx_user).
|
||||
|
||||
-import(proplists, [get_value/2]).
|
||||
-import(minirest, [return/1]).
|
||||
-export([paginate_qh/5]).
|
||||
|
||||
-export([ list_clientid/2
|
||||
, lookup_clientid/2
|
||||
, add_clientid/2
|
||||
, update_clientid/2
|
||||
, delete_clientid/2
|
||||
, query_clientid/3
|
||||
, query_username/3
|
||||
]).
|
||||
|
||||
-rest_api(#{name => list_clientid,
|
||||
|
@ -109,13 +111,28 @@
|
|||
descr => "Delete username in the cluster"
|
||||
}).
|
||||
|
||||
-define(CLIENTID_SCHEMA, {?TABLE,
|
||||
[
|
||||
{<<"clientid">>, binary},
|
||||
{<<"_like_clientid">>, binary}
|
||||
]}).
|
||||
|
||||
-define(USERNAME_SCHEMA, {?TABLE,
|
||||
[
|
||||
{<<"username">>, binary},
|
||||
{<<"_like_username">>, binary}
|
||||
]}).
|
||||
|
||||
-define(query_clientid, {?MODULE, query_clientid}).
|
||||
-define(query_username, {?MODULE, query_username}).
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% Auth Clientid Api
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
list_clientid(_Bindings, Params) ->
|
||||
MatchSpec = ets:fun2ms(fun({?TABLE, {clientid, Clientid}, Password, CreatedAt}) -> {?TABLE, {clientid, Clientid}, Password, CreatedAt} end),
|
||||
return({ok, paginate(?TABLE, MatchSpec, Params, fun emqx_auth_mnesia_cli:comparing/2, fun({?TABLE, {clientid, X}, _, _}) -> #{clientid => X} end)}).
|
||||
SortFun = fun(#{created_at := C1}, #{created_at := C2}) -> C1 > C2 end,
|
||||
return({ok, emqx_mgmt_api:node_query(node(), Params, ?CLIENTID_SCHEMA, ?query_clientid, SortFun)}).
|
||||
|
||||
lookup_clientid(#{clientid := Clientid}, _Params) ->
|
||||
return({ok, format(emqx_auth_mnesia_cli:lookup_user({clientid, urldecode(Clientid)}))}).
|
||||
|
@ -164,8 +181,8 @@ delete_clientid(#{clientid := Clientid}, _) ->
|
|||
%%------------------------------------------------------------------------------
|
||||
|
||||
list_username(_Bindings, Params) ->
|
||||
MatchSpec = ets:fun2ms(fun({?TABLE, {username, Username}, Password, CreatedAt}) -> {?TABLE, {username, Username}, Password, CreatedAt} end),
|
||||
return({ok, paginate(?TABLE, MatchSpec, Params, fun emqx_auth_mnesia_cli:comparing/2, fun({?TABLE, {username, X}, _, _}) -> #{username => X} end)}).
|
||||
SortFun = fun(#{created_at := C1}, #{created_at := C2}) -> C1 > C2 end,
|
||||
return({ok, emqx_mgmt_api:node_query(node(), Params, ?USERNAME_SCHEMA, ?query_username, SortFun)}).
|
||||
|
||||
lookup_username(#{username := Username}, _Params) ->
|
||||
return({ok, format(emqx_auth_mnesia_cli:lookup_user({username, urldecode(Username)}))}).
|
||||
|
@ -211,57 +228,52 @@ delete_username(#{username := Username}, _) ->
|
|||
%%------------------------------------------------------------------------------
|
||||
%% Paging Query
|
||||
%%------------------------------------------------------------------------------
|
||||
query_clientid(Qs, Start, Limit) -> query(clientid, Qs, Start, Limit).
|
||||
query_username(Qs, Start, Limit) -> query(username, Qs, Start, Limit).
|
||||
|
||||
paginate(Table, MatchSpec, Params, ComparingFun, RowFun) ->
|
||||
Qh = query_handle(Table, MatchSpec),
|
||||
Count = count(Table, MatchSpec),
|
||||
paginate_qh(Qh, Count, Params, ComparingFun, RowFun).
|
||||
query(Type, {Qs, []}, Start, Limit) ->
|
||||
Ms = qs2ms(Type, Qs),
|
||||
emqx_mgmt_api:select_table(?TABLE, Ms, Start, Limit, fun format/1);
|
||||
|
||||
paginate_qh(Qh, Count, Params, ComparingFun, RowFun) ->
|
||||
Page = page(Params),
|
||||
Limit = limit(Params),
|
||||
Cursor = qlc:cursor(Qh),
|
||||
case Page > 1 of
|
||||
true ->
|
||||
_ = qlc:next_answers(Cursor, (Page - 1) * Limit),
|
||||
ok;
|
||||
false -> ok
|
||||
end,
|
||||
Rows = qlc:next_answers(Cursor, Limit),
|
||||
qlc:delete_cursor(Cursor),
|
||||
#{meta => #{page => Page, limit => Limit, count => Count},
|
||||
data => [RowFun(Row) || Row <- lists:sort(ComparingFun, Rows)]}.
|
||||
query(Type, {Qs, Fuzzy}, Start, Limit) ->
|
||||
Ms = qs2ms(Type, Qs),
|
||||
MatchFun = match_fun(Ms, Fuzzy),
|
||||
emqx_mgmt_api:traverse_table(?TABLE, MatchFun, Start, Limit, fun format/1).
|
||||
|
||||
query_handle(Table, MatchSpec) when is_atom(Table) ->
|
||||
Options = {traverse, {select, MatchSpec}},
|
||||
qlc:q([R || R <- ets:table(Table, Options)]).
|
||||
-spec qs2ms(clientid | username, list()) -> ets:match_spec().
|
||||
qs2ms(Type, Qs) ->
|
||||
Init = #?TABLE{login = {Type, '_'}, password = '_', created_at = '_'},
|
||||
MatchHead = lists:foldl(fun(Q, Acc) -> match_ms(Q, Acc) end, Init, Qs),
|
||||
[{MatchHead, [], ['$_']}].
|
||||
|
||||
count(Table, MatchSpec) when is_atom(Table) ->
|
||||
[{MatchPattern, Where, _Re}] = MatchSpec,
|
||||
NMatchSpec = [{MatchPattern, Where, [true]}],
|
||||
ets:select_count(Table, NMatchSpec).
|
||||
match_ms({Type, '=:=', Value}, MatchHead) -> MatchHead#?TABLE{login = {Type, Value}};
|
||||
match_ms(_, MatchHead) -> MatchHead.
|
||||
|
||||
page(Params) ->
|
||||
binary_to_integer(proplists:get_value(<<"_page">>, Params, <<"1">>)).
|
||||
|
||||
limit(Params) ->
|
||||
case proplists:get_value(<<"_limit">>, Params) of
|
||||
undefined -> 10;
|
||||
Size -> binary_to_integer(Size)
|
||||
match_fun(Ms, Fuzzy) ->
|
||||
MsC = ets:match_spec_compile(Ms),
|
||||
fun(Rows) ->
|
||||
Ls = ets:match_spec_run(Rows, MsC),
|
||||
lists:filter(fun(E) -> run_fuzzy_match(E, Fuzzy) end, Ls)
|
||||
end.
|
||||
|
||||
run_fuzzy_match(_, []) -> true;
|
||||
run_fuzzy_match(E = #?TABLE{login = {Key, Str}}, [{Key, like, SubStr}|Fuzzy]) ->
|
||||
binary:match(Str, SubStr) /= nomatch andalso run_fuzzy_match(E, Fuzzy);
|
||||
run_fuzzy_match(_E, [{_Key, like, _SubStr}| _Fuzzy]) -> false.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% Interval Funcs
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
format([{?TABLE, {clientid, ClientId}, _Password, _InterTime}]) ->
|
||||
#{clientid => ClientId};
|
||||
format([{?TABLE, {clientid, ClientId}, _Password, CreatedAt}]) ->
|
||||
#{clientid => ClientId, created_at => CreatedAt};
|
||||
|
||||
format([{?TABLE, {username, Username}, _Password, _InterTime}]) ->
|
||||
#{username => Username};
|
||||
format([{?TABLE, {username, Username}, _Password, CreatedAt}]) ->
|
||||
#{username => Username, created_at => CreatedAt};
|
||||
|
||||
format([]) ->
|
||||
#{}.
|
||||
#{};
|
||||
format(User) -> format([User]).
|
||||
|
||||
validate([], []) ->
|
||||
ok;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
||||
|
||||
-import(emqx_ct_http, [ request_api/3
|
||||
, request_api/4
|
||||
, request_api/5
|
||||
, get_http_data/1
|
||||
, create_default_app/0
|
||||
|
@ -357,13 +358,31 @@ t_rest_api(_Config) ->
|
|||
<<"topic">> => <<"topic/C">>,
|
||||
<<"action">> => <<"pubsub">>,
|
||||
<<"access">> => <<"deny">>
|
||||
},
|
||||
#{<<"clientid">> => <<"good_clientid1">>,
|
||||
<<"topic">> => <<"topic/D">>,
|
||||
<<"action">> => <<"pubsub">>,
|
||||
<<"access">> => <<"deny">>
|
||||
}],
|
||||
{ok, _} = request_http_rest_add([], Params1),
|
||||
|
||||
{ok, Re1} = request_http_rest_list(["clientid", "test_clientid"]),
|
||||
?assertMatch(4, length(get_http_data(Re1))),
|
||||
{ok, Re11} = request_http_rest_list(["clientid"], "_like_clientid=good"),
|
||||
?assertMatch(2, length(get_http_data(Re11))),
|
||||
{ok, Re12} = request_http_rest_list(["clientid"], "_like_clientid=clientid"),
|
||||
?assertMatch(6, length(get_http_data(Re12))),
|
||||
{ok, Re13} = request_http_rest_list(["clientid"], "_like_clientid=clientid&action=pub"),
|
||||
?assertMatch(3, length(get_http_data(Re13))),
|
||||
{ok, Re14} = request_http_rest_list(["clientid"], "_like_clientid=clientid&access=deny"),
|
||||
?assertMatch(4, length(get_http_data(Re14))),
|
||||
{ok, Re15} = request_http_rest_list(["clientid"], "_like_clientid=clientid&topic=topic/A"),
|
||||
?assertMatch(1, length(get_http_data(Re15))),
|
||||
|
||||
{ok, _} = request_http_rest_delete(["clientid", "test_clientid", "topic", "topic/A"]),
|
||||
{ok, _} = request_http_rest_delete(["clientid", "test_clientid", "topic", "topic/B"]),
|
||||
{ok, _} = request_http_rest_delete(["clientid", "test_clientid", "topic", "topic/C"]),
|
||||
{ok, _} = request_http_rest_delete(["clientid", "good_clientid1", "topic", "topic/D"]),
|
||||
{ok, Res1} = request_http_rest_list(["clientid"]),
|
||||
?assertMatch([], get_http_data(Res1)),
|
||||
|
||||
|
@ -381,13 +400,30 @@ t_rest_api(_Config) ->
|
|||
<<"topic">> => <<"topic/C">>,
|
||||
<<"action">> => <<"pubsub">>,
|
||||
<<"access">> => <<"deny">>
|
||||
},
|
||||
#{<<"username">> => <<"good_username">>,
|
||||
<<"topic">> => <<"topic/D">>,
|
||||
<<"action">> => <<"pubsub">>,
|
||||
<<"access">> => <<"deny">>
|
||||
}],
|
||||
{ok, _} = request_http_rest_add([], Params2),
|
||||
{ok, Re2} = request_http_rest_list(["username", "test_username"]),
|
||||
?assertMatch(4, length(get_http_data(Re2))),
|
||||
{ok, Re21} = request_http_rest_list(["username"], "_like_username=good"),
|
||||
?assertMatch(2, length(get_http_data(Re21))),
|
||||
{ok, Re22} = request_http_rest_list(["username"], "_like_username=username"),
|
||||
?assertMatch(6, length(get_http_data(Re22))),
|
||||
{ok, Re23} = request_http_rest_list(["username"], "_like_username=username&action=pub"),
|
||||
?assertMatch(3, length(get_http_data(Re23))),
|
||||
{ok, Re24} = request_http_rest_list(["username"], "_like_username=username&access=deny"),
|
||||
?assertMatch(4, length(get_http_data(Re24))),
|
||||
{ok, Re25} = request_http_rest_list(["username"], "_like_username=username&topic=topic/A"),
|
||||
?assertMatch(1, length(get_http_data(Re25))),
|
||||
|
||||
{ok, _} = request_http_rest_delete(["username", "test_username", "topic", "topic/A"]),
|
||||
{ok, _} = request_http_rest_delete(["username", "test_username", "topic", "topic/B"]),
|
||||
{ok, _} = request_http_rest_delete(["username", "test_username", "topic", "topic/C"]),
|
||||
{ok, _} = request_http_rest_delete(["username", "good_username", "topic", "topic/D"]),
|
||||
{ok, Res2} = request_http_rest_list(["username"]),
|
||||
?assertMatch([], get_http_data(Res2)),
|
||||
|
||||
|
@ -402,13 +438,29 @@ t_rest_api(_Config) ->
|
|||
#{<<"topic">> => <<"topic/C">>,
|
||||
<<"action">> => <<"pubsub">>,
|
||||
<<"access">> => <<"deny">>
|
||||
}],
|
||||
},
|
||||
#{<<"topic">> => <<"topic/D">>,
|
||||
<<"action">> => <<"pubsub">>,
|
||||
<<"access">> => <<"deny">>
|
||||
}
|
||||
],
|
||||
{ok, _} = request_http_rest_add([], Params3),
|
||||
|
||||
{ok, Re3} = request_http_rest_list(["$all"]),
|
||||
?assertMatch(4, length(get_http_data(Re3))),
|
||||
?assertMatch(6, length(get_http_data(Re3))),
|
||||
{ok, Re31} = request_http_rest_list(["$all"], "topic=topic/A"),
|
||||
?assertMatch(1, length(get_http_data(Re31))),
|
||||
{ok, Re32} = request_http_rest_list(["$all"], "action=sub"),
|
||||
?assertMatch(3, length(get_http_data(Re32))),
|
||||
{ok, Re33} = request_http_rest_list(["$all"], "access=deny"),
|
||||
?assertMatch(4, length(get_http_data(Re33))),
|
||||
{ok, Re34} = request_http_rest_list(["$all"], "action=sub&access=deny"),
|
||||
?assertMatch(2, length(get_http_data(Re34))),
|
||||
|
||||
{ok, _} = request_http_rest_delete(["$all", "topic", "topic/A"]),
|
||||
{ok, _} = request_http_rest_delete(["$all", "topic", "topic/B"]),
|
||||
{ok, _} = request_http_rest_delete(["$all", "topic", "topic/C"]),
|
||||
{ok, _} = request_http_rest_delete(["$all", "topic", "topic/D"]),
|
||||
{ok, Res3} = request_http_rest_list(["$all"]),
|
||||
?assertMatch([], get_http_data(Res3)).
|
||||
|
||||
|
@ -442,6 +494,9 @@ combined_conflicting_records() ->
|
|||
request_http_rest_list(Path) ->
|
||||
request_api(get, uri(Path), default_auth_header()).
|
||||
|
||||
request_http_rest_list(Path, Qs) ->
|
||||
request_api(get, uri(Path), Qs, default_auth_header()).
|
||||
|
||||
request_http_rest_lookup(Path) ->
|
||||
request_api(get, uri(Path), default_auth_header()).
|
||||
|
||||
|
|
|
@ -286,20 +286,26 @@ t_clientid_rest_api(_Config) ->
|
|||
|
||||
Params3 = [ #{<<"clientid">> => ?CLIENTID, <<"password">> => ?PASSWORD}
|
||||
, #{<<"clientid">> => <<"clientid1">>, <<"password">> => ?PASSWORD}
|
||||
, #{<<"clientid">> => <<"clientid2">>, <<"password">> => ?PASSWORD}
|
||||
, #{<<"clientid">> => <<"client2">>, <<"password">> => ?PASSWORD}
|
||||
],
|
||||
{ok, Result3} = request_http_rest_add(["auth_clientid"], Params3),
|
||||
?assertMatch(#{ ?CLIENTID := <<"{error,existed}">>
|
||||
, <<"clientid1">> := <<"ok">>
|
||||
, <<"clientid2">> := <<"ok">>
|
||||
, <<"client2">> := <<"ok">>
|
||||
}, get_http_data(Result3)),
|
||||
|
||||
{ok, Result4} = request_http_rest_list(["auth_clientid"]),
|
||||
|
||||
?assertEqual(3, length(get_http_data(Result4))),
|
||||
|
||||
{ok, Result5} = request_http_rest_list(["auth_clientid?_like_clientid=id"]),
|
||||
?assertEqual(2, length(get_http_data(Result5))),
|
||||
{ok, Result6} = request_http_rest_list(["auth_clientid?_like_clientid=x"]),
|
||||
?assertEqual(0, length(get_http_data(Result6))),
|
||||
|
||||
{ok, _} = request_http_rest_delete(Path),
|
||||
{ok, Result5} = request_http_rest_lookup(Path),
|
||||
?assertMatch(#{}, get_http_data(Result5)).
|
||||
{ok, Result7} = request_http_rest_lookup(Path),
|
||||
?assertMatch(#{}, get_http_data(Result7)).
|
||||
|
||||
t_username_rest_api(_Config) ->
|
||||
clean_all_users(),
|
||||
|
@ -330,9 +336,14 @@ t_username_rest_api(_Config) ->
|
|||
{ok, Result4} = request_http_rest_list(["auth_username"]),
|
||||
?assertEqual(3, length(get_http_data(Result4))),
|
||||
|
||||
{ok, Result5} = request_http_rest_list(["auth_username?_like_username=for"]),
|
||||
?assertEqual(1, length(get_http_data(Result5))),
|
||||
{ok, Result6} = request_http_rest_list(["auth_username?_like_username=x"]),
|
||||
?assertEqual(0, length(get_http_data(Result6))),
|
||||
|
||||
{ok, _} = request_http_rest_delete(Path),
|
||||
{ok, Result5} = request_http_rest_lookup([Path]),
|
||||
?assertMatch(#{}, get_http_data(Result5)).
|
||||
{ok, Result6} = request_http_rest_lookup([Path]),
|
||||
?assertMatch(#{}, get_http_data(Result6)).
|
||||
|
||||
t_password_hash(_) ->
|
||||
clean_all_users(),
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
%% first_next query APIs
|
||||
-export([ params2qs/2
|
||||
, node_query/4
|
||||
, node_query/5
|
||||
, cluster_query/3
|
||||
, traverse_table/5
|
||||
, select_table/5
|
||||
|
@ -82,6 +83,9 @@ limit(Params) ->
|
|||
%%--------------------------------------------------------------------
|
||||
|
||||
node_query(Node, Params, {Tab, QsSchema}, QueryFun) ->
|
||||
node_query(Node, Params, {Tab, QsSchema}, QueryFun, undefined).
|
||||
|
||||
node_query(Node, Params, {Tab, QsSchema}, QueryFun, SortFun) ->
|
||||
{CodCnt, Qs} = params2qs(Params, QsSchema),
|
||||
Limit = limit(Params),
|
||||
Page = page(Params),
|
||||
|
@ -94,7 +98,11 @@ node_query(Node, Params, {Tab, QsSchema}, QueryFun) ->
|
|||
true -> Meta#{count => count(Tab), hasnext => length(Rows) > Limit};
|
||||
_ -> Meta#{count => -1, hasnext => length(Rows) > Limit}
|
||||
end,
|
||||
#{meta => NMeta, data => lists:sublist(Rows, Limit)}.
|
||||
NRows = case SortFun of
|
||||
undefined -> Rows;
|
||||
_ -> lists:sort(SortFun, Rows)
|
||||
end,
|
||||
#{meta => NMeta, data => lists:sublist(NRows, Limit)}.
|
||||
|
||||
%% @private
|
||||
do_query(Node, Qs, {M,F}, Start, Limit) when Node =:= node() ->
|
||||
|
|
Loading…
Reference in New Issue