Remove all unused code related to the deprecated login command

This commit is contained in:
Chris Roberts 2020-10-27 15:32:12 -07:00
parent 70d36bc7cd
commit baa24af179
8 changed files with 0 additions and 834 deletions

View File

@ -1,4 +1,3 @@
require "rest_client"
require "vagrant_cloud"
require "vagrant/util/downloader"
require "vagrant/util/presence"

View File

@ -1,253 +0,0 @@
require "rest_client"
require "vagrant/util/downloader"
require "vagrant/util/presence"
module VagrantPlugins
module LoginCommand
class Client
APP = "app".freeze
include Vagrant::Util::Presence
attr_accessor :username_or_email
attr_accessor :password
attr_reader :two_factor_default_delivery_method
attr_reader :two_factor_delivery_methods
# Initializes a login client with the given Vagrant::Environment.
#
# @param [Vagrant::Environment] env
def initialize(env)
@logger = Log4r::Logger.new("vagrant::login::client")
@env = env
end
# Removes the token, effectively logging the user out.
def clear_token
@logger.info("Clearing token")
token_path.delete if token_path.file?
end
# Checks if the user is logged in by verifying their authentication
# token.
#
# @return [Boolean]
def logged_in?
token = self.token
return false if !token
with_error_handling do
url = "#{Vagrant.server_url}/api/v1/authenticate" +
"?access_token=#{token}"
RestClient.get(url, content_type: :json)
true
end
rescue Errors::Unauthorized
false
end
# Login logs a user in and returns the token for that user. The token
# is _not_ stored unless {#store_token} is called.
#
# @param [String] description
# @param [String] code
# @return [String] token The access token, or nil if auth failed.
def login(description: nil, code: nil)
@logger.info("Logging in '#{username_or_email}'")
response = post(
"/api/v1/authenticate", {
user: {
login: username_or_email,
password: password
},
token: {
description: description
},
two_factor: {
code: code
}
}
)
response["token"]
end
# Requests a 2FA code
# @param [String] delivery_method
def request_code(delivery_method)
@env.ui.warn("Requesting 2FA code via #{delivery_method.upcase}...")
response = post(
"/api/v1/two-factor/request-code", {
user: {
login: username_or_email,
password: password
},
two_factor: {
delivery_method: delivery_method.downcase
}
}
)
two_factor = response['two_factor']
obfuscated_destination = two_factor['obfuscated_destination']
@env.ui.success("2FA code sent to #{obfuscated_destination}.")
end
# Issues a post to a Vagrant Cloud path with the given payload.
# @param [String] path
# @param [Hash] payload
# @return [Hash] response data
def post(path, payload)
with_error_handling do
url = File.join(Vagrant.server_url, path)
proxy = nil
proxy ||= ENV["HTTPS_PROXY"] || ENV["https_proxy"]
proxy ||= ENV["HTTP_PROXY"] || ENV["http_proxy"]
RestClient.proxy = proxy
response = RestClient::Request.execute(
method: :post,
url: url,
payload: JSON.dump(payload),
proxy: proxy,
headers: {
accept: :json,
content_type: :json,
user_agent: Vagrant::Util::Downloader::USER_AGENT,
},
)
JSON.load(response.to_s)
end
end
# Stores the given token locally, removing any previous tokens.
#
# @param [String] token
def store_token(token)
@logger.info("Storing token in #{token_path}")
token_path.open("w") do |f|
f.write(token)
end
nil
end
# Reads the access token if there is one. This will first read the
# `VAGRANT_CLOUD_TOKEN` environment variable and then fallback to the stored
# access token on disk.
#
# @return [String]
def token
if present?(ENV["VAGRANT_CLOUD_TOKEN"]) && token_path.exist?
@env.ui.warn <<-EOH.strip
Vagrant detected both the VAGRANT_CLOUD_TOKEN environment variable and a Vagrant login
token are present on this system. The VAGRANT_CLOUD_TOKEN environment variable takes
precedence over the locally stored token. To remove this error, either unset
the VAGRANT_CLOUD_TOKEN environment variable or remove the login token stored on disk:
~/.vagrant.d/data/vagrant_login_token
EOH
end
if present?(ENV["VAGRANT_CLOUD_TOKEN"])
@logger.debug("Using authentication token from environment variable")
return ENV["VAGRANT_CLOUD_TOKEN"]
end
if token_path.exist?
@logger.debug("Using authentication token from disk at #{token_path}")
return token_path.read.strip
end
if present?(ENV["ATLAS_TOKEN"])
@logger.warn("ATLAS_TOKEN detected within environment. Using ATLAS_TOKEN in place of VAGRANT_CLOUD_TOKEN.")
return ENV["ATLAS_TOKEN"]
end
@logger.debug("No authentication token in environment or #{token_path}")
nil
end
protected
def with_error_handling(&block)
yield
rescue RestClient::Unauthorized
@logger.debug("Unauthorized!")
raise Errors::Unauthorized
rescue RestClient::BadRequest => e
@logger.debug("Bad request:")
@logger.debug(e.message)
@logger.debug(e.backtrace.join("\n"))
parsed_response = JSON.parse(e.response)
errors = parsed_response["errors"].join("\n")
raise Errors::ServerError, errors: errors
rescue RestClient::NotAcceptable => e
@logger.debug("Got unacceptable response:")
@logger.debug(e.message)
@logger.debug(e.backtrace.join("\n"))
parsed_response = JSON.parse(e.response)
if two_factor = parsed_response['two_factor']
store_two_factor_information two_factor
if two_factor_default_delivery_method != APP
request_code two_factor_default_delivery_method
end
raise Errors::TwoFactorRequired
end
begin
errors = parsed_response["errors"].join("\n")
raise Errors::ServerError, errors: errors
rescue JSON::ParserError; end
raise "An unexpected error occurred: #{e.inspect}"
rescue SocketError
@logger.info("Socket error")
raise Errors::ServerUnreachable, url: Vagrant.server_url.to_s
end
def token_path
@env.data_dir.join("vagrant_login_token")
end
def store_two_factor_information(two_factor)
@two_factor_default_delivery_method =
two_factor['default_delivery_method']
@two_factor_delivery_methods =
two_factor['delivery_methods']
@env.ui.warn "2FA is enabled for your account."
if two_factor_default_delivery_method == APP
@env.ui.info "Enter the code from your authenticator."
else
@env.ui.info "Default method is " \
"'#{two_factor_default_delivery_method}'."
end
other_delivery_methods =
two_factor_delivery_methods - [APP]
if other_delivery_methods.any?
other_delivery_methods_sentence = other_delivery_methods
.map { |word| "'#{word}'" }
.join(' or ')
@env.ui.info "You can also type #{other_delivery_methods_sentence} " \
"to request a new code."
end
end
end
end
end

