diff options
author | The Bundler Bot <bot@bundler.io> | 2017-08-12 14:18:37 +0000 |
---|---|---|
committer | The Bundler Bot <bot@bundler.io> | 2017-08-12 14:18:37 +0000 |
commit | 76b251b03dc7ffea7e45bd3c6ea2c75ebf5c523d (patch) | |
tree | a06b933bec92f4eba2ada03e2e829602d67686b9 | |
parent | 66facbb67665f30e97db2b2c4c9c127399e9a935 (diff) | |
parent | c9c4f23e46673ac953b34774934447c779b25e0b (diff) | |
download | bundler-76b251b03dc7ffea7e45bd3c6ea2c75ebf5c523d.tar.gz |
Auto merge of #5658 - adrian-gomez:passowrds_on_remotes, r=segiddins
Allow to add username and password to a remote during a deployment
This is my first PR to bundler, so if something is out of place let me know and I'll do my best to correct it.
The issue I'm trying to fix is allowing to change a remote on a frozen bundler for the same remote adding username and password.
In our dev boxes we have configured bundle to store the username and password for a given domain, but on our qa boxes we don't want to do that for various reasons.
As far as I can tell this should not have any negative side effects as we are keeping the same remote but only adding some auth to it
-rw-r--r-- | .rubocop.yml | 3 | ||||
-rw-r--r-- | lib/bundler/definition.rb | 12 | ||||
-rw-r--r-- | lib/bundler/settings.rb | 1 | ||||
-rw-r--r-- | lib/bundler/source.rb | 4 | ||||
-rw-r--r-- | lib/bundler/source/path.rb | 4 | ||||
-rw-r--r-- | lib/bundler/source/rubygems.rb | 16 | ||||
-rw-r--r-- | lib/bundler/source_list.rb | 32 | ||||
-rw-r--r-- | man/bundle-config.ronn | 3 | ||||
-rw-r--r-- | spec/install/deploy_spec.rb | 76 |
9 files changed, 140 insertions, 11 deletions
diff --git a/.rubocop.yml b/.rubocop.yml index d1241ec52f..f12289800a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -100,6 +100,9 @@ Style/TrailingCommaInArguments: Performance/FlatMap: Enabled: false +Security/YAMLLoad: + Enabled: false + # Metrics # We've chosen to use Rubocop only for style, and not for complexity or quality checks. diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 19f1c1c929..fea66f4245 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -410,8 +410,8 @@ module Bundler # Check if it is possible that the source is only changed thing if (new_deps.empty? && deleted_deps.empty?) && (!new_sources.empty? && !deleted_sources.empty?) - new_sources.reject! {|source| source.is_a_path? && source.path.exist? } - deleted_sources.reject! {|source| source.is_a_path? && source.path.exist? } + new_sources.reject! {|source| (source.path? && source.path.exist?) || equivalent_rubygems_remotes?(source) } + deleted_sources.reject! {|source| (source.path? && source.path.exist?) || equivalent_rubygems_remotes?(source) } end if @locked_sources != gemfile_sources @@ -639,7 +639,7 @@ module Bundler if !locked_gem_sources.empty? && !actual_remotes.empty? locked_gem_sources.each do |locked_gem| # Merge the remotes from the Gemfile into the Gemfile.lock - changes |= locked_gem.replace_remotes(actual_remotes) + changes |= locked_gem.replace_remotes(actual_remotes, Bundler.settings[:allow_deployment_source_credential_changes]) end end @@ -967,5 +967,11 @@ module Bundler requirements end.values end + + def equivalent_rubygems_remotes?(source) + return false unless source.is_a?(Source::Rubygems) + + Bundler.settings[:allow_deployment_source_credential_changes] && source.equivalent_remotes?(sources.rubygems_remotes) + end end end diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index a23264ecf4..d19cc18dd6 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -10,6 +10,7 @@ module Bundler BOOL_KEYS = %w[ allow_bundler_dependency_conflicts + allow_deployment_source_credential_changes allow_offline_install auto_install cache_all diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb index 48f73d960e..38e001535c 100644 --- a/lib/bundler/source.rb +++ b/lib/bundler/source.rb @@ -46,6 +46,10 @@ module Bundler "#<#{self.class}:0x#{object_id} #{self}>" end + def path? + instance_of?(Bundler::Source::Path) + end + private def version_color(spec_version, locked_spec_version) diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb index 310a30f1ec..806ba81935 100644 --- a/lib/bundler/source/path.rb +++ b/lib/bundler/source/path.rb @@ -116,10 +116,6 @@ module Bundler Bundler.root end - def is_a_path? - instance_of?(Path) - end - def expanded_original_path @expanded_original_path ||= expand(original_path) end diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb index 511216a5c5..70dc5ac038 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -219,13 +219,21 @@ module Bundler @remotes.unshift(uri) unless @remotes.include?(uri) end - def replace_remotes(other_remotes) + def equivalent_remotes?(other_remotes) + other_remotes.map(&method(:remove_auth)) == @remotes.map(&method(:remove_auth)) + end + + def replace_remotes(other_remotes, allow_equivalent = false) return false if other_remotes == @remotes + equivalent = allow_equivalent && equivalent_remotes?(other_remotes) + @remotes = [] other_remotes.reverse_each do |r| add_remote r.to_s end + + !equivalent end def unmet_deps @@ -297,7 +305,7 @@ module Bundler end def suppress_configured_credentials(remote) - remote_nouser = remote.dup.tap {|uri| uri.user = uri.password = nil }.to_s + remote_nouser = remove_auth(remote) if remote.userinfo && remote.userinfo == Bundler.settings[remote_nouser] remote_nouser else @@ -305,6 +313,10 @@ module Bundler end end + def remove_auth(remote) + remote.dup.tap {|uri| uri.user = uri.password = nil }.to_s + end + def installed_specs @installed_specs ||= Index.build do |idx| Bundler.rubygems.all_specs.reverse_each do |spec| diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb index dd5a07afd7..ac2adacb3d 100644 --- a/lib/bundler/source_list.rb +++ b/lib/bundler/source_list.rb @@ -73,7 +73,7 @@ module Bundler end def get(source) - source_list_for(source).find {|s| source == s } + source_list_for(source).find {|s| equal_source?(source, s) || equivalent_source?(source, s) } end def lock_sources @@ -101,7 +101,7 @@ module Bundler replacement_sources.detect {|s| s.is_a?(Source::Rubygems) } @rubygems_aggregate = replacement_rubygems if replacement_rubygems - return true if lock_sources.to_set != replacement_sources.to_set + return true if !equal_sources?(lock_sources, replacement_sources) && !equivalent_sources?(lock_sources, replacement_sources) return true if replacement_rubygems && rubygems_remotes.to_set != replacement_rubygems.remotes.to_set false @@ -154,5 +154,33 @@ module Bundler "protocol to keep your data secure." end end + + def equal_sources?(lock_sources, replacement_sources) + lock_sources.to_set == replacement_sources.to_set + end + + def equal_source?(source, other_source) + source == other_source + end + + def equivalent_source?(source, other_source) + return false unless Bundler.settings[:allow_deployment_source_credential_changes] && source.is_a?(Source::Rubygems) + + equivalent_rubygems_sources?([source], [other_source]) + end + + def equivalent_sources?(lock_sources, replacement_sources) + return false unless Bundler.settings[:allow_deployment_source_credential_changes] + + lock_rubygems_sources, lock_other_sources = lock_sources.partition {|s| s.is_a?(Source::Rubygems) } + replacement_rubygems_sources, replacement_other_sources = replacement_sources.partition {|s| s.is_a?(Source::Rubygems) } + + equivalent_rubygems_sources?(lock_rubygems_sources, replacement_rubygems_sources) && equal_sources?(lock_other_sources, replacement_other_sources) + end + + def equivalent_rubygems_sources?(lock_sources, replacement_sources) + actual_remotes = replacement_sources.map(&:remotes).flatten.uniq + lock_sources.all? {|s| s.equivalent_remotes?(actual_remotes) } + end end end diff --git a/man/bundle-config.ronn b/man/bundle-config.ronn index 8e5e776a98..32cc640f60 100644 --- a/man/bundle-config.ronn +++ b/man/bundle-config.ronn @@ -121,6 +121,9 @@ learn more about their operation in [bundle install(1)][bundle-install]. * `allow_bundler_dependency_conflicts` (`BUNDLE_ALLOW_BUNDLER_DEPENDENCY_CONFLICTS`): Allow resolving to specifications that have dependencies on `bundler` that are incompatible with the running Bundler version. +* `allow_deployment_source_credential_changes` (`BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES`): + When in deployment mode, allow changing the credentials to a gem's source. + Ex: `https://some.host.com/gems/path/` -> `https://user_name:password@some.host.com/gems/path` * `allow_offline_install` (`BUNDLE_ALLOW_OFFLINE_INSTALL`): Allow Bundler to use cached data when installing without network access. * `auto_install` (`BUNDLE_AUTO_INSTALL`): diff --git a/spec/install/deploy_spec.rb b/spec/install/deploy_spec.rb index 11cf38a577..de812b5e65 100644 --- a/spec/install/deploy_spec.rb +++ b/spec/install/deploy_spec.rb @@ -295,6 +295,82 @@ RSpec.describe "install with --deployment or --frozen" do expect(out).not_to include("You have deleted from the Gemfile") end + context "when replacing a host with the same host with credentials" do + let(:success_message) do + if Bundler::VERSION.split(".", 2).first == "1" + "Could not reach host localgemserver.test" + else + "Bundle complete!" + end + end + + before do + install_gemfile <<-G + source "http://user_name:password@localgemserver.test/" + gem "rack" + G + + lockfile <<-G + GEM + remote: http://localgemserver.test/ + specs: + rack (1.0.0) + + PLATFORMS + #{local} + + DEPENDENCIES + rack + G + end + + it "prevents the replace by default" do + bundle :install, forgotten_command_line_options(:deployment => true) + + expect(out).to match(/The list of sources changed/) + end + + context "when allow_deployment_source_credential_changes is true" do + before { bundle! "config allow_deployment_source_credential_changes true" } + + it "allows the replace" do + bundle :install, forgotten_command_line_options(:deployment => true) + + expect(out).to match(/#{success_message}/) + end + end + + context "when allow_deployment_source_credential_changes is false" do + before { bundle! "config allow_deployment_source_credential_changes false" } + + it "prevents the replace" do + bundle :install, forgotten_command_line_options(:deployment => true) + + expect(out).to match(/The list of sources changed/) + end + end + + context "when BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES env var is true" do + before { ENV["BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES"] = "true" } + + it "allows the replace" do + bundle :install, forgotten_command_line_options(:deployment => true) + + expect(out).to match(/#{success_message}/) + end + end + + context "when BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES env var is false" do + before { ENV["BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES"] = "false" } + + it "prevents the replace" do + bundle :install, forgotten_command_line_options(:deployment => true) + + expect(out).to match(/The list of sources changed/) + end + end + end + it "remembers that the bundle is frozen at runtime" do bundle! :lock |