PageRenderTime 28ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/rubygems/config_file.rb

https://github.com/mrkn/ruby
Ruby | 508 lines | 267 code | 120 blank | 121 comment | 39 complexity | cf1a5d6254de922e9749ccc6ee702f57 MD5 | raw file
  1. # frozen_string_literal: true
  2. #--
  3. # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
  4. # All rights reserved.
  5. # See LICENSE.txt for permissions.
  6. #++
  7. require 'rubygems/user_interaction'
  8. require 'rbconfig'
  9. ##
  10. # Gem::ConfigFile RubyGems options and gem command options from gemrc.
  11. #
  12. # gemrc is a YAML file that uses strings to match gem command arguments and
  13. # symbols to match RubyGems options.
  14. #
  15. # Gem command arguments use a String key that matches the command name and
  16. # allow you to specify default arguments:
  17. #
  18. # install: --no-rdoc --no-ri
  19. # update: --no-rdoc --no-ri
  20. #
  21. # You can use <tt>gem:</tt> to set default arguments for all commands.
  22. #
  23. # RubyGems options use symbol keys. Valid options are:
  24. #
  25. # +:backtrace+:: See #backtrace
  26. # +:sources+:: Sets Gem::sources
  27. # +:verbose+:: See #verbose
  28. # +:concurrent_downloads+:: See #concurrent_downloads
  29. #
  30. # gemrc files may exist in various locations and are read and merged in
  31. # the following order:
  32. #
  33. # - system wide (/etc/gemrc)
  34. # - per user (~/.gemrc)
  35. # - per environment (gemrc files listed in the GEMRC environment variable)
  36. class Gem::ConfigFile
  37. include Gem::UserInteraction
  38. DEFAULT_BACKTRACE = false
  39. DEFAULT_BULK_THRESHOLD = 1000
  40. DEFAULT_VERBOSITY = true
  41. DEFAULT_UPDATE_SOURCES = true
  42. DEFAULT_CONCURRENT_DOWNLOADS = 8
  43. DEFAULT_CERT_EXPIRATION_LENGTH_DAYS = 365
  44. DEFAULT_IPV4_FALLBACK_ENABLED = false
  45. ##
  46. # For Ruby packagers to set configuration defaults. Set in
  47. # rubygems/defaults/operating_system.rb
  48. OPERATING_SYSTEM_DEFAULTS = Gem.operating_system_defaults
  49. ##
  50. # For Ruby implementers to set configuration defaults. Set in
  51. # rubygems/defaults/#{RUBY_ENGINE}.rb
  52. PLATFORM_DEFAULTS = Gem.platform_defaults
  53. # :stopdoc:
  54. SYSTEM_CONFIG_PATH =
  55. begin
  56. require "etc"
  57. Etc.sysconfdir
  58. rescue LoadError, NoMethodError
  59. RbConfig::CONFIG["sysconfdir"] || "/etc"
  60. end
  61. # :startdoc:
  62. SYSTEM_WIDE_CONFIG_FILE = File.join SYSTEM_CONFIG_PATH, 'gemrc'
  63. ##
  64. # List of arguments supplied to the config file object.
  65. attr_reader :args
  66. ##
  67. # Where to look for gems (deprecated)
  68. attr_accessor :path
  69. ##
  70. # Where to install gems (deprecated)
  71. attr_accessor :home
  72. ##
  73. # True if we print backtraces on errors.
  74. attr_writer :backtrace
  75. ##
  76. # Bulk threshold value. If the number of missing gems are above this
  77. # threshold value, then a bulk download technique is used. (deprecated)
  78. attr_accessor :bulk_threshold
  79. ##
  80. # Verbose level of output:
  81. # * false -- No output
  82. # * true -- Normal output
  83. # * :loud -- Extra output
  84. attr_accessor :verbose
  85. ##
  86. # Number of gem downloads that should be performed concurrently.
  87. attr_accessor :concurrent_downloads
  88. ##
  89. # True if we want to update the SourceInfoCache every time, false otherwise
  90. attr_accessor :update_sources
  91. ##
  92. # True if we want to force specification of gem server when pushing a gem
  93. attr_accessor :disable_default_gem_server
  94. # openssl verify mode value, used for remote https connection
  95. attr_reader :ssl_verify_mode
  96. ##
  97. # Path name of directory or file of openssl CA certificate, used for remote
  98. # https connection
  99. attr_accessor :ssl_ca_cert
  100. ##
  101. # sources to look for gems
  102. attr_accessor :sources
  103. ##
  104. # Expiration length to sign a certificate
  105. attr_accessor :cert_expiration_length_days
  106. ##
  107. # == Experimental ==
  108. # Fallback to IPv4 when IPv6 is not reachable or slow (default: false)
  109. attr_accessor :ipv4_fallback_enabled
  110. ##
  111. # Path name of directory or file of openssl client certificate, used for remote https connection with client authentication
  112. attr_reader :ssl_client_cert
  113. ##
  114. # Create the config file object. +args+ is the list of arguments
  115. # from the command line.
  116. #
  117. # The following command line options are handled early here rather
  118. # than later at the time most command options are processed.
  119. #
  120. # <tt>--config-file</tt>, <tt>--config-file==NAME</tt>::
  121. # Obviously these need to be handled by the ConfigFile object to ensure we
  122. # get the right config file.
  123. #
  124. # <tt>--backtrace</tt>::
  125. # Backtrace needs to be turned on early so that errors before normal
  126. # option parsing can be properly handled.
  127. #
  128. # <tt>--debug</tt>::
  129. # Enable Ruby level debug messages. Handled early for the same reason as
  130. # --backtrace.
  131. #--
  132. # TODO: parse options upstream, pass in options directly
  133. def initialize(args)
  134. set_config_file_name(args)
  135. @backtrace = DEFAULT_BACKTRACE
  136. @bulk_threshold = DEFAULT_BULK_THRESHOLD
  137. @verbose = DEFAULT_VERBOSITY
  138. @update_sources = DEFAULT_UPDATE_SOURCES
  139. @concurrent_downloads = DEFAULT_CONCURRENT_DOWNLOADS
  140. @cert_expiration_length_days = DEFAULT_CERT_EXPIRATION_LENGTH_DAYS
  141. @ipv4_fallback_enabled = ENV['IPV4_FALLBACK_ENABLED'] == 'true' || DEFAULT_IPV4_FALLBACK_ENABLED
  142. operating_system_config = Marshal.load Marshal.dump(OPERATING_SYSTEM_DEFAULTS)
  143. platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS)
  144. system_config = load_file SYSTEM_WIDE_CONFIG_FILE
  145. user_config = load_file config_file_name.dup.tap(&Gem::UNTAINT)
  146. environment_config = (ENV['GEMRC'] || '')
  147. .split(File::PATH_SEPARATOR).inject({}) do |result, file|
  148. result.merge load_file file
  149. end
  150. @hash = operating_system_config.merge platform_config
  151. unless args.index '--norc'
  152. @hash = @hash.merge system_config
  153. @hash = @hash.merge user_config
  154. @hash = @hash.merge environment_config
  155. end
  156. # HACK these override command-line args, which is bad
  157. @backtrace = @hash[:backtrace] if @hash.key? :backtrace
  158. @bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold
  159. @home = @hash[:gemhome] if @hash.key? :gemhome
  160. @path = @hash[:gempath] if @hash.key? :gempath
  161. @update_sources = @hash[:update_sources] if @hash.key? :update_sources
  162. @verbose = @hash[:verbose] if @hash.key? :verbose
  163. @disable_default_gem_server = @hash[:disable_default_gem_server] if @hash.key? :disable_default_gem_server
  164. @sources = @hash[:sources] if @hash.key? :sources
  165. @cert_expiration_length_days = @hash[:cert_expiration_length_days] if @hash.key? :cert_expiration_length_days
  166. @ipv4_fallback_enabled = @hash[:ipv4_fallback_enabled] if @hash.key? :ipv4_fallback_enabled
  167. @ssl_verify_mode = @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
  168. @ssl_ca_cert = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
  169. @ssl_client_cert = @hash[:ssl_client_cert] if @hash.key? :ssl_client_cert
  170. @api_keys = nil
  171. @rubygems_api_key = nil
  172. handle_arguments args
  173. end
  174. ##
  175. # Hash of RubyGems.org and alternate API keys
  176. def api_keys
  177. load_api_keys unless @api_keys
  178. @api_keys
  179. end
  180. ##
  181. # Checks the permissions of the credentials file. If they are not 0600 an
  182. # error message is displayed and RubyGems aborts.
  183. def check_credentials_permissions
  184. return if Gem.win_platform? # windows doesn't write 0600 as 0600
  185. return unless File.exist? credentials_path
  186. existing_permissions = File.stat(credentials_path).mode & 0777
  187. return if existing_permissions == 0600
  188. alert_error <<-ERROR
  189. Your gem push credentials file located at:
  190. \t#{credentials_path}
  191. has file permissions of 0#{existing_permissions.to_s 8} but 0600 is required.
  192. To fix this error run:
  193. \tchmod 0600 #{credentials_path}
  194. You should reset your credentials at:
  195. \thttps://rubygems.org/profile/edit
  196. if you believe they were disclosed to a third party.
  197. ERROR
  198. terminate_interaction 1
  199. end
  200. ##
  201. # Location of RubyGems.org credentials
  202. def credentials_path
  203. credentials = File.join Gem.user_home, '.gem', 'credentials'
  204. if File.exist? credentials
  205. credentials
  206. else
  207. File.join Gem.data_home, "gem", "credentials"
  208. end
  209. end
  210. def load_api_keys
  211. check_credentials_permissions
  212. @api_keys = if File.exist? credentials_path
  213. load_file(credentials_path)
  214. else
  215. @hash
  216. end
  217. if @api_keys.key? :rubygems_api_key
  218. @rubygems_api_key = @api_keys[:rubygems_api_key]
  219. @api_keys[:rubygems] = @api_keys.delete :rubygems_api_key unless
  220. @api_keys.key? :rubygems
  221. end
  222. end
  223. ##
  224. # Returns the RubyGems.org API key
  225. def rubygems_api_key
  226. load_api_keys unless @rubygems_api_key
  227. @rubygems_api_key
  228. end
  229. ##
  230. # Sets the RubyGems.org API key to +api_key+
  231. def rubygems_api_key=(api_key)
  232. set_api_key :rubygems_api_key, api_key
  233. @rubygems_api_key = api_key
  234. end
  235. ##
  236. # Set a specific host's API key to +api_key+
  237. def set_api_key(host, api_key)
  238. check_credentials_permissions
  239. config = load_file(credentials_path).merge(host => api_key)
  240. dirname = File.dirname credentials_path
  241. Dir.mkdir(dirname) unless File.exist? dirname
  242. Gem.load_yaml
  243. permissions = 0600 & (~File.umask)
  244. File.open(credentials_path, 'w', permissions) do |f|
  245. f.write config.to_yaml
  246. end
  247. load_api_keys # reload
  248. end
  249. ##
  250. # Remove the +~/.gem/credentials+ file to clear all the current sessions.
  251. def unset_api_key!
  252. return false unless File.exist?(credentials_path)
  253. File.delete(credentials_path)
  254. end
  255. def load_file(filename)
  256. Gem.load_yaml
  257. yaml_errors = [ArgumentError]
  258. yaml_errors << Psych::SyntaxError if defined?(Psych::SyntaxError)
  259. return {} unless filename && !filename.empty? && File.exist?(filename)
  260. begin
  261. content = Gem::SafeYAML.load(File.read(filename))
  262. unless content.kind_of? Hash
  263. warn "Failed to load #{filename} because it doesn't contain valid YAML hash"
  264. return {}
  265. end
  266. return content
  267. rescue *yaml_errors => e
  268. warn "Failed to load #{filename}, #{e}"
  269. rescue Errno::EACCES
  270. warn "Failed to load #{filename} due to permissions problem."
  271. end
  272. {}
  273. end
  274. # True if the backtrace option has been specified, or debug is on.
  275. def backtrace
  276. @backtrace or $DEBUG
  277. end
  278. # The name of the configuration file.
  279. def config_file_name
  280. @config_file_name || Gem.config_file
  281. end
  282. # Delegates to @hash
  283. def each(&block)
  284. hash = @hash.dup
  285. hash.delete :update_sources
  286. hash.delete :verbose
  287. hash.delete :backtrace
  288. hash.delete :bulk_threshold
  289. yield :update_sources, @update_sources
  290. yield :verbose, @verbose
  291. yield :backtrace, @backtrace
  292. yield :bulk_threshold, @bulk_threshold
  293. yield 'config_file_name', @config_file_name if @config_file_name
  294. hash.each(&block)
  295. end
  296. # Handle the command arguments.
  297. def handle_arguments(arg_list)
  298. @args = []
  299. arg_list.each do |arg|
  300. case arg
  301. when /^--(backtrace|traceback)$/ then
  302. @backtrace = true
  303. when /^--debug$/ then
  304. $DEBUG = true
  305. warn 'NOTE: Debugging mode prints all exceptions even when rescued'
  306. else
  307. @args << arg
  308. end
  309. end
  310. end
  311. # Really verbose mode gives you extra output.
  312. def really_verbose
  313. case verbose
  314. when true, false, nil then
  315. false
  316. else
  317. true
  318. end
  319. end
  320. # to_yaml only overwrites things you can't override on the command line.
  321. def to_yaml # :nodoc:
  322. yaml_hash = {}
  323. yaml_hash[:backtrace] = @hash.fetch(:backtrace, DEFAULT_BACKTRACE)
  324. yaml_hash[:bulk_threshold] = @hash.fetch(:bulk_threshold, DEFAULT_BULK_THRESHOLD)
  325. yaml_hash[:sources] = Gem.sources.to_a
  326. yaml_hash[:update_sources] = @hash.fetch(:update_sources, DEFAULT_UPDATE_SOURCES)
  327. yaml_hash[:verbose] = @hash.fetch(:verbose, DEFAULT_VERBOSITY)
  328. yaml_hash[:concurrent_downloads] =
  329. @hash.fetch(:concurrent_downloads, DEFAULT_CONCURRENT_DOWNLOADS)
  330. yaml_hash[:ssl_verify_mode] =
  331. @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
  332. yaml_hash[:ssl_ca_cert] =
  333. @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
  334. yaml_hash[:ssl_client_cert] =
  335. @hash[:ssl_client_cert] if @hash.key? :ssl_client_cert
  336. keys = yaml_hash.keys.map {|key| key.to_s }
  337. keys << 'debug'
  338. re = Regexp.union(*keys)
  339. @hash.each do |key, value|
  340. key = key.to_s
  341. next if key =~ re
  342. yaml_hash[key.to_s] = value
  343. end
  344. yaml_hash.to_yaml
  345. end
  346. # Writes out this config file, replacing its source.
  347. def write
  348. unless File.exist?(File.dirname(config_file_name))
  349. FileUtils.mkdir_p File.dirname(config_file_name)
  350. end
  351. File.open config_file_name, 'w' do |io|
  352. io.write to_yaml
  353. end
  354. end
  355. # Return the configuration information for +key+.
  356. def [](key)
  357. @hash[key.to_s]
  358. end
  359. # Set configuration option +key+ to +value+.
  360. def []=(key, value)
  361. @hash[key.to_s] = value
  362. end
  363. def ==(other) # :nodoc:
  364. self.class === other and
  365. @backtrace == other.backtrace and
  366. @bulk_threshold == other.bulk_threshold and
  367. @verbose == other.verbose and
  368. @update_sources == other.update_sources and
  369. @hash == other.hash
  370. end
  371. attr_reader :hash
  372. protected :hash
  373. private
  374. def set_config_file_name(args)
  375. @config_file_name = ENV["GEMRC"]
  376. need_config_file_name = false
  377. args.each do |arg|
  378. if need_config_file_name
  379. @config_file_name = arg
  380. need_config_file_name = false
  381. elsif arg =~ /^--config-file=(.*)/
  382. @config_file_name = $1
  383. elsif arg =~ /^--config-file$/
  384. need_config_file_name = true
  385. end
  386. end
  387. end
  388. end