View File

@ -1,137 +0,0 @@
require 'socket'
module VagrantPlugins
module LoginCommand
class Command < Vagrant.plugin("2", "command")
def self.synopsis
"log in to HashiCorp's Vagrant Cloud"
end
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant login"
o.separator ""
o.on("-c", "--check", "Only checks if you're logged in") do |c|
options[:check] = c
end
o.on("-d", "--description DESCRIPTION", String, "Description for the Vagrant Cloud token") do |t|
options[:description] = t
end
o.on("-k", "--logout", "Logs you out if you're logged in") do |k|
options[:logout] = k
end
o.on("-t", "--token TOKEN", String, "Set the Vagrant Cloud token") do |t|
options[:token] = t
end
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
options[:login] = t
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
@client = Client.new(@env)
@client.username_or_email = options[:login]
# Determine what task we're actually taking based on flags
if options[:check]
return execute_check
elsif options[:logout]
return execute_logout
elsif options[:token]
return execute_token(options[:token])
end
# Let the user know what is going on.
@env.ui.output(I18n.t("login_command.command_header") + "\n")
# If it is a private cloud installation, show that
if Vagrant.server_url != Vagrant::DEFAULT_SERVER_URL
@env.ui.output("Vagrant Cloud URL: #{Vagrant.server_url}")
end
# Ask for the username
if @client.username_or_email
@env.ui.output("Vagrant Cloud username or email: #{@client.username_or_email}")
end
until @client.username_or_email
@client.username_or_email = @env.ui.ask("Vagrant Cloud username or email: ")
end
until @client.password
@client.password = @env.ui.ask("Password (will be hidden): ", echo: false)
end
description = options[:description]
if description
@env.ui.output("Token description: #{description}")
else
description_default = "Vagrant login from #{Socket.gethostname}"
until description
description =
@env.ui.ask("Token description (Defaults to #{description_default.inspect}): ")
end
description = description_default if description.empty?
end
code = nil
begin
token = @client.login(description: description, code: code)
rescue Errors::TwoFactorRequired
until code
code = @env.ui.ask("2FA code: ")
if @client.two_factor_delivery_methods.include?(code.downcase)
delivery_method, code = code, nil
@client.request_code delivery_method
end
end
retry
end
@client.store_token(token)
@env.ui.success(I18n.t("login_command.logged_in"))
0
end
def execute_check
if @client.logged_in?
@env.ui.success(I18n.t("login_command.check_logged_in"))
return 0
else
@env.ui.error(I18n.t("login_command.check_not_logged_in"))
return 1
end
end
def execute_logout
@client.clear_token
@env.ui.success(I18n.t("login_command.logged_out"))
return 0
end
def execute_token(token)
@client.store_token(token)
@env.ui.success(I18n.t("login_command.token_saved"))
if @client.logged_in?
@env.ui.success(I18n.t("login_command.check_logged_in"))
return 0
else
@env.ui.error(I18n.t("login_command.invalid_token"))
return 1
end
end
end
end
end

