From 2a594b1a7371acfd205facd197e546d3e86875a0 Mon Sep 17 00:00:00 2001 From: zhouzb Date: Mon, 19 Jul 2021 15:56:39 +0800 Subject: [PATCH] fix(authn): fix some bugs --- apps/emqx_authn/src/emqx_authn_api.erl | 716 ++++++++++-------- .../src/simple_authn/emqx_authn_http.erl | 112 +-- .../src/simple_authn/emqx_authn_jwt.erl | 23 +- .../src/simple_authn/emqx_authn_mysql.erl | 4 + 4 files changed, 466 insertions(+), 389 deletions(-) diff --git a/apps/emqx_authn/src/emqx_authn_api.erl b/apps/emqx_authn/src/emqx_authn_api.erl index c875cc717..de2755bef 100644 --- a/apps/emqx_authn/src/emqx_authn_api.erl +++ b/apps/emqx_authn/src/emqx_authn_api.erl @@ -16,338 +16,414 @@ -module(emqx_authn_api). +-behavior(minirest_api). + -include("emqx_authn.hrl"). --export([ create_authenticator/2 - , delete_authenticator/2 - , update_authenticator/2 - , lookup_authenticator/2 - , list_authenticators/2 - , move_authenticator/2 - , import_users/2 - , add_user/2 - , delete_user/2 - , update_user/2 - , lookup_user/2 - , list_users/2 - ]). +-export([ api_spec/0 ]). --rest_api(#{name => create_authenticator, - method => 'POST', - path => "/authentication/authenticators", - func => create_authenticator, - descr => "Create authenticator" - }). +api_spec() -> + {[authenticator_api()], definitions()}. --rest_api(#{name => delete_authenticator, - method => 'DELETE', - path => "/authentication/authenticators/:bin:name", - func => delete_authenticator, - descr => "Delete authenticator" - }). - --rest_api(#{name => update_authenticator, - method => 'PUT', - path => "/authentication/authenticators/:bin:name", - func => update_authenticator, - descr => "Update authenticator" - }). - --rest_api(#{name => lookup_authenticator, - method => 'GET', - path => "/authentication/authenticators/:bin:name", - func => lookup_authenticator, - descr => "Lookup authenticator" - }). - --rest_api(#{name => list_authenticators, - method => 'GET', - path => "/authentication/authenticators", - func => list_authenticators, - descr => "List authenticators" - }). - --rest_api(#{name => move_authenticator, - method => 'POST', - path => "/authentication/authenticators/:bin:name/position", - func => move_authenticator, - descr => "Change the order of authenticators" - }). - --rest_api(#{name => import_users, - method => 'POST', - path => "/authentication/authenticators/:bin:name/import-users", - func => import_users, - descr => "Import users" - }). - --rest_api(#{name => add_user, - method => 'POST', - path => "/authentication/authenticators/:bin:name/users", - func => add_user, - descr => "Add user" - }). - --rest_api(#{name => delete_user, - method => 'DELETE', - path => "/authentication/authenticators/:bin:name/users/:bin:user_id", - func => delete_user, - descr => "Delete user" - }). - --rest_api(#{name => update_user, - method => 'PUT', - path => "/authentication/authenticators/:bin:name/users/:bin:user_id", - func => update_user, - descr => "Update user" - }). - --rest_api(#{name => lookup_user, - method => 'GET', - path => "/authentication/authenticators/:bin:name/users/:bin:user_id", - func => lookup_user, - descr => "Lookup user" - }). - -%% TODO: Support pagination --rest_api(#{name => list_users, - method => 'GET', - path => "/authentication/authenticators/:bin:name/users", - func => list_users, - descr => "List all users" - }). - -create_authenticator(Binding, Params) -> - do_create_authenticator(uri_decode(Binding), lists_to_map(Params)). - -do_create_authenticator(_Binding, Authenticator0) -> - Config = #{<<"emqx_authn">> => #{ - <<"authenticators">> => [Authenticator0] - }}, - #{emqx_authn := #{authenticators := [Authenticator1]}} - = hocon_schema:check_plain(emqx_authn_schema, Config, - #{atom_key => true, nullable => true}), - case emqx_authn:create_authenticator(?CHAIN, Authenticator1) of - {ok, Authenticator2} -> - return({ok, Authenticator2}); - {error, Reason} -> - return(serialize_error(Reason)) - end. - -delete_authenticator(Binding, Params) -> - do_delete_authenticator(uri_decode(Binding), maps:from_list(Params)). - -do_delete_authenticator(#{name := Name}, _Params) -> - case emqx_authn:delete_authenticator(?CHAIN, Name) of - ok -> - return(ok); - {error, Reason} -> - return(serialize_error(Reason)) - end. - -%% TODO: Support incremental update -update_authenticator(Binding, Params) -> - do_update_authenticator(uri_decode(Binding), lists_to_map(Params)). - -%% TOOD: PUT method supports creation and update -do_update_authenticator(#{name := Name}, NewConfig0) -> - case emqx_authn:lookup_authenticator(?CHAIN, Name) of - {ok, #{mechanism := Mechanism}} -> - Authenticator = #{<<"name">> => Name, - <<"mechanism">> => Mechanism, - <<"config">> => NewConfig0}, - Config = #{<<"emqx_authn">> => #{ - <<"authenticators">> => [Authenticator] - }}, - #{ - emqx_authn := #{ - authenticators := [#{ - config := NewConfig1 - }] +authenticator_api() -> + Example1 = #{name => <<"example">>, + mechanism => <<"password-based">>, + config => #{ + server_type => <<"built-in-example">>, + user_id_type => <<"username">>, + password_hash_algorithm => #{ + name => <<"sha256">> + } + }}, + Example2 = #{name => <<"example">>, + mechanism => <<"password-based">>, + config => #{ + server_type => <<"http-server">>, + method => <<"post">>, + url => <<"http://localhost:80/login">>, + headers => #{ + <<"content-type">> => <<"application/json">> + }, + form_data => #{ + <<"username">> => <<"${mqtt-username}">>, + <<"password">> => <<"${mqtt-password}">> + } + }}, + Example3 = #{name => <<"example">>, + mechanism => <<"jwt">>, + config => #{ + use_jwks => false, + algorithm => <<"hmac-based">>, + secret => <<"mysecret">>, + secret_base64_encoded => false, + verify_claims => #{ + <<"username">> => <<"${mqtt-username}">> + } + }}, + Metadata = #{ + post => #{ + description => "Create authenticator", + requestBody => #{ + content => #{ + 'application/json' => #{ + schema => minirest:ref(<<"authenticator">>), + examples => #{ + default => #{ + summary => <<"Default">>, + value => emqx_json:encode(Example1) + }, + http => #{ + summary => <<"Authentication provided by HTTP Server">>, + value => emqx_json:encode(Example2) + }, + jwt => #{ + summary => <<"JWT Authentication">>, + value => emqx_json:encode(Example3) + } + } + } } - } = hocon_schema:check_plain(emqx_authn_schema, Config, - #{atom_key => true, nullable => true}), - case emqx_authn:update_authenticator(?CHAIN, Name, NewConfig1) of - {ok, NAuthenticator} -> - return({ok, NAuthenticator}); - {error, Reason} -> - return(serialize_error(Reason)) - end; - {error, Reason} -> - return(serialize_error(Reason)) - end. + }, + responses => #{ + <<"201">> => #{ + description => <<"Created successfully">>, + content => #{} + } + } + } + }, + {"/authentication/authenticators", Metadata, clients}. -lookup_authenticator(Binding, Params) -> - do_lookup_authenticator(uri_decode(Binding), maps:from_list(Params)). +definitions() -> + AuthenticatorDef = #{ + allOf => [ + #{ + type => object, + required => [name], + properties => #{ + name => #{ + type => string, + example => "exmaple" + } + } + }, + #{ + oneOf => [ minirest:ref(<<"password_based">>) + , minirest:ref(<<"jwt">>) + , minirest:ref(<<"scram">>) + ] + } + ] + }, -do_lookup_authenticator(#{name := Name}, _Params) -> - case emqx_authn:lookup_authenticator(?CHAIN, Name) of - {ok, Authenticator} -> - return({ok, Authenticator}); - {error, Reason} -> - return(serialize_error(Reason)) - end. + PasswordBasedDef = #{ + type => object, + properties => #{ + mechanism => #{ + type => string, + enum => [<<"password-based">>], + example => <<"password-based">> + }, + config => #{ + oneOf => [ minirest:ref(<<"password_based_built_in_database">>) + , minirest:ref(<<"password_based_mysql">>) + , minirest:ref(<<"password_based_pgsql">>) + , minirest:ref(<<"password_based_http_server">>) + ] + } + } + }, -list_authenticators(Binding, Params) -> - do_list_authenticators(uri_decode(Binding), maps:from_list(Params)). + JWTDef = #{ + type => object, + properties => #{ + mechanism => #{ + type => string, + enum => [<<"jwt">>], + example => <<"jwt">> + }, + config => #{ + type => object, + properties => #{ + use_jwks => #{ + type => boolean, + default => false, + example => false + }, + algorithm => #{ + type => string, + enum => [<<"hmac-based">>, <<"public-key">>], + default => <<"hmac-based">>, + example => <<"hmac-based">> + }, + secret => #{ + type => string + }, + secret_base64_encoded => #{ + type => boolean, + default => false + }, + certificate => #{ + type => string + }, + verify_claims => #{ + type => object, + additionalProperties => #{ + type => string + } + }, + ssl => minirest:ref(<<"ssl">>) + } + } + } + }, + + SCRAMDef = #{ + type => object, + properties => #{ + mechanism => #{ + type => string, + enum => [<<"scram">>], + example => <<"scram">> + }, + config => #{ + type => object, + properties => #{ + server_type => #{ + type => string, + enum => [<<"built-in-database">>], + default => <<"built-in-database">> + }, + algorithm => #{ + type => string, + enum => [<<"sha256">>, <<"sha512">>], + default => <<"sha256">> + }, + iteration_count => #{ + type => integer, + default => 4096 + } + } + } + } + }, -do_list_authenticators(_Binding, _Params) -> - case emqx_authn:list_authenticators(?CHAIN) of - {ok, Authenticators} -> - return({ok, Authenticators}); - {error, Reason} -> - return(serialize_error(Reason)) - end. + PasswordBasedBuiltInDatabaseDef = #{ + type => object, + properties => #{ + server_type => #{ + type => string, + enum => [<<"built-in-database">>], + example => <<"built-in-database">> + }, + user_id_type => #{ + type => string, + enum => [<<"username">>, <<"clientid">>], + default => <<"username">>, + example => <<"username">> + }, + password_hash_algorithm => minirest:ref(<<"password_hash_algorithm">>) + } + }, -move_authenticator(Binding, Params) -> - do_move_authenticator(uri_decode(Binding), maps:from_list(Params)). + PasswordBasedMySQLDef = #{ + type => object, + properties => #{ + server_type => #{ + type => string, + enum => [<<"mysql">>], + example => <<"mysql">> + }, + server => #{ + type => string, + example => <<"localhost:3306">> + }, + database => #{ + type => string + }, + pool_size => #{ + type => integer, + default => 8 + }, + username => #{ + type => string + }, + password => #{ + type => string + }, + auto_reconnect => #{ + type => boolean, + default => true + }, + ssl => minirest:ref(<<"ssl">>), + password_hash_algorithm => minirest:ref(<<"password_hash_algorithm">>), + salt_position => #{ + type => string, + enum => [<<"prefix">>, <<"suffix">>], + default => <<"prefix">> + }, + query => #{ + type => string, + example => <<"SELECT password_hash FROM mqtt_user WHERE username = ${mqtt-username}">> + }, + query_timeout => #{ + type => integer, + description => <<"Query timeout, Unit: Milliseconds">>, + default => 5000 + } + } + }, -do_move_authenticator(#{name := Name}, #{<<"position">> := <<"the front">>}) -> - case emqx_authn:move_authenticator_to_the_front(?CHAIN, Name) of - ok -> - return(ok); - {error, Reason} -> - return(serialize_error(Reason)) - end; -do_move_authenticator(#{name := Name}, #{<<"position">> := <<"the end">>}) -> - case emqx_authn:move_authenticator_to_the_end(?CHAIN, Name) of - ok -> - return(ok); - {error, Reason} -> - return(serialize_error(Reason)) - end; -do_move_authenticator(#{name := Name}, #{<<"position">> := N}) when is_number(N) -> - case emqx_authn:move_authenticator_to_the_nth(?CHAIN, Name, N) of - ok -> - return(ok); - {error, Reason} -> - return(serialize_error(Reason)) - end; -do_move_authenticator(_Binding, _Params) -> - return(serialize_error({missing_parameter, <<"position">>})). + PasswordBasedPgSQLDef = #{ + type => object, + properties => #{ + server_type => #{ + type => string, + enum => [<<"pgsql">>], + example => <<"pgsql">> + }, + server => #{ + type => string, + example => <<"localhost:5432">> + }, + database => #{ + type => string + }, + pool_size => #{ + type => integer, + default => 8 + }, + username => #{ + type => string + }, + password => #{ + type => string + }, + auto_reconnect => #{ + type => boolean, + default => true + }, + password_hash_algorithm => minirest:ref(<<"password_hash_algorithm">>), + salt_position => #{ + type => string, + enum => [<<"prefix">>, <<"suffix">>], + default => <<"prefix">> + }, + query => #{ + type => string, + example => <<"SELECT password_hash FROM mqtt_user WHERE username = ${mqtt-username}">> + } + } + }, -import_users(Binding, Params) -> - do_import_users(uri_decode(Binding), maps:from_list(Params)). + PasswordBasedHTTPServerDef = #{ + type => object, + properties => #{ + server_type => #{ + type => string, + enum => [<<"http-server">>], + example => <<"http-server">> + }, + method => #{ + type => string, + enum => [<<"get">>, <<"post">>], + default => <<"post">> + }, + url => #{ + type => string, + example => <<"http://localhost:80/login">> + }, + headers => #{ + type => object, + additionalProperties => #{ + type => string + } + }, + format_data => #{ + type => string + }, + connect_timeout => #{ + type => integer, + default => 5000 + }, + max_retries => #{ + type => integer, + default => 5 + }, + retry_interval => #{ + type => integer, + default => 1000 + }, + request_timout => #{ + type => integer, + default => 5000 + }, + pool_size => #{ + type => integer, + default => 8 + } + } + }, -do_import_users(#{name := Name}, - #{<<"filename">> := Filename}) -> - case emqx_authn:import_users(?CHAIN, Name, Filename) of - ok -> - return(ok); - {error, Reason} -> - return(serialize_error(Reason)) - end; -do_import_users(_Binding, Params) -> - Missed = get_missed_params(Params, [<<"filename">>, <<"file_format">>]), - return(serialize_error({missing_parameter, Missed})). + PasswordHashAlgorithmDef = #{ + type => object, + required => [name], + properties => #{ + name => #{ + type => string, + enum => [<<"plain">>, <<"md5">>, <<"sha">>, <<"sha256">>, <<"sha512">>, <<"bcrypt">>], + default => <<"sha256">> + }, + salt_rounds => #{ + type => integer, + default => 10 + } + } + }, -add_user(Binding, Params) -> - do_add_user(uri_decode(Binding), maps:from_list(Params)). + SSLDef = #{ + type => object, + properties => #{ + enable => #{ + type => boolean, + default => false + }, + certfile => #{ + type => string + }, + keyfile => #{ + type => string + }, + cacertfile => #{ + type => string + }, + verify => #{ + type => boolean, + default => true + }, + server_name_indication => #{ + type => object, + properties => #{ + enable => #{ + type => boolean, + default => false + }, + hostname => #{ + type => string + } + } + } + } + }, -do_add_user(#{name := Name}, UserInfo) -> - case emqx_authn:add_user(?CHAIN, Name, UserInfo) of - {ok, User} -> - return({ok, User}); - {error, Reason} -> - return(serialize_error(Reason)) - end. - -delete_user(Binding, Params) -> - do_delete_user(uri_decode(Binding), maps:from_list(Params)). - -do_delete_user(#{name := Name, - user_id := UserID}, _Params) -> - case emqx_authn:delete_user(?CHAIN, Name, UserID) of - ok -> - return(ok); - {error, Reason} -> - return(serialize_error(Reason)) - end. - -update_user(Binding, Params) -> - do_update_user(uri_decode(Binding), maps:from_list(Params)). - -do_update_user(#{name := Name, - user_id := UserID}, NewUserInfo) -> - case emqx_authn:update_user(?CHAIN, Name, UserID, NewUserInfo) of - {ok, User} -> - return({ok, User}); - {error, Reason} -> - return(serialize_error(Reason)) - end. - -lookup_user(Binding, Params) -> - do_lookup_user(uri_decode(Binding), maps:from_list(Params)). - -do_lookup_user(#{name := Name, - user_id := UserID}, _Params) -> - case emqx_authn:lookup_user(?CHAIN, Name, UserID) of - {ok, User} -> - return({ok, User}); - {error, Reason} -> - return(serialize_error(Reason)) - end. - -list_users(Binding, Params) -> - do_list_users(uri_decode(Binding), maps:from_list(Params)). - -do_list_users(#{name := Name}, _Params) -> - case emqx_authn:list_users(?CHAIN, Name) of - {ok, Users} -> - return({ok, Users}); - {error, Reason} -> - return(serialize_error(Reason)) - end. - -%%------------------------------------------------------------------------------ -%% Internal functions -%%------------------------------------------------------------------------------ - -uri_decode(Params) -> - maps:fold(fun(K, V, Acc) -> - Acc#{K => emqx_http_lib:uri_decode(V)} - end, #{}, Params). - -lists_to_map(L) -> - lists_to_map(L, #{}). - -lists_to_map([], Acc) -> - Acc; -lists_to_map([{K, V} | More], Acc) when is_list(V) -> - NV = lists_to_map(V), - lists_to_map(More, Acc#{K => NV}); -lists_to_map([{K, V} | More], Acc) -> - lists_to_map(More, Acc#{K => V}); -lists_to_map([_ | _] = L, _) -> - L. - -serialize_error({already_exists, {Type, ID}}) -> - {error, <<"ALREADY_EXISTS">>, list_to_binary(io_lib:format("~s '~s' already exists", [serialize_type(Type), ID]))}; -serialize_error({not_found, {Type, ID}}) -> - {error, <<"NOT_FOUND">>, list_to_binary(io_lib:format("~s '~s' not found", [serialize_type(Type), ID]))}; -serialize_error({duplicate, Name}) -> - {error, <<"INVALID_PARAMETER">>, list_to_binary(io_lib:format("Authenticator name '~s' is duplicated", [Name]))}; -serialize_error({missing_parameter, Names = [_ | Rest]}) -> - Format = ["~s," || _ <- Rest] ++ ["~s"], - NFormat = binary_to_list(iolist_to_binary(Format)), - {error, <<"MISSING_PARAMETER">>, list_to_binary(io_lib:format("The input parameters " ++ NFormat ++ " that are mandatory for processing this request are not supplied.", Names))}; -serialize_error({missing_parameter, Name}) -> - {error, <<"MISSING_PARAMETER">>, list_to_binary(io_lib:format("The input parameter '~s' that is mandatory for processing this request is not supplied.", [Name]))}; -serialize_error(_) -> - {error, <<"UNKNOWN_ERROR">>, <<"Unknown error">>}. - -serialize_type(authenticator) -> - "Authenticator". - -get_missed_params(Actual, Expected) -> - Keys = lists:foldl(fun(Key, Acc) -> - case maps:is_key(Key, Actual) of - true -> Acc; - false -> [Key | Acc] - end - end, [], Expected), - lists:reverse(Keys). - -return(_) -> -%% TODO: V5 API - ok. + [ #{<<"authenticator">> => AuthenticatorDef} + , #{<<"password_based">> => PasswordBasedDef} + , #{<<"jwt">> => JWTDef} + , #{<<"scram">> => SCRAMDef} + , #{<<"password_based_built_in_database">> => PasswordBasedBuiltInDatabaseDef} + , #{<<"password_based_mysql">> => PasswordBasedMySQLDef} + , #{<<"password_based_pgsql">> => PasswordBasedPgSQLDef} + , #{<<"password_based_http_server">> => PasswordBasedHTTPServerDef} + , #{<<"password_hash_algorithm">> => PasswordHashAlgorithmDef} + , #{<<"ssl">> => SSLDef} + ]. \ No newline at end of file diff --git a/apps/emqx_authn/src/simple_authn/emqx_authn_http.erl b/apps/emqx_authn/src/simple_authn/emqx_authn_http.erl index 14240b578..4d3740a8e 100644 --- a/apps/emqx_authn/src/simple_authn/emqx_authn_http.erl +++ b/apps/emqx_authn/src/simple_authn/emqx_authn_http.erl @@ -26,13 +26,6 @@ , validations/0 ]). --type accept() :: 'application/json' | 'application/x-www-form-urlencoded'. --type content_type() :: accept(). - --reflect_type([ accept/0 - , content_type/0 - ]). - -export([ create/3 , update/4 , authenticate/2 @@ -53,45 +46,53 @@ fields("") -> fields(get) -> [ {method, #{type => get, - default => get}} + default => post}} + , {headers, fun headers_no_content_type/1} ] ++ common_fields(); fields(post) -> [ {method, #{type => post, - default => get}} - , {content_type, fun content_type/1} + default => post}} + , {headers, fun headers/1} ] ++ common_fields(). common_fields() -> [ {server_type, {enum, ['http-server']}} , {url, fun url/1} - , {accept, fun accept/1} - , {headers, fun headers/1} , {form_data, fun form_data/1} , {request_timeout, fun request_timeout/1} - ] ++ proplists:delete(base_url, emqx_connector_http:fields(config)). + ] ++ maps:to_list(maps:without([ base_url + , pool_type], + maps:from_list(emqx_connector_http:fields(config)))). validations() -> - [ {check_ssl_opts, fun check_ssl_opts/1} ]. + [ {check_ssl_opts, fun check_ssl_opts/1} + , {check_headers, fun check_headers/1} + ]. url(type) -> binary(); url(nullable) -> false; url(validate) -> [fun check_url/1]; url(_) -> undefined. -accept(type) -> accept(); -accept(default) -> 'application/json'; -accept(_) -> undefined. - -content_type(type) -> content_type(); -content_type(default) -> 'application/json'; -content_type(_) -> undefined. - -headers(type) -> list(); -headers(default) -> []; +headers(type) -> map(); +headers(converter) -> + fun(Headers) -> + maps:merge(default_headers(), transform_header_name(Headers)) + end; +headers(default) -> default_headers(); headers(_) -> undefined. -form_data(type) -> binary(); +headers_no_content_type(type) -> map(); +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. + +%% TODO: Using map() +form_data(type) -> map(); form_data(nullable) -> false; form_data(validate) -> [fun check_form_data/1]; form_data(_) -> undefined. @@ -107,28 +108,17 @@ request_timeout(_) -> undefined. create(ChainID, AuthenticatorName, #{method := Method, url := URL, - accept := Accept, headers := Headers, form_data := FormData, request_timeout := RequestTimeout} = Config) -> - ContentType = maps:get(content_type, Config, undefined), - DefaultHeader0 = case ContentType of - undefined -> #{}; - _ -> #{<<"content-type">> => atom_to_binary(ContentType, utf8)} - end, - DefaultHeader = DefaultHeader0#{<<"accept">> => atom_to_binary(Accept, utf8)}, - NHeaders = maps:to_list(maps:merge(DefaultHeader, maps:from_list(Headers))), - NFormData = preprocess_form_data(FormData), #{path := Path, query := Query} = URIMap = parse_url(URL), BaseURL = generate_base_url(URIMap), State = #{method => Method, path => Path, base_query => cow_qs:parse_qs(list_to_binary(Query)), - accept => Accept, - content_type => ContentType, - headers => NHeaders, - form_data => NFormData, + headers => maps:to_list(Headers), + form_data => FormData, request_timeout => RequestTimeout}, ResourceID = <>, case emqx_resource:create_local(ResourceID, emqx_connector_http, Config#{base_url => BaseURL}) of @@ -182,26 +172,38 @@ check_url(URL) -> end. check_form_data(FormData) -> - KVs = binary:split(FormData, [<<"&">>], [global]), - case false =:= lists:any(fun(T) -> T =:= <<>> end, KVs) of - true -> - NKVs = [list_to_tuple(binary:split(KV, [<<"=">>], [global])) || KV <- KVs], - false =:= - lists:any(fun({K, V}) -> - K =:= <<>> orelse V =:= <<>>; - (_) -> - true - end, NKVs); - false -> - false - end. + lists:any(fun({_, V}) -> + not is_binary(V) + end, maps:to_list(FormData)). + +default_headers() -> + maps:put(<<"content-type">>, + <<"application/json">>, + default_headers_no_content_type()). + +default_headers_no_content_type() -> + #{ <<"accept">> => <<"application/json">> + , <<"cache-control">> => <<"no-cache">> + , <<"connection">> => <<"keep-alive">> + , <<"keep-alive">> => <<"timeout=5">> + }. + +transform_header_name(Headers) -> + maps:fold(fun(K0, V, Acc) -> + K = list_to_binary(string:to_lower(binary_to_list(K0))), + maps:put(K, V, Acc) + end, #{}, Headers). check_ssl_opts(Conf) -> emqx_connector_http:check_ssl_opts("url", Conf). -preprocess_form_data(FormData) -> - KVs = binary:split(FormData, [<<"&">>], [global]), - [list_to_tuple(binary:split(KV, [<<"=">>], [global])) || KV <- KVs]. +check_headers(Conf) -> + Method = hocon_schema:get_value("method", Conf), + Headers = hocon_schema:get_value("headers", Conf), + case Method =:= get andalso maps:get(<<"content-type">>, Headers, undefined) =/= undefined of + true -> false; + false -> true + end. parse_url(URL) -> {ok, URIMap} = emqx_http_lib:uri_parse(URL), @@ -220,7 +222,6 @@ generate_base_url(#{scheme := Scheme, generate_request(Credential, #{method := Method, path := Path, base_query := BaseQuery, - content_type := ContentType, headers := Headers, form_data := FormData0}) -> FormData = replace_placeholders(FormData0, Credential), @@ -230,6 +231,7 @@ generate_request(Credential, #{method := Method, {NPath, Headers}; post -> NPath = append_query(Path, BaseQuery), + ContentType = proplists:get_value(<<"content-type">>, Headers), Body = serialize_body(ContentType, FormData), {NPath, Headers, Body} end. diff --git a/apps/emqx_authn/src/simple_authn/emqx_authn_jwt.erl b/apps/emqx_authn/src/simple_authn/emqx_authn_jwt.erl index 437dac72d..2e4e473c0 100644 --- a/apps/emqx_authn/src/simple_authn/emqx_authn_jwt.erl +++ b/apps/emqx_authn/src/simple_authn/emqx_authn_jwt.erl @@ -22,7 +22,6 @@ -export([ structs/0 , fields/1 - , validations/0 ]). -export([ create/3 @@ -81,19 +80,13 @@ fields(ssl_enable) -> ]; fields(ssl_disable) -> - [ {enable, #{type => false}} ]; - -fields(claim) -> - [ {"$name", fun expected_claim_value/1} ]. - -validations() -> - [ {check_verify_claims, fun check_verify_claims/1} ]. + [ {enable, #{type => false}} ]. secret(type) -> string(); secret(_) -> undefined. secret_base64_encoded(type) -> boolean(); -secret_base64_encoded(defualt) -> false; +secret_base64_encoded(default) -> false; secret_base64_encoded(_) -> undefined. certificate(type) -> string(); @@ -123,13 +116,15 @@ verify(_) -> undefined. server_name_indication(type) -> string(); server_name_indication(_) -> undefined. -verify_claims(type) -> hoconsc:array(hoconsc:ref(claim)); -verify_claims(default) -> []; +verify_claims(type) -> list(); +verify_claims(default) -> #{}; +verify_claims(validate) -> [fun check_verify_claims/1]; +verify_claims(converter) -> + fun(VerifyClaims) -> + maps:to_list(VerifyClaims) + end; verify_claims(_) -> undefined. -expected_claim_value(type) -> string(); -expected_claim_value(_) -> undefined. - %%------------------------------------------------------------------------------ %% APIs %%------------------------------------------------------------------------------ diff --git a/apps/emqx_authn/src/simple_authn/emqx_authn_mysql.erl b/apps/emqx_authn/src/simple_authn/emqx_authn_mysql.erl index 4f18e32da..c112b9666 100644 --- a/apps/emqx_authn/src/simple_authn/emqx_authn_mysql.erl +++ b/apps/emqx_authn/src/simple_authn/emqx_authn_mysql.erl @@ -57,6 +57,10 @@ password_hash_algorithm(type) -> {union, [hoconsc:ref(bcrypt), hoconsc:ref(other password_hash_algorithm(default) -> #{<<"name">> => sha256}; password_hash_algorithm(_) -> undefined. +salt_rounds(type) -> integer(); +salt_rounds(default) -> 10; +salt_rounds(_) -> undefined. + salt_position(type) -> {enum, [prefix, suffix]}; salt_position(default) -> prefix; salt_position(_) -> undefined.