Skip to content

Commit cf830ef

Browse files
committed
🗑️ Add deprecation warnings to .new and #starttls [🚧 WIP]
* `ssl` was renamed to `tls` in most places, with backwards compatible aliases. Using `ssl` does not print any deprecation warnings. Using both `tls` and `ssl` keywords raises an ArgumentError. * Preparing for a (backwards-incompatible) secure-by-default configuration, `Net::IMAP.default_tls` will determine the value for `tls` when no explicit port or tls setting is provided. Using port 143 will be insecure by default. Using port 993 will be secure by default. Providing no explicit port will use `Net::IMAP.default_tls` with the appropriate port. And providing any other unknown port will use `default_tls` with a warning. 🚧 TODO: should we use a different config var for default tls params when port is 993 and `tls` is unspecified? 🚧 TODO: should we use a different config var for choosing `tls` when `port` is non-standard vs choosing `port` and `tls` when neither are specified? 🚧 TODO: should we use a different var for `default_tls` be used to config params when port is 993 but tls is implicit? Another var?
1 parent 2066e7f commit cf830ef

File tree

2 files changed

+79
-10
lines changed

2 files changed

+79
-10
lines changed

lib/net/imap.rb

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,19 @@ class << self
703703
alias default_imap_port default_port
704704
alias default_imaps_port default_tls_port
705705
alias default_ssl_port default_tls_port
706+
707+
# The default value for the +tls+ option of ::new, when +port+ is
708+
# unspecified or non-standard.
709+
#
710+
# *Note*: A future release of Net::IMAP will set the default to +true+, as
711+
# per RFC7525[https://tools.ietf.org/html/rfc7525],
712+
# RFC7817[https://tools.ietf.org/html/rfc7817], and
713+
# RFC8314[https://tools.ietf.org/html/rfc8314].
714+
#
715+
# Set to +true+ for the secure default without warnings. Set to
716+
# +false+ to globally silence warnings and use insecure defaults.
717+
attr_accessor :default_tls
718+
alias default_ssl default_tls
706719
end
707720