View File

@ -1,24 +0,0 @@
module VagrantPlugins
module LoginCommand
module Errors
class Error < Vagrant::Errors::VagrantError
error_namespace("login_command.errors")
end
class ServerError < Error
error_key(:server_error)
end
class ServerUnreachable < Error
error_key(:server_unreachable)
end
class Unauthorized < Error
error_key(:unauthorized)
end
class TwoFactorRequired < Error
end
end
end
end

View File

@ -1,49 +0,0 @@
en:
login_command:
middleware:
authentication:
different_target: |-
Vagrant has detected a custom Vagrant server in use for downloading
box files. An authentication token is currently set which will be
added to the box request. If the custom Vagrant server should not
be receiving the authentication token, please unset it.
Known Vagrant server: %{known_host}
Custom Vagrant server: %{custom_host}
Press ctrl-c to cancel...
errors:
server_error: |-
The Vagrant Cloud server responded with a not-OK response:
%{errors}
server_unreachable: |-
The Vagrant Cloud server is not currently accepting connections. Please check
your network connection and try again later.
unauthorized: |-
Invalid username or password. Please try again.
check_logged_in: |-
You are already logged in.
check_not_logged_in: |-
You are not currently logged in. Please run `vagrant login` and provide
your login information to authenticate.
command_header: |-
In a moment we will ask for your username and password to HashiCorp's
Vagrant Cloud. After authenticating, we will store an access token locally on
disk. Your login details will be transmitted over a secure connection, and
are never stored on disk locally.
If you do not have an Vagrant Cloud account, sign up at
https://www.vagrantcloud.com
invalid_login: |-
Invalid username or password. Please try again.
invalid_token: |-
Invalid token. Please try again.
logged_in: |-
You are now logged in.
logged_out: |-
You are logged out.
token_saved: |-
The token was successfully saved.

View File

