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

This commit is contained in:
JimMoen 2022-04-12 18:42:08 +08:00
parent c67e565755
commit 341973880d
5 changed files with 95 additions and 59 deletions

View File

@ -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">>).

View File

@ -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),
#{}
)}
];

View File

@ -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,

View File

@ -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,

View File

@ -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).