فهرست منبع

Merge pull request #7360 from JimMoen/fix-authz-cluster-update-config

fix authz cluster update config
JianBo He 3 سال پیش
والد
کامیت
5a6023676e

+ 141 - 82
apps/emqx_authz/src/emqx_authz.erl

@@ -101,7 +101,7 @@ deinit() ->
     emqx_authz_utils:cleanup_resources().
 
 lookup() ->
-    {_M, _F, [A]}= find_action_in_hooks(),
+    {_M, _F, [A]} = find_action_in_hooks(),
     A.
 
 lookup(Type) ->
@@ -125,98 +125,103 @@ update({?CMD_DELETE, Type}, Sources) ->
 update(Cmd, Sources) ->
     emqx_authz_utils:update_config(?CONF_KEY_PATH, {Cmd, Sources}).
 
-do_update({?CMD_MOVE, Type, ?CMD_MOVE_FRONT}, Conf) when is_list(Conf) ->
-    {Source, Front, Rear} = take(Type, Conf),
-    [Source | Front] ++ Rear;
-do_update({?CMD_MOVE, Type, ?CMD_MOVE_REAR}, Conf) when is_list(Conf) ->
-    {Source, Front, Rear} = take(Type, Conf),
-    Front ++ Rear ++ [Source];
-do_update({?CMD_MOVE, Type, ?CMD_MOVE_BEFORE(Before)}, Conf) when is_list(Conf) ->
-    {S1, Front1, Rear1} = take(Type, Conf),
-    {S2, Front2, Rear2} = take(Before, Front1 ++ Rear1),
-    Front2 ++ [S1, S2] ++ Rear2;
-do_update({?CMD_MOVE, Type, ?CMD_MOVE_AFTER(After)}, Conf) when is_list(Conf) ->
-    {S1, Front1, Rear1} = take(Type, Conf),
-    {S2, Front2, Rear2} = take(After, Front1 ++ Rear1),
-    Front2 ++ [S2, S1] ++ Rear2;
-do_update({?CMD_PREPEND, Sources}, Conf) when is_list(Sources), is_list(Conf) ->
-    NConf = Sources ++ Conf,
-    ok = check_dup_types(NConf),
-    NConf;
-do_update({?CMD_APPEND, Sources}, Conf) when is_list(Sources), is_list(Conf) ->
-    NConf = Conf ++ Sources,
-    ok = check_dup_types(NConf),
-    NConf;
-do_update({{?CMD_REPLACE, Type}, #{<<"enable">> := Enable} = Source}, Conf)
-  when is_map(Source), is_list(Conf), ?IS_ENABLED(Enable) ->
-    case create_dry_run(Type, Source)  of
+pre_config_update(_, Cmd, Sources) ->
+    {ok, do_pre_config_update(Cmd, Sources)}.
+
+do_pre_config_update({?CMD_MOVE, _, _} = Cmd, Sources) ->
+    do_move(Cmd, Sources);
+do_pre_config_update({?CMD_PREPEND, Source}, Sources) ->
+    NSource = maybe_write_files(Source),
+    NSources = [NSource] ++ Sources,
+    ok = check_dup_types(NSources),
+    NSources;
+do_pre_config_update({?CMD_APPEND, Source}, Sources) ->
+    NSource = maybe_write_files(Source),
+    NSources = Sources ++ [NSource],
+    ok = check_dup_types(NSources),
+    NSources;
+do_pre_config_update({{?CMD_REPLACE, Type}, #{<<"enable">> := Enable} = Source}, Sources)
+  when ?IS_ENABLED(Enable) ->
+    NSource = maybe_write_files(Source),
+    {_Old, Front, Rear} = take(Type, Sources),
+    case create_dry_run(Type, NSource)  of
         ok ->
-            {_Old, Front, Rear} = take(Type, Conf),
-            NConf = Front ++ [Source | Rear],
-            ok = check_dup_types(NConf),
-            NConf;
-        {error, _} = Error -> Error
+            NSources = Front ++ [NSource | Rear],
+            ok = check_dup_types(NSources),
+            NSources;
+        {error, _} = Error ->
+            throw(Error)
     end;
-do_update({{?CMD_REPLACE, Type}, Source}, Conf)
-  when is_map(Source), is_list(Conf) ->
-    {_Old, Front, Rear} = take(Type, Conf),
-    NConf = Front ++ [Source | Rear],
-    ok = check_dup_types(NConf),
-    NConf;
-do_update({{?CMD_DELETE, Type}, _Source}, Conf) when is_list(Conf) ->
-    {_Old, Front, Rear} = take(Type, Conf),
-    NConf = Front ++ Rear,
-    NConf;
-do_update({_, Sources}, _Conf) when is_list(Sources)->
+do_pre_config_update({{?CMD_REPLACE, Type}, Source}, Sources) ->
+    NSource = maybe_write_files(Source),
+    {_Old, Front, Rear} = take(Type, Sources),
+    NSources = Front ++ [NSource | Rear],
+    ok = check_dup_types(NSources),
+    NSources;
+do_pre_config_update({{?CMD_DELETE, Type}, _Source}, Sources) ->
+    {_Old, Front, Rear} = take(Type, Sources),
+    NSources = Front ++ Rear,
+    NSources;
+do_pre_config_update({?CMD_REPLACE, Sources}, _OldSources) ->
     %% overwrite the entire config!
-    Sources;
-do_update({Op, Sources}, Conf) ->
-    error({bad_request, #{op => Op, sources => Sources, conf => Conf}}).
-
-pre_config_update(_, Cmd, Conf) ->
-    {ok, do_update(Cmd, Conf)}.
-
+    NSources = lists:map(fun maybe_write_files/1, Sources),
+    ok = check_dup_types(NSources),
+    NSources;
+do_pre_config_update({Op, Source}, Sources) ->
+    throw({bad_request, #{op => Op, source => Source, sources => Sources}}).
 
-post_config_update(_, _, undefined, _Conf, _AppEnvs) ->
+post_config_update(_, _, undefined, _OldSource, _AppEnvs) ->
     ok;
 post_config_update(_, Cmd, NewSources, _OldSource, _AppEnvs) ->
-    ok = do_post_update(Cmd, NewSources),
+    Actions = do_post_config_update(Cmd, NewSources),
+    ok = update_authz_chain(Actions),
     ok = emqx_authz_cache:drain_cache().
 
-do_post_update({?CMD_MOVE, _Type, _Where} = Cmd, _NewSources) ->
+do_post_config_update({?CMD_MOVE, _Type, _Where} = Cmd, _Sources) ->
     InitedSources = lookup(),
-    MovedSources = do_update(Cmd, InitedSources),
-    ok = emqx_hooks:put('client.authorize', {?MODULE, authorize, [MovedSources]}, -1),
-    ok = emqx_authz_cache:drain_cache();
-do_post_update({?CMD_PREPEND, Sources}, _NewSources) ->
-    InitedSources = init_sources(check_sources(Sources)),
-    ok = emqx_hooks:put('client.authorize', {?MODULE, authorize, [InitedSources ++ lookup()]}, -1),
-    ok = emqx_authz_cache:drain_cache();
-do_post_update({?CMD_APPEND, Sources}, _NewSources) ->
-    InitedSources = init_sources(check_sources(Sources)),
-    emqx_hooks:put('client.authorize', {?MODULE, authorize, [lookup() ++ InitedSources]}, -1),
-    ok = emqx_authz_cache:drain_cache();
-do_post_update({{?CMD_REPLACE, Type}, Source}, _NewSources) when is_map(Source) ->
-    OldInitedSources = lookup(),
-    {OldSource, Front, Rear} = take(Type, OldInitedSources),
+    do_move(Cmd, InitedSources);
+do_post_config_update({?CMD_PREPEND, RawNewSource}, Sources) ->
+    InitedNewSource = init_source(get_source_by_type(type(RawNewSource), Sources)),
+    [InitedNewSource] ++ lookup();
+do_post_config_update({?CMD_APPEND, RawNewSource}, Sources) ->
+    InitedNewSource = init_source(get_source_by_type(type(RawNewSource), Sources)),
+    lookup() ++ [InitedNewSource];
+do_post_config_update({{?CMD_REPLACE, Type}, RawNewSource}, Sources) ->
+    OldSources = lookup(),
+    {OldSource, Front, Rear} = take(Type, OldSources),
+    NewSource = get_source_by_type(type(RawNewSource), Sources),
     ok = ensure_resource_deleted(OldSource),
-    InitedSources = init_sources(check_sources([Source])),
-    ok = emqx_hooks:put( 'client.authorize'
-                       , {?MODULE, authorize, [Front ++ InitedSources ++ Rear]}, -1),
-    ok = emqx_authz_cache:drain_cache();
-do_post_update({{?CMD_DELETE, Type}, _Source}, _NewSources) ->
+    clear_certs(OldSource),
+    InitedSources = init_source(NewSource),
+    Front ++ [InitedSources] ++ Rear;
+do_post_config_update({{?CMD_DELETE, Type}, _RawNewSource}, _Sources) ->
     OldInitedSources = lookup(),
     {OldSource, Front, Rear} = take(Type, OldInitedSources),
     ok = ensure_resource_deleted(OldSource),
-    ok = emqx_hooks:put('client.authorize', {?MODULE, authorize, [Front ++ Rear]}, -1),
-    ok = emqx_authz_cache:drain_cache();
-do_post_update({?CMD_REPLACE, Sources}, _NewSources) ->
+    clear_certs(OldSource),
+    Front ++ Rear;
+do_post_config_update({?CMD_REPLACE, _RawNewSources}, Sources) ->
     %% overwrite the entire config!
     OldInitedSources = lookup(),
-    InitedSources = init_sources(check_sources(Sources)),
-    ok = emqx_hooks:put('client.authorize', {?MODULE, authorize, [InitedSources]}, -1),
     lists:foreach(fun ensure_resource_deleted/1, OldInitedSources),
-    ok = emqx_authz_cache:drain_cache().
+    lists:foreach(fun clear_certs/1, OldInitedSources),
+    init_sources(Sources).
+
+%% @doc do source move
+do_move({?CMD_MOVE, Type, ?CMD_MOVE_FRONT}, Sources) ->
+    {Source, Front, Rear} = take(Type, Sources),
+    [Source | Front] ++ Rear;
+do_move({?CMD_MOVE, Type, ?CMD_MOVE_REAR}, Sources) ->
+    {Source, Front, Rear} = take(Type, Sources),
+    Front ++ Rear ++ [Source];
+do_move({?CMD_MOVE, Type, ?CMD_MOVE_BEFORE(Before)}, Sources) ->
+    {S1, Front1, Rear1} = take(Type, Sources),
+    {S2, Front2, Rear2} = take(Before, Front1 ++ Rear1),
+    Front2 ++ [S1, S2] ++ Rear2;
+do_move({?CMD_MOVE, Type, ?CMD_MOVE_AFTER(After)}, Sources) ->
+    {S1, Front1, Rear1} = take(Type, Sources),
+    {S2, Front2, Rear2} = take(After, Front1 ++ Rear1),
+    Front2 ++ [S2, S1] ++ Rear2.
 
 ensure_resource_deleted(#{enable := false}) -> ok;
 ensure_resource_deleted(#{type := Type} = Source) ->
@@ -233,14 +238,14 @@ check_dup_types([Source | Sources], Checked) ->
     Type = case maps:get(<<"type">>, Source, maps:get(type, Source, undefined)) of
                undefined ->
                    %% this should never happen if the value is type checked by honcon schema
-                   error({bad_source_input, Source});
+                   throw({bad_source_input, Source});
                Type0 ->
                    type(Type0)
            end,
     case lists:member(Type, Checked) of
         true ->
             %% we have made it clear not to support more than one authz instance for each type
-            error({duplicated_authz_source_type, Type});
+            throw({duplicated_authz_source_type, Type});
         false ->
             check_dup_types(Sources, [Type | Checked])
     end.
@@ -307,7 +312,7 @@ do_authorize(_Client, _PubSub, _Topic, []) ->
 do_authorize(Client, PubSub, Topic, [#{enable := false} | Rest]) ->
     do_authorize(Client, PubSub, Topic, Rest);
 do_authorize(Client, PubSub, Topic,
-               [Connector = #{type := Type} | Tail] ) ->
+             [Connector = #{type := Type} | Tail] ) ->
     Module = authz_module(Type),
     case Module:authorize(Client, PubSub, Topic, Connector) of
         nomatch -> do_authorize(Client, PubSub, Topic, Tail);
@@ -332,7 +337,7 @@ take(Type, Sources) ->
     {Front, Rear} =  lists:splitwith(fun(T) -> type(T) =/= type(Type) end, Sources),
     case Rear =:= [] of
         true ->
-            error({not_found_source, Type});
+            throw({not_found_source, Type});
         _ ->
             {hd(Rear), Front, tl(Rear)}
     end.
@@ -364,8 +369,62 @@ type(<<"postgresql">>) -> postgresql;
 type('built_in_database') -> 'built_in_database';
 type(<<"built_in_database">>) -> 'built_in_database';
 %% should never happen if the input is type-checked by hocon schema
-type(Unknown) -> error({unknown_authz_source_type, Unknown}).
+type(Unknown) -> throw({unknown_authz_source_type, Unknown}).
+
+maybe_write_files(#{<<"type">> := <<"file">>} = Source) ->
+    write_acl_file(Source);
+maybe_write_files(NewSource) ->
+    maybe_write_certs(NewSource).
+
+write_acl_file(#{<<"rules">> := Rules} = Source) ->
+    NRules = check_acl_file_rules(Rules),
+    Path = acl_conf_file(),
+    {ok, _Filename} = write_file(Path, NRules),
+    maps:without([<<"rules">>], Source#{<<"path">> => Path}).
 
 %% @doc where the acl.conf file is stored.
 acl_conf_file() ->
     filename:join([emqx:data_dir(), "authz", "acl.conf"]).
+
+maybe_write_certs(#{<<"type">> := Type} = Source) ->
+    case emqx_tls_lib:ensure_ssl_files(
+           ssl_file_path(Type), maps:get(<<"ssl">>, Source, undefined)) of
+        {ok, SSL} ->
+            new_ssl_source(Source, SSL);
+        {error, Reason} ->
+            ?SLOG(error, Reason#{msg => "bad_ssl_config"}),
+            throw({bad_ssl_config, Reason})
+    end.
+
+clear_certs(OldSource) ->
+    OldSSL = maps:get(ssl, OldSource, undefined),
+    ok = emqx_tls_lib:delete_ssl_files(ssl_file_path(type(OldSource)), undefined, OldSSL).
+
+write_file(Filename, Bytes) ->
+    ok = filelib:ensure_dir(Filename),
+    case file:write_file(Filename, Bytes) of
+        ok -> {ok, iolist_to_binary(Filename)};
+        {error, Reason} ->
+            ?SLOG(error, #{filename => Filename, msg => "write_file_error", reason => Reason}),
+            throw(Reason)
+    end.
+
+ssl_file_path(Type) ->
+    filename:join(["authz", Type]).
+
+new_ssl_source(Source, undefined) ->
+    Source;
+new_ssl_source(Source, SSL) ->
+    Source#{<<"ssl">> => SSL}.
+
+get_source_by_type(Type, Sources) ->
+    {Source, _Front, _Rear} = take(Type, Sources),
+    Source.
+
+%% @doc put hook with (maybe) initialized new source and old sources
+update_authz_chain(Actions) ->
+    emqx_hooks:put('client.authorize', {?MODULE, authorize, [Actions]}, -1).
+
+check_acl_file_rules(RawRules) ->
+    %% TODO: make sure the bin rules checked
+    RawRules.

+ 6 - 7
apps/emqx_authz/src/emqx_authz_api_schema.erl

@@ -65,13 +65,12 @@ fields(redis_cluster) ->
     ++ emqx_connector_redis:fields(cluster);
 fields(file) ->
     authz_common_fields(file)
-    ++ [ {rules, #{ type => binary()
-                  , example =>
-                        <<"{allow,{username,\"^dashboard?\"},","subscribe,[\"$SYS/#\"]}.\n",
-                          "{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}.">>}}
-         %% The path will be deprecated, `acl.conf` will be fixed in subdir of `data`
-       , {path, #{ type => binary()
-                 , example => <<"acl.conf">>}}];
+    ++ [ { rules, #{ type => binary()
+                   , required => true
+                   , example =>
+                         <<"{allow,{username,\"^dashboard?\"},","subscribe,[\"$SYS/#\"]}.\n",
+                           "{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}.">>}}
+    ];
 fields(position) ->
     [ { position
       , mk( string()

+ 21 - 64
apps/emqx_authz/src/emqx_authz_api_sources.erl

@@ -27,8 +27,6 @@
 -define(BAD_REQUEST, 'BAD_REQUEST').
 -define(NOT_FOUND, 'NOT_FOUND').
 
--define(IS_TRUE(Val), ((Val =:= true) or (Val =:= <<"true">>))).
-
 -define(API_SCHEMA_MODULE, emqx_authz_api_schema).
 
 -export([ get_raw_sources/0
@@ -95,7 +93,7 @@ schema("/authorization/sources/:type") ->
      , put =>
            #{ description => <<"Update source">>
             , parameters => parameters_field()
-            , 'requestBody' => mk( hoconsc:union(authz_sources_type_refs()))
+            , 'requestBody' => mk(hoconsc:union(authz_sources_type_refs()))
             , responses =>
                   #{ 204 => <<"Authorization source updated successfully">>
                    , 400 => emqx_dashboard_swagger:error_codes([?BAD_REQUEST], <<"Bad Request">>)
@@ -168,18 +166,10 @@ sources(get, _) ->
                                   lists:append(AccIn, [read_certs(Source)])
                           end, [], get_raw_sources()),
     {200, #{sources => Sources}};
-sources(post, #{body := #{<<"type">> := <<"file">>, <<"rules">> := Rules}}) ->
-    {ok, Filename} = write_file(acl_conf_file(), Rules),
-    update_config(?CMD_PREPEND, [#{<<"type">> => <<"file">>,
-                                   <<"enable">> => true, <<"path">> => Filename}]);
-sources(post, #{body := Body}) when is_map(Body) ->
-    case maybe_write_certs(Body) of
-        Config when is_map(Config) ->
-            update_config(?CMD_PREPEND, [Config]);
-        {error, Reason} ->
-            {400, #{code => <<"BAD_REQUEST">>,
-                    message => bin(Reason)}}
-    end.
+sources(post, #{body := #{<<"type">> := <<"file">>} = Body}) ->
+    create_authz_file(Body);
+sources(post, #{body := Body}) ->
+    update_config(?CMD_PREPEND, Body).
 
 source(Method, #{bindings := #{type := Type} = Bindings } = Req)
   when is_atom(Type) ->
@@ -201,13 +191,10 @@ source(get, #{bindings := #{type := Type}}) ->
             end;
         [Source] -> {200, read_certs(Source)}
     end;
-source(put, #{bindings := #{type := <<"file">>}, body := #{<<"type">> := <<"file">>,
-                                                           <<"rules">> := Rules,
-                                                           <<"enable">> := Enable}}) ->
-    {ok, Filename} = write_file(maps:get(path, emqx_authz:lookup(file), ""), Rules),
-    case emqx_authz:update({?CMD_REPLACE, <<"file">>}, #{<<"type">> => <<"file">>,
-                                                         <<"enable">> => Enable,
-                                                         <<"path">> => Filename}) of
+source(put, #{bindings := #{type := <<"file">>}, body := #{<<"type">> := <<"file">>} = Body}) ->
+    update_authz_file(Body);
+source(put, #{bindings := #{type := Type}, body := Body}) ->
+    case emqx_authz:update({?CMD_REPLACE, Type}, Body) of
         {ok, _} -> {204};
         {error, {emqx_conf_schema, _}} ->
             {400, #{code => <<"BAD_REQUEST">>,
@@ -216,14 +203,6 @@ source(put, #{bindings := #{type := <<"file">>}, body := #{<<"type">> := <<"file
             {400, #{code => <<"BAD_REQUEST">>,
                     message => bin(Reason)}}
     end;
-source(put, #{bindings := #{type := Type}, body := Body}) when is_map(Body) ->
-    case maybe_write_certs(Body#{<<"type">> => Type}) of
-        Config when is_map(Config) ->
-            update_config({?CMD_REPLACE, Type}, Config);
-        {error, Reason} ->
-            {400, #{code => <<"BAD_REQUEST">>,
-                    message => bin(Reason)}}
-    end;
 source(delete, #{bindings := #{type := Type}}) ->
     update_config({?CMD_DELETE, Type}, #{}).
 
@@ -423,45 +402,13 @@ update_config(Cmd, Sources) ->
 read_certs(#{<<"ssl">> := SSL} = Source) ->
     case emqx_tls_lib:file_content_as_options(SSL) of
         {error, Reason} ->
-            ?SLOG(error, Reason#{msg => failed_to_readd_ssl_file}),
-            throw(failed_to_readd_ssl_file);
+            ?SLOG(error, Reason#{msg => failed_to_read_ssl_file}),
+            throw(failed_to_read_ssl_file);
         {ok, NewSSL} ->
             Source#{<<"ssl">> => NewSSL}
     end;
 read_certs(Source) -> Source.
 
-maybe_write_certs(#{<<"ssl">> := #{<<"enable">> := True} = SSL} = Source) when ?IS_TRUE(True) ->
-    Type = maps:get(<<"type">>, Source),
-    case emqx_tls_lib:ensure_ssl_files(filename:join(["authz", Type]), SSL) of
-        {ok, Return} ->
-            maps:put(<<"ssl">>, Return, Source);
-        {error, _} ->
-            {error, ensuer_ssl_files_failed}
-    end;
-maybe_write_certs(Source) -> Source.
-
-write_file(Filename, Bytes0) ->
-    ok = filelib:ensure_dir(Filename),
-    case file:read_file(Filename) of
-        {ok, Bytes1} ->
-            case crypto:hash(md5, Bytes1) =:= crypto:hash(md5, Bytes0) of
-                true -> {ok, iolist_to_binary(Filename)};
-                false -> do_write_file(Filename, Bytes0)
-            end;
-        _ -> do_write_file(Filename, Bytes0)
-    end.
-
-do_write_file(Filename, Bytes) ->
-    case file:write_file(Filename, Bytes) of
-       ok -> {ok, iolist_to_binary(Filename)};
-       {error, Reason} ->
-           ?SLOG(error, #{filename => Filename, msg => "write_file_error", reason => Reason}),
-           error(Reason)
-    end.
-
-acl_conf_file() ->
-    emqx_authz:acl_conf_file().
-
 parameters_field() ->
     [ {type, mk( enum(?API_SCHEMA_MODULE:authz_sources_types(simple))
                , #{in => path, desc => <<"Authorization type">>})
@@ -528,3 +475,13 @@ status_metrics_example() ->
                          }
                       ]
      }.
+
+create_authz_file(Body) ->
+    do_update_authz_file(?CMD_PREPEND, Body).
+
+update_authz_file(Body) ->
+    do_update_authz_file({?CMD_REPLACE, <<"file">>}, Body).
+
+do_update_authz_file(Cmd, Body) ->
+    %% API update will placed in `authz` subdirectory inside EMQX's `data_dir`
+    update_config(Cmd,  Body).

+ 1 - 0
apps/emqx_authz/src/emqx_authz_schema.erl

@@ -93,6 +93,7 @@ fields(file) ->
     , {enable, #{type => boolean(),
                  default => true}}
     , {path, #{type => string(),
+               required => true,
                desc => """
 Path to the file which contains the ACL rules.<br>
 If the file provisioned before starting EMQX node,

+ 14 - 7
apps/emqx_authz/test/emqx_authz_SUITE.erl

@@ -34,6 +34,10 @@ init_per_suite(Config) ->
     meck:expect(emqx_resource, create_local, fun(_, _, _, _) -> {ok, meck_data} end),
     meck:expect(emqx_resource, remove_local, fun(_) -> ok end),
     meck:expect(emqx_resource, create_dry_run_local, fun(_, _) -> ok end),
+    meck:expect(emqx_authz, acl_conf_file,
+                fun() ->
+                        emqx_common_test_helpers:deps_path(emqx_authz, "etc/acl.conf")
+                end),
 
     ok = emqx_common_test_helpers:start_apps(
            [emqx_connector, emqx_conf, emqx_authz],
@@ -116,7 +120,9 @@ set_special_configs(_App) ->
                   }).
 -define(SOURCE6, #{<<"type">> => <<"file">>,
                    <<"enable">> => true,
-                   <<"path">> => emqx_common_test_helpers:deps_path(emqx_authz, "etc/acl.conf")
+                   <<"rules">> =>
+<<"{allow,{username,\"^dashboard?\"},subscribe,[\"$SYS/#\"]}."
+  "\n{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}.">>
                   }).
 
 
@@ -125,12 +131,13 @@ set_special_configs(_App) ->
 %%------------------------------------------------------------------------------
 
 t_update_source(_) ->
+    %% replace all
     {ok, _} = emqx_authz:update(?CMD_REPLACE, [?SOURCE3]),
-    {ok, _} = emqx_authz:update(?CMD_PREPEND, [?SOURCE2]),
-    {ok, _} = emqx_authz:update(?CMD_PREPEND, [?SOURCE1]),
-    {ok, _} = emqx_authz:update(?CMD_APPEND, [?SOURCE4]),
-    {ok, _} = emqx_authz:update(?CMD_APPEND, [?SOURCE5]),
-    {ok, _} = emqx_authz:update(?CMD_APPEND, [?SOURCE6]),
+    {ok, _} = emqx_authz:update(?CMD_PREPEND, ?SOURCE2),
+    {ok, _} = emqx_authz:update(?CMD_PREPEND, ?SOURCE1),
+    {ok, _} = emqx_authz:update(?CMD_APPEND, ?SOURCE4),
+    {ok, _} = emqx_authz:update(?CMD_APPEND, ?SOURCE5),
+    {ok, _} = emqx_authz:update(?CMD_APPEND, ?SOURCE6),
 
     ?assertMatch([ #{type := http,  enable := true}
                  , #{type := mongodb, enable := true}
@@ -170,7 +177,7 @@ t_delete_source(_) ->
     ?assertMatch([ #{type := http,  enable := true}
                  ], emqx_conf:get([authorization, sources], [])),
 
-    {ok, _} = emqx_authz:update({?CMD_DELETE, http},  #{}),
+    {ok, _} = emqx_authz:update({?CMD_DELETE, http}, #{}),
 
     ?assertMatch([], emqx_conf:get([authorization, sources], [])).
 

+ 3 - 3
apps/emqx_authz/test/emqx_authz_api_mnesia_SUITE.erl

@@ -230,16 +230,16 @@ t_api(_) ->
     {ok, 204, _} =
         request( put
                , uri(["authorization", "sources", "built_in_database"])
-               ,  #{<<"enable">> => true}),
+               , #{<<"enable">> => true, <<"type">> => <<"built_in_database">>}),
     %% test idempotence
     {ok, 204, _} =
         request( put
                , uri(["authorization", "sources", "built_in_database"])
-               ,  #{<<"enable">> => true}),
+               , #{<<"enable">> => true, <<"type">> => <<"built_in_database">>}),
     {ok, 204, _} =
         request( put
                , uri(["authorization", "sources", "built_in_database"])
-               ,  #{<<"enable">> => false}),
+               , #{<<"enable">> => false, <<"type">> => <<"built_in_database">>}),
     {ok, 204, _} =
         request( delete
                , uri(["authorization", "sources", "built_in_database", "purge-all"])

+ 4 - 0
apps/emqx_authz/test/emqx_authz_api_sources_SUITE.erl

@@ -108,6 +108,10 @@ init_per_suite(Config) ->
                 end),
     meck:expect(emqx_resource, health_check, fun(St) -> {ok, St} end),
     meck:expect(emqx_resource, remove_local, fun(_) -> ok end ),
+    meck:expect(emqx_authz, acl_conf_file,
+                fun() ->
+                        emqx_common_test_helpers:deps_path(emqx_authz, "etc/acl.conf")
+                end),
 
     ok = emqx_common_test_helpers:start_apps(
            [emqx_conf, emqx_authz, emqx_dashboard],

+ 20 - 37
apps/emqx_authz/test/emqx_authz_file_SUITE.erl

@@ -22,6 +22,13 @@
 -include_lib("eunit/include/eunit.hrl").
 -include_lib("common_test/include/ct.hrl").
 
+-define(RAW_SOURCE, #{<<"type">> => <<"file">>,
+                      <<"enable">> => true,
+                      <<"rules">> =>
+<<"{allow,{username,\"^dashboard?\"},subscribe,[\"$SYS/#\"]}."
+  "\n{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}.">>
+                     }).
+
 all() ->
     emqx_common_test_helpers:all(?MODULE).
 
@@ -32,6 +39,11 @@ init_per_suite(Config) ->
     ok = emqx_common_test_helpers:start_apps(
            [emqx_conf, emqx_authz],
            fun set_special_configs/1),
+    %% meck after authz started
+    meck:expect(emqx_authz, acl_conf_file,
+                fun() ->
+                        emqx_common_test_helpers:deps_path(emqx_authz, "etc/acl.conf")
+                end),
     Config.
 
 end_per_suite(_Config) ->
@@ -61,8 +73,9 @@ t_ok(_Config) ->
                    listener => {tcp, default}
                   },
 
-    ok = setup_rules([{allow, {user, "username"}, publish, ["t"]}]),
-    ok = setup_config(#{}),
+    ok = setup_config(?RAW_SOURCE#{<<"rules">> => <<"{allow, {user, \"username\"}, publish, [\"t\"]}.">>}),
+
+    io:format("~p", [emqx_authz:acl_conf_file()]),
 
     ?assertEqual(
        allow,
@@ -73,61 +86,31 @@ t_ok(_Config) ->
        emqx_access_control:authorize(ClientInfo, subscribe, <<"t">>)).
 
 t_invalid_file(_Config) ->
-    ok = file:write_file(<<"acl.conf">>, <<"{{invalid term">>),
-
     ?assertMatch(
        {error, bad_acl_file_content},
-       emqx_authz:update(?CMD_REPLACE, [raw_file_authz_config()])).
-
-t_nonexistent_file(_Config) ->
-    ?assertEqual(
-       {error, failed_to_read_acl_file},
-       emqx_authz:update(?CMD_REPLACE,
-                         [maps:merge(raw_file_authz_config(),
-                                     #{<<"path">> => <<"nonexistent.conf">>})
-                         ])).
+       emqx_authz:update(?CMD_REPLACE, [?RAW_SOURCE#{<<"rules">> => <<"{{invalid term">>}])).
 
 t_update(_Config) ->
-    ok = setup_rules([{allow, {user, "username"}, publish, ["t"]}]),
-    ok = setup_config(#{}),
+    ok = setup_config(?RAW_SOURCE#{<<"rules">> => <<"{allow, {user, \"username\"}, publish, [\"t\"]}.">>}),
 
     ?assertMatch(
        {error, _},
        emqx_authz:update(
          {?CMD_REPLACE, file},
-         maps:merge(raw_file_authz_config(),
-                    #{<<"path">> => <<"nonexistent.conf">>}))),
+         ?RAW_SOURCE#{<<"rules">> => <<"{{invalid term">>})),
 
     ?assertMatch(
        {ok, _},
        emqx_authz:update(
-         {?CMD_REPLACE, file},
-         raw_file_authz_config())).
+         {?CMD_REPLACE, file}, ?RAW_SOURCE)).
 
 %%------------------------------------------------------------------------------
 %% Helpers
 %%------------------------------------------------------------------------------
 
-raw_file_authz_config() ->
-    #{
-        <<"enable">> => <<"true">>,
-
-        <<"type">> => <<"file">>,
-        <<"path">> => <<"acl.conf">>
-    }.
-
-setup_rules(Rules) ->
-    {ok, F} = file:open(<<"acl.conf">>, [write]),
-    lists:foreach(
-      fun(Rule) ->
-              io:format(F, "~p.~n", [Rule])
-      end,
-      Rules),
-    ok = file:close(F).
-
 setup_config(SpecialParams) ->
     emqx_authz_test_lib:setup_config(
-      raw_file_authz_config(),
+      ?RAW_SOURCE,
       SpecialParams).
 
 stop_apps(Apps) ->