diff --git a/apps/emqx_auth/src/emqx_authn/emqx_authn_utils.erl b/apps/emqx_auth/src/emqx_authn/emqx_authn_utils.erl index 13f8f238b..3e12e3992 100644 --- a/apps/emqx_auth/src/emqx_authn/emqx_authn_utils.erl +++ b/apps/emqx_auth/src/emqx_authn/emqx_authn_utils.erl @@ -46,14 +46,16 @@ 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 +%% VAR_NS_CLIENT_ATTRS is added here because it can be initialized before authn. +%% NOTE: authn return may add more to (or even overwrite) client_attrs. -define(ALLOWED_VARS, [ ?VAR_USERNAME, ?VAR_CLIENTID, ?VAR_PASSWORD, ?VAR_PEERHOST, ?VAR_CERT_SUBJECT, - ?VAR_CERT_CN_NAME + ?VAR_CERT_CN_NAME, + ?VAR_NS_CLIENT_ATTRS ]). -define(DEFAULT_RESOURCE_OPTS, #{ diff --git a/apps/emqx_auth/src/emqx_authz/emqx_authz_rule.erl b/apps/emqx_auth/src/emqx_authz/emqx_authz_rule.erl index 68ee05b52..9a877b5c8 100644 --- a/apps/emqx_auth/src/emqx_authz/emqx_authz_rule.erl +++ b/apps/emqx_auth/src/emqx_authz/emqx_authz_rule.erl @@ -88,6 +88,7 @@ -type rule_precompile() :: {permission(), who_condition(), action_precompile(), [topic_filter()]}. -define(IS_PERMISSION(Permission), (Permission =:= allow orelse Permission =:= deny)). +-define(ALLOWED_VARS, [?VAR_USERNAME, ?VAR_CLIENTID, ?VAR_NS_CLIENT_ATTRS]). -spec compile(permission(), who_condition(), action_precompile(), [topic_filter()]) -> rule(). compile(Permission, Who, Action, TopicFilters) -> @@ -223,9 +224,7 @@ compile_topic(<<"eq ", Topic/binary>>) -> compile_topic({eq, Topic}) -> {eq, emqx_topic:words(bin(Topic))}; compile_topic(Topic) -> - Template = emqx_authz_utils:parse_str(Topic, [ - ?VAR_USERNAME, ?VAR_CLIENTID, ?VAR_NS_CLIENT_ATTRS - ]), + Template = emqx_authz_utils:parse_str(Topic, ?ALLOWED_VARS), case emqx_template:is_const(Template) of true -> emqx_topic:words(bin(Topic)); false -> {pattern, Template} diff --git a/apps/emqx_auth_http/test/emqx_authn_http_SUITE.erl b/apps/emqx_auth_http/test/emqx_authn_http_SUITE.erl index 7b2aae9f1..3f16c9e2c 100644 --- a/apps/emqx_auth_http/test/emqx_authn_http_SUITE.erl +++ b/apps/emqx_auth_http/test/emqx_authn_http_SUITE.erl @@ -36,7 +36,8 @@ listener => 'tcp:default', protocol => mqtt, cert_subject => <<"cert_subject_data">>, - cert_common_name => <<"cert_common_name_data">> + cert_common_name => <<"cert_common_name_data">>, + client_attrs => #{<<"group">> => <<"g1">>} }). -define(SERVER_RESPONSE_JSON(Result), ?SERVER_RESPONSE_JSON(Result, false)). @@ -655,7 +656,8 @@ samples() -> <<"clientid">> := <<"clienta">>, <<"peerhost">> := <<"127.0.0.1">>, <<"cert_subject">> := <<"cert_subject_data">>, - <<"cert_common_name">> := <<"cert_common_name_data">> + <<"cert_common_name">> := <<"cert_common_name_data">>, + <<"the_group">> := <<"g1">> } = emqx_utils_json:decode(RawBody, [return_maps]), Req = cowboy_req:reply( 200, @@ -674,7 +676,8 @@ samples() -> <<"password">> => ?PH_PASSWORD, <<"peerhost">> => ?PH_PEERHOST, <<"cert_subject">> => ?PH_CERT_SUBJECT, - <<"cert_common_name">> => ?PH_CERT_CN_NAME + <<"cert_common_name">> => ?PH_CERT_CN_NAME, + <<"the_group">> => <<"${client_attrs.group}">> } }, result => {ok, #{is_superuser => false, client_attrs => #{}}} diff --git a/rel/i18n/emqx_schema.hocon b/rel/i18n/emqx_schema.hocon index 8232b4fe7..b65cb76b3 100644 --- a/rel/i18n/emqx_schema.hocon +++ b/rel/i18n/emqx_schema.hocon @@ -1578,8 +1578,8 @@ client_attrs_init { One initial client attribute can be initialized as `client_attrs.NAME`, where `NAME` is the name of the attribute specified in the config `extract_as`. The initialized client attribute will be stored in the `client_attr` property with the specified name, - and can be used as a placeholder in a template. - For example, `${client_attrs.alias}` if `extract_as` is set to `alias`.""" + and can be used as a placeholder in a template for authentication and authorization. + For example, use `${client_attrs.alias}` to render a HTTP POST body when `extract_as = alias`.""" } client_attrs_init_extract_from {