More CLI commands for tagged values
This commit is contained in:
		
							parent
							
								
									c2fdd73c4b
								
							
						
					
					
						commit
						e1490c6d9c
					
				|  | @ -117,7 +117,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DecodeCommand do | ||||||
|   def formatter(), do: RabbitMQ.CLI.Formatters.Erlang |   def formatter(), do: RabbitMQ.CLI.Formatters.Erlang | ||||||
| 
 | 
 | ||||||
|   def banner(_, _) do |   def banner(_, _) do | ||||||
|     "Decrypting value..." |     "Decrypting an advanced.config (Erlang term) value..." | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def usage, |   def usage, | ||||||
|  | @ -125,7 +125,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DecodeCommand do | ||||||
| 
 | 
 | ||||||
|   def usage_additional() do |   def usage_additional() do | ||||||
|     [ |     [ | ||||||
|       ["<value>", "config value to decode"], |       ["<value>", "advanced.config (Erlang term) value to decode"], | ||||||
|       ["<passphrase>", "passphrase to use with the config value encryption key"], |       ["<passphrase>", "passphrase to use with the config value encryption key"], | ||||||
|       ["--cipher <cipher>", "cipher suite to use"], |       ["--cipher <cipher>", "cipher suite to use"], | ||||||
|       ["--hash <hash>", "hashing function to use"], |       ["--hash <hash>", "hashing function to use"], | ||||||
|  | @ -141,7 +141,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DecodeCommand do | ||||||
| 
 | 
 | ||||||
|   def help_section(), do: :configuration |   def help_section(), do: :configuration | ||||||
| 
 | 
 | ||||||
|   def description(), do: "Decrypts an encrypted configuration value" |   def description(), do: "Decrypts an encrypted advanced.config value" | ||||||
| 
 | 
 | ||||||
|   # |   # | ||||||
|   # Implementation |   # Implementation | ||||||
|  |  | ||||||
							
								
								
									
										172
									
								
								deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/decrypt_conf_value_command.ex
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										172
									
								
								deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/decrypt_conf_value_command.ex
								
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,172 @@ | ||||||
|  | ## 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 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.  All rights reserved. | ||||||
|  | 
 | ||||||
|  | alias RabbitMQ.CLI.Core.Helpers | ||||||
|  | 
 | ||||||
|  | defmodule RabbitMQ.CLI.Ctl.Commands.DecryptConfValueCommand do | ||||||
|  |   alias RabbitMQ.CLI.Core.{DocGuide, Input} | ||||||
|  | 
 | ||||||
|  |   @behaviour RabbitMQ.CLI.CommandBehaviour | ||||||
|  |   use RabbitMQ.CLI.DefaultOutput | ||||||
|  | 
 | ||||||
|  |   def switches() do | ||||||
|  |     [ | ||||||
|  |       cipher: :string, | ||||||
|  |       hash: :string, | ||||||
|  |       iterations: :integer | ||||||
|  |     ] | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   @atomized_keys [:cipher, :hash] | ||||||
|  |   @prefix "encrypted:" | ||||||
|  | 
 | ||||||
|  |   def distribution(_), do: :none | ||||||
|  | 
 | ||||||
|  |   def merge_defaults(args, opts) do | ||||||
|  |     with_defaults = | ||||||
|  |       Map.merge( | ||||||
|  |         %{ | ||||||
|  |           cipher: :rabbit_pbe.default_cipher(), | ||||||
|  |           hash: :rabbit_pbe.default_hash(), | ||||||
|  |           iterations: :rabbit_pbe.default_iterations() | ||||||
|  |         }, | ||||||
|  |         opts | ||||||
|  |       ) | ||||||
|  | 
 | ||||||
|  |     {args, Helpers.atomize_values(with_defaults, @atomized_keys)} | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def validate(args, _) when length(args) < 1 do | ||||||
|  |     {:validation_failure, {:not_enough_args, "Please provide a value to decode and a passphrase"}} | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def validate(args, _) when length(args) > 2 do | ||||||
|  |     {:validation_failure, :too_many_args} | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def validate(_args, opts) do | ||||||
|  |     case {supports_cipher(opts.cipher), supports_hash(opts.hash), opts.iterations > 0} do | ||||||
|  |       {false, _, _} -> | ||||||
|  |         {:validation_failure, {:bad_argument, "The requested cipher is not supported"}} | ||||||
|  | 
 | ||||||
|  |       {_, false, _} -> | ||||||
|  |         {:validation_failure, {:bad_argument, "The requested hash is not supported"}} | ||||||
|  | 
 | ||||||
|  |       {_, _, false} -> | ||||||
|  |         {:validation_failure, | ||||||
|  |          {:bad_argument, | ||||||
|  |           "The requested number of iterations is incorrect (must be a positive integer)"}} | ||||||
|  | 
 | ||||||
|  |       {true, true, true} -> | ||||||
|  |         :ok | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def run([value], %{cipher: cipher, hash: hash, iterations: iterations} = opts) do | ||||||
|  |     case Input.consume_single_line_string_with_prompt("Passphrase: ", opts) do | ||||||
|  |       :eof -> | ||||||
|  |         {:error, :not_enough_args} | ||||||
|  | 
 | ||||||
|  |       passphrase -> | ||||||
|  |         try do | ||||||
|  |           term_value = Helpers.evaluate_input_as_term(value) | ||||||
|  | 
 | ||||||
|  |           term_to_decrypt = | ||||||
|  |             case term_value do | ||||||
|  |               prefixed_val when is_bitstring(prefixed_val) or is_list(prefixed_val) -> | ||||||
|  |                 tag_input_value_with_encrypted(prefixed_val) | ||||||
|  | 
 | ||||||
|  |               {:encrypted, _} = encrypted -> | ||||||
|  |                 encrypted | ||||||
|  | 
 | ||||||
|  |               _ -> | ||||||
|  |                 {:encrypted, term_value} | ||||||
|  |             end | ||||||
|  | 
 | ||||||
|  |           result = :rabbit_pbe.decrypt_term(cipher, hash, iterations, passphrase, term_to_decrypt) | ||||||
|  |           {:ok, result} | ||||||
|  |         catch | ||||||
|  |           _, _ -> | ||||||
|  |             IO.inspect(__STACKTRACE__) | ||||||
|  |             {:error, | ||||||
|  |              "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?"} | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def run([value, passphrase], %{cipher: cipher, hash: hash, iterations: iterations}) do | ||||||
|  |     try do | ||||||
|  |       term_value = Helpers.evaluate_input_as_term(value) | ||||||
|  | 
 | ||||||
|  |       term_to_decrypt = | ||||||
|  |         case term_value do | ||||||
|  |           prefixed_val when is_bitstring(prefixed_val) or is_list(prefixed_val) -> | ||||||
|  |             tag_input_value_with_encrypted(prefixed_val) | ||||||
|  | 
 | ||||||
|  |           {:encrypted, _} = encrypted -> | ||||||
|  |             encrypted | ||||||
|  | 
 | ||||||
|  |           _ -> | ||||||
|  |             {:encrypted, term_value} | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |       result = :rabbit_pbe.decrypt_term(cipher, hash, iterations, passphrase, term_to_decrypt) | ||||||
|  |       {:ok, result} | ||||||
|  |     catch | ||||||
|  |       _, _ -> | ||||||
|  |         IO.inspect(__STACKTRACE__) | ||||||
|  |         {:error, | ||||||
|  |          "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?"} | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def formatter(), do: RabbitMQ.CLI.Formatters.Erlang | ||||||
|  | 
 | ||||||
|  |   def banner(_, _) do | ||||||
|  |     "Decrypting a rabbitmq.conf string value..." | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def usage, | ||||||
|  |     do: "decrypt_conf_value value passphrase [--cipher <cipher>] [--hash <hash>] [--iterations <iterations>]" | ||||||
|  | 
 | ||||||
|  |   def usage_additional() do | ||||||
|  |     [ | ||||||
|  |       ["<value>", "a double-quoted rabbitmq.conf string value to decode"], | ||||||
|  |       ["<passphrase>", "passphrase to use with the config value encryption key"], | ||||||
|  |       ["--cipher <cipher>", "cipher suite to use"], | ||||||
|  |       ["--hash <hash>", "hashing function to use"], | ||||||
|  |       ["--iterations <iterations>", "number of iteration to apply"] | ||||||
|  |     ] | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def usage_doc_guides() do | ||||||
|  |     [ | ||||||
|  |       DocGuide.configuration() | ||||||
|  |     ] | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def help_section(), do: :configuration | ||||||
|  | 
 | ||||||
|  |   def description(), do: "Decrypts an encrypted configuration value" | ||||||
|  | 
 | ||||||
|  |   # | ||||||
|  |   # Implementation | ||||||
|  |   # | ||||||
|  | 
 | ||||||
|  |   defp supports_cipher(cipher), do: Enum.member?(:rabbit_pbe.supported_ciphers(), cipher) | ||||||
|  | 
 | ||||||
|  |   defp supports_hash(hash), do: Enum.member?(:rabbit_pbe.supported_hashes(), hash) | ||||||
|  | 
 | ||||||
|  |   defp tag_input_value_with_encrypted(value) when is_bitstring(value) or is_list(value) do | ||||||
|  |     bin_val = :rabbit_data_coercion.to_binary(value) | ||||||
|  |     untagged_val = String.replace_prefix(bin_val, @prefix, "") | ||||||
|  | 
 | ||||||
|  |     {:encrypted, untagged_val} | ||||||
|  |   end | ||||||
|  |   defp tag_input_value_with_encrypted(value) do | ||||||
|  |     {:encrypted, value} | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -130,7 +130,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EncodeCommand do | ||||||
| 
 | 
 | ||||||
|   def usage_additional() do |   def usage_additional() do | ||||||
|     [ |     [ | ||||||
|       ["<value>", "config value to encode"], |       ["<value>", "value to encode, to be used in advanced.config"], | ||||||
|       ["<passphrase>", "passphrase to use with the config value encryption key"], |       ["<passphrase>", "passphrase to use with the config value encryption key"], | ||||||
|       ["--cipher <cipher>", "cipher suite to use"], |       ["--cipher <cipher>", "cipher suite to use"], | ||||||
|       ["--hash <hash>", "hashing function to use"], |       ["--hash <hash>", "hashing function to use"], | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| ## License, v. 2.0. If a copy of the MPL was not distributed with this | ## 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/. | ## file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||||||
| ## | ## | ||||||
| ## Copyright (c) 2007-2023 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.  All rights reserved. | ## Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.  All rights reserved. | ||||||
| 
 | 
 | ||||||
| defmodule RabbitMQ.CLI.Ctl.Commands.EncryptConfValueCommand do | defmodule RabbitMQ.CLI.Ctl.Commands.EncryptConfValueCommand do | ||||||
|   alias RabbitMQ.CLI.Core.{DocGuide, Helpers, Input} |   alias RabbitMQ.CLI.Core.{DocGuide, Helpers, Input} | ||||||
|  |  | ||||||
|  | @ -0,0 +1,78 @@ | ||||||
|  | ## 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-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.  All rights reserved. | ||||||
|  | 
 | ||||||
|  | defmodule EncryptConfValueCommandTest do | ||||||
|  |   use ExUnit.Case, async: false | ||||||
|  | 
 | ||||||
|  |   @command RabbitMQ.CLI.Ctl.Commands.EncryptConfValueCommand | ||||||
|  | 
 | ||||||
|  |   setup _context do | ||||||
|  |     {:ok, | ||||||
|  |      opts: %{ | ||||||
|  |        cipher: :rabbit_pbe.default_cipher(), | ||||||
|  |        hash: :rabbit_pbe.default_hash(), | ||||||
|  |        iterations: :rabbit_pbe.default_iterations() | ||||||
|  |      }} | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   test "validate: providing exactly 2 positional arguments passes", context do | ||||||
|  |     assert :ok == @command.validate(["value", "secret"], context[:opts]) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   test "validate: providing zero or one positional argument passes", context do | ||||||
|  |     assert :ok == @command.validate([], context[:opts]) | ||||||
|  |     assert :ok == @command.validate(["value"], context[:opts]) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   test "validate: providing three or more positional argument fails", context do | ||||||
|  |     assert match?( | ||||||
|  |              {:validation_failure, :too_many_args}, | ||||||
|  |              @command.validate(["value", "secret", "incorrect"], context[:opts]) | ||||||
|  |            ) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   test "validate: hash and cipher must be supported", context do | ||||||
|  |     assert match?( | ||||||
|  |              {:validation_failure, {:bad_argument, _}}, | ||||||
|  |              @command.validate( | ||||||
|  |                ["value", "secret"], | ||||||
|  |                Map.merge(context[:opts], %{cipher: :funny_cipher}) | ||||||
|  |              ) | ||||||
|  |            ) | ||||||
|  | 
 | ||||||
|  |     assert match?( | ||||||
|  |              {:validation_failure, {:bad_argument, _}}, | ||||||
|  |              @command.validate( | ||||||
|  |                ["value", "secret"], | ||||||
|  |                Map.merge(context[:opts], %{hash: :funny_hash}) | ||||||
|  |              ) | ||||||
|  |            ) | ||||||
|  | 
 | ||||||
|  |     assert match?( | ||||||
|  |              {:validation_failure, {:bad_argument, _}}, | ||||||
|  |              @command.validate( | ||||||
|  |                ["value", "secret"], | ||||||
|  |                Map.merge(context[:opts], %{cipher: :funny_cipher, hash: :funny_hash}) | ||||||
|  |              ) | ||||||
|  |            ) | ||||||
|  | 
 | ||||||
|  |     assert :ok == @command.validate(["value", "secret"], context[:opts]) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   test "validate: number of iterations must greater than 0", context do | ||||||
|  |     assert match?( | ||||||
|  |              {:validation_failure, {:bad_argument, _}}, | ||||||
|  |              @command.validate(["value", "secret"], Map.merge(context[:opts], %{iterations: 0})) | ||||||
|  |            ) | ||||||
|  | 
 | ||||||
|  |     assert match?( | ||||||
|  |              {:validation_failure, {:bad_argument, _}}, | ||||||
|  |              @command.validate(["value", "secret"], Map.merge(context[:opts], %{iterations: -1})) | ||||||
|  |            ) | ||||||
|  | 
 | ||||||
|  |     assert :ok == @command.validate(["value", "secret"], context[:opts]) | ||||||
|  |   end | ||||||
|  | end | ||||||
		Loading…
	
		Reference in New Issue