Bladeren bron

fix(authz): refine authz-http api with default headers

JimMoen 3 jaren geleden
bovenliggende
commit
341973880d

+ 20 - 0
apps/emqx/include/emqx_access_control.hrl

@@ -0,0 +1,20 @@
+%%--------------------------------------------------------------------
+%% Copyright (c) 2022 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%--------------------------------------------------------------------
+
+%% config root name all auth providers have to agree on.
+-define(EMQX_AUTHORIZATION_CONFIG_ROOT_NAME, "authorization").
+-define(EMQX_AUTHORIZATION_CONFIG_ROOT_NAME_ATOM, authorization).
+-define(EMQX_AUTHORIZATION_CONFIG_ROOT_NAME_BINARY, <<"authorization">>).

+ 3 - 2
apps/emqx/src/emqx_schema.erl

@@ -24,6 +24,7 @@
 -elvis([{elvis_style, invalid_dynamic_call, disable}]).
 
 -include("emqx_authentication.hrl").
+-include("emqx_access_control.hrl").
 -include_lib("typerefl/include/types.hrl").
 
 -type duration() :: integer().
@@ -159,9 +160,9 @@ roots(high) ->
             )},
         %% NOTE: authorization schema here is only to keep emqx app prue
         %% the full schema for EMQX node is injected in emqx_conf_schema.
-        {"authorization",
+        {?EMQX_AUTHORIZATION_CONFIG_ROOT_NAME,
             sc(
-                ref("authorization"),
+                ref(?EMQX_AUTHORIZATION_CONFIG_ROOT_NAME),
                 #{}
             )}
     ];

+ 7 - 0
apps/emqx_authz/include/emqx_authz.hrl

@@ -14,6 +14,8 @@
 %% limitations under the License.
 %%--------------------------------------------------------------------
 
