From 6941614a43ccf799e5c3e34ab5d57e5fdb83b7f9 Mon Sep 17 00:00:00 2001 From: Simon Unge Date: Mon, 23 Jan 2023 09:45:50 -0800 Subject: [PATCH] See #5957. CLI command to generate hashed password from cleartext password --- .../cli/ctl/commands/hash_password_command.ex | 46 +++++++++++++++++ .../test/ctl/hash_password_command_test.exs | 51 +++++++++++++++++++ deps/rabbitmq_cli/test/test_helper.exs | 7 +++ 3 files changed, 104 insertions(+) create mode 100644 deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/hash_password_command.ex create mode 100644 deps/rabbitmq_cli/test/ctl/hash_password_command_test.exs diff --git a/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/hash_password_command.ex b/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/hash_password_command.ex new file mode 100644 index 000000000000..0acef8ddd93b --- /dev/null +++ b/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/hash_password_command.ex @@ -0,0 +1,46 @@ +## 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. + +defmodule RabbitMQ.CLI.Ctl.Commands.HashPasswordCommand do + @behaviour RabbitMQ.CLI.CommandBehaviour + + use RabbitMQ.CLI.Core.MergesNoDefaults + + def run([cleartextpassword], %{node: node_name}) do + r = + :rabbit_misc.rpc_call( + node_name, + :rabbit_password, + :hash, + [cleartextpassword] + ) + + Base.encode64(r) + end + + def validate(args, _options) when length(args) > 1 do + {:validation_failure, :too_many_args} + end + + def validate(args, _options) when length(args) < 1 do + {:validation_failure, :not_enough_args} + end + + def validate([""], _options) do + {:bad_argument, "password cannot be an empty string"} + end + + def validate([_arg], _options) do + :ok + end + + use RabbitMQ.CLI.DefaultOutput + + def usage, do: "hash_password " + + def banner([arg], _options), + do: "Will hash password #{arg}" +end diff --git a/deps/rabbitmq_cli/test/ctl/hash_password_command_test.exs b/deps/rabbitmq_cli/test/ctl/hash_password_command_test.exs new file mode 100644 index 000000000000..06ebb3f8e37e --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/hash_password_command_test.exs @@ -0,0 +1,51 @@ +## 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-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule HashPasswordCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.HashPasswordCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + :ok + end + + setup context do + on_exit(context, fn -> delete_user(context[:user]) end) + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: too many arguments", context do + assert @command.validate(["foo", "bar"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + test "validate: too few arguments", context do + assert @command.validate([], context[:opts]) == {:validation_failure, :not_enough_args} + end + + test "validate: empty string", context do + assert @command.validate([""], context[:opts]) == + {:bad_argument, "password cannot be an empty string"} + end + + @tag user: "someone", password: "hashed_password" + test "run: successfully create user with a hashed password from cli cmd", context do + hashed_pwd = @command.run([context[:password]], context[:opts]) + add_user_hashed_password(context[:user], hashed_pwd) + assert {:ok, _} = authenticate_user(context[:user], context[:password]) + end + + @tag user: "someone", password: "hashed_password" + test "run: Create user with a hashed password from cli cmd, use hashed pwd as cleartest password", + context do + hashed_pwd = @command.run([context[:password]], context[:opts]) + add_user_hashed_password(context[:user], hashed_pwd) + assert {:refused, _, _, _} = authenticate_user(context[:user], hashed_pwd) + end +end diff --git a/deps/rabbitmq_cli/test/test_helper.exs b/deps/rabbitmq_cli/test/test_helper.exs index a736041569dd..2f6013569379 100644 --- a/deps/rabbitmq_cli/test/test_helper.exs +++ b/deps/rabbitmq_cli/test/test_helper.exs @@ -73,6 +73,13 @@ defmodule TestHelper do ]) end + def add_user_hashed_password(name, hash_password) do + :rpc.call(get_rabbit_hostname(), :rabbit_auth_backend_internal, :put_user, [ + %{:name => name, :password_hash => hash_password, :tags => "administrator"}, + "acting-user" + ]) + end + def delete_user(name) do :rpc.call(get_rabbit_hostname(), :rabbit_auth_backend_internal, :delete_user, [ name,