138 lines
4.5 KiB
Erlang
138 lines
4.5 KiB
Erlang
|
%% This Source Code Form is subject to the terms of the Mozilla Public
|
||
|
%% License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
|
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||
|
%%
|
||
|
%% Copyright (c) 2007-2023 VMware, Inc. or its affiliates. All rights reserved.
|
||
|
%%
|
||
|
-module('Elixir.RabbitMQ.CLI.Ctl.Commands.AddSigningKeyCommand').
|
||
|
|
||
|
-behaviour('Elixir.RabbitMQ.CLI.CommandBehaviour').
|
||
|
|
||
|
-export([
|
||
|
usage/0,
|
||
|
validate/2,
|
||
|
merge_defaults/2,
|
||
|
banner/2,
|
||
|
run/2,
|
||
|
switches/0,
|
||
|
aliases/0,
|
||
|
output/2,
|
||
|
description/0,
|
||
|
formatter/0
|
||
|
]).
|
||
|
|
||
|
|
||
|
usage() ->
|
||
|
<<"add_signing_key <name> [--json=<json_key>] [--pem=<public_key>] [--pem-file=<pem_file>]">>.
|
||
|
|
||
|
description() -> <<"Add signing key required to validate JWT's digital signatures">>.
|
||
|
|
||
|
switches() ->
|
||
|
[{json, string},
|
||
|
{pem, string},
|
||
|
{pem_file, string}].
|
||
|
|
||
|
aliases() -> [].
|
||
|
|
||
|
validate([], _Options) -> {validation_failure, not_enough_args};
|
||
|
validate([_,_|_], _Options) -> {validation_failure, too_many_args};
|
||
|
validate([_], Options) ->
|
||
|
Json = maps:get(json, Options, undefined),
|
||
|
Pem = maps:get(pem, Options, undefined),
|
||
|
PemFile = maps:get(pem_file, Options, undefined),
|
||
|
case {is_binary(Json), is_binary(Pem), is_binary(PemFile)} of
|
||
|
{false, false, false} ->
|
||
|
{validation_failure,
|
||
|
{bad_argument, <<"No key specified">>}};
|
||
|
{true, false, false} ->
|
||
|
validate_json(Json);
|
||
|
{false, true, false} ->
|
||
|
validate_pem(Pem);
|
||
|
{false, false, true} ->
|
||
|
validate_pem_file(PemFile);
|
||
|
{_, _, _} ->
|
||
|
{validation_failure,
|
||
|
{bad_argument, <<"There can be only one key type">>}}
|
||
|
end.
|
||
|
|
||
|
validate_json(Json) ->
|
||
|
case rabbit_json:try_decode(Json) of
|
||
|
{ok, _} ->
|
||
|
case uaa_jwt:verify_signing_key(json, Json) of
|
||
|
ok -> ok;
|
||
|
{error, {fields_missing_for_kty, Kty}} ->
|
||
|
{validation_failure,
|
||
|
{bad_argument,
|
||
|
<<"Key fields are missing fot kty \"", Kty/binary, "\"">>}};
|
||
|
{error, unknown_kty} ->
|
||
|
{validation_failure,
|
||
|
{bad_argument, <<"\"kty\" field is invalid">>}};
|
||
|
{error, no_kty} ->
|
||
|
{validation_failure,
|
||
|
{bad_argument, <<"Json key should contain \"kty\" field">>}};
|
||
|
{error, Err} ->
|
||
|
{validation_failure, {bad_argument, Err}}
|
||
|
end;
|
||
|
{error, _} ->
|
||
|
{validation_failure, {bad_argument, <<"Invalid JSON">>}}
|
||
|
end.
|
||
|
|
||
|
validate_pem(Pem) ->
|
||
|
case uaa_jwt:verify_signing_key(pem, Pem) of
|
||
|
ok -> ok;
|
||
|
{error, invalid_pem_string} ->
|
||
|
{validation_failure, <<"Unable to read a key from the PEM string">>};
|
||
|
{error, Err} ->
|
||
|
{validation_failure, Err}
|
||
|
end.
|
||
|
|
||
|
validate_pem_file(PemFile) ->
|
||
|
case uaa_jwt:verify_signing_key(pem_file, PemFile) of
|
||
|
ok -> ok;
|
||
|
{error, enoent} ->
|
||
|
{validation_failure, {bad_argument, <<"PEM file not found">>}};
|
||
|
{error, invalid_pem_file} ->
|
||
|
{validation_failure, <<"Unable to read a key from the PEM file">>};
|
||
|
{error, Err} ->
|
||
|
{validation_failure, Err}
|
||
|
end.
|
||
|
|
||
|
merge_defaults(Args, #{pem_file := FileName} = Options) ->
|
||
|
AbsFileName = filename:absname(FileName),
|
||
|
{Args, Options#{pem_file := AbsFileName}};
|
||
|
merge_defaults(Args, Options) -> {Args, Options}.
|
||
|
|
||
|
banner([Name], #{json := Json}) ->
|
||
|
<<"Adding OAuth signing key \"",
|
||
|
Name/binary,
|
||
|
"\" in JSON format: \"",
|
||
|
Json/binary, "\"">>;
|
||
|
banner([Name], #{pem := Pem}) ->
|
||
|
<<"Adding OAuth signing key \"",
|
||
|
Name/binary,
|
||
|
"\" public key: \"",
|
||
|
Pem/binary, "\"">>;
|
||
|
banner([Name], #{pem_file := PemFile}) ->
|
||
|
<<"Adding OAuth signing key \"",
|
||
|
Name/binary,
|
||
|
"\" filename: \"",
|
||
|
PemFile/binary, "\"">>.
|
||
|
|
||
|
run([Name], #{node := Node} = Options) ->
|
||
|
{Type, Value} = case Options of
|
||
|
#{json := Json} -> {json, Json};
|
||
|
#{pem := Pem} -> {pem, Pem};
|
||
|
#{pem_file := PemFile} -> {pem_file, PemFile}
|
||
|
end,
|
||
|
case rabbit_misc:rpc_call(Node,
|
||
|
uaa_jwt, add_signing_key,
|
||
|
[Name, Type, Value]) of
|
||
|
{ok, _Keys} -> ok;
|
||
|
{error, Err} -> {error, Err}
|
||
|
end.
|
||
|
|
||
|
output(E, _Opts) ->
|
||
|
'Elixir.RabbitMQ.CLI.DefaultOutput':output(E).
|
||
|
|
||
|
formatter() -> 'Elixir.RabbitMQ.CLI.Formatters.Erlang'.
|