feat(authz): more better update function

Signed-off-by: zhanghongtong <rory-z@outlook.com>
This commit is contained in:
zhanghongtong 2021-08-06 18:04:03 +08:00 committed by Rory Z
parent c26ec5c0dd
commit 4bb1e9c964
8 changed files with 369 additions and 166 deletions

View File

@ -20,11 +20,16 @@
-include("emqx_authz.hrl"). -include("emqx_authz.hrl").
-include_lib("emqx/include/logger.hrl"). -include_lib("emqx/include/logger.hrl").
-ifdef(TEST).
-compile(export_all).
-compile(nowarn_export_all).
-endif.
-export([ register_metrics/0 -export([ register_metrics/0
, init/0 , init/0
, init_rule/1 , init_rule/1
, lookup/0 , lookup/0
, lookup/1
, update/2 , update/2
, authorize/5 , authorize/5
, match/4 , match/4
@ -45,8 +50,13 @@ init() ->
ok = emqx_hooks:add('client.authorize', {?MODULE, authorize, [NRules]}, -1). ok = emqx_hooks:add('client.authorize', {?MODULE, authorize, [NRules]}, -1).
lookup() -> lookup() ->
{_M, _F, A}= find_action_in_hooks(), {_M, _F, [A]}= find_action_in_hooks(),
A. A.
lookup(Id) ->
case find_rule_by_id(Id, lookup()) of
{error, Reason} -> {error, Reason};
{_, Rule} -> Rule
end.
update(Cmd, Rules) -> update(Cmd, Rules) ->
emqx_config:update(emqx_authz_schema, ?CONF_KEY_PATH, {Cmd, Rules}). emqx_config:update(emqx_authz_schema, ?CONF_KEY_PATH, {Cmd, Rules}).
@ -56,6 +66,13 @@ pre_config_update({head, Rules}, OldConf) when is_list(Rules), is_list(OldConf)
Rules ++ OldConf; Rules ++ OldConf;
pre_config_update({tail, Rules}, OldConf) when is_list(Rules), is_list(OldConf) -> pre_config_update({tail, Rules}, OldConf) when is_list(Rules), is_list(OldConf) ->
OldConf ++ Rules; OldConf ++ Rules;
pre_config_update({{replace_once, Id}, Rule}, OldConf) when is_map(Rule), is_list(OldConf) ->
{Index, _} = case find_rule_by_id(Id, lookup()) of
{error, Reason} -> error(Reason);
R -> R
end,
{OldConf1, OldConf2} = lists:split(Index, OldConf),
lists:droplast(OldConf1) ++ [Rule] ++ OldConf2;
pre_config_update({_, Rules}, _OldConf) when is_list(Rules)-> pre_config_update({_, Rules}, _OldConf) when is_list(Rules)->
%% overwrite the entire config! %% overwrite the entire config!
Rules. Rules.
@ -63,19 +80,38 @@ pre_config_update({_, Rules}, _OldConf) when is_list(Rules)->
post_config_update(_, undefined, _OldConf) -> post_config_update(_, undefined, _OldConf) ->
ok; ok;
post_config_update({head, Rules}, _NewRules, _OldConf) -> post_config_update({head, Rules}, _NewRules, _OldConf) ->
InitedRules = [init_rule(Rule) || Rule <- check_rules(Rules)], InitedRules = [init_rule(R) || R <- check_rules(Rules)],
ok = emqx_hooks:put('client.authorize', {?MODULE, authorize, [lists:append(InitedRules, lookup())]}, -1), ok = emqx_hooks:put('client.authorize', {?MODULE, authorize, [InitedRules ++ lookup()]}, -1),
ok = emqx_authz_cache:drain_cache(); ok = emqx_authz_cache:drain_cache();
post_config_update({tail, Rules}, _NewRules, _OldConf) -> post_config_update({tail, Rules}, _NewRules, _OldConf) ->
InitedRules = [init_rule(Rule) || Rule <- check_rules(Rules)], InitedRules = [init_rule(R) || R <- check_rules(Rules)],
emqx_hooks:put('client.authorize', {?MODULE, authorize, [lists:append(InitedRules, lookup())]}, -1), emqx_hooks:put('client.authorize', {?MODULE, authorize, [lookup() ++ InitedRules]}, -1),
ok = emqx_authz_cache:drain_cache(); ok = emqx_authz_cache:drain_cache();
post_config_update({{replace_once, Id}, Rule}, _NewRules, _OldConf) when is_map(Rule) ->
OldInitedRules = lookup(),
{Index, OldRule} = case find_rule_by_id(Id, OldInitedRules) of
{error, Reason} -> error(Reason);
R -> R
end,
case maps:get(type, OldRule, undefined) of
undefined -> ok;
_ ->
#{annotations := #{id := Id}} = OldRule,
ok = emqx_resource:remove(Id)
end,
{OldRules1, OldRules2 } = lists:split(Index, OldInitedRules),
InitedRules = [init_rule(R#{annotations => #{id => Id}}) || R <- check_rules([Rule])],
ok = emqx_hooks:put('client.authorize', {?MODULE, authorize, [lists:droplast(OldRules1) ++ InitedRules ++ OldRules2]}, -1),
ok = emqx_authz_cache:drain_cache();
post_config_update(_, NewRules, _OldConf) -> post_config_update(_, NewRules, _OldConf) ->
%% overwrite the entire config! %% overwrite the entire config!
OldInitedRules = lookup(), OldInitedRules = lookup(),
InitedRules = [init_rule(Rule) || Rule <- NewRules], InitedRules = [init_rule(Rule) || Rule <- NewRules],
ok = emqx_hooks:put('client.authorize', {?MODULE, authorize, [InitedRules]}, -1), ok = emqx_hooks:put('client.authorize', {?MODULE, authorize, [InitedRules]}, -1),
lists:foreach(fun (#{type := _Type, enable := true, metadata := #{id := Id}}) -> lists:foreach(fun (#{type := _Type, enable := true, annotations := #{id := Id}}) ->
ok = emqx_resource:remove(Id); ok = emqx_resource:remove(Id);
(_) -> ok (_) -> ok
end, OldInitedRules), end, OldInitedRules),
@ -91,6 +127,14 @@ check_rules(RawRules) ->
#{authorization := #{rules := Rules}} = hocon_schema:richmap_to_map(CheckConf), #{authorization := #{rules := Rules}} = hocon_schema:richmap_to_map(CheckConf),
Rules. Rules.
find_rule_by_id(Id, Rules) -> find_rule_by_id(Id, Rules, 1).
find_rule_by_id(_RuleId, [], _N) -> {error, not_found_rule};
find_rule_by_id(RuleId, [ Rule = #{annotations := #{id := Id}} | Tail], N) ->
case RuleId =:= Id of
true -> {N, Rule};
false -> find_rule_by_id(RuleId, Tail, N + 1)
end.
find_action_in_hooks() -> find_action_in_hooks() ->
Callbacks = emqx_hooks:lookup('client.authorize'), Callbacks = emqx_hooks:lookup('client.authorize'),
[Action] = [Action || {callback,{?MODULE, authorize, _} = Action, _, _} <- Callbacks ], [Action] = [Action || {callback,{?MODULE, authorize, _} = Action, _, _} <- Callbacks ],
@ -99,6 +143,19 @@ find_action_in_hooks() ->
gen_id(Type) -> gen_id(Type) ->
iolist_to_binary([io_lib:format("~s_~s",[?APP, Type]), "_", integer_to_list(erlang:system_time())]). iolist_to_binary([io_lib:format("~s_~s",[?APP, Type]), "_", integer_to_list(erlang:system_time())]).
create_resource(#{type := DB,
config := Config,
annotations := #{id := ResourceID}}) ->
case emqx_resource:update(
ResourceID,
list_to_existing_atom(io_lib:format("~s_~s",[emqx_connector, DB])),
Config,
[])
of
{ok, _} -> ResourceID;
{error, already_created} -> ResourceID;
{error, Reason} -> {error, Reason}
end;
create_resource(#{type := DB, create_resource(#{type := DB,
config := Config}) -> config := Config}) ->
ResourceID = gen_id(DB), ResourceID = gen_id(DB),
@ -116,13 +173,19 @@ create_resource(#{type := DB,
init_rule(#{topics := Topics, init_rule(#{topics := Topics,
action := Action, action := Action,
permission := Permission, permission := Permission,
principal := Principal principal := Principal,
} = Rule) when ?ALLOW_DENY(Permission), ?PUBSUB(Action), is_list(Topics) -> annotations := #{id := Id}
} = Rule) when ?ALLOW_DENY(Permission), ?PUBSUB(Action), is_list(Topics) ->
Rule#{annotations => Rule#{annotations =>
#{id => gen_id(simple), #{id => Id,
principal => compile_principal(Principal), principal => compile_principal(Principal),
topics => [compile_topic(Topic) || Topic <- Topics]} topics => [compile_topic(Topic) || Topic <- Topics]}
}; };
init_rule(#{topics := Topics,
action := Action,
permission := Permission
} = Rule) when ?ALLOW_DENY(Permission), ?PUBSUB(Action), is_list(Topics) ->
init_rule(Rule#{annotations =>#{id => gen_id(simple)}});
init_rule(#{principal := Principal, init_rule(#{principal := Principal,
enable := true, enable := true,
@ -132,7 +195,7 @@ init_rule(#{principal := Principal,
NConfig = maps:merge(Config, #{base_url => maps:remove(query, Url)}), NConfig = maps:merge(Config, #{base_url => maps:remove(query, Url)}),
case create_resource(Rule#{config := NConfig}) of case create_resource(Rule#{config := NConfig}) of
{error, Reason} -> error({load_config_error, Reason}); {error, Reason} -> error({load_config_error, Reason});
Id -> Rule#{annotations => Id -> Rule#{annotations =>
#{id => Id, #{id => Id,
principal => compile_principal(Principal) principal => compile_principal(Principal)
} }
@ -146,7 +209,7 @@ init_rule(#{principal := Principal,
DB =:= mongo -> DB =:= mongo ->
case create_resource(Rule) of case create_resource(Rule) of
{error, Reason} -> error({load_config_error, Reason}); {error, Reason} -> error({load_config_error, Reason});
Id -> Rule#{annotations => Id -> Rule#{annotations =>
#{id => Id, #{id => Id,
principal => compile_principal(Principal) principal => compile_principal(Principal)
} }
@ -162,7 +225,7 @@ init_rule(#{principal := Principal,
Mod = list_to_existing_atom(io_lib:format("~s_~s",[?APP, DB])), Mod = list_to_existing_atom(io_lib:format("~s_~s",[?APP, DB])),
case create_resource(Rule) of case create_resource(Rule) of
{error, Reason} -> error({load_config_error, Reason}); {error, Reason} -> error({load_config_error, Reason});
Id -> Rule#{annotations => Id -> Rule#{annotations =>
#{id => Id, #{id => Id,
principal => compile_principal(Principal), principal => compile_principal(Principal),
sql => Mod:parse_query(SQL) sql => Mod:parse_query(SQL)

View File

@ -20,13 +20,17 @@
-include("emqx_authz.hrl"). -include("emqx_authz.hrl").
-define(EXAMPLE_RETURNED_RULE1,
#{principal => <<"all">>,
permission => <<"allow">>,
action => <<"all">>,
topics => [<<"#">>],
annotations => #{id => 1}
}).
-define(EXAMPLE_RETURNED_RULES, -define(EXAMPLE_RETURNED_RULES,
#{rules => [ #{principal => <<"all">>, #{rules => [?EXAMPLE_RETURNED_RULE1
permission => <<"allow">>,
action => <<"all">>,
topics => [<<"#">>],
metadata => #{id => 1}
}
] ]
}). }).
@ -37,10 +41,12 @@
-export([ api_spec/0 -export([ api_spec/0
, authorization/2 , authorization/2
, authorization_once/2
]). ]).
api_spec() -> api_spec() ->
{[ authorization_api() {[ authorization_api(),
authorization_api2()
], definitions()}. ], definitions()}.
definitions() -> emqx_authz_api_schema:definitions(). definitions() -> emqx_authz_api_schema:definitions().
@ -99,13 +105,79 @@ authorization_api() ->
}, },
{"/authorization", Metadata, authorization}. {"/authorization", Metadata, authorization}.
authorization_api2() ->
Metadata = #{
get => #{
description => "List authorization rules",
parameters => [
#{
name => id,
in => path,
schema => #{
type => string
},
required => true
}
],
responses => #{
<<"200">> => #{
description => <<"OK">>,
content => #{
'application/json' => #{
schema => minirest:ref(<<"returned_rules">>),
examples => #{
rules => #{
summary => <<"Rules">>,
value => jsx:encode(?EXAMPLE_RETURNED_RULE1)
}
}
}
}
},
<<"404">> => #{description => <<"Not Found">>}
}
},
put => #{
description => "Update rule",
parameters => [
#{
name => id,
in => path,
schema => #{
type => string
},
required => true
}
],
requestBody => #{
content => #{
'application/json' => #{
schema => minirest:ref(<<"rules">>),
examples => #{
simple_rule => #{
summary => <<"Rules">>,
value => jsx:encode(?EXAMPLE_RULE1)
}
}
}
}
},
responses => #{
<<"201">> => #{description => <<"Created">>},
<<"400">> => #{description => <<"Bad Request">>}
}
}
},
{"/authorization/:id", Metadata, authorization_once}.
authorization(get, _Request) -> authorization(get, _Request) ->
Rules = lists:foldl(fun (#{type := _Type, enable := true, metadata := #{id := Id} = MataData} = Rule, AccIn) -> Rules = lists:foldl(fun (#{type := _Type, enable := true, annotations := #{id := Id} = Annotations} = Rule, AccIn) ->
NRule = case emqx_resource:health_check(Id) of NRule = case emqx_resource:health_check(Id) of
ok -> ok ->
Rule#{metadata => MataData#{status => healthy}}; Rule#{annotations => Annotations#{status => healthy}};
_ -> _ ->
Rule#{metadata => MataData#{status => unhealthy}} Rule#{annotations => Annotations#{status => unhealthy}}
end, end,
lists:append(AccIn, [NRule]); lists:append(AccIn, [NRule]);
(Rule, AccIn) -> (Rule, AccIn) ->
@ -120,4 +192,29 @@ authorization(post, Request) ->
{error, Reason} -> {400, #{messgae => atom_to_binary(Reason)}} {error, Reason} -> {400, #{messgae => atom_to_binary(Reason)}}
end. end.
authorization_once(get, Request) ->
Id = cowboy_req:binding(id, Request),
case emqx_authz:lookup(Id) of
{error, Reason} -> {404, #{messgae => atom_to_binary(Reason)}};
Rule ->
case maps:get(type, Rule, undefined) of
undefined -> {200, Rule};
_ ->
case emqx_resource:health_check(Id) of
ok ->
{200, Rule#{annotations => #{status => healthy}}};
_ ->
{200, Rule#{annotations => #{status => unhealthy}}}
end
end
end;
authorization_once(put, Request) ->
RuleId = cowboy_req:binding(id, Request),
{ok, Body, _} = cowboy_req:read_body(Request),
RawConfig = jsx:decode(Body, [return_maps]),
case emqx_authz:update({replace_once, RuleId}, RawConfig) of
ok -> {200};
{error, Reason} -> {400, #{messgae => atom_to_binary(Reason)}}
end.

View File

@ -22,6 +22,8 @@
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct.hrl").
-define(CONF_DEFAULT, <<"authorization: {rules: []}">>).
all() -> all() ->
emqx_ct:all(?MODULE). emqx_ct:all(?MODULE).
@ -29,82 +31,104 @@ groups() ->
[]. [].
init_per_suite(Config) -> init_per_suite(Config) ->
ok = emqx_config:init_load(emqx_authz_schema, ?CONF_DEFAULT),
ok = emqx_ct_helpers:start_apps([emqx_authz]), ok = emqx_ct_helpers:start_apps([emqx_authz]),
ok = emqx_config:update([zones, default, authorization, cache, enable], false), ok = emqx_config:update([zones, default, authorization, cache, enable], false),
ok = emqx_config:update([zones, default, authorization, enable], true), ok = emqx_config:update([zones, default, authorization, enable], true),
emqx_authz:update(replace, []),
Config. Config.
end_per_suite(_Config) -> end_per_suite(_Config) ->
emqx_ct_helpers:stop_apps([emqx_authz]). ok = emqx_authz:update(replace, []),
emqx_ct_helpers:stop_apps([emqx_authz]),
ok.
-define(RULE1, #{principal => all, -define(RULE1, #{<<"principal">> => <<"all">>,
topics => [<<"#">>], <<"topics">> => [<<"#">>],
action => all, <<"action">> => <<"all">>,
permission => deny} <<"permission">> => <<"deny">>}
). ).
-define(RULE2, #{principal => -define(RULE2, #{<<"principal">> =>
#{ipaddress => <<"127.0.0.1">>}, #{<<"ipaddress">> => <<"127.0.0.1">>},
topics => <<"topics">> =>
[#{eq => <<"#">>}, [#{<<"eq">> => <<"#">>},
#{eq => <<"+">>} #{<<"eq">> => <<"+">>}
] , ] ,
action => all, <<"action">> => <<"all">>,
permission => allow} <<"permission">> => <<"allow">>}
). ).
-define(RULE3,#{principal => -define(RULE3,#{<<"principal">> =>
#{'and' => [#{username => "^test?"}, #{<<"and">> => [#{<<"username">> => <<"^test?">>},
#{clientid => "^test?"} #{<<"clientid">> => <<"^test?">>}
]}, ]},
topics => [<<"test">>], <<"topics">> => [<<"test">>],
action => publish, <<"action">> => <<"publish">>,
permission => allow} <<"permission">> => <<"allow">>}
). ).
-define(RULE4,#{principal => -define(RULE4,#{<<"principal">> =>
#{'or' => [#{username => <<"^test">>}, #{<<"or">> => [#{<<"username">> => <<"^test">>},
#{clientid => <<"test?">>} #{<<"clientid">> => <<"test?">>}
]}, ]},
topics => [<<"%u">>,<<"%c">>], <<"topics">> => [<<"%u">>,<<"%c">>],
action => publish, <<"action">> => <<"publish">>,
permission => deny} <<"permission">> => <<"deny">>}
). ).
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% Testcases %% Testcases
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
t_init_rule(_) ->
?assertMatch(#{annotations := #{id := _ID, t_update_rule(_) ->
principal := all, ok = emqx_authz:update(replace, [?RULE2]),
topics := [['#']]} ok = emqx_authz:update(head, [?RULE1]),
}, emqx_authz:init_rule(?RULE1)), ok = emqx_authz:update(tail, [?RULE3]),
?assertMatch(#{annotations := #{principal :=
#{ipaddress := {{127,0,0,1},{127,0,0,1},32}}, Lists1 = emqx_authz:check_rules([?RULE1, ?RULE2, ?RULE3]),
topics := [#{eq := ['#']}, ?assertMatch(Lists1, emqx_config:get([authorization, rules], [])),
#{eq := ['+']}],
id := _ID} [#{annotations := #{id := Id1,
}, emqx_authz:init_rule(?RULE2)), principal := all,
?assertMatch(#{annotations := topics := [['#']]}
#{principal := },
#{annotations := #{id := Id2,
principal := #{ipaddress := {{127,0,0,1},{127,0,0,1},32}},
topics := [#{eq := ['#']}, #{eq := ['+']}]}
},
#{annotations := #{id := Id3,
principal :=
#{'and' := [#{username := {re_pattern, _, _, _, _}}, #{'and' := [#{username := {re_pattern, _, _, _, _}},
#{clientid := {re_pattern, _, _, _, _}} #{clientid := {re_pattern, _, _, _, _}}
] ]
}, },
topics := [[<<"test">>]], topics := [[<<"test">>]]}
id := _ID} }
}, emqx_authz:init_rule(?RULE3)), ] = emqx_authz:lookup(),
?assertMatch(#{annotations :=
#{principal := ok = emqx_authz:update({replace_once, Id3}, ?RULE4),
#{'or' := [#{username := {re_pattern, _, _, _, _}}, Lists2 = emqx_authz:check_rules([?RULE1, ?RULE2, ?RULE4]),
#{clientid := {re_pattern, _, _, _, _}} ?assertMatch(Lists2, emqx_config:get([authorization, rules], [])),
]
}, [#{annotations := #{id := Id1,
topics := [#{pattern := [<<"%u">>]}, principal := all,
#{pattern := [<<"%c">>]} topics := [['#']]}
], },
id := _ID} #{annotations := #{id := Id2,
}, emqx_authz:init_rule(?RULE4)), principal := #{ipaddress := {{127,0,0,1},{127,0,0,1},32}},
ok. topics := [#{eq := ['#']},
#{eq := ['+']}]}
},
#{annotations := #{id := Id3,
principal :=
#{'or' := [#{username := {re_pattern, _, _, _, _}},
#{clientid := {re_pattern, _, _, _, _}}
]
},
topics := [#{pattern := [<<"%u">>]},
#{pattern := [<<"%c">>]}
]}
}
] = emqx_authz:lookup(),
ok = emqx_authz:update(replace, []).
t_authz(_) -> t_authz(_) ->
ClientInfo1 = #{clientid => <<"test">>, ClientInfo1 = #{clientid => <<"test">>,
@ -132,10 +156,10 @@ t_authz(_) ->
listener => mqtt_tcp listener => mqtt_tcp
}, },
Rules1 = [emqx_authz:init_rule(Rule) || Rule <- [?RULE1, ?RULE2]], Rules1 = [emqx_authz:init_rule(Rule) || Rule <- emqx_authz:check_rules([?RULE1, ?RULE2])],
Rules2 = [emqx_authz:init_rule(Rule) || Rule <- [?RULE2, ?RULE1]], Rules2 = [emqx_authz:init_rule(Rule) || Rule <- emqx_authz:check_rules([?RULE2, ?RULE1])],
Rules3 = [emqx_authz:init_rule(Rule) || Rule <- [?RULE3, ?RULE4]], Rules3 = [emqx_authz:init_rule(Rule) || Rule <- emqx_authz:check_rules([?RULE3, ?RULE4])],
Rules4 = [emqx_authz:init_rule(Rule) || Rule <- [?RULE4, ?RULE1]], Rules4 = [emqx_authz:init_rule(Rule) || Rule <- emqx_authz:check_rules([?RULE4, ?RULE1])],
?assertEqual({stop, deny}, ?assertEqual({stop, deny},
emqx_authz:authorize(ClientInfo1, subscribe, <<"#">>, deny, [])), emqx_authz:authorize(ClientInfo1, subscribe, <<"#">>, deny, [])),

View File

@ -18,33 +18,26 @@
-compile(nowarn_export_all). -compile(nowarn_export_all).
-compile(export_all). -compile(export_all).
% -include("emqx_authz.hrl"). -include("emqx_authz.hrl").
% -include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
% -include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct.hrl").
% -import(emqx_ct_http, [ request_api/3 -import(emqx_ct_http, [ request_api/3
% , request_api/5 , request_api/5
% , get_http_data/1 , get_http_data/1
% , create_default_app/0 , create_default_app/0
% , delete_default_app/0 , delete_default_app/0
% , default_auth_header/0 , default_auth_header/0
% ]). ]).
% -define(HOST, "http://127.0.0.1:8081/"). -define(HOST, "http://127.0.0.1:8081/").
% -define(API_VERSION, "v4"). -define(API_VERSION, "v5").
% -define(BASE_PATH, "api"). -define(BASE_PATH, "api/authorization").
-define(CONF_DEFAULT, <<""" -define(CONF_DEFAULT, <<"authorization: {rules: []}">>).
authorization:{
rules: [
]
}
""">>).
all() -> all() ->
%% TODO: V5 API emqx_ct:all(?MODULE).
%% emqx_ct:all(?MODULE).
[t_api_unit_test].
groups() -> groups() ->
[]. [].
@ -52,12 +45,15 @@ groups() ->
init_per_suite(Config) -> init_per_suite(Config) ->
ok = emqx_config:init_load(emqx_authz_schema, ?CONF_DEFAULT), ok = emqx_config:init_load(emqx_authz_schema, ?CONF_DEFAULT),
ok = emqx_ct_helpers:start_apps([emqx_authz]), ok = emqx_ct_helpers:start_apps([emqx_authz]),
ok = emqx_config:update([zones, default, authorization, cache, enable], false),
ok = emqx_config:update([zones, default, authorization, enable], true),
%create_default_app(), create_default_app(),
Config. Config.
end_per_suite(_Config) -> end_per_suite(_Config) ->
ok = emqx_authz:update(replace, []), ok = emqx_authz:update(replace, []),
delete_default_app(),
emqx_ct_helpers:stop_apps([emqx_authz]), emqx_ct_helpers:stop_apps([emqx_authz]),
ok. ok.
@ -77,29 +73,12 @@ end_per_suite(_Config) ->
% set_special_configs(_App) -> % set_special_configs(_App) ->
% ok. % ok.
% %%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
% %% Testcases %% Testcases
% %%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
t_api_unit_test(_Config) -> % t_api_unit_test(_Config) ->
%% TODO: Decode from JSON or HOCON, instead of hand-crafting decode result % %% TODO: Decode from JSON or HOCON, instead of hand-crafting decode result
Rule1 = #{<<"principal">> =>
#{<<"and">> => [#{<<"username">> => <<"^test?">>},
#{<<"clientid">> => <<"^test?">>}
]},
<<"action">> => <<"subscribe">>,
<<"topics">> => [<<"%u">>],
<<"permission">> => <<"allow">>
},
ok = emqx_authz_api:push_authz(#{}, Rule1),
[#{action := subscribe,
permission := allow,
principal :=
#{'and' := [#{username := <<"^test?">>},
#{clientid := <<"^test?">>}]},
topics := [<<"%u">>]}] = emqx_config:get([authorization, rules]).
% t_api(_Config) ->
% Rule1 = #{<<"principal">> => % Rule1 = #{<<"principal">> =>
% #{<<"and">> => [#{<<"username">> => <<"^test?">>}, % #{<<"and">> => [#{<<"username">> => <<"^test?">>},
% #{<<"clientid">> => <<"^test?">>} % #{<<"clientid">> => <<"^test?">>}
@ -108,53 +87,89 @@ t_api_unit_test(_Config) ->
% <<"topics">> => [<<"%u">>], % <<"topics">> => [<<"%u">>],
% <<"permission">> => <<"allow">> % <<"permission">> => <<"allow">>
% }, % },
% {ok, _} = request_http_rest_add(["authz/push"], #{rules => [Rule1]}), % ok = emqx_authz_api:push_authz(#{}, Rule1),
% {ok, Result1} = request_http_rest_lookup(["authz"]), % [#{action := subscribe,
% ?assertMatch([Rule1 | _ ], get_http_data(Result1)), % permission := allow,
% principal :=
% #{'and' := [#{username := <<"^test?">>},
% #{clientid := <<"^test?">>}]},
% topics := [<<"%u">>]}] = emqx_config:get([authorization, rules]).
% Rule2 = #{<<"principal">> => #{<<"ipaddress">> => <<"127.0.0.1">>}, t_post(_) ->
% <<"action">> => <<"publish">>, Rules1 = request(get, uri(), []),
% <<"topics">> => [#{<<"eq">> => <<"#">>}, ct:print("============~p~n",[Rules1]),
% #{<<"eq">> => <<"+">>} ok.
% ],
% <<"permission">> => <<"deny">>
% },
% {ok, _} = request_http_rest_add(["authz/append"], #{rules => [Rule2]}),
% {ok, Result2} = request_http_rest_lookup(["authz"]),
% ?assertEqual(Rule2#{<<"principal">> => #{<<"ipaddress">> => "127.0.0.1"}},
% lists:last(get_http_data(Result2))),
% {ok, _} = request_http_rest_update(["authz"], #{rules => []}), t_api(_Config) ->
% {ok, Result3} = request_http_rest_lookup(["authz"]), Rule1 = #{<<"principal">> =>
% ?assertEqual([], get_http_data(Result3)), #{<<"and">> => [#{<<"username">> => <<"^test?">>},
% ok. #{<<"clientid">> => <<"^test?">>}
]},
<<"action">> => <<"subscribe">>,
<<"topics">> => [<<"%u">>],
<<"permission">> => <<"allow">>
},
{ok, _} = request_http_rest_add(["authz/push"], #{rules => [Rule1]}),
{ok, Result1} = request_http_rest_lookup(["authz"]),
?assertMatch([Rule1 | _ ], get_http_data(Result1)),
% %%-------------------------------------------------------------------- Rule2 = #{<<"principal">> => #{<<"ipaddress">> => <<"127.0.0.1">>},
% %% HTTP Request <<"action">> => <<"publish">>,
% %%-------------------------------------------------------------------- <<"topics">> => [#{<<"eq">> => <<"#">>},
#{<<"eq">> => <<"+">>}
],
<<"permission">> => <<"deny">>
},
{ok, _} = request_http_rest_add(["authz/append"], #{rules => [Rule2]}),
{ok, Result2} = request_http_rest_lookup(["authz"]),
?assertEqual(Rule2#{<<"principal">> => #{<<"ipaddress">> => "127.0.0.1"}},
lists:last(get_http_data(Result2))),
% request_http_rest_list(Path) -> {ok, _} = request_http_rest_update(["authz"], #{rules => []}),
% request_api(get, uri(Path), default_auth_header()). {ok, Result3} = request_http_rest_lookup(["authz"]),
?assertEqual([], get_http_data(Result3)),
ok.
% request_http_rest_lookup(Path) -> %%--------------------------------------------------------------------
% request_api(get, uri([Path]), default_auth_header()). %% HTTP Request
%%--------------------------------------------------------------------
% request_http_rest_add(Path, Params) -> request(Method, Url, Body) ->
% request_api(post, uri(Path), [], default_auth_header(), Params). Request = case Body of
[] -> {Url, [{"username", "admin"}, {"password", "public"}]};
_ -> {Url, [{"username", "admin"}, {"password", "public"}], "application/json", Body}
end,
case httpc:request(Method, Request, [], [{body_format, binary}]) of
{error, socket_closed_remotely} ->
{error, socket_closed_remotely};
{ok, {{"HTTP/1.1", Code, _}, _Headers, Return} } ->
{ok, Code, Return};
{ok, {Reason, _, _}} ->
{error, Reason}
end.
% request_http_rest_update(Path, Params) -> request_http_rest_list(Path) ->
% request_api(put, uri([Path]), [], default_auth_header(), Params). request_api(get, uri(Path), default_auth_header()).
% request_http_rest_delete(Login) -> request_http_rest_lookup(Path) ->
% request_api(delete, uri([Login]), default_auth_header()). request_api(get, uri([Path]), default_auth_header()).
% uri() -> uri([]). request_http_rest_add(Path, Params) ->
% uri(Parts) when is_list(Parts) -> request_api(post, uri(Path), [], default_auth_header(), Params).
% NParts = [b2l(E) || E <- Parts],
% ?HOST ++ filename:join([?BASE_PATH, ?API_VERSION | NParts]).
% %% @private request_http_rest_update(Path, Params) ->
% b2l(B) when is_binary(B) -> request_api(put, uri([Path]), [], default_auth_header(), Params).
% binary_to_list(B);
% b2l(L) when is_list(L) -> request_http_rest_delete(Login) ->
% L. request_api(delete, uri([Login]), default_auth_header()).
uri() -> uri([]).
uri(Parts) when is_list(Parts) ->
NParts = [b2l(E) || E <- Parts],
?HOST ++ filename:join([?BASE_PATH, ?API_VERSION | NParts]).
%% @private
b2l(B) when is_binary(B) ->
binary_to_list(B);
b2l(L) when is_list(L) ->
L.

View File

@ -31,6 +31,7 @@ groups() ->
init_per_suite(Config) -> init_per_suite(Config) ->
meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]), meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]),
meck:expect(emqx_resource, create, fun(_, _, _) -> {ok, meck_data} end), meck:expect(emqx_resource, create, fun(_, _, _) -> {ok, meck_data} end),
meck:expect(emqx_resource, remove, fun(_) -> ok end ),
ok = emqx_ct_helpers:start_apps([emqx_authz]), ok = emqx_ct_helpers:start_apps([emqx_authz]),
ok = emqx_config:update([zones, default, authorization, cache, enable], false), ok = emqx_config:update([zones, default, authorization, cache, enable], false),