@ -2,9 +2,6 @@ require "vagrant"
module VagrantPlugins
module LoginCommand
autoload :Client, File.expand_path("../client", __FILE__)
autoload :Errors, File.expand_path("../errors", __FILE__)
class Plugin < Vagrant.plugin("2")
name "vagrant-login"
description <<-DESC
@ -13,18 +10,8 @@ module VagrantPlugins
command(:login) do
require File.expand_path("../../cloud/auth/login", __FILE__)
init!
VagrantPlugins::CloudCommand::AuthCommand::Command::Login
end
protected
def self.init!
return if defined?(@_init)
I18n.load_path << File.expand_path("../../cloud/locales/en.yml", __FILE__)
I18n.reload!
@_init = true
end
end
end
end

View File

@ -1,261 +0,0 @@
require File.expand_path("../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/login/command")
describe VagrantPlugins::LoginCommand::Client do
include_context "unit"
let(:env) { isolated_environment.create_vagrant_env }
subject(:client) { described_class.new(env) }
before(:all) do
I18n.load_path << Vagrant.source_root.join("plugins/commands/login/locales/en.yml")
I18n.reload!
end
before do
stub_env("ATLAS_TOKEN" => nil)
subject.clear_token
end
describe "#logged_in?" do
let(:url) { "#{Vagrant.server_url}/api/v1/authenticate?access_token=#{token}" }
let(:headers) { { "Content-Type" => "application/json" } }
before { allow(subject).to receive(:token).and_return(token) }
context "when there is no token" do
let(:token) { nil }
it "returns false" do
expect(subject.logged_in?).to be(false)
end
end
context "when there is a token" do
let(:token) { "ABCD1234" }
it "returns true if the endpoint returns a 200" do
stub_request(:get, url)
.with(headers: headers)
.to_return(body: JSON.pretty_generate("token" => token))
expect(subject.logged_in?).to be(true)
end
it "raises an error if the endpoint returns a non-200" do
stub_request(:get, url)
.with(headers: headers)
.to_return(body: JSON.pretty_generate("bad" => true), status: 401)
expect(subject.logged_in?).to be(false)
end
it "raises an exception if the server cannot be found" do
stub_request(:get, url)
.to_raise(SocketError)
expect { subject.logged_in? }
.to raise_error(VagrantPlugins::LoginCommand::Errors::ServerUnreachable)
end
end
end
describe "#login" do
let(:request) {
{
user: {
login: login,
password: password,
},
token: {
description: description,
},
two_factor: {
code: nil
}
}
}
let(:login) { "foo" }
let(:password) { "bar" }
let(:description) { "Token description" }
let(:headers) {
{
"Accept" => "application/json",
"Content-Type" => "application/json",
}
}
let(:response) {
{
token: "baz"
}
}
it "returns the access token after successful login" do
stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate").
with(body: JSON.dump(request), headers: headers).
to_return(status: 200, body: JSON.dump(response))
client.username_or_email = login
client.password = password
expect(client.login(description: "Token description")).to eq("baz")
end
context "when 2fa is required" do
let(:response) {
{
two_factor: {
default_delivery_method: default_delivery_method,
delivery_methods: delivery_methods
}
}
}
let(:default_delivery_method) { "app" }
let(:delivery_methods) { ["app"] }
before do
stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate").
to_return(status: 406, body: JSON.dump(response))
end
it "raises a two-factor required error" do
expect {
client.login
}.to raise_error(VagrantPlugins::LoginCommand::Errors::TwoFactorRequired)
end
context "when the default delivery method is not app" do
let(:default_delivery_method) { "sms" }
let(:delivery_methods) { ["app", "sms"] }
it "requests a code and then raises a two-factor required error" do
expect(client)
.to receive(:request_code)
.with(default_delivery_method)
expect {
client.login
}.to raise_error(VagrantPlugins::LoginCommand::Errors::TwoFactorRequired)
end
end
end
context "on bad login" do
before do
stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate").
to_return(status: 401, body: "")
end
it "raises an error" do
expect {
client.login
}.to raise_error(VagrantPlugins::LoginCommand::Errors::Unauthorized)
end
end
context "if it can't reach the server" do
before do
stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate").
to_raise(SocketError)
end
it "raises an exception" do
expect {
subject.login
}.to raise_error(VagrantPlugins::LoginCommand::Errors::ServerUnreachable)
end
end
end
describe "#request_code" do
let(:request) {
{
user: {
login: login,
password: password,
},
two_factor: {
delivery_method: delivery_method
}
}
}
let(:login) { "foo" }
let(:password) { "bar" }
let(:delivery_method) { "sms" }
let(:headers) {
{
"Accept" => "application/json",
"Content-Type" => "application/json"
}
}
let(:response) {
{
two_factor: {
obfuscated_destination: "SMS number ending in 1234"
}
}
}
it "displays that the code was sent" do
expect(env.ui)
.to receive(:success)
.with("2FA code sent to SMS number ending in 1234.")
stub_request(:post, "#{Vagrant.server_url}/api/v1/two-factor/request-code").
with(body: JSON.dump(request), headers: headers).
to_return(status: 201, body: JSON.dump(response))
client.username_or_email = login
client.password = password
client.request_code delivery_method
end
end
describe "#token" do
it "reads ATLAS_TOKEN" do
stub_env("ATLAS_TOKEN" => "ABCD1234")
expect(subject.token).to eq("ABCD1234")
end
it "reads the stored file" do
subject.store_token("EFGH5678")
expect(subject.token).to eq("EFGH5678")
end
it "prefers the environment variable" do
stub_env("VAGRANT_CLOUD_TOKEN" => "ABCD1234")
subject.store_token("EFGH5678")
expect(subject.token).to eq("ABCD1234")
end
it "prints a warning if the envvar and stored file are both present" do
stub_env("VAGRANT_CLOUD_TOKEN" => "ABCD1234")
subject.store_token("EFGH5678")
expect(env.ui).to receive(:warn).with(/detected both/)
subject.token
end
it "returns nil if there's no token set" do
expect(subject.token).to be(nil)
end
end
describe "#store_token, #clear_token" do
it "stores the token and can re-access it" do
subject.store_token("foo")
expect(subject.token).to eq("foo")
expect(described_class.new(env).token).to eq("foo")
end
it "deletes the token" do
subject.store_token("foo")
subject.clear_token
expect(subject.token).to be_nil
end
end
end

