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-2021 VMware, Inc. or its affiliates.  All rights reserved.
6
7defmodule RabbitMQ.CLI.Ctl.Commands.EncodeCommand do
8  alias RabbitMQ.CLI.Core.{DocGuide, Helpers}
9
10  @behaviour RabbitMQ.CLI.CommandBehaviour
11  use RabbitMQ.CLI.DefaultOutput
12
13  def switches() do
14    [
15      cipher: :atom,
16      hash: :atom,
17      iterations: :integer
18    ]
19  end
20
21  def distribution(_), do: :none
22
23  def merge_defaults(args, opts) do
24    with_defaults = Map.merge(%{
25         cipher: :rabbit_pbe.default_cipher(),
26         hash: :rabbit_pbe.default_hash(),
27         iterations: :rabbit_pbe.default_iterations()
28       }, opts)
29    {args, with_defaults}
30  end
31
32  def validate(args, _) when length(args) < 2 do
33    {:validation_failure, {:not_enough_args, "Please provide a value to decode and a passphrase."}}
34  end
35
36  def validate(args, _) when length(args) > 2 do
37    {:validation_failure, :too_many_args}
38  end
39
40  def validate(args, opts) when length(args) === 2 do
41    case {supports_cipher(opts.cipher), supports_hash(opts.hash), opts.iterations > 0} do
42      {false, _, _} ->
43        {:validation_failure, {:bad_argument, "The requested cipher is not supported."}}
44
45      {_, false, _} ->
46        {:validation_failure, {:bad_argument, "The requested hash is not supported"}}
47
48      {_, _, false} ->
49        {:validation_failure, {:bad_argument, "The requested number of iterations is incorrect"}}
50
51      {true, true, true} ->
52        :ok
53    end
54  end
55
56  def run([value, passphrase], %{cipher: cipher, hash: hash, iterations: iterations}) do
57    try do
58      term_value = Helpers.evaluate_input_as_term(value)
59      result = {:encrypted, _} = :rabbit_pbe.encrypt_term(cipher, hash, iterations, passphrase, term_value)
60      {:ok, result}
61    catch
62      _, _ ->
63        {:error, "Error during cipher operation."}
64    end
65  end
66
67  def formatter(), do: RabbitMQ.CLI.Formatters.Erlang
68
69  def banner([_, _], _) do
70    "Encrypting value ..."
71  end
72
73  def usage, do: "encode value passphrase [--cipher <cipher>] [--hash <hash>] [--iterations <iterations>]"
74
75  def usage_additional() do
76    [
77      ["<value>", "config value to encode"],
78      ["<passphrase>", "passphrase to use with the config value encryption key"],
79      ["--cipher <cipher>", "cipher suite to use"],
80      ["--hash <hash>", "hashing function to use"],
81      ["--iterations <iterations>", "number of iteration to apply"]
82    ]
83  end
84
85  def usage_doc_guides() do
86    [
87      DocGuide.configuration()
88    ]
89  end
90
91  def help_section(), do: :configuration
92
93  def description(), do: "Encrypts a sensitive configuration value"
94
95  #
96  # Implementation
97  #
98
99  defp supports_cipher(cipher), do: Enum.member?(:rabbit_pbe.supported_ciphers(), cipher)
100
101  defp supports_hash(hash), do: Enum.member?(:rabbit_pbe.supported_hashes(), hash)
102end
103