+-include_lib("emqx/include/emqx_access_control.hrl").
+
 -define(APP, emqx_authz).
 
 -define(ALLOW_DENY(A),
@@ -45,6 +47,11 @@
 
 -define(RE_PLACEHOLDER, "\\$\\{[a-z0-9_]+\\}").
 
+%% has to be the same as the root field name defined in emqx_schema
+-define(CONF_NS, ?EMQX_AUTHORIZATION_CONFIG_ROOT_NAME).
+-define(CONF_NS_ATOM, ?EMQX_AUTHORIZATION_CONFIG_ROOT_NAME_ATOM).
+-define(CONF_NS_BINARY, ?EMQX_AUTHORIZATION_CONFIG_ROOT_NAME_BINARY).
+
 %% API examples
 -define(USERNAME_RULES_EXAMPLE, #{
     username => user1,

+ 52 - 25
apps/emqx_authz/src/emqx_authz_api_schema.erl

@@ -16,36 +16,32 @@
 
 -module(emqx_authz_api_schema).
 
+-include("emqx_authz.hrl").
 -include_lib("typerefl/include/types.hrl").
 -include_lib("emqx_connector/include/emqx_connector.hrl").
 
 -import(hoconsc, [mk/2, enum/1]).
 -import(emqx_schema, [mk_duration/2]).
 
--export([fields/1, authz_sources_types/1]).
+-export([
+    fields/1,
+    authz_sources_types/1
+]).
 
-fields(http) ->
-    authz_common_fields(http) ++
-        [
-            {url, fun url/1},
-            {method, #{
-                type => enum([get, post]),
-                default => get,
-                required => true
-            }},
-            {headers, fun headers/1},
-            {body, map([{fuzzy, term(), binary()}])},
-            {request_timeout, mk_duration("Request timeout", #{default => "30s"})}
-        ] ++
-        maps:to_list(
-            maps:without(
-                [
-                    base_url,
-                    pool_type
-                ],
-                maps:from_list(emqx_connector_http:fields(config))
-            )
-        );
+%%------------------------------------------------------------------------------
+%% Hocon Schema
+%%------------------------------------------------------------------------------
+
+fields(http_get) ->
+    [
+        {method, #{type => get, default => get, required => true}},
+        {headers, fun headers_no_content_type/1}
+    ] ++ authz_http_common_fields();
+fields(http_post) ->
+    [
+        {method, #{type => post, default => post, required => true}},
+        {headers, fun headers/1}
+    ] ++ authz_http_common_fields();
 fields(built_in_database) ->
     authz_common_fields(built_in_database);
 fields(mongo_single) ->
@@ -101,6 +97,23 @@ fields(position) ->
 %%------------------------------------------------------------------------------
 %% http type funcs
 
+authz_http_common_fields() ->
+    authz_common_fields(http) ++
+        [
+            {url, fun url/1},
+            {body, map([{fuzzy, term(), binary()}])},
+            {request_timeout, mk_duration("Request timeout", #{default => "30s"})}
+        ] ++
+        maps:to_list(
+            maps:without(
+                [
+                    base_url,
+                    pool_type
+                ],
+                maps:from_list(emqx_connector_http:fields(config))
+            )
+        ).
+
 url(type) -> binary();
 url(validator) -> [?NOT_EMPTY("the value of the field 'url' cannot be empty")];
 url(required) -> true;
@@ -119,6 +132,19 @@ headers(default) ->
 headers(_) ->
     undefined.
 
+headers_no_content_type(type) ->
+    map();
+headers_no_content_type(desc) ->
+    "List of HTTP headers.";
+headers_no_content_type(converter) ->
+    fun(Headers) ->
+        maps:merge(default_headers_no_content_type(), transform_header_name(Headers))
+    end;
+headers_no_content_type(default) ->
+    default_headers_no_content_type();
+headers_no_content_type(_) ->
+    undefined.
+
 %% headers
 default_headers() ->
     maps:put(
@@ -208,9 +234,11 @@ enable(_) -> undefined.
 authz_sources_types(Type) ->
     case Type of
         simple ->
-            [mongodb, redis];
+            [http, mongodb, redis];
         detailed ->
             [
+                http_get,
+                http_post,
                 mongo_single,
                 mongo_rs,
                 mongo_sharded,
@@ -220,7 +248,6 @@ authz_sources_types(Type) ->
             ]
     end ++
         [
-            http,
             built_in_database,
             mysql,
             postgresql,

+ 13 - 32
apps/emqx_authz/src/emqx_authz_schema.erl

@@ -16,6 +16,7 @@
 
 -module(emqx_authz_schema).
 
+-include("emqx_authz.hrl").
 -include_lib("typerefl/include/types.hrl").
 -include_lib("emqx_connector/include/emqx_connector.hrl").
 
@@ -40,9 +41,6 @@
     headers/1
 ]).
 
--import(emqx_schema, [mk_duration/2]).
--include_lib("hocon/include/hoconsc.hrl").
-
 %%--------------------------------------------------------------------
 %% Hocon Schema
 %%--------------------------------------------------------------------
@@ -197,7 +195,9 @@ http_common_fields() ->
     [
         {url, fun url/1},
         {request_timeout,
-            mk_duration("Request timeout", #{default => "30s", desc => "Request timeout."})},
+            emqx_schema:mk_duration("Request timeout", #{
+                default => "30s", desc => "Request timeout."
+            })},
         {body, #{type => map(), required => false, desc => "HTTP request body."}}
     ] ++
         maps:to_list(
@@ -232,8 +232,7 @@ mongo_common_fields() ->
 
 validations() ->
     [
-        {check_ssl_opts, fun check_ssl_opts/1},
-        {check_headers, fun check_headers/1}
+        {check_ssl_opts, fun check_ssl_opts/1}
     ].
 
 headers(type) ->
@@ -259,6 +258,13 @@ headers_no_content_type(converter) ->
     end;
 headers_no_content_type(default) ->
     default_headers_no_content_type();
+headers_no_content_type(validator) ->
+    fun(Headers) ->
+        case lists:keyfind(<<"content-type">>, 1, Headers) of
+            false -> ok;
+            _ -> {error, do_not_include_content_type}
+        end
+    end;
 headers_no_content_type(_) ->
     undefined.
 
@@ -297,6 +303,7 @@ transform_header_name(Headers) ->
         Headers
     ).
 
+%% TODO: fix me, not work
 check_ssl_opts(Conf) ->
     case hocon_maps:get("config.url", Conf) of
         undefined ->
@@ -315,25 +322,6 @@ check_ssl_opts(Conf) ->
             end
     end.
 
-check_headers(Conf) ->
-    case hocon_maps:get("config.method", Conf) of
-        undefined ->
-            true;
-        Method0 ->
-            Method = to_bin(Method0),
-            Headers = hocon_maps:get("config.headers", Conf),
-            case Method of
-                <<"post">> ->
-                    true;
-                _ when Headers =:= undefined -> true;
-                _ when is_list(Headers) ->
-                    case lists:member(<<"content-type">>, Headers) of
-                        false -> true;
-                        true -> {Method0, do_not_include_content_type}
-                    end
-            end
-    end.
-
 union_array(Item) when is_list(Item) ->
     hoconsc:array(hoconsc:union(Item)).
 
@@ -376,10 +364,3 @@ to_list(A) when is_atom(A) ->
     atom_to_list(A);
 to_list(B) when is_binary(B) ->
     binary_to_list(B).
-
-to_bin(A) when is_atom(A) ->
-    atom_to_binary(A);
-to_bin(B) when is_binary(B) ->
-    B;
-to_bin(L) when is_list(L) ->
-    list_to_binary(L).