View File

@ -1,96 +0,0 @@
require File.expand_path("../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/login/command")
describe VagrantPlugins::LoginCommand::Command do
include_context "unit"
let(:env) { isolated_environment.create_vagrant_env }
let(:token_path) { env.data_dir.join("vagrant_login_token") }
let(:stdout) { StringIO.new }
let(:stderr) { StringIO.new }
subject { described_class.new(argv, env) }
before do
stub_env("ATLAS_TOKEN" => "")
end
describe "#execute" do
context "with no args" do
let(:argv) { [] }
end
context "with --check" do
let(:argv) { ["--check"] }
context "when there is a token" do
before do
stub_request(:get, %r{^#{Vagrant.server_url}/api/v1/authenticate})
.to_return(status: 200)
end
before do
File.open(token_path, "w+") { |f| f.write("abcd1234") }
end
it "returns 0" do
expect(subject.execute).to eq(0)
end
end
context "when there is no token" do
it "returns 1" do
expect(subject.execute).to eq(1)
end
end
end
context "with --logout" do
let(:argv) { ["--logout"] }
it "returns 0" do
expect(subject.execute).to eq(0)
end
it "clears the token" do
subject.execute
expect(File.exist?(token_path)).to be(false)
end
end
context "with --token" do
let(:argv) { ["--token", "efgh5678"] }
context "when the token is valid" do
before do
stub_request(:get, %r{^#{Vagrant.server_url}/api/v1/authenticate})
.to_return(status: 200)
end
it "sets the token" do
subject.execute
token = File.read(token_path).strip
expect(token).to eq("efgh5678")
end
it "returns 0" do
expect(subject.execute).to eq(0)
end
end
context "when the token is invalid" do
before do
stub_request(:get, %r{^#{Vagrant.server_url}/api/v1/authenticate})
.to_return(status: 401)
end
it "returns 1" do
expect(subject.execute).to eq(1)
end
end
end
end
end