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

Merge pull request #13863 from zmstone/0924-fix-acl-file-placeholder

feat: allow TLS cert common name in file authz templates
zmstone 1 год назад
Родитель
Сommit
d170948dfb

+ 2 - 1
apps/emqx/src/emqx.erl

@@ -217,8 +217,9 @@ update_config(KeyPath, UpdateReq, Opts) ->
 ) ->
     {ok, emqx_config:update_result()} | {error, emqx_config:update_error()}.
 update_config([RootName | _] = KeyPath, UpdateReq, Opts, ClusterRpcOpts) ->
+    Mod = emqx_config:get_schema_mod(RootName),
     emqx_config_handler:update_config(
-        emqx_config:get_schema_mod(RootName),
+        Mod,
         KeyPath,
         {{update, UpdateReq}, Opts},
         ClusterRpcOpts

+ 2 - 2
apps/emqx_auth/etc/acl.conf

@@ -113,9 +113,9 @@
 %%   topics prefixed by their own client ID.
 %%
 %%   Supported placeholders are:
-%%   - `${cn}`: TLS certificate common name.
-%%   - `${clientid}`: The client ID.
 %%   - `${username}`: The username.
+%%   - `${clientid}`: The client ID.
+%%   - `${cert_common_name}`: TLS certificate common name.
 %%   - `${client_attrs.NAME}`: A client attribute named `NAME`, which can be initialized by
 %%     `mqtt.client_attrs_init` config or extended by certain authentication backends.
 %%   NOTE: Placeholder is not rendered as empty string if the referencing value is not

+ 5 - 1
apps/emqx_auth/src/emqx_auth_utils.erl

@@ -29,7 +29,8 @@
     render_deep_for_raw/2,
     render_str/2,
     render_urlencoded_str/2,
-    render_sql_params/2
+    render_sql_params/2,
+    render_strict/2
 ]).
 
 %% URL parsing
@@ -216,6 +217,9 @@ render_var(?VAR_PASSWORD, Value) ->
 render_var(_Name, Value) ->
     Value.
 
+render_strict(Topic, ClientInfo) ->
+    emqx_template:render_strict(Topic, rename_client_info_vars(ClientInfo)).
+
 rename_client_info_vars(ClientInfo) ->
     Renames = [
         {cn, cert_common_name},

+ 2 - 0
apps/emqx_auth/src/emqx_authz/emqx_authz.erl

@@ -713,6 +713,8 @@ format_for_api(Source) ->
 
 maybe_write_source_files(Source) ->
     Module = authz_module(type(Source)),
+    %% ensure module loaded
+    _ = catch Module:module_info(module),
     case erlang:function_exported(Module, write_files, 1) of
         true ->
             Module:write_files(Source);

+ 7 - 2
apps/emqx_auth/src/emqx_authz/emqx_authz_rule.erl

@@ -122,7 +122,12 @@
 ]).
 
 -define(IS_PERMISSION(Permission), (Permission =:= allow orelse Permission =:= deny)).
--define(ALLOWED_VARS, [?VAR_USERNAME, ?VAR_CLIENTID, ?VAR_NS_CLIENT_ATTRS]).
+-define(ALLOWED_VARS, [
+    ?VAR_USERNAME,
+    ?VAR_CLIENTID,
+    ?VAR_CERT_CN_NAME,
+    ?VAR_NS_CLIENT_ATTRS
+]).
 
 -spec compile(permission_resolution_precompile(), who_precompile(), action_precompile(), [
     topic_precompile()
@@ -402,7 +407,7 @@ match_topic(Topic, TopicFilter) ->
 
 render_topic(Topic, ClientInfo) ->
     try
-        bin(emqx_template:render_strict(Topic, ClientInfo))
+        bin(emqx_auth_utils:render_strict(Topic, ClientInfo))
     catch
         error:Reason ->
             ?SLOG(debug, #{

+ 23 - 0
apps/emqx_auth/test/emqx_authz/emqx_authz_file_SUITE.erl

@@ -98,6 +98,29 @@ t_client_attrs(_Config) ->
     ),
     ok.
 
+t_cert_common_name(_Config) ->
+    ClientInfo0 = emqx_authz_test_lib:base_client_info(),
+    ClientInfo = ClientInfo0#{cn => <<"mycn">>},
+    ok = setup_config(?RAW_SOURCE#{
+        <<"rules">> => <<"{allow, all, all, [\"t/${cert_common_name}/#\"]}.">>
+    }),
+
+    ?assertEqual(
+        allow,
+        emqx_access_control:authorize(ClientInfo, ?AUTHZ_PUBLISH, <<"t/mycn/1">>)
+    ),
+
+    ?assertEqual(
+        allow,
+        emqx_access_control:authorize(ClientInfo, ?AUTHZ_SUBSCRIBE, <<"t/mycn/#">>)
+    ),
+
+    ?assertEqual(
+        deny,
+        emqx_access_control:authorize(ClientInfo, ?AUTHZ_SUBSCRIBE, <<"t/othercn/1">>)
+    ),
+    ok.
+
 t_rich_actions(_Config) ->
     ClientInfo = emqx_authz_test_lib:base_client_info(),
 

+ 1 - 0
changes/ce/feat-13863.en.md

@@ -0,0 +1 @@
+Support `${cert_common_name}` placeholder in topic name templates for raw ACL rules.