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

fix(authz): prohibit overriding of existing client/user

firest 3 лет назад
Родитель
Сommit
86a3ac0bef

+ 60 - 14
apps/emqx_authz/src/emqx_authz_api_mnesia.erl

@@ -57,6 +57,7 @@
 
 
 -define(BAD_REQUEST, 'BAD_REQUEST').
 -define(BAD_REQUEST, 'BAD_REQUEST').
 -define(NOT_FOUND, 'NOT_FOUND').
 -define(NOT_FOUND, 'NOT_FOUND').
+-define(ALREADY_EXISTS, 'ALREADY_EXISTS').
 
 
 -define(TYPE_REF, ref).
 -define(TYPE_REF, ref).
 -define(TYPE_ARRAY, array).
 -define(TYPE_ARRAY, array).
@@ -120,6 +121,9 @@ schema("/authorization/sources/built_in_database/username") ->
                         204 => <<"Created">>,
                         204 => <<"Created">>,
                         400 => emqx_dashboard_swagger:error_codes(
                         400 => emqx_dashboard_swagger:error_codes(
                             [?BAD_REQUEST], <<"Bad username or bad rule schema">>
                             [?BAD_REQUEST], <<"Bad username or bad rule schema">>
+                        ),
+                        409 => emqx_dashboard_swagger:error_codes(
+                            [?ALREADY_EXISTS], <<"ALREADY_EXISTS">>
                         )
                         )
                     }
                     }
             }
             }
@@ -416,13 +420,21 @@ users(get, #{query_string := QueryString}) ->
             {200, Result}
             {200, Result}
     end;
     end;
 users(post, #{body := Body}) when is_list(Body) ->
 users(post, #{body := Body}) when is_list(Body) ->
-    lists:foreach(
-        fun(#{<<"username">> := Username, <<"rules">> := Rules}) ->
-            emqx_authz_mnesia:store_rules({username, Username}, format_rules(Rules))
-        end,
-        Body
-    ),
-    {204}.
+    case ensure_all_not_exists(<<"username">>, username, Body) of
+        [] ->
+            lists:foreach(
+                fun(#{<<"username">> := Username, <<"rules">> := Rules}) ->
+                    emqx_authz_mnesia:store_rules({username, Username}, format_rules(Rules))
+                end,
+                Body
+            ),
+            {204};
+        Exists ->
+            {409, #{
+                code => <<"ALREADY_EXISTS">>,
+                message => binfmt("Users '~ts' already exist", [binjoin(Exists)])
+            }}
+    end.
 
 
 clients(get, #{query_string := QueryString}) ->
 clients(get, #{query_string := QueryString}) ->
     case
     case
@@ -443,13 +455,21 @@ clients(get, #{query_string := QueryString}) ->
             {200, Result}
             {200, Result}
     end;
     end;
 clients(post, #{body := Body}) when is_list(Body) ->
 clients(post, #{body := Body}) when is_list(Body) ->
-    lists:foreach(
-        fun(#{<<"clientid">> := ClientID, <<"rules">> := Rules}) ->
-            emqx_authz_mnesia:store_rules({clientid, ClientID}, format_rules(Rules))
-        end,
-        Body
-    ),
-    {204}.
+    case ensure_all_not_exists(<<"clientid">>, clientid, Body) of
+        [] ->
+            lists:foreach(
+                fun(#{<<"clientid">> := ClientID, <<"rules">> := Rules}) ->
+                    emqx_authz_mnesia:store_rules({clientid, ClientID}, format_rules(Rules))
+                end,
+                Body
+            ),
+            {204};
+        Exists ->
+            {409, #{
+                code => <<"ALREADY_EXISTS">>,
+                message => binfmt("Clients '~ts' already exist", [binjoin(Exists)])
+            }}
+    end.
 
 
 user(get, #{bindings := #{username := Username}}) ->
 user(get, #{bindings := #{username := Username}}) ->
     case emqx_authz_mnesia:get_rules({username, Username}) of
     case emqx_authz_mnesia:get_rules({username, Username}) of
@@ -715,3 +735,29 @@ rules_example({ExampleName, ExampleType}) ->
             value => Value
             value => Value
         }
         }
     }.
     }.
+
+ensure_all_not_exists(Key, Type, Cfgs) ->
+    lists:foldl(
+        fun(#{Key := Id}, Acc) ->
+            case emqx_authz_mnesia:get_rules({Type, Id}) of
+                not_found ->
+                    Acc;
+                _ ->
+                    [Id | Acc]
+            end
+        end,
+        [],
+        Cfgs
+    ).
+
+binjoin([Bin]) ->
+    Bin;
+binjoin(Bins) ->
+    binjoin(Bins, <<>>).
+
+binjoin([H | T], Acc) ->
+    binjoin(T, <<H/binary, $,, Acc/binary>>);
+binjoin([], Acc) ->
+    Acc.
+
+binfmt(Fmt, Args) -> iolist_to_binary(io_lib:format(Fmt, Args)).

+ 15 - 0
apps/emqx_authz/test/emqx_authz_api_mnesia_SUITE.erl

@@ -74,6 +74,13 @@ t_api(_) ->
             [?USERNAME_RULES_EXAMPLE]
             [?USERNAME_RULES_EXAMPLE]
         ),
         ),
 
 
+    {ok, 409, _} =
+        request(
+            post,
+            uri(["authorization", "sources", "built_in_database", "username"]),
+            [?USERNAME_RULES_EXAMPLE]
+        ),
+
     {ok, 200, Request1} =
     {ok, 200, Request1} =
         request(
         request(
             get,
             get,
@@ -158,6 +165,14 @@ t_api(_) ->
             uri(["authorization", "sources", "built_in_database", "clientid"]),
             uri(["authorization", "sources", "built_in_database", "clientid"]),
             [?CLIENTID_RULES_EXAMPLE]
             [?CLIENTID_RULES_EXAMPLE]
         ),
         ),
+
+    {ok, 409, _} =
+        request(
+            post,
+            uri(["authorization", "sources", "built_in_database", "clientid"]),
+            [?CLIENTID_RULES_EXAMPLE]
+        ),
+
     {ok, 200, Request4} =
     {ok, 200, Request4} =
         request(
         request(
             get,
             get,