Sperate hook for authenticating url and adding headers for authentication

This commit is contained in:
sophia 2020-08-18 17:38:49 -05:00
parent f440012b30
commit 0d1635303f
6 changed files with 132 additions and 27 deletions

View File

@ -76,8 +76,18 @@ module Vagrant
end
end
# Call the hook to transform URLs into authenticated URLs.
# In the case we don't have a plugin that does this, then it
# will just return the same URLs.
hook_env = env[:hook].call(
:authenticate_box_url, box_urls: url.dup)
authed_urls = hook_env[:box_urls]
if !authed_urls || authed_urls.length != url.length
raise "Bad box authentication hook, did not generate proper results."
end
# Test if any of our URLs point to metadata
is_metadata_results = url.map do |u|
is_metadata_results = authed_urls.map do |u|
begin
metadata_url?(u, env)
rescue Errors::DownloaderError => e
@ -105,7 +115,8 @@ module Vagrant
end
if is_metadata
add_from_metadata(url.first, env, expanded)
url = [url.first, authed_urls.first]
add_from_metadata(url, env, expanded)
else
add_direct(url, env)
end
@ -147,7 +158,10 @@ module Vagrant
# Adds a box given that the URL is a metadata document.
#
# @param [String] url The URL of the metadata for the box to add.
# @param [String | Array<String>] url The URL of the metadata for
# the box to add. If this is an array, then it must be a two-element
# array where the first element is the original URL and the second
# element is an authenticated URL.
# @param [Hash] env
# @param [Bool] expanded True if the metadata URL was expanded with
# a Atlas server URL.
@ -156,6 +170,15 @@ module Vagrant
provider = env[:box_provider]
provider = Array(provider) if provider
version = env[:box_version]
authenticated_url = url
if url.is_a?(Array)
# We have both a normal URL and "authenticated" URL. Split
# them up.
authenticated_url = url[1]
url = url[0]
end
display_original_url = Util::CredentialScrubber.scrub(Array(original_url).first)
display_url = Util::CredentialScrubber.scrub(url)
@ -170,7 +193,7 @@ module Vagrant
metadata = nil
begin
metadata_path = download(
url, env, json: true, ui: false)
authenticated_url, env, json: true, ui: false)
return if @download_interrupted
File.open(metadata_path) do |f|
@ -248,6 +271,16 @@ module Vagrant
end
provider_url = metadata_provider.url
if provider_url != authenticated_url
# Authenticate the provider URL since we're using auth
hook_env = env[:hook].call(:authenticate_box_url, box_urls: [provider_url])
authed_urls = hook_env[:box_urls]
if !authed_urls || authed_urls.length != 1
raise "Bad box authentication hook, did not generate proper results."
end
provider_url = authed_urls[0]
end
box_add(
[[provider_url, metadata_provider.url]],
metadata.name,
@ -404,7 +437,7 @@ module Vagrant
downloader_options[:box_extra_download_options] = env[:box_extra_download_options]
d = Util::Downloader.new(url, temp_path, downloader_options)
env[:hook].call(:authenticate_box_url, downloader: d)
env[:hook].call(:authenticate_box_downloader, downloader: d)
d
end
@ -412,7 +445,7 @@ module Vagrant
opts[:ui] = true if !opts.key?(:ui)
d = downloader(url, env, **opts)
env[:hook].call(:authenticate_box_url, downloader: d)
env[:hook].call(:authenticate_box_downloader, downloader: d)
# Download the box to a temporary path. We store the temporary
# path as an instance variable so that the `#recover` method can
@ -456,7 +489,7 @@ module Vagrant
# @return [Boolean] true if metadata
def metadata_url?(url, env)
d = downloader(url, env, json: true, ui: false)
env[:hook].call(:authenticate_box_url, downloader: d)
env[:hook].call(:authenticate_box_downloader, downloader: d)
# If we're downloading a file, cURL just returns no
# content-type (makes sense), so we just test if it is JSON

View File

@ -136,7 +136,7 @@ module Vagrant
opts = { headers: ["Accept: application/json"] }.merge(download_options)
d = Util::Downloader.new(url, tf.path, opts)
if @hook
@hook.call(:authenticate_box_url, downloader: d)
@hook.call(:authenticate_box_downloader, downloader: d)
end
d.download!
BoxMetadata.new(File.open(tf.path, "r"))

View File

@ -317,6 +317,12 @@ module Vagrant
metadata_url_file = box_directory.join("metadata_url")
metadata_url = metadata_url_file.read if metadata_url_file.file?
if metadata_url && @hook
hook_env = @hook.call(
:authenticate_box_url, box_urls: [metadata_url])
metadata_url = hook_env[:box_urls].first
end
return Box.new(
name, provider, version_dir_map[v.to_s], provider_dir,
metadata_url: metadata_url, hook: @hook

View File

@ -1,8 +1,6 @@
require "cgi"
require "uri"
require "vagrant/util/credential_scrubber"
require Vagrant.source_root.join("plugins/commands/cloud/client/client")
module VagrantPlugins
@ -35,34 +33,47 @@ module VagrantPlugins
def call(env)
client = Client.new(env[:env])
token = client.token
target_url = URI.parse(env[:downloader].source)
Vagrant::Util::CredentialScrubber.sensitive(token)
if target_url.host != TARGET_HOST && REPLACEMENT_HOSTS.include?(target_url.host)
env[:box_urls].map! do |url|
begin
target_url.host = TARGET_HOST
target_url = target_url.to_s
u = URI.parse(url)
if u.host != TARGET_HOST && REPLACEMENT_HOSTS.include?(u.host)
u.host = TARGET_HOST
u.to_s
else
url
end
rescue URI::Error
# if there is an error, use current target_url
url
end
end
server_uri = URI.parse(Vagrant.server_url.to_s)
if token && !server_uri.host.to_s.empty?
if target_url.host == server_uri.host
if server_uri.host != TARGET_HOST && !self.class.custom_host_notified?
env[:ui].warn(I18n.t("cloud_command.middleware.authentication.different_target",
custom_host: server_uri.host, known_host: TARGET_HOST) + "\n")
sleep CUSTOM_HOST_NOTIFY_WAIT
self.class.custom_host_notified!
env[:box_urls].map! do |url|
u = URI.parse(url)
if u.host == server_uri.host
if server_uri.host != TARGET_HOST && !self.class.custom_host_notified?
env[:ui].warn(I18n.t("cloud_command.middleware.authentication.different_target",
custom_host: server_uri.host, known_host: TARGET_HOST) + "\n")
sleep CUSTOM_HOST_NOTIFY_WAIT
self.class.custom_host_notified!
end
q = CGI.parse(u.query || "")
current = q["access_token"]
if current && current.empty?
q["access_token"] = token
end
u.query = URI.encode_www_form(q)
end
if env[:downloader].headers && !env[:downloader].headers.any? { |h| h.include?("Authorization") }
env[:downloader].headers << "Authorization: Bearer #{token}"
end
u.to_s
end
env[:downloader]
end
@app.call(env)

View File

@ -0,0 +1,50 @@
require "cgi"
require "uri"
require "vagrant/util/credential_scrubber"
require_relative "./add_authentication"
require Vagrant.source_root.join("plugins/commands/cloud/client/client")
module VagrantPlugins
module CloudCommand
class AddDownloaderAuthentication < AddAuthentication
def call(env)
client = Client.new(env[:env])
token = client.token
target_url = URI.parse(env[:downloader].source)
Vagrant::Util::CredentialScrubber.sensitive(token)
if target_url.host != TARGET_HOST && REPLACEMENT_HOSTS.include?(target_url.host)
begin
target_url.host = TARGET_HOST
target_url = target_url.to_s
rescue URI::Error
# if there is an error, use current target_url
end
end
server_uri = URI.parse(Vagrant.server_url.to_s)
if token && !server_uri.host.to_s.empty?
if target_url.host == server_uri.host
if server_uri.host != TARGET_HOST && !self.class.custom_host_notified?
env[:ui].warn(I18n.t("cloud_command.middleware.authentication.different_target",
custom_host: server_uri.host, known_host: TARGET_HOST) + "\n")
sleep CUSTOM_HOST_NOTIFY_WAIT
self.class.custom_host_notified!
end
if env[:downloader].headers && !env[:downloader].headers.any? { |h| h.include?("Authorization") }
env[:downloader].headers << "Authorization: Bearer #{token}"
end
end
env[:downloader]
end
@app.call(env)
end.freeze
end
end
end

View File

@ -22,6 +22,11 @@ module VagrantPlugins
hook.prepend(AddAuthentication)
end
action_hook(:cloud_authenticated_boxes, :authenticate_box_downloader) do |hook|
require_relative "auth/middleware/add_downloader_authentication"
hook.prepend(AddDownloaderAuthentication)
end
protected
def self.init!