Skip to content

Commit e1490c6

Browse files
More CLI commands for tagged values
1 parent c2fdd73 commit e1490c6

File tree

5 files changed

+255
-5
lines changed

5 files changed

+255
-5
lines changed

deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/decode_command.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,15 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DecodeCommand do
117117
def formatter(), do: RabbitMQ.CLI.Formatters.Erlang
118118

119119
def banner(_, _) do
120-
"Decrypting value..."
120+
"Decrypting an advanced.config (Erlang term) value..."
121121
end
122122

123123
def usage,
124124
do: "decode value passphrase [--cipher <cipher>] [--hash <hash>] [--iterations <iterations>]"
125125

126126
def usage_additional() do
127127
[
128-
["<value>", "config value to decode"],
128+
["<value>", "advanced.config (Erlang term) value to decode"],
129129
["<passphrase>", "passphrase to use with the config value encryption key"],
130130
["--cipher <cipher>", "cipher suite to use"],
131131
["--hash <hash>", "hashing function to use"],
@@ -141,7 +141,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DecodeCommand do
141141

142142
def help_section(), do: :configuration
143143

144-
def description(), do: "Decrypts an encrypted configuration value"
144+
def description(), do: "Decrypts an encrypted advanced.config value"
145145

146146
#
147147
# Implementation
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
## This Source Code Form is subject to the terms of the Mozilla Public
2+
## License, v. 2.0. If a copy of the MPL was not distributed with this
3+
## file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
##
5+
## Copyright (c) 2007-2023 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
6+
7+
alias RabbitMQ.CLI.Core.Helpers
8+
9+
defmodule RabbitMQ.CLI.Ctl.Commands.DecryptConfValueCommand do
10+
alias RabbitMQ.CLI.Core.{DocGuide, Input}
11+
12+
@behaviour RabbitMQ.CLI.CommandBehaviour
13+
use RabbitMQ.CLI.DefaultOutput
14+
15+
def switches() do
16+
[
17+
cipher: :string,
18+
hash: :string,
19+
iterations: :integer
20+
]
21+
end
22+
23+
@atomized_keys [:cipher, :hash]
24+
@prefix "encrypted:"
25+
26+
def distribution(_), do: :none
27+
28+
def merge_defaults(args, opts) do
29+
with_defaults =
30+
Map.merge(
31+
%{
32+
cipher: :rabbit_pbe.default_cipher(),
33+
hash: :rabbit_pbe.default_hash(),
34+
iterations: :rabbit_pbe.default_iterations()
35+
},
36+
opts
37+
)
38+
39+
{args, Helpers.atomize_values(with_defaults, @atomized_keys)}
40+
end
41+
42+
def validate(args, _) when length(args) < 1 do
43+
{:validation_failure, {:not_enough_args, "Please provide a value to decode and a passphrase"}}
44+
end
45+
46+
def validate(args, _) when length(args) > 2 do
47+
{:validation_failure, :too_many_args}
48+
end
49+
50+
def validate(_args, opts) do
51+
case {supports_cipher(opts.cipher), supports_hash(opts.hash), opts.iterations > 0} do
52+
{false, _, _} ->
53+
{:validation_failure, {:bad_argument, "The requested cipher is not supported"}}
54+
55+
{_, false, _} ->
56+
{:validation_failure, {:bad_argument, "The requested hash is not supported"}}
57+
58+
{_, _, false} ->
59+
{:validation_failure,
60+
{:bad_argument,
61+
"The requested number of iterations is incorrect (must be a positive integer)"}}
62+
63+
{true, true, true} ->
64+
:ok
65+
end
66+
end
67+
68+
def run([value], %{cipher: cipher, hash: hash, iterations: iterations} = opts) do
69+
case Input.consume_single_line_string_with_prompt("Passphrase: ", opts) do
70+
:eof ->
71+
{:error, :not_enough_args}
72+
73+
passphrase ->
74+
try do
75+
term_value = Helpers.evaluate_input_as_term(value)
76+
77+
term_to_decrypt =
78+
case term_value do
79+
prefixed_val when is_bitstring(prefixed_val) or is_list(prefixed_val) ->
80+
tag_input_value_with_encrypted(prefixed_val)
81+
82+
{:encrypted, _} = encrypted ->
83+
encrypted
84+
85+
_ ->
86+
{:encrypted, term_value}
87+
end
88+
89+
result = :rabbit_pbe.decrypt_term(cipher, hash, iterations, passphrase, term_to_decrypt)
90+
{:ok, result}
91+
catch
92+
_, _ ->
93+
IO.inspect(__STACKTRACE__)
94+
{:error,
95+
"Failed to decrypt the value. Things to check: is the passphrase correct? Are the cipher and hash algorithms the same as those used for encryption?"}
96+
end
97+
end
98+
end
99+
100+
def run([value, passphrase], %{cipher: cipher, hash: hash, iterations: iterations}) do
101+
try do
102+
term_value = Helpers.evaluate_input_as_term(value)
103+
104+
term_to_decrypt =
105+
case term_value do
106+
prefixed_val when is_bitstring(prefixed_val) or is_list(prefixed_val) ->
107+
tag_input_value_with_encrypted(prefixed_val)
108+
109+
{:encrypted, _} = encrypted ->
110+
encrypted
111+
112+
_ ->
113+
{:encrypted, term_value}
114+
end
115+
116+
result = :rabbit_pbe.decrypt_term(cipher, hash, iterations, passphrase, term_to_decrypt)
117+
{:ok, result}
118+
catch
119+
_, _ ->
120+
IO.inspect(__STACKTRACE__)
121+
{:error,
122+
"Failed to decrypt the value. Things to check: is the passphrase correct? Are the cipher and hash algorithms the same as those used for encryption?"}
123+
end
124+
end
125+
126+
def formatter(), do: RabbitMQ.CLI.Formatters.Erlang
127+
128+
def banner(_, _) do
129+
"Decrypting a rabbitmq.conf string value..."
130+
end
131+
132+
def usage,
133+
do: "decrypt_conf_value value passphrase [--cipher <cipher>] [--hash <hash>] [--iterations <iterations>]"
134+
135+
def usage_additional() do
136+
[
137+
["<value>", "a double-quoted rabbitmq.conf string value to decode"],
138+
["<passphrase>", "passphrase to use with the config value encryption key"],
139+
["--cipher <cipher>", "cipher suite to use"],
140+
["--hash <hash>", "hashing function to use"],
141+
["--iterations <iterations>", "number of iteration to apply"]
142+
]
143+
end
144+
145+
def usage_doc_guides() do
146+
[
147+
DocGuide.configuration()
148+
]
149+
end
150+
151+
def help_section(), do: :configuration
152+
153+
def description(), do: "Decrypts an encrypted configuration value"
154+
155+
#
156+
# Implementation
157+
#
158+
159+
defp supports_cipher(cipher), do: Enum.member?(:rabbit_pbe.supported_ciphers(), cipher)
160+
161+
defp supports_hash(hash), do: Enum.member?(:rabbit_pbe.supported_hashes(), hash)
162+
163+
defp tag_input_value_with_encrypted(value) when is_bitstring(value) or is_list(value) do
164+
bin_val = :rabbit_data_coercion.to_binary(value)
165+
untagged_val = String.replace_prefix(bin_val, @prefix, "")
166+
167+
{:encrypted, untagged_val}
168+
end
169+
defp tag_input_value_with_encrypted(value) do
170+
{:encrypted, value}
171+
end
172+
end

deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/encode_command.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EncodeCommand do
130130

131131
def usage_additional() do
132132
[
133-
["<value>", "config value to encode"],
133+
["<value>", "value to encode, to be used in advanced.config"],
134134
["<passphrase>", "passphrase to use with the config value encryption key"],
135135
["--cipher <cipher>", "cipher suite to use"],
136136
["--hash <hash>", "hashing function to use"],

deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/encrypt_conf_value_command.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
## License, v. 2.0. If a copy of the MPL was not distributed with this
33
## file, You can obtain one at https://mozilla.org/MPL/2.0/.
44
##
5-
## Copyright (c) 2007-2023 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
5+
## Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
66

77
defmodule RabbitMQ.CLI.Ctl.Commands.EncryptConfValueCommand do
88
alias RabbitMQ.CLI.Core.{DocGuide, Helpers, Input}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
## This Source Code Form is subject to the terms of the Mozilla Public
2+
## License, v. 2.0. If a copy of the MPL was not distributed with this
3+
## file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
##
5+
## Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
6+
7+
defmodule EncryptConfValueCommandTest do
8+
use ExUnit.Case, async: false
9+
10+
@command RabbitMQ.CLI.Ctl.Commands.EncryptConfValueCommand
11+
12+
setup _context do
13+
{:ok,
14+
opts: %{
15+
cipher: :rabbit_pbe.default_cipher(),
16+
hash: :rabbit_pbe.default_hash(),
17+
iterations: :rabbit_pbe.default_iterations()
18+
}}
19+
end
20+
21+
test "validate: providing exactly 2 positional arguments passes", context do
22+
assert :ok == @command.validate(["value", "secret"], context[:opts])
23+
end
24+
25+
test "validate: providing zero or one positional argument passes", context do
26+
assert :ok == @command.validate([], context[:opts])
27+
assert :ok == @command.validate(["value"], context[:opts])
28+
end
29+
30+
test "validate: providing three or more positional argument fails", context do
31+
assert match?(
32+
{:validation_failure, :too_many_args},
33+
@command.validate(["value", "secret", "incorrect"], context[:opts])
34+
)
35+
end
36+
37+
test "validate: hash and cipher must be supported", context do
38+
assert match?(
39+
{:validation_failure, {:bad_argument, _}},
40+
@command.validate(
41+
["value", "secret"],
42+
Map.merge(context[:opts], %{cipher: :funny_cipher})
43+
)
44+
)
45+
46+
assert match?(
47+
{:validation_failure, {:bad_argument, _}},
48+
@command.validate(
49+
["value", "secret"],
50+
Map.merge(context[:opts], %{hash: :funny_hash})
51+
)
52+
)
53+
54+
assert match?(
55+
{:validation_failure, {:bad_argument, _}},
56+
@command.validate(
57+
["value", "secret"],
58+
Map.merge(context[:opts], %{cipher: :funny_cipher, hash: :funny_hash})
59+
)
60+
)
61+
62+
assert :ok == @command.validate(["value", "secret"], context[:opts])
63+
end
64+
65+
test "validate: number of iterations must greater than 0", context do
66+
assert match?(
67+
{:validation_failure, {:bad_argument, _}},
68+
@command.validate(["value", "secret"], Map.merge(context[:opts], %{iterations: 0}))
69+
)
70+
71+
assert match?(
72+
{:validation_failure, {:bad_argument, _}},
73+
@command.validate(["value", "secret"], Map.merge(context[:opts], %{iterations: -1}))
74+
)
75+
76+
assert :ok == @command.validate(["value", "secret"], context[:opts])
77+
end
78+
end

0 commit comments

Comments
 (0)