feat(upload certs): support return cert content by http api
This commit is contained in:
parent
63d3a7b525
commit
ee178ccea9
|
@ -198,7 +198,7 @@ pre_config_update(UpdateReq, OldConfig) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
do_pre_config_update({create_authenticator, _ChainName, Config}, OldConfig) ->
|
do_pre_config_update({create_authenticator, _ChainName, Config}, OldConfig) ->
|
||||||
try convert_cert_options(Config) of
|
try convert_certs(Config) of
|
||||||
NConfig ->
|
NConfig ->
|
||||||
{ok, OldConfig ++ [NConfig]}
|
{ok, OldConfig ++ [NConfig]}
|
||||||
catch
|
catch
|
||||||
|
@ -213,7 +213,7 @@ do_pre_config_update({delete_authenticator, _ChainName, AuthenticatorID}, OldCon
|
||||||
do_pre_config_update({update_authenticator, _ChainName, AuthenticatorID, Config}, OldConfig) ->
|
do_pre_config_update({update_authenticator, _ChainName, AuthenticatorID, Config}, OldConfig) ->
|
||||||
try lists:map(fun(OldConfig0) ->
|
try lists:map(fun(OldConfig0) ->
|
||||||
case AuthenticatorID =:= generate_id(OldConfig0) of
|
case AuthenticatorID =:= generate_id(OldConfig0) of
|
||||||
true -> convert_cert_options(Config, OldConfig0);
|
true -> convert_certs(Config, OldConfig0);
|
||||||
false -> OldConfig0
|
false -> OldConfig0
|
||||||
end
|
end
|
||||||
end, OldConfig) of
|
end, OldConfig) of
|
||||||
|
@ -616,42 +616,48 @@ reply(Reply, State) ->
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
convert_cert_options(Config) ->
|
convert_certs(#{<<"ssl">> := SSLOpts} = Config) ->
|
||||||
Keys = maps:keys(filter_empty(maps:with([<<"certfile">>, <<"keyfile">>, <<"cacertfile">>], Config))),
|
NSSLOPts = lists:foldl(fun(K, Acc) ->
|
||||||
lists:foldl(fun(Key, Acc) ->
|
case maps:get(K, Acc, undefined) of
|
||||||
convert_cert_option(Key, Acc)
|
undefined -> Acc;
|
||||||
end, Config, Keys).
|
PemBin ->
|
||||||
|
CertFile = generate_filename(K),
|
||||||
|
ok = save_cert_to_file(CertFile, PemBin),
|
||||||
|
Acc#{K => CertFile}
|
||||||
|
end
|
||||||
|
end, SSLOpts, [<<"certfile">>, <<"keyfile">>, <<"cacertfile">>]),
|
||||||
|
Config#{<<"ssl">> => NSSLOPts};
|
||||||
|
convert_certs(Config) ->
|
||||||
|
Config.
|
||||||
|
|
||||||
convert_cert_options(NewConfig, OldConfig) ->
|
convert_certs(#{<<"ssl">> := NewSSLOpts} = NewConfig, OldConfig) ->
|
||||||
Keys = [<<"cacertfile">>, <<"certfile">>, <<"keyfile">>],
|
OldSSLOpts = maps:get(<<"ssl">>, OldConfig, #{}),
|
||||||
NewCerts = maps:with(Keys, NewConfig),
|
Diff = diff_certs(NewSSLOpts, OldSSLOpts),
|
||||||
OldCerts = maps:fold(fun(K, V, Acc) ->
|
NSSLOpts = lists:foldl(fun({identical, K}, Acc) ->
|
||||||
{ok, Bin} = file:read_file(V),
|
Acc#{K => maps:get(K, OldSSLOpts)};
|
||||||
Acc#{K => Bin}
|
({_, K}, Acc) ->
|
||||||
end, #{}, maps:with(Keys, OldConfig)),
|
CertFile = generate_filename(K),
|
||||||
Diff = diff_certs(NewCerts, OldCerts),
|
ok = save_cert_to_file(CertFile, maps:get(K, NewSSLOpts)),
|
||||||
lists:foldl(fun({identical, K}, Acc) ->
|
Acc#{K => CertFile}
|
||||||
Acc#{K => maps:get(K, OldConfig)};
|
end, NewSSLOpts, Diff),
|
||||||
({T, K}, Acc) when T =:= added orelse T =:= changed ->
|
NewConfig#{<<"ssl">> => NSSLOpts};
|
||||||
convert_cert_option(K, Acc)
|
convert_certs(NewConfig, _OldConfig) ->
|
||||||
end, NewConfig, Diff).
|
NewConfig.
|
||||||
|
|
||||||
convert_cert_option(Key, Config) ->
|
save_cert_to_file(Filename, PemBin) ->
|
||||||
PemBin = maps:get(Key, Config),
|
|
||||||
case public_key:pem_decode(PemBin) =/= [] of
|
case public_key:pem_decode(PemBin) =/= [] of
|
||||||
true ->
|
true ->
|
||||||
Filename = to_bin(filename:join([emqx:get_config([node, data_dir]), "certs/authn", generate_filename(Key)])),
|
|
||||||
case filelib:ensure_dir(Filename) of
|
case filelib:ensure_dir(Filename) of
|
||||||
ok ->
|
ok ->
|
||||||
case file:write_file(Filename, PemBin) of
|
case file:write_file(Filename, PemBin) of
|
||||||
ok -> Config#{Key => Filename};
|
ok -> ok;
|
||||||
{error, Reason} -> error({convert_cert_option, {write_file, Reason}})
|
{error, Reason} -> error({save_cert_to_file, {write_file, Reason}})
|
||||||
end;
|
end;
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
error({convert_cert_option, {ensure_dir, Reason}})
|
error({save_cert_to_file, {ensure_dir, Reason}})
|
||||||
end;
|
end;
|
||||||
false ->
|
false ->
|
||||||
error({convert_cert_option, invalid_certificate})
|
error({save_cert_to_file, invalid_certificate})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
generate_filename(Key) ->
|
generate_filename(Key) ->
|
||||||
|
@ -660,31 +666,27 @@ generate_filename(Key) ->
|
||||||
<<"certfile">> -> "cert-";
|
<<"certfile">> -> "cert-";
|
||||||
<<"cacertfile">> -> "cacert-"
|
<<"cacertfile">> -> "cacert-"
|
||||||
end,
|
end,
|
||||||
Prefix ++ emqx_plugin_libs_id:gen() ++ ".pem".
|
to_bin(filename:join([emqx:get_config([node, data_dir]), "certs/authn", Prefix ++ emqx_plugin_libs_id:gen() ++ ".pem"])).
|
||||||
|
|
||||||
filter_empty(L) when is_list(L) ->
|
diff_certs(NewSSLOpts, OldSSLOpts) ->
|
||||||
[I || I <- L, I =/= "" andalso I =/= undefined];
|
Keys = [<<"cacertfile">>, <<"certfile">>, <<"keyfile">>],
|
||||||
filter_empty(M) when is_map(M) ->
|
CertPems = maps:with(Keys, NewSSLOpts),
|
||||||
maps:from_list(filter_empty(maps:to_list(M))).
|
CertFiles = maps:with(Keys, OldSSLOpts),
|
||||||
|
Diff = lists:foldl(fun({K, CertFile}, Acc) ->
|
||||||
diff_certs(NewCerts0, OldCerts0) ->
|
case maps:find(K, CertPems) of
|
||||||
NewCerts = filter_empty(NewCerts0),
|
error -> Acc;
|
||||||
OldCerts = filter_empty(OldCerts0),
|
{ok, PemBin1} ->
|
||||||
Diff = lists:foldl(fun({OldK, OldPem}, Acc) ->
|
{ok, PemBin2} = file:read_file(CertFile),
|
||||||
case maps:find(OldK, NewCerts) of
|
case diff_cert(PemBin1, PemBin2) of
|
||||||
error ->
|
|
||||||
Acc;
|
|
||||||
{ok, NewPem} ->
|
|
||||||
case diff_cert(NewPem, OldPem) of
|
|
||||||
true ->
|
true ->
|
||||||
[{changed, OldK} | Acc];
|
[{changed, K} | Acc];
|
||||||
false ->
|
false ->
|
||||||
[{identical, OldK} | Acc]
|
[{identical, K} | Acc]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
[], maps:to_list(OldCerts)),
|
[], maps:to_list(CertFiles)),
|
||||||
Added = [{added, K} || K <- maps:keys(maps:without(maps:keys(OldCerts), NewCerts))],
|
Added = [{added, K} || K <- maps:keys(maps:without(maps:keys(CertFiles), CertPems))],
|
||||||
Diff ++ Added.
|
Diff ++ Added.
|
||||||
|
|
||||||
diff_cert(Pem1, Pem2) ->
|
diff_cert(Pem1, Pem2) ->
|
||||||
|
|
|
@ -252,13 +252,13 @@ t_convert_cert_options(_) ->
|
||||||
, {<<"certfile">>, "cert.pem"}
|
, {<<"certfile">>, "cert.pem"}
|
||||||
, {<<"cacertfile">>, "cacert.pem"}
|
, {<<"cacertfile">>, "cacert.pem"}
|
||||||
]),
|
]),
|
||||||
NCerts = ?AUTHN:convert_cert_options(Certs),
|
#{<<"ssl">> := NCerts} = ?AUTHN:convert_certs(#{<<"ssl">> => Certs}),
|
||||||
?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts), maps:get(<<"keyfile">>, Certs))),
|
?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts), maps:get(<<"keyfile">>, Certs))),
|
||||||
|
|
||||||
Certs2 = certs([ {<<"keyfile">>, "key.pem"}
|
Certs2 = certs([ {<<"keyfile">>, "key.pem"}
|
||||||
, {<<"certfile">>, "cert.pem"}
|
, {<<"certfile">>, "cert.pem"}
|
||||||
]),
|
]),
|
||||||
NCerts2 = ?AUTHN:convert_cert_options(Certs2, NCerts),
|
#{<<"ssl">> := NCerts2} = ?AUTHN:convert_certs(#{<<"ssl">> => Certs2}, #{<<"ssl">> => NCerts}),
|
||||||
?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts2), maps:get(<<"keyfile">>, Certs2))),
|
?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts2), maps:get(<<"keyfile">>, Certs2))),
|
||||||
?assertEqual(maps:get(<<"keyfile">>, NCerts), maps:get(<<"keyfile">>, NCerts2)),
|
?assertEqual(maps:get(<<"keyfile">>, NCerts), maps:get(<<"keyfile">>, NCerts2)),
|
||||||
?assertEqual(maps:get(<<"certfile">>, NCerts), maps:get(<<"certfile">>, NCerts2)),
|
?assertEqual(maps:get(<<"certfile">>, NCerts), maps:get(<<"certfile">>, NCerts2)),
|
||||||
|
@ -267,7 +267,7 @@ t_convert_cert_options(_) ->
|
||||||
, {<<"certfile">>, "client-cert.pem"}
|
, {<<"certfile">>, "client-cert.pem"}
|
||||||
, {<<"cacertfile">>, "cacert.pem"}
|
, {<<"cacertfile">>, "cacert.pem"}
|
||||||
]),
|
]),
|
||||||
NCerts3 = ?AUTHN:convert_cert_options(Certs3, NCerts2),
|
#{<<"ssl">> := NCerts3} = ?AUTHN:convert_certs(#{<<"ssl">> => Certs3}, #{<<"ssl">> => NCerts2}),
|
||||||
?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts3), maps:get(<<"keyfile">>, Certs3))),
|
?assertEqual(false, diff_cert(maps:get(<<"keyfile">>, NCerts3), maps:get(<<"keyfile">>, Certs3))),
|
||||||
?assertNotEqual(maps:get(<<"keyfile">>, NCerts2), maps:get(<<"keyfile">>, NCerts3)),
|
?assertNotEqual(maps:get(<<"keyfile">>, NCerts2), maps:get(<<"keyfile">>, NCerts3)),
|
||||||
?assertNotEqual(maps:get(<<"certfile">>, NCerts2), maps:get(<<"certfile">>, NCerts3)).
|
?assertNotEqual(maps:get(<<"certfile">>, NCerts2), maps:get(<<"certfile">>, NCerts3)).
|
||||||
|
|
|
@ -1835,23 +1835,20 @@ find_listener(ListenerID) ->
|
||||||
{ok, {Type, Name}}
|
{ok, {Type, Name}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
% convert_tls_options(Config)->
|
|
||||||
|
|
||||||
create_authenticator(ConfKeyPath, ChainName0, Config) ->
|
create_authenticator(ConfKeyPath, ChainName0, Config) ->
|
||||||
ChainName = to_atom(ChainName0),
|
ChainName = to_atom(ChainName0),
|
||||||
% {NConfig, Certs} = convert_tls_options(Config),
|
|
||||||
case update_config(ConfKeyPath, {create_authenticator, ChainName, Config}) of
|
case update_config(ConfKeyPath, {create_authenticator, ChainName, Config}) of
|
||||||
{ok, #{post_config_update := #{?AUTHN := #{id := ID}},
|
{ok, #{post_config_update := #{?AUTHN := #{id := ID}},
|
||||||
raw_config := AuthenticatorsConfig}} ->
|
raw_config := AuthenticatorsConfig}} ->
|
||||||
{ok, AuthenticatorConfig} = find_config(ID, AuthenticatorsConfig),
|
{ok, AuthenticatorConfig} = find_config(ID, AuthenticatorsConfig),
|
||||||
{200, maps:put(id, ID, fill_defaults(AuthenticatorConfig))};
|
{200, maps:put(id, ID, convert_certs(fill_defaults(AuthenticatorConfig)))};
|
||||||
{error, {_, _, Reason}} ->
|
{error, {_, _, Reason}} ->
|
||||||
serialize_error(Reason)
|
serialize_error(Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
list_authenticators(ConfKeyPath) ->
|
list_authenticators(ConfKeyPath) ->
|
||||||
AuthenticatorsConfig = get_raw_config_with_defaults(ConfKeyPath),
|
AuthenticatorsConfig = get_raw_config_with_defaults(ConfKeyPath),
|
||||||
NAuthenticators = [maps:put(id, ?AUTHN:generate_id(AuthenticatorConfig), AuthenticatorConfig)
|
NAuthenticators = [maps:put(id, ?AUTHN:generate_id(AuthenticatorConfig), convert_certs(AuthenticatorConfig))
|
||||||
|| AuthenticatorConfig <- AuthenticatorsConfig],
|
|| AuthenticatorConfig <- AuthenticatorsConfig],
|
||||||
{200, NAuthenticators}.
|
{200, NAuthenticators}.
|
||||||
|
|
||||||
|
@ -1859,7 +1856,7 @@ list_authenticator(ConfKeyPath, AuthenticatorID) ->
|
||||||
AuthenticatorsConfig = get_raw_config_with_defaults(ConfKeyPath),
|
AuthenticatorsConfig = get_raw_config_with_defaults(ConfKeyPath),
|
||||||
case find_config(AuthenticatorID, AuthenticatorsConfig) of
|
case find_config(AuthenticatorID, AuthenticatorsConfig) of
|
||||||
{ok, AuthenticatorConfig} ->
|
{ok, AuthenticatorConfig} ->
|
||||||
{200, AuthenticatorConfig#{id => AuthenticatorID}};
|
{200, maps:put(id, AuthenticatorID, convert_certs(AuthenticatorConfig))};
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
serialize_error(Reason)
|
serialize_error(Reason)
|
||||||
end.
|
end.
|
||||||
|
@ -1870,7 +1867,7 @@ update_authenticator(ConfKeyPath, ChainName0, AuthenticatorID, Config) ->
|
||||||
{ok, #{post_config_update := #{?AUTHN := #{id := ID}},
|
{ok, #{post_config_update := #{?AUTHN := #{id := ID}},
|
||||||
raw_config := AuthenticatorsConfig}} ->
|
raw_config := AuthenticatorsConfig}} ->
|
||||||
{ok, AuthenticatorConfig} = find_config(ID, AuthenticatorsConfig),
|
{ok, AuthenticatorConfig} = find_config(ID, AuthenticatorsConfig),
|
||||||
{200, maps:put(id, ID, fill_defaults(AuthenticatorConfig))};
|
{200, maps:put(id, ID, convert_certs(fill_defaults(AuthenticatorConfig)))};
|
||||||
{error, {_, _, Reason}} ->
|
{error, {_, _, Reason}} ->
|
||||||
serialize_error(Reason)
|
serialize_error(Reason)
|
||||||
end.
|
end.
|
||||||
|
@ -1974,6 +1971,19 @@ fill_defaults(Config) ->
|
||||||
?AUTHN, #{<<"authentication">> => Config}, #{nullable => true, no_conversion => true}),
|
?AUTHN, #{<<"authentication">> => Config}, #{nullable => true, no_conversion => true}),
|
||||||
CheckedConfig.
|
CheckedConfig.
|
||||||
|
|
||||||
|
convert_certs(#{<<"ssl">> := SSLOpts} = Config) ->
|
||||||
|
NSSLOpts = lists:foldl(fun(K, Acc) ->
|
||||||
|
case maps:get(K, Acc, undefined) of
|
||||||
|
undefined -> Acc;
|
||||||
|
Filename ->
|
||||||
|
{ok, Bin} = file:read_file(Filename),
|
||||||
|
Acc#{K => Bin}
|
||||||
|
end
|
||||||
|
end, SSLOpts, [<<"certfile">>, <<"keyfile">>, <<"cacertfile">>]),
|
||||||
|
Config#{<<"ssl">> => NSSLOpts};
|
||||||
|
convert_certs(Config) ->
|
||||||
|
Config.
|
||||||
|
|
||||||
serialize_error({not_found, {authenticator, ID}}) ->
|
serialize_error({not_found, {authenticator, ID}}) ->
|
||||||
{404, #{code => <<"NOT_FOUND">>,
|
{404, #{code => <<"NOT_FOUND">>,
|
||||||
message => list_to_binary(
|
message => list_to_binary(
|
||||||
|
|
Loading…
Reference in New Issue