708721
# Returns the initial greeting the server, an UntaggedResponse.
@@ -745,16 +758,28 @@ class << self
745758
# Accepts the following options:
746759
#
747760
# [port]
748-
# Port number. Defaults to 993 when +ssl+ is truthy, and 143 otherwise.
761+
# Port number. Defaults to 143 when +tls+ is false, 993 when +tls+ is
762+
# truthy. Based on ::default_tls when both +port+ and +tls+ are nil.
749763
#
750-
# [ssl]
764+
# [tls]
751765
# If +true+, the connection will use TLS with the default params set by
752766
# {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params].
753-
# If +ssl+ is a hash, it's passed to
767+
# If +tls+ is a hash, it's passed to
754768
# {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params];
755769
# the keys are names of attribute assignment methods on
756770
# SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html].
757771
#
772+
# When <tt>port: 993</tt>, +tls+ defaults to +true+.
773+
# When <tt>port: 143</tt>, +tls+ defaults to +false+.
774+
# When port is unspecified or non-standard, +tls+ defaults to
775+
# ::default_tls. When ::default_tls is also +nil+, a warning is printed
776+
# and the connection does _not_ use TLS.
777+
#
778+
# When +nil+ or unassigned a default value is assigned: the default is
779+
# +true+ if <tt>port: 993</tt>, +false+ if <tt>port: 143</tt>, and
780+
# ::default_tls when +port+ is unspecified or non-standard. When
781+
# ::default_tls is +nil+, a back
782+
#
758783
# [open_timeout]
759784
# Seconds to wait until a connection is opened
760785
# [idle_response_timeout]
@@ -810,15 +835,15 @@ class << self
810835
# [Net::IMAP::ByeResponseError]
811836
# Connected to the host successfully, but it immediately said goodbye.
812837
#
813-
def initialize(host, port: nil, ssl: nil,
838+
def initialize(host, port: nil, tls: nil,
814839
open_timeout: 30, idle_response_timeout: 5)
815840
super()
816841
# Config options
817842
@host = host
818-
@port = port || (ssl ? SSL_PORT : PORT)
843+
tls, @port = default_tls_and_port(tls, port)
819844
@open_timeout = Integer(open_timeout)
820845
@idle_response_timeout = Integer(idle_response_timeout)
821-
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(ssl)
846+
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(tls)
822847

823848
# Basic Client State
824849
@utf8_strings = false
@@ -933,7 +958,7 @@ def capabilities
933958
# servers will drop all <tt>AUTH=</tt> mechanisms from #capabilities after
934959
# the connection has authenticated.
935960
#
936-
# imap = Net::IMAP.new(hostname, ssl: false)
961+
# imap = Net::IMAP.new(hostname, tls: false)
937962
# imap.capabilities # => ["IMAP4REV1", "LOGINDISABLED"]
938963
# imap.auth_mechanisms # => []
939964
#
@@ -2352,6 +2377,27 @@ def remove_response_handler(handler)
23522377

23532378
@@debug = false
23542379

2380+
def default_tls_and_port(tls, port)
2381+
if tls.nil? && port
2382+
tls = true if port == SSL_PORT || /\Aimaps\z/i === port
2383+
tls = false if port == PORT
2384+
elsif port.nil? && !tls.nil?
2385+
port = tls ? SSL_PORT : PORT
2386+
end
2387+
if tls.nil? && port.nil?
2388+
tls = self.class.default_tls.dup.freeze
2389+
port = tls ? SSL_PORT : PORT
2390+
if tls.nil?
2391+
warn "A future version of Net::IMAP.default_tls " \
2392+
"will default to 'true', for secure connections by default. " \
2393+
"Use 'Net::IMAP.new(host, tls: false)' or set " \
2394+
"Net::IMAP.default_tls = false' to silence this warning."
2395+
end
2396+
end
2397+
tls &&= tls.respond_to?(:to_hash) ? tls.to_hash : {}
2398+
[tls, port]
2399+
end
2400+
23552401
def start_imap_connection
23562402
@greeting = get_server_greeting
23572403
@capabilities = capabilities_from_resp_code @greeting

lib/net/imap/deprecated_client_options.rb

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ class IMAP < Protocol
55

66
# This module handles deprecated arguments to various Net::IMAP methods.
77
module DeprecatedClientOptions
8+
UNDEF = Module.new.freeze
9+
private_constant :UNDEF
810

911
# :call-seq:
1012
# Net::IMAP.new(host, **options) # standard keyword options
13+
# Net::IMAP.new(host, ssl: nil, **options) # ssl => tls
1114
# Net::IMAP.new(host, options) # obsolete hash options
1215
# Net::IMAP.new(host, port) # obsolete port argument
1316
# Net::IMAP.new(host, port, usessl, certs = nil, verify = true) # deprecated SSL arguments
@@ -19,6 +22,13 @@ module DeprecatedClientOptions
1922
# Using obsolete arguments does not a warning. Obsolete arguments will be
2023
# deprecated by a future release.
2124
#
25+
# If +ssl+ is given, it is silently converted to the +tls+ keyword
26+
# argument. Combining both +ssl+ and +tls+ raises an ArgumentError. Both
27+
# of the following behave identically:
28+
#
29+
# Net::IMAP.new("imap.example.com", port: 993, ssl: {ca_path: "path/to/certs"})
30+
# Net::IMAP.new("imap.example.com", port: 993, tls: {ca_path: "path/to/certs"})
31+
#
2232
# If a second positional argument is given and it is a hash (or is
2333
# convertable via +#to_hash+), it is converted to keyword arguments.
2434
#
@@ -71,6 +81,7 @@ module DeprecatedClientOptions
7181
#
7282
def initialize(host, port_or_options = nil, *deprecated, **options)
7383
if port_or_options.nil? && deprecated.empty?
84+
translate_ssl_to_tls(options)
7485
super host, **options
7586
elsif options.any?
7687
# Net::IMAP.new(host, *__invalid__, **options)
@@ -79,15 +90,17 @@ def initialize(host, port_or_options = nil, *deprecated, **options)
7990
# Net::IMAP.new(host, options, *__invalid__)
8091
raise ArgumentError, "Do not use deprecated SSL params with options hash"
8192
elsif port_or_options.respond_to?(:to_hash)
82-
super host, **Hash.try_convert(port_or_options)
93+
options = Hash.try_convert(port_or_options)
94+
translate_ssl_to_tls(options)
95+
super host, **options
8396
elsif deprecated.empty?
8497
super host, port: port_or_options
8598
elsif deprecated.shift
8699
warn "DEPRECATED: Call Net::IMAP.new with keyword options", uplevel: 1
87-
super host, port: port_or_options, ssl: create_ssl_params(*deprecated)
100+
super host, port: port_or_options, tls: create_ssl_params(*deprecated)
88101
else
89102
warn "DEPRECATED: Call Net::IMAP.new with keyword options", uplevel: 1
90-
super host, port: port_or_options, ssl: false
103+
super host, port: port_or_options, tls: false
91104
end
92105
end
93106

@@ -120,7 +133,17 @@ def starttls(*deprecated, **options)
120133

121134
private
122135

136+
def translate_ssl_to_tls(options)
137+
return unless options.key?(:ssl)
138+
if options.key?(:tls)
139+
raise ArgumentError, "conflicting :ssl and :tls keyword arguments"
140+
end
141+
options.merge!(tls: options.delete(:ssl))
142+
end
143+
123144
def create_ssl_params(certs = nil, verify = true)
145+
certs = nil if certs == UNDEF
146+
verify = true if verify == UNDEF
124147
params = {}
125148
if certs
126149
if File.file?(certs)

0 commit comments

Comments
 (0)