Drop --[no-]interactive support
Per discussion with @lukebakken. It serves no purpose and to make scripting with stream redirection work we had to make validation changes that make that flag irrelevant and even confusing. The only downside of this behavior is that something like rabbitmqctl add_user --silent "a-username" (without a password or redirected stream, with suppressed output) would "hang" waiting for stdin input. If --silent is omitted there would be an input prompt, making it clearer what's going on. Closes #365 with a different behavior from the originally suggested.
This commit is contained in:
parent
b890fe787b
commit
37450cc0dc
|
|
@ -17,9 +17,14 @@ defmodule RabbitMQ.CLI.Core.Input do
|
|||
alias RabbitMQ.CLI.Core.Config
|
||||
|
||||
def infer_password(prompt, opts) do
|
||||
case Config.output_less?(opts) do
|
||||
val = case Config.output_less?(opts) do
|
||||
true -> IO.gets("")
|
||||
false -> IO.gets(prompt)
|
||||
end |> String.trim()
|
||||
end
|
||||
|
||||
case val do
|
||||
:eof -> :eof
|
||||
s -> String.trim(s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,18 +18,10 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AddUserCommand do
|
|||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
|
||||
def switches, do: [interactive: :boolean]
|
||||
def aliases, do: [i: :interactive]
|
||||
|
||||
def merge_defaults(args, opts) do
|
||||
{args, Map.merge(%{interactive: true}, opts)}
|
||||
end
|
||||
use RabbitMQ.CLI.Core.MergesNoDefaults
|
||||
|
||||
def validate(args, _) when length(args) < 1, do: {:validation_failure, :not_enough_args}
|
||||
def validate(args, _) when length(args) > 2, do: {:validation_failure, :too_many_args}
|
||||
def validate([_], %{interactive: false}) do
|
||||
{:validation_failure, {:not_enough_args, "password must be provided as an argument in non-interactive mode"}}
|
||||
end
|
||||
def validate([_], _), do: :ok
|
||||
def validate(["", _], _) do
|
||||
{:validation_failure, {:bad_argument, "user cannot be an empty string"}}
|
||||
|
|
@ -43,14 +35,15 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AddUserCommand do
|
|||
# e.g. when a user only authenticates using X.509 certificates.
|
||||
# Credential validators can be used to require passwords of a certain length
|
||||
# or following a certain pattern. This is a core server responsibility. MK.
|
||||
password = Input.infer_password("Password: ", opts)
|
||||
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_auth_backend_internal,
|
||||
:add_user,
|
||||
[username, password, Helpers.cli_acting_user()]
|
||||
)
|
||||
case Input.infer_password("Password: ", opts) do
|
||||
:eof -> {:error, :not_enough_args}
|
||||
password -> :rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_auth_backend_internal,
|
||||
:add_user,
|
||||
[username, password, Helpers.cli_acting_user()]
|
||||
)
|
||||
end
|
||||
end
|
||||
def run([username, password], %{node: node_name}) do
|
||||
:rabbit_misc.rpc_call(
|
||||
|
|
@ -66,9 +59,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AddUserCommand do
|
|||
def usage_additional() do
|
||||
[
|
||||
["<username>", "Self-explanatory"],
|
||||
["<password>", "Password this user will authenticate with. Use a blank string to disable password-based authentication."],
|
||||
["--interactive", "Indicates that password might be provided via stdin. This is the default."],
|
||||
["--no-interactive", "Indicates that password must not be read from stdin."]
|
||||
["<password>", "Password this user will authenticate with. Use a blank string to disable password-based authentication."]
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -84,6 +75,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AddUserCommand do
|
|||
|
||||
def banner([username | _], _), do: "Adding user \"#{username}\" ..."
|
||||
|
||||
def output({:error, :not_enough_args}, _) do
|
||||
{:error, ExitCodes.exit_software(), "Password is not provided via argument or stdin"}
|
||||
end
|
||||
def output({:error, {:user_already_exists, username}}, _) do
|
||||
{:error, ExitCodes.exit_software(), "User \"#{username}\" already exists"}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,36 +14,33 @@
|
|||
## Copyright (c) 2007-2019 Pivotal Software, Inc. All rights reserved.
|
||||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.AuthenticateUserCommand do
|
||||
alias RabbitMQ.CLI.Core.{DocGuide, Input}
|
||||
alias RabbitMQ.CLI.Core.{DocGuide, ExitCodes, Input}
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
|
||||
def switches, do: [interactive: :boolean]
|
||||
def aliases, do: [i: :interactive]
|
||||
|
||||
def merge_defaults(args, opts) do
|
||||
{args, Map.merge(%{interactive: true}, opts)}
|
||||
end
|
||||
use RabbitMQ.CLI.Core.MergesNoDefaults
|
||||
|
||||
def validate(args, _) when length(args) < 1, do: {:validation_failure, :not_enough_args}
|
||||
def validate(args, _) when length(args) > 2, do: {:validation_failure, :too_many_args}
|
||||
def validate([_], %{interactive: false}) do
|
||||
{:validation_failure, {:not_enough_args, "password must be provided as an argument in non-interactive mode"}}
|
||||
end
|
||||
def validate([_], _), do: :ok
|
||||
def validate([_, _], _), do: :ok
|
||||
|
||||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||
|
||||
def run([user], %{node: node_name} = opts) do
|
||||
password = Input.infer_password("Password: ", opts)
|
||||
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_access_control,
|
||||
:check_user_pass_login,
|
||||
[user, password]
|
||||
)
|
||||
# note: blank passwords are currently allowed, they make sense
|
||||
# e.g. when a user only authenticates using X.509 certificates.
|
||||
# Credential validators can be used to require passwords of a certain length
|
||||
# or following a certain pattern. This is a core server responsibility. MK.
|
||||
case Input.infer_password("Password: ", opts) do
|
||||
:eof -> {:error, :not_enough_args}
|
||||
password -> :rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_access_control,
|
||||
:check_user_pass_login,
|
||||
[user, password]
|
||||
)
|
||||
end
|
||||
end
|
||||
def run([user, password], %{node: node_name}) do
|
||||
:rabbit_misc.rpc_call(
|
||||
|
|
@ -54,14 +51,12 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AuthenticateUserCommand do
|
|||
)
|
||||
end
|
||||
|
||||
def usage, do: "authenticate_user <username> <password> [--interactive]"
|
||||
def usage, do: "authenticate_user <username> <password>"
|
||||
|
||||
def usage_additional() do
|
||||
[
|
||||
["<username>", "Username to use"],
|
||||
["<password>", "Password to use. Can be entered via stdin in interactive mode."],
|
||||
["--interactive", "Indicates that password might be provided via stdin. This is the default."],
|
||||
["--no-interactive", "Indicates that password must not be read from stdin."]
|
||||
["<password>", "Password to use. Can be entered via stdin in interactive mode."]
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -77,12 +72,14 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AuthenticateUserCommand do
|
|||
|
||||
def banner([username | _], _), do: "Authenticating user \"#{username}\" ..."
|
||||
|
||||
def output({:error, :not_enough_args}, _) do
|
||||
{:error, ExitCodes.exit_software(), "Password is not provided via argument or stdin"}
|
||||
end
|
||||
def output({:refused, user, msg, args}, _) do
|
||||
{:error, RabbitMQ.CLI.Core.ExitCodes.exit_dataerr(),
|
||||
"Error: failed to authenticate user \"#{user}\"\n" <>
|
||||
to_string(:io_lib.format(msg, args))}
|
||||
end
|
||||
|
||||
def output({:ok, _user}, _) do
|
||||
{:ok, "Success"}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,26 +14,15 @@
|
|||
## Copyright (c) 2007-2019 Pivotal Software, Inc. All rights reserved.
|
||||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ChangePasswordCommand do
|
||||
alias RabbitMQ.CLI.Core.{DocGuide, Helpers, Input}
|
||||
alias RabbitMQ.CLI.Core.{DocGuide, ExitCodes, Helpers, Input}
|
||||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
|
||||
def switches, do: [interactive: :boolean]
|
||||
def aliases, do: [i: :interactive]
|
||||
|
||||
def merge_defaults(args, opts) do
|
||||
{args, Map.merge(%{interactive: true}, opts)}
|
||||
end
|
||||
use RabbitMQ.CLI.Core.MergesNoDefaults
|
||||
|
||||
def validate(args, _) when length(args) < 1, do: {:validation_failure, :not_enough_args}
|
||||
def validate(args, _) when length(args) > 2, do: {:validation_failure, :too_many_args}
|
||||
def validate([_], %{interactive: false}) do
|
||||
{:validation_failure, {:not_enough_args, "password must be provided as an argument in non-interactive mode"}}
|
||||
end
|
||||
def validate([_], _), do: :ok
|
||||
def validate(["", _], _) do
|
||||
{:validation_failure, {:bad_argument, "user cannot be an empty string"}}
|
||||
end
|
||||
def validate([_, _], _), do: :ok
|
||||
|
||||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||
|
|
@ -43,14 +32,15 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ChangePasswordCommand do
|
|||
# e.g. when a user only authenticates using X.509 certificates.
|
||||
# Credential validators can be used to require passwords of a certain length
|
||||
# or following a certain pattern. This is a core server responsibility. MK.
|
||||
password = Input.infer_password("Password: ", opts)
|
||||
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_auth_backend_internal,
|
||||
:change_password,
|
||||
[username, password, Helpers.cli_acting_user()]
|
||||
)
|
||||
case Input.infer_password("Password: ", opts) do
|
||||
:eof -> {:error, :not_enough_args}
|
||||
password -> :rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_auth_backend_internal,
|
||||
:change_password,
|
||||
[username, password, Helpers.cli_acting_user()]
|
||||
)
|
||||
end
|
||||
end
|
||||
def run([username, password], %{node: node_name}) do
|
||||
:rabbit_misc.rpc_call(
|
||||
|
|
@ -61,6 +51,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ChangePasswordCommand do
|
|||
)
|
||||
end
|
||||
|
||||
def output({:error, :not_enough_args}, _) do
|
||||
{:error, ExitCodes.exit_software(), "Password is not provided via argument or stdin"}
|
||||
end
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage, do: "change_password <username> <password>"
|
||||
|
|
|
|||
|
|
@ -40,17 +40,12 @@ defmodule AddUserCommandTest do
|
|||
{:validation_failure, :too_many_args}
|
||||
end
|
||||
|
||||
test "validate: one argument in non-interactive mode fails" do
|
||||
assert match?({:validation_failure, {:not_enough_args, _}},
|
||||
@command.validate(["user"], %{interactive: false}))
|
||||
end
|
||||
|
||||
test "validate: two arguments passes" do
|
||||
assert @command.validate(["user", "password"], %{}) == :ok
|
||||
end
|
||||
|
||||
test "validate: one argument in interactive mode passes" do
|
||||
assert @command.validate(["user"], %{interactive: true}) == :ok
|
||||
test "validate: one argument passes" do
|
||||
assert @command.validate(["user"], %{}) == :ok
|
||||
end
|
||||
|
||||
@tag user: "", password: "password"
|
||||
|
|
|
|||
|
|
@ -43,19 +43,14 @@ defmodule AuthenticateUserCommandTest do
|
|||
{:validation_failure, :too_many_args}
|
||||
end
|
||||
|
||||
test "validate: one argument in non-interactive mode fails" do
|
||||
assert match?({:validation_failure, {:not_enough_args, _}},
|
||||
@command.validate(["user"], %{interactive: false}))
|
||||
test "validate: one argument passes" do
|
||||
assert @command.validate(["user"], %{}) == :ok
|
||||
end
|
||||
|
||||
test "validate: two arguments passes" do
|
||||
assert @command.validate(["user", "password"], %{}) == :ok
|
||||
end
|
||||
|
||||
test "validate: one argument in interactive mode passes" do
|
||||
assert @command.validate(["user"], %{interactive: true}) == :ok
|
||||
end
|
||||
|
||||
@tag user: @user, password: @password
|
||||
test "run: a valid username and password returns okay", context do
|
||||
assert {:ok, _} = @command.run([context[:user], context[:password]], context[:opts])
|
||||
|
|
|
|||
|
|
@ -40,17 +40,12 @@ defmodule ChangePasswordCommandTest do
|
|||
{:validation_failure, :too_many_args}
|
||||
end
|
||||
|
||||
test "validate: one argument in non-interactive mode fails" do
|
||||
assert match?({:validation_failure, {:not_enough_args, _}},
|
||||
@command.validate(["user"], %{interactive: false}))
|
||||
end
|
||||
|
||||
test "validate: two arguments passes" do
|
||||
assert @command.validate(["user", "password"], %{}) == :ok
|
||||
end
|
||||
|
||||
test "validate: one argument in interactive mode passes" do
|
||||
assert @command.validate(["user"], %{interactive: true}) == :ok
|
||||
test "validate: one argument passes" do
|
||||
assert @command.validate(["user"], %{}) == :ok
|
||||
end
|
||||
|
||||
@tag user: @user, password: "new_password"
|
||||
|
|
|
|||
Loading…
Reference in New Issue