feat: allow client_attr used in authz rules
This commit is contained in:
parent
e5816f5a13
commit
9ec99fef4a
|
@ -36,6 +36,7 @@
|
||||||
-define(VAR_CLIENTID, "clientid").
|
-define(VAR_CLIENTID, "clientid").
|
||||||
-define(VAR_USERNAME, "username").
|
-define(VAR_USERNAME, "username").
|
||||||
-define(VAR_TOPIC, "topic").
|
-define(VAR_TOPIC, "topic").
|
||||||
|
-define(VAR_NS_CLIENT_ATTRS, {var_namespace, "client_attrs"}).
|
||||||
-define(PH_PASSWORD, ?PH(?VAR_PASSWORD)).
|
-define(PH_PASSWORD, ?PH(?VAR_PASSWORD)).
|
||||||
-define(PH_CLIENTID, ?PH(?VAR_CLIENTID)).
|
-define(PH_CLIENTID, ?PH(?VAR_CLIENTID)).
|
||||||
-define(PH_FROM_CLIENTID, ?PH("from_clientid")).
|
-define(PH_FROM_CLIENTID, ?PH("from_clientid")).
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
default_headers_no_content_type/0
|
default_headers_no_content_type/0
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
%% VAR_NS_CLIENT_ATTRS is not added to this list because client_attrs is to be initialized from authn result
|
||||||
-define(ALLOWED_VARS, [
|
-define(ALLOWED_VARS, [
|
||||||
?VAR_USERNAME,
|
?VAR_USERNAME,
|
||||||
?VAR_CLIENTID,
|
?VAR_CLIENTID,
|
||||||
|
|
|
@ -223,7 +223,9 @@ compile_topic(<<"eq ", Topic/binary>>) ->
|
||||||
compile_topic({eq, Topic}) ->
|
compile_topic({eq, Topic}) ->
|
||||||
{eq, emqx_topic:words(bin(Topic))};
|
{eq, emqx_topic:words(bin(Topic))};
|
||||||
compile_topic(Topic) ->
|
compile_topic(Topic) ->
|
||||||
Template = emqx_authz_utils:parse_str(Topic, [?VAR_USERNAME, ?VAR_CLIENTID]),
|
Template = emqx_authz_utils:parse_str(Topic, [
|
||||||
|
?VAR_USERNAME, ?VAR_CLIENTID, ?VAR_NS_CLIENT_ATTRS
|
||||||
|
]),
|
||||||
case emqx_template:is_const(Template) of
|
case emqx_template:is_const(Template) of
|
||||||
true -> emqx_topic:words(bin(Topic));
|
true -> emqx_topic:words(bin(Topic));
|
||||||
false -> {pattern, Template}
|
false -> {pattern, Template}
|
||||||
|
|
|
@ -74,6 +74,30 @@ t_ok(_Config) ->
|
||||||
emqx_access_control:authorize(ClientInfo, ?AUTHZ_SUBSCRIBE, <<"t">>)
|
emqx_access_control:authorize(ClientInfo, ?AUTHZ_SUBSCRIBE, <<"t">>)
|
||||||
).
|
).
|
||||||
|
|
||||||
|
t_client_attrs(_Config) ->
|
||||||
|
ClientInfo0 = emqx_authz_test_lib:base_client_info(),
|
||||||
|
ClientInfo = ClientInfo0#{client_attrs => #{<<"device_id">> => <<"id1">>}},
|
||||||
|
|
||||||
|
ok = setup_config(?RAW_SOURCE#{
|
||||||
|
<<"rules">> => <<"{allow, all, all, [\"t/${client_attrs.device_id}/#\"]}.">>
|
||||||
|
}),
|
||||||
|
|
||||||
|
?assertEqual(
|
||||||
|
allow,
|
||||||
|
emqx_access_control:authorize(ClientInfo, ?AUTHZ_PUBLISH, <<"t/id1/1">>)
|
||||||
|
),
|
||||||
|
|
||||||
|
?assertEqual(
|
||||||
|
allow,
|
||||||
|
emqx_access_control:authorize(ClientInfo, ?AUTHZ_SUBSCRIBE, <<"t/id1/#">>)
|
||||||
|
),
|
||||||
|
|
||||||
|
?assertEqual(
|
||||||
|
deny,
|
||||||
|
emqx_access_control:authorize(ClientInfo, ?AUTHZ_SUBSCRIBE, <<"t/id2/#">>)
|
||||||
|
),
|
||||||
|
ok.
|
||||||
|
|
||||||
t_rich_actions(_Config) ->
|
t_rich_actions(_Config) ->
|
||||||
ClientInfo = emqx_authz_test_lib:base_client_info(),
|
ClientInfo = emqx_authz_test_lib:base_client_info(),
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,8 @@
|
||||||
?VAR_TOPIC,
|
?VAR_TOPIC,
|
||||||
?VAR_ACTION,
|
?VAR_ACTION,
|
||||||
?VAR_CERT_SUBJECT,
|
?VAR_CERT_SUBJECT,
|
||||||
?VAR_CERT_CN_NAME
|
?VAR_CERT_CN_NAME,
|
||||||
|
?VAR_NS_CLIENT_ATTRS
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-define(ALLOWED_VARS_RICH_ACTIONS, [
|
-define(ALLOWED_VARS_RICH_ACTIONS, [
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
%% -*- mode: erlang -*-
|
%% -*- mode: erlang -*-
|
||||||
{application, emqx_auth_mongodb, [
|
{application, emqx_auth_mongodb, [
|
||||||
{description, "EMQX MongoDB Authentication and Authorization"},
|
{description, "EMQX MongoDB Authentication and Authorization"},
|
||||||
{vsn, "0.1.1"},
|
{vsn, "0.2.0"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{mod, {emqx_auth_mongodb_app, []}},
|
{mod, {emqx_auth_mongodb_app, []}},
|
||||||
{applications, [
|
{applications, [
|
||||||
|
|
|
@ -40,7 +40,8 @@
|
||||||
?VAR_CLIENTID,
|
?VAR_CLIENTID,
|
||||||
?VAR_PEERHOST,
|
?VAR_PEERHOST,
|
||||||
?VAR_CERT_CN_NAME,
|
?VAR_CERT_CN_NAME,
|
||||||
?VAR_CERT_SUBJECT
|
?VAR_CERT_SUBJECT,
|
||||||
|
?VAR_NS_CLIENT_ATTRS
|
||||||
]).
|
]).
|
||||||
|
|
||||||
description() ->
|
description() ->
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
%% -*- mode: erlang -*-
|
%% -*- mode: erlang -*-
|
||||||
{application, emqx_auth_mysql, [
|
{application, emqx_auth_mysql, [
|
||||||
{description, "EMQX MySQL Authentication and Authorization"},
|
{description, "EMQX MySQL Authentication and Authorization"},
|
||||||
{vsn, "0.1.2"},
|
{vsn, "0.2.0"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{mod, {emqx_auth_mysql_app, []}},
|
{mod, {emqx_auth_mysql_app, []}},
|
||||||
{applications, [
|
{applications, [
|
||||||
|
|
|
@ -42,7 +42,8 @@
|
||||||
?VAR_CLIENTID,
|
?VAR_CLIENTID,
|
||||||
?VAR_PEERHOST,
|
?VAR_PEERHOST,
|
||||||
?VAR_CERT_CN_NAME,
|
?VAR_CERT_CN_NAME,
|
||||||
?VAR_CERT_SUBJECT
|
?VAR_CERT_SUBJECT,
|
||||||
|
?VAR_NS_CLIENT_ATTRS
|
||||||
]).
|
]).
|
||||||
|
|
||||||
description() ->
|
description() ->
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
%% -*- mode: erlang -*-
|
%% -*- mode: erlang -*-
|
||||||
{application, emqx_auth_postgresql, [
|
{application, emqx_auth_postgresql, [
|
||||||
{description, "EMQX PostgreSQL Authentication and Authorization"},
|
{description, "EMQX PostgreSQL Authentication and Authorization"},
|
||||||
{vsn, "0.1.1"},
|
{vsn, "0.2.0"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{mod, {emqx_auth_postgresql_app, []}},
|
{mod, {emqx_auth_postgresql_app, []}},
|
||||||
{applications, [
|
{applications, [
|
||||||
|
|
|
@ -42,7 +42,8 @@
|
||||||
?VAR_CLIENTID,
|
?VAR_CLIENTID,
|
||||||
?VAR_PEERHOST,
|
?VAR_PEERHOST,
|
||||||
?VAR_CERT_CN_NAME,
|
?VAR_CERT_CN_NAME,
|
||||||
?VAR_CERT_SUBJECT
|
?VAR_CERT_SUBJECT,
|
||||||
|
?VAR_NS_CLIENT_ATTRS
|
||||||
]).
|
]).
|
||||||
|
|
||||||
description() ->
|
description() ->
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
%% -*- mode: erlang -*-
|
%% -*- mode: erlang -*-
|
||||||
{application, emqx_auth_redis, [
|
{application, emqx_auth_redis, [
|
||||||
{description, "EMQX Redis Authentication and Authorization"},
|
{description, "EMQX Redis Authentication and Authorization"},
|
||||||
{vsn, "0.1.2"},
|
{vsn, "0.2.0"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{mod, {emqx_auth_redis_app, []}},
|
{mod, {emqx_auth_redis_app, []}},
|
||||||
{applications, [
|
{applications, [
|
||||||
|
|
|
@ -40,7 +40,8 @@
|
||||||
?VAR_CERT_SUBJECT,
|
?VAR_CERT_SUBJECT,
|
||||||
?VAR_PEERHOST,
|
?VAR_PEERHOST,
|
||||||
?VAR_CLIENTID,
|
?VAR_CLIENTID,
|
||||||
?VAR_USERNAME
|
?VAR_USERNAME,
|
||||||
|
?VAR_NS_CLIENT_ATTRS
|
||||||
]).
|
]).
|
||||||
|
|
||||||
description() ->
|
description() ->
|
||||||
|
|
|
@ -145,18 +145,42 @@ parse_accessor(Var) ->
|
||||||
%% @doc Validate a template against a set of allowed variables.
|
%% @doc Validate a template against a set of allowed variables.
|
||||||
%% If the given template contains any variable not in the allowed set, an error
|
%% If the given template contains any variable not in the allowed set, an error
|
||||||
%% is returned.
|
%% is returned.
|
||||||
-spec validate([varname()], t()) ->
|
-spec validate([varname() | {var_namespace, varname()}], t()) ->
|
||||||
ok | {error, [_Error :: {varname(), disallowed}]}.
|
ok | {error, [_Error :: {varname(), disallowed}]}.
|
||||||
validate(Allowed, Template) ->
|
validate(Allowed, Template) ->
|
||||||
{_, Errors} = render(Template, #{}),
|
{_, Errors} = render(Template, #{}),
|
||||||
{Used, _} = lists:unzip(Errors),
|
{Used, _} = lists:unzip(Errors),
|
||||||
case lists:usort(Used) -- Allowed of
|
case find_disallowed(lists:usort(Used), Allowed) of
|
||||||
[] ->
|
[] ->
|
||||||
ok;
|
ok;
|
||||||
Disallowed ->
|
Disallowed ->
|
||||||
{error, [{Var, disallowed} || Var <- Disallowed]}
|
{error, [{Var, disallowed} || Var <- Disallowed]}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
find_disallowed([], _Allowed) ->
|
||||||
|
[];
|
||||||
|
find_disallowed([Var | Rest], Allowed) ->
|
||||||
|
case is_allowed(Var, Allowed) of
|
||||||
|
true ->
|
||||||
|
find_disallowed(Rest, Allowed);
|
||||||
|
false ->
|
||||||
|
[Var | find_disallowed(Rest, Allowed)]
|
||||||
|
end.
|
||||||
|
|
||||||
|
is_allowed(_Var, []) ->
|
||||||
|
false;
|
||||||
|
is_allowed(Var, [{var_namespace, VarPrefix} | Allowed]) ->
|
||||||
|
case lists:prefix(VarPrefix ++ ".", Var) of
|
||||||
|
true ->
|
||||||
|
true;
|
||||||
|
false ->
|
||||||
|
is_allowed(Var, Allowed)
|
||||||
|
end;
|
||||||
|
is_allowed(Var, [Var | _Allowed]) ->
|
||||||
|
true;
|
||||||
|
is_allowed(Var, [_ | Allowed]) ->
|
||||||
|
is_allowed(Var, Allowed).
|
||||||
|
|
||||||
%% @doc Check if a template is constant with respect to rendering, i.e. does not
|
%% @doc Check if a template is constant with respect to rendering, i.e. does not
|
||||||
%% contain any placeholders.
|
%% contain any placeholders.
|
||||||
-spec is_const(t()) ->
|
-spec is_const(t()) ->
|
||||||
|
|
|
@ -337,6 +337,18 @@ t_unparse_tmpl_deep(_) ->
|
||||||
Template = emqx_template:parse_deep(Term),
|
Template = emqx_template:parse_deep(Term),
|
||||||
?assertEqual(Term, emqx_template:unparse(Template)).
|
?assertEqual(Term, emqx_template:unparse(Template)).
|
||||||
|
|
||||||
|
t_allow_var_by_namespace(_) ->
|
||||||
|
Context = #{d => #{d1 => <<"hi">>}},
|
||||||
|
Template = emqx_template:parse(<<"d.d1:${d.d1}">>),
|
||||||
|
?assertEqual(
|
||||||
|
ok,
|
||||||
|
emqx_template:validate([{var_namespace, "d"}], Template)
|
||||||
|
),
|
||||||
|
?assertEqual(
|
||||||
|
{<<"d.d1:hi">>, []},
|
||||||
|
render_string(Template, Context)
|
||||||
|
).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
render_string(Template, Context) ->
|
render_string(Template, Context) ->
|
||||||
|
|
Loading…
Reference in New Issue