View File

@ -31,6 +31,7 @@ groups() ->
init_per_suite(Config) -> init_per_suite(Config) ->
meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]), meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]),
meck:expect(emqx_resource, create, fun(_, _, _) -> {ok, meck_data} end ), meck:expect(emqx_resource, create, fun(_, _, _) -> {ok, meck_data} end ),
meck:expect(emqx_resource, remove, fun(_) -> ok end ),
ok = emqx_ct_helpers:start_apps([emqx_authz]), ok = emqx_ct_helpers:start_apps([emqx_authz]),

View File

@ -31,6 +31,7 @@ groups() ->
init_per_suite(Config) -> init_per_suite(Config) ->
meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]), meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]),
meck:expect(emqx_resource, create, fun(_, _, _) -> {ok, meck_data} end ), meck:expect(emqx_resource, create, fun(_, _, _) -> {ok, meck_data} end ),
meck:expect(emqx_resource, remove, fun(_) -> ok end ),
ok = emqx_ct_helpers:start_apps([emqx_authz]), ok = emqx_ct_helpers:start_apps([emqx_authz]),

View File

@ -31,6 +31,7 @@ groups() ->
init_per_suite(Config) -> init_per_suite(Config) ->
meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]), meck:new(emqx_resource, [non_strict, passthrough, no_history, no_link]),
meck:expect(emqx_resource, create, fun(_, _, _) -> {ok, meck_data} end ), meck:expect(emqx_resource, create, fun(_, _, _) -> {ok, meck_data} end ),
meck:expect(emqx_resource, remove, fun(_) -> ok end ),
ok = emqx_ct_helpers:start_apps([emqx_authz]), ok = emqx_ct_helpers:start_apps([emqx_authz]),