diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index fe57307..0c6a6df 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -7,7 +7,7 @@ jobs:
uses: ruby/actions/.github/workflows/ruby_versions.yml@master
with:
engine: cruby
- min_version: 2.6
+ min_version: 2.7
build:
needs: ruby-versions
@@ -26,4 +26,4 @@ jobs:
- name: Install dependencies
run: bundle install
- name: Run test
- run: rake test
+ run: bundle exec rake test
diff --git a/lib/net/smtp.rb b/lib/net/smtp.rb
index 13e1f1f..28b15d0 100644
--- a/lib/net/smtp.rb
+++ b/lib/net/smtp.rb
@@ -175,20 +175,48 @@ class SMTPUnsupportedCommand < ProtocolError
#
# The Net::SMTP class supports the \SMTP extension for SASL Authentication
# [RFC4954[https://www.rfc-editor.org/rfc/rfc4954.html]] and the following
- # SASL mechanisms: +PLAIN+, +LOGIN+ _(deprecated)_, and +CRAM-MD5+
- # _(deprecated)_.
+ # SASL mechanisms: +ANONYMOUS+, +EXTERNAL+, +OAUTHBEARER+, +PLAIN+,
+ # +SCRAM-SHA-1+, +SCRAM-SHA-256+, and +XOAUTH2+.
#
# To use \SMTP authentication, pass extra arguments to
# SMTP.start or SMTP#start.
#
# # PLAIN
# Net::SMTP.start('your.smtp.server', 25,
- # user: 'Your Account', secret: 'Your Password', authtype: :plain)
+ # username: 'Your Account', secret: 'Your Password', authtype: :plain)
#
- # Support for other SASL mechanisms-such as +EXTERNAL+, +OAUTHBEARER+,
- # +SCRAM-SHA-256+, and +XOAUTH2+-will be added in a future release.
+ # # SCRAM-SHA-256
+ # Net::SMTP.start("your.smtp.server", 25,
+ # user: "authentication identity", secret: password,
+ # authtype: :scram_sha_256)
+ # Net::SMTP.start("your.smtp.server", 25,
+ # auth: {type: :scram_sha_256,
+ # username: "authentication identity",
+ # password: password,
+ # authzid: "authorization identity"}) # optional
#
- # The +LOGIN+ and +CRAM-MD5+ mechanisms are still available for backwards
+ # # OAUTHBEARER
+ # Net::SMTP.start("your.smtp.server", 25,
+ # auth: {type: :oauthbearer,
+ # oauth2_token: oauth2_access_token,
+ # authzid: "authorization identity", # optional
+ # host: "your.smtp.server", # optional
+ # port: 25}) # optional
+ #
+ # # XOAUTH2
+ # Net::SMTP.start("your.smtp.server", 25,
+ # user: "username", secret: oauth2_access_token, authtype: :xoauth2)
+ # Net::SMTP.start("your.smtp.server", 25,
+ # auth: {type: :xoauth2,
+ # username: "username",
+ # oauth2_token: oauth2_token})
+ #
+ # # EXTERNAL
+ # Net::SMTP.start("your.smtp.server", 587,
+ # starttls: :always, ssl_context_params: ssl_ctx_params,
+ # authtype: "external")
+ #
+ # +DIGEST-MD5+, +LOGIN+, and +CRAM-MD5+ are still available for backwards
# compatibility, but are deprecated and should be avoided.
#
class SMTP < Protocol
@@ -459,15 +487,16 @@ def debug_output=(arg)
#
# :call-seq:
- # start(address, port = nil, helo: 'localhost', user: nil, secret: nil, authtype: nil, tls: false, starttls: :auto, tls_verify: true, tls_hostname: nil, ssl_context_params: nil) { |smtp| ... }
- # start(address, port = nil, helo = 'localhost', user = nil, secret = nil, authtype = nil) { |smtp| ... }
+ # start(address, port = nil, helo: 'localhost', auth: nil, tls: false, starttls: :auto, tls_verify: true, tls_hostname: nil, ssl_context_params: nil) { |smtp| ... }
+ # start(address, port = nil, helo: 'localhost', username: nil, secret: nil, authtype: nil, tls: false, starttls: :auto, tls_verify: true, tls_hostname: nil, ssl_context_params: nil) { |smtp| ... }
+ # start(address, port = nil, helo = 'localhost', username = nil, secret = nil, authtype = nil) { |smtp| ... }
#
# Creates a new Net::SMTP object and connects to the server.
#
# This method is equivalent to:
#
# Net::SMTP.new(address, port, tls_verify: flag, tls_hostname: hostname, ssl_context_params: nil)
- # .start(helo: helo_domain, user: account, secret: password, authtype: authtype)
+ # .start(helo: helo_domain, username: account, secret: password, authtype: authtype)
#
# See also: Net::SMTP.new, #start
#
@@ -514,13 +543,15 @@ def debug_output=(arg)
#
# +authtype+ is the SASL authentication mechanism.
#
- # +user+ is the authentication or authorization identity.
+ # +username+ or +user+ is the authentication or authorization identity.
#
# +secret+ or +password+ is your password or other authentication token.
#
# These will be sent to #authenticate as positional arguments-the exact
# semantics are dependent on the +authtype+.
#
+ # +auth+ is an optional hash of keyword arguments for #authenticate.
+ #
# See the discussion of Net::SMTP@SMTP+Authentication in the overview notes.
#
# === Errors
@@ -538,15 +569,18 @@ def debug_output=(arg)
#
def SMTP.start(address, port = nil, *args, helo: nil,
user: nil, secret: nil, password: nil, authtype: nil,
+ username: nil,
+ auth: nil,
tls: false, starttls: :auto,
tls_verify: true, tls_hostname: nil, ssl_context_params: nil,
&block)
raise ArgumentError, "wrong number of arguments (given #{args.size + 2}, expected 1..6)" if args.size > 4
helo ||= args[0] || 'localhost'
- user ||= args[1]
+ username ||= user || args[1]
secret ||= password || args[2]
authtype ||= args[3]
- new(address, port, tls: tls, starttls: starttls, tls_verify: tls_verify, tls_hostname: tls_hostname, ssl_context_params: ssl_context_params).start(helo: helo, user: user, secret: secret, authtype: authtype, &block)
+ new(address, port, tls: tls, starttls: starttls, tls_verify: tls_verify, tls_hostname: tls_hostname, ssl_context_params: ssl_context_params)
+ .start(helo: helo, username: username, secret: secret, authtype: authtype, auth: auth, &block)
end
# +true+ if the \SMTP session has been started.
@@ -556,8 +590,9 @@ def started?
#
# :call-seq:
- # start(helo: 'localhost', user: nil, secret: nil, authtype: nil) { |smtp| ... }
- # start(helo = 'localhost', user = nil, secret = nil, authtype = nil) { |smtp| ... }
+ # start(helo: 'localhost', username: nil, secret: nil, authtype: nil) { |smtp| ... }
+ # start(helo = 'localhost', username = nil, secret = nil, authtype = nil) { |smtp| ... }
+ # start(helo = 'localhost', auth: {type: nil, **auth_kwargs}) { |smtp| ... }
#
# Opens a TCP connection and starts the SMTP session.
#
@@ -571,13 +606,15 @@ def started?
#
# +authtype+ is the SASL authentication mechanism.
#
- # +user+ is the authentication or authorization identity.
+ # +username+ or +user+ is the authentication or authorization identity.
#
# +secret+ or +password+ is your password or other authentication token.
#
# These will be sent to #authenticate as positional arguments-the exact
# semantics are dependent on the +authtype+.
#
+ # +auth+ is an optional hash of keyword arguments for #authenticate.
+ #
# See the discussion of Net::SMTP@SMTP+Authentication in the overview notes.
#
# See also: Net::SMTP.start
@@ -595,7 +632,7 @@ def started?
#
# require 'net/smtp'
# smtp = Net::SMTP.new('smtp.mail.server', 25)
- # smtp.start(helo: helo_domain, user: account, secret: password, authtype: authtype) do |smtp|
+ # smtp.start(helo: helo_domain, username: account, secret: password, authtype: authtype) do |smtp|
# smtp.send_message msgstr, 'from@example.com', ['dest@example.com']
# end
#
@@ -619,12 +656,15 @@ def started?
# * Net::ReadTimeout
# * IOError
#
- def start(*args, helo: nil, user: nil, secret: nil, password: nil, authtype: nil)
+ def start(*args, helo: nil,
+ user: nil, username: nil, secret: nil, password: nil,
+ authtype: nil, auth: nil)
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..4)" if args.size > 4
helo ||= args[0] || 'localhost'
- user ||= args[1]
+ username ||= user || args[1]
secret ||= password || args[2]
authtype ||= args[3]
+ auth ||= {}
if defined?(OpenSSL::VERSION)
ssl_context_params = @ssl_context_params || {}
unless ssl_context_params.has_key?(:verify_mode)
@@ -639,13 +679,13 @@ def start(*args, helo: nil, user: nil, secret: nil, password: nil, authtype: nil
end
if block_given?
begin
- do_start helo, user, secret, authtype
+ do_start helo, username, secret, authtype, **auth
return yield(self)
ensure
- do_finish
+ quit!
end
else
- do_start helo, user, secret, authtype
+ do_start helo, username, secret, authtype, **auth
return self
end
end
@@ -654,7 +694,7 @@ def start(*args, helo: nil, user: nil, secret: nil, password: nil, authtype: nil
# Raises IOError if not started.
def finish
raise IOError, 'not yet started' unless started?
- do_finish
+ quit!
end
private
@@ -663,10 +703,10 @@ def tcp_socket(address, port)
TCPSocket.open address, port
end
- def do_start(helo_domain, user, secret, authtype)
+ def do_start(helo_domain, user, secret, authtype, **auth)
raise IOError, 'SMTP session already started' if @started
- if user || secret || authtype
- check_auth_args authtype, user, secret
+ if user || secret || authtype || auth.any?
+ check_auth_args(authtype, user, secret, **auth)
end
s = Timeout.timeout(@open_timeout, Net::OpenTimeout) do
tcp_socket(@address, @port)
@@ -684,7 +724,11 @@ def do_start(helo_domain, user, secret, authtype)
# helo response may be different after STARTTLS
do_helo helo_domain
end
- authenticate user, secret, (authtype || DEFAULT_AUTH_TYPE) if user
+ if user or secret
+ authenticate(user, secret, authtype, **auth)
+ elsif authtype or auth.any?
+ authenticate(authtype, **auth)
+ end
@started = true
ensure
unless @started
@@ -728,15 +772,46 @@ def do_helo(helo_domain)
raise
end
- def do_finish
+ public
+
+ # Calls #quit and ensures that #disconnect is called. Returns the result
+ # from #quit. Returns +nil+ when the client is already disconnected or when
+ # a prior error prevents the client from calling #quit. Unlike #finish,
+ # this an exception will not be raised when the client has not started.
+ #
+ # When exception: :warn is specified, when #quit raises a
+ # StandardError, a warning will be printed and the exception is returned,
+ # not re-raised. When exception: false is specified, a warning
+ # will not be printed. This is useful when the connection must be dropped,
+ # for example in a test suite or due to security concerns.
+ #
+ # Related: #finish, #quit, #disconnect
+ def quit!(exception: true)
quit if @socket and not @socket.closed? and not @error_occurred
+ rescue => ex
+ if exception == :warn
+ warn "%s during %p #%s: %s" % [ex.class, self, __method__, ex]
+ elsif exception
+ raise ex
+ end
+ ex
ensure
+ disconnect
+ end
+
+ # Disconnects the socket without checking if the connection has started yet,
+ # and without sending a final QUIT message to the server.
+ #
+ # Generally, either #finish or #quit! should be used instead.
+ def disconnect
@started = false
@error_occurred = false
@socket.close if @socket
@socket = nil
end
+ private
+
def requires_smtputf8(address)
if address.kind_of? Address
!address.address.ascii_only?
@@ -862,26 +937,56 @@ def open_message_stream(from_addr, *to_addrs, &block) # :yield: stream
DEFAULT_AUTH_TYPE = :plain
+ # call-seq:
+ # authenticate(type: DEFAULT_AUTH_TYPE, **, &)
+ # authenticate(type = DEFAULT_AUTH_TYPE, **, &)
+ # authenticate(username, secret, type: DEFAULT_AUTH_TYPE, **, &)
+ # authenticate(username, secret, type = DEFAULT_AUTH_TYPE, **, &)
+ #
# Authenticates with the server, using the "AUTH" command.
#
- # +authtype+ is the name of a SASL authentication mechanism.
+ # +type+ is the name of a SASL authentication mechanism.
#
# All arguments-other than +authtype+-are forwarded to the authenticator.
- # Different authenticators may interpret the +user+ and +secret+
+ # Different authenticators may interpret the +username+ and +secret+
# arguments differently.
- def authenticate(user, secret, authtype = DEFAULT_AUTH_TYPE)
- check_auth_args authtype, user, secret
+ def authenticate(*args, **kwargs, &block)
+ case args.length
+ when 1, 3 then authtype = args.pop
+ when (4..)
+ raise ArgumentError, "wrong number of arguments " \
+ "(given %d, expected 0..3)" % [args.length]
+ end
+ auth(authtype, *args, **kwargs, &block)
+ end
+
+ # call-seq:
+ # auth(type = DEFAULT_AUTH_TYPE, ...)
+ # auth(type: DEFAULT_AUTH_TYPE, **kwargs, &block)
+ #
+ # All arguments besides +mechanism+ are forwarded directly to the
+ # authenticator. Alternatively, +mechanism+ can be provided by the +type+
+ # keyword parameter. Positional parameters cannot be used with +type+.
+ #
+ # Different authenticators take different options, but common options
+ # include +authcid+ for authentication identity, +authzid+ for authorization
+ # identity, +username+ for either "authentication identity" or
+ # "authorization identity" depending on the +mechanism+, and +password+.
+ def auth(authtype = DEFAULT_AUTH_TYPE, *args, **kwargs, &block)
+ authtype, args, kwargs = check_auth_args authtype, *args, **kwargs
authenticator = Authenticator.auth_class(authtype).new(self)
- authenticator.auth(user, secret)
+ authenticator.auth(*args, **kwargs, &block)
end
private
- def check_auth_args(type, *args, **kwargs)
- type ||= DEFAULT_AUTH_TYPE
+ def check_auth_args(type_arg = nil, *args, type: nil, user: nil, **kwargs)
+ type ||= type_arg || DEFAULT_AUTH_TYPE
+ kwargs[:username] ||= user if user
klass = Authenticator.auth_class(type) or
raise ArgumentError, "wrong authentication type #{type}"
klass.check_args(*args, **kwargs)
+ [type, args, kwargs]
end
#
@@ -992,6 +1097,21 @@ def get_response(reqline)
recv_response()
end
+ # Returns a successful Response.
+ #
+ # Yields continuation data and replies to the server using the block result.
+ #
+ # Raises an exception for any non-successful, non-continuation response.
+ def send_command_with_continuations(*args)
+ server_resp = get_response args.join(" ")
+ while server_resp.continue?
+ client_resp = yield server_resp.string.strip.split(nil, 2).last
+ server_resp = get_response client_resp
+ end
+ server_resp.success? or raise server_resp.exception_class.new(server_resp)
+ server_resp
+ end
+
private
def validate_line(line)
diff --git a/lib/net/smtp/auth_cram_md5.rb b/lib/net/smtp/auth_cram_md5.rb
deleted file mode 100644
index 0490cd6..0000000
--- a/lib/net/smtp/auth_cram_md5.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-unless defined? OpenSSL
- begin
- require 'digest/md5'
- rescue LoadError
- end
-end
-
-class Net::SMTP
- class AuthCramMD5 < Net::SMTP::Authenticator
- auth_type :cram_md5
-
- def auth(user, secret)
- challenge = continue('AUTH CRAM-MD5')
- crammed = cram_md5_response(secret, challenge.unpack1('m'))
- finish(base64_encode("#{user} #{crammed}"))
- end
-
- IMASK = 0x36
- OMASK = 0x5c
-
- # CRAM-MD5: [RFC2195]
- def cram_md5_response(secret, challenge)
- tmp = digest_class::MD5.digest(cram_secret(secret, IMASK) + challenge)
- digest_class::MD5.hexdigest(cram_secret(secret, OMASK) + tmp)
- end
-
- CRAM_BUFSIZE = 64
-
- def cram_secret(secret, mask)
- secret = digest_class::MD5.digest(secret) if secret.size > CRAM_BUFSIZE
- buf = secret.ljust(CRAM_BUFSIZE, "\0")
- 0.upto(buf.size - 1) do |i|
- buf[i] = (buf[i].ord ^ mask).chr
- end
- buf
- end
-
- def digest_class
- @digest_class ||= if defined?(OpenSSL::Digest)
- OpenSSL::Digest
- elsif defined?(::Digest)
- ::Digest
- else
- raise '"openssl" or "digest" library is required'
- end
- end
- end
-end
diff --git a/lib/net/smtp/auth_login.rb b/lib/net/smtp/auth_login.rb
deleted file mode 100644
index 545c1f9..0000000
--- a/lib/net/smtp/auth_login.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-class Net::SMTP
- class AuthLogin < Net::SMTP::Authenticator
- auth_type :login
-
- def auth(user, secret)
- continue('AUTH LOGIN')
- continue(base64_encode(user))
- finish(base64_encode(secret))
- end
- end
-end
diff --git a/lib/net/smtp/auth_plain.rb b/lib/net/smtp/auth_plain.rb
deleted file mode 100644
index 7fa1198..0000000
--- a/lib/net/smtp/auth_plain.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class Net::SMTP
- class AuthPlain < Net::SMTP::Authenticator
- auth_type :plain
-
- def auth(user, secret)
- finish('AUTH PLAIN ' + base64_encode("\0#{user}\0#{secret}"))
- end
- end
-end
diff --git a/lib/net/smtp/auth_sasl_client_adapter.rb b/lib/net/smtp/auth_sasl_client_adapter.rb
new file mode 100644
index 0000000..a65b157
--- /dev/null
+++ b/lib/net/smtp/auth_sasl_client_adapter.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require "net/imap"
+
+module Net
+ class SMTP
+ SASL = Net::IMAP::SASL
+
+ # Experimental
+ #
+ # Initialize with a block that runs a command, yielding for continuations.
+ class SASLClientAdapter < SASL::ClientAdapter
+ include SASL::ProtocolAdapters::SMTP
+
+ RESPONSE_ERRORS = [
+ SMTPAuthenticationError,
+ SMTPServerBusy,
+ SMTPSyntaxError,
+ SMTPFatalError,
+ ].freeze
+
+ def initialize(...)
+ super
+ @command_proc ||= client.method(:send_command_with_continuations)
+ end
+
+ def authenticate(...)
+ super
+ rescue SMTPServerBusy, SMTPSyntaxError, SMTPFatalError => error
+ raise SMTPAuthenticationError.new(error.response)
+ rescue SASL::AuthenticationIncomplete => error
+ raise error.response.exception_class.new(error.response)
+ end
+
+ def host; client.address end
+ def response_errors; RESPONSE_ERRORS end
+ def sasl_ir_capable?; true end
+ def drop_connection; client.quit!(exception: :warn) end
+ def drop_connection!; client.disconnect end
+ end
+ end
+end
diff --git a/lib/net/smtp/auth_sasl_compatibility_adapter.rb b/lib/net/smtp/auth_sasl_compatibility_adapter.rb
new file mode 100644
index 0000000..e6b5642
--- /dev/null
+++ b/lib/net/smtp/auth_sasl_compatibility_adapter.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Net
+ class SMTP
+
+ # Curries arguments to SASLAdapter.authenticate.
+ class AuthSASLCompatibilityAdapter
+ def initialize(mechanism) @mechanism = mechanism end
+ def check_args(...) SASL.authenticator(@mechanism, ...) end
+ def new(smtp) @sasl_adapter = SASLClientAdapter.new(smtp); self end
+ def auth(...) @sasl_adapter.authenticate(@mechanism, ...) end
+ end
+
+ Authenticator.auth_classes.default_proc = ->hash, mechanism {
+ hash[mechanism] = AuthSASLCompatibilityAdapter.new(mechanism)
+ }
+
+ end
+end
diff --git a/lib/net/smtp/auth_xoauth2.rb b/lib/net/smtp/auth_xoauth2.rb
deleted file mode 100644
index ece2e11..0000000
--- a/lib/net/smtp/auth_xoauth2.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-class Net::SMTP
- class AuthXoauth2 < Net::SMTP::Authenticator
- auth_type :xoauth2
-
- def auth(user, secret)
- token = xoauth2_string(user, secret)
-
- finish("AUTH XOAUTH2 #{base64_encode(token)}")
- end
-
- private
-
- def xoauth2_string(user, secret)
- "user=#{user}\1auth=Bearer #{secret}\1\1"
- end
- end
-end
diff --git a/lib/net/smtp/authenticator.rb b/lib/net/smtp/authenticator.rb
index 4e91228..6e381af 100644
--- a/lib/net/smtp/authenticator.rb
+++ b/lib/net/smtp/authenticator.rb
@@ -15,11 +15,14 @@ def self.auth_class(type)
Authenticator.auth_classes[type]
end
- def self.check_args(user_arg = nil, secret_arg = nil, *, **)
- unless user_arg
+ def self.check_args(user_arg = nil, secret_arg = nil, *,
+ authcid: nil, username: nil, user: nil,
+ secret: nil, password: nil,
+ **)
+ unless authcid || username || user || user_arg
raise ArgumentError, 'SMTP-AUTH requested but missing user name'
end
- unless secret_arg
+ unless password || secret || secret_arg
raise ArgumentError, 'SMTP-AUTH requested but missing secret phrase'
end
end
@@ -52,6 +55,12 @@ def base64_encode(str)
# expects "str" may not become too long
[str].pack('m0')
end
+
+ def req_param(*args, name)
+ args.compact.first or
+ raise ArgumentError, "SMTP-AUTH requested but missing #{name}"
+ end
+
end
end
end
diff --git a/net-smtp.gemspec b/net-smtp.gemspec
index 07d9703..eca091d 100644
--- a/net-smtp.gemspec
+++ b/net-smtp.gemspec
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
spec.description = %q{Simple Mail Transfer Protocol client library for Ruby.}
spec.homepage = "https://github.com/ruby/net-smtp"
spec.licenses = ["Ruby", "BSD-2-Clause"]
- spec.required_ruby_version = ">= 2.6.0"
+ spec.required_ruby_version = ">= 2.7.3"
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
@@ -26,4 +26,6 @@ Gem::Specification.new do |spec|
spec.require_paths = ["lib"]
spec.add_dependency "net-protocol"
+
+ spec.add_dependency "net-imap", ">= 0.4.2" # experimental SASL support
end
diff --git a/test/net/smtp/test_smtp.rb b/test/net/smtp/test_smtp.rb
index 320e75d..3f9d50c 100644
--- a/test/net/smtp/test_smtp.rb
+++ b/test/net/smtp/test_smtp.rb
@@ -110,6 +110,21 @@ def test_auth_plain
smtp = Net::SMTP.start 'localhost', server.port
assert smtp.authenticate("account", "password", :plain).success?
assert_equal "AUTH PLAIN AGFjY291bnQAcGFzc3dvcmQ=\r\n", server.commands.last
+
+ server = FakeServer.start(auth: 'plain')
+ smtp = Net::SMTP.start 'localhost', server.port
+ assert smtp.auth("PLAIN", "account", "password").success?
+ assert_equal "AUTH PLAIN AGFjY291bnQAcGFzc3dvcmQ=\r\n", server.commands.last
+
+ server = FakeServer.start(auth: 'plain')
+ smtp = Net::SMTP.start 'localhost', server.port
+ assert smtp.auth(type: "PLAIN", username: "account", secret: "password").success?
+ assert_equal "AUTH PLAIN AGFjY291bnQAcGFzc3dvcmQ=\r\n", server.commands.last
+
+ server = FakeServer.start(auth: 'plain')
+ smtp = Net::SMTP.start 'localhost', server.port
+ assert smtp.auth("PLAIN", username: "account", password: "password").success?
+ assert_equal "AUTH PLAIN AGFjY291bnQAcGFzc3dvcmQ=\r\n", server.commands.last
end
def test_unsucessful_auth_plain
@@ -120,16 +135,31 @@ def test_unsucessful_auth_plain
assert_equal "535", err.response.status
end
+ def test_auth_cram_md5
+ server = FakeServer.start(auth: 'CRAM-MD5')
+ smtp = Net::SMTP.start 'localhost', server.port
+ assert smtp.auth(:cram_md5, "account", password: "password",
+ warn_deprecation: false).success?
+ end
+
def test_auth_login
server = FakeServer.start(auth: 'login')
smtp = Net::SMTP.start 'localhost', server.port
- assert smtp.authenticate("account", "password", :login).success?
+ assert smtp.auth(:login, "account", "password",
+ warn_deprecation: false).success?
+
+ server = FakeServer.start(auth: 'login')
+ smtp = Net::SMTP.start 'localhost', server.port
+ assert smtp.auth(username: "account", secret: "password",
+ type: :login, warn_deprecation: false).success?
end
def test_unsucessful_auth_login
server = FakeServer.start(auth: 'login')
smtp = Net::SMTP.start 'localhost', server.port
- err = assert_raise(Net::SMTPAuthenticationError) { smtp.authenticate("foo", "bar", :login) }
+ err = assert_raise(Net::SMTPAuthenticationError) {
+ smtp.auth(:login, "foo", "bar", warn_deprecation: false)
+ }
assert_equal "535 5.7.8 Error: authentication failed: authentication failure\n", err.message
assert_equal "535", err.response.status
end
@@ -142,7 +172,9 @@ def server.auth(*)
@sock.puts "235 2.7.0 Authentication successful\r\n"
end
smtp = Net::SMTP.start 'localhost', server.port
- err = assert_raise(Net::SMTPUnknownError) { smtp.authenticate("account", "password", :login) }
+ err = assert_raise(Net::SMTPUnknownError) {
+ smtp.auth(:login, "account", "password", warn_deprecation: false)
+ }
assert_equal "235 2.7.0 Authentication successful\n", err.message
assert_equal "235", err.response.status
end
@@ -470,6 +502,15 @@ def test_start_auth_plain
port = fake_server_start(auth: 'plain')
Net::SMTP.start('localhost', port, user: 'account', password: 'password', authtype: :plain){}
+ port = fake_server_start(auth: 'plain')
+ Net::SMTP.start('localhost', port, authtype: "PLAIN",
+ auth: {username: 'account', password: 'password'}){}
+
+ port = fake_server_start(auth: 'plain')
+ Net::SMTP.start('localhost', port, auth: {username: 'account',
+ password: 'password',
+ type: :plain}){}
+
port = fake_server_start(auth: 'plain')
assert_raise Net::SMTPAuthenticationError do
Net::SMTP.start('localhost', port, user: 'account', password: 'invalid', authtype: :plain){}
@@ -483,16 +524,19 @@ def test_start_auth_plain
def test_start_auth_login
port = fake_server_start(auth: 'LOGIN')
- Net::SMTP.start('localhost', port, user: 'account', password: 'password', authtype: :login){}
+ Net::SMTP.start('localhost', port, user: 'account', password: 'password',
+ authtype: :login, auth: {warn_deprecation: false}){}
port = fake_server_start(auth: 'LOGIN')
assert_raise Net::SMTPAuthenticationError do
- Net::SMTP.start('localhost', port, user: 'account', password: 'invalid', authtype: :login){}
+ Net::SMTP.start('localhost', port, user: 'account', password: 'invalid',
+ authtype: :login, auth: {warn_deprecation: false}){}
end
port = fake_server_start(auth: 'PLAIN')
assert_raise Net::SMTPAuthenticationError do
- Net::SMTP.start('localhost', port, user: 'account', password: 'password', authtype: :login){}
+ Net::SMTP.start('localhost', port, user: 'account', password: 'password',
+ authtype: :login, auth: {warn_deprecation: false}){}
end
end
@@ -500,27 +544,20 @@ def test_start_auth_cram_md5
omit "openssl or digest library not loaded" unless defined? OpenSSL or defined? Digest
port = fake_server_start(auth: 'CRAM-MD5')
- Net::SMTP.start('localhost', port, user: 'account', password: 'password', authtype: "CRAM-MD5"){}
+ Net::SMTP.start('localhost', port, user: 'account', password: 'password',
+ authtype: "CRAM-MD5", auth: {warn_deprecation: false}){}
port = fake_server_start(auth: 'CRAM-MD5')
assert_raise Net::SMTPAuthenticationError do
- Net::SMTP.start('localhost', port, user: 'account', password: 'invalid', authtype: :cram_md5){}
+ Net::SMTP.start('localhost', port, user: 'account', password: 'invalid',
+ authtype: :cram_md5, auth: {warn_deprecation: false}){}
end
port = fake_server_start(auth: 'PLAIN')
assert_raise Net::SMTPAuthenticationError do
- Net::SMTP.start('localhost', port, user: 'account', password: 'password', authtype: :cram_md5){}
- end
-
- port = fake_server_start(auth: 'CRAM-MD5')
- smtp = Net::SMTP.new('localhost', port)
- auth_cram_md5 = Net::SMTP::AuthCramMD5.new(smtp)
- auth_cram_md5.define_singleton_method(:digest_class) { raise '"openssl" or "digest" library is required' }
- Net::SMTP::AuthCramMD5.define_singleton_method(:new) { |_| auth_cram_md5 }
- e = assert_raise RuntimeError do
- smtp.start(user: 'account', password: 'password', authtype: :cram_md5){}
+ Net::SMTP.start('localhost', port, user: 'account', password: 'password',
+ authtype: :cram_md5, auth: {warn_deprecation: false}){}
end
- assert_equal('"openssl" or "digest" library is required', e.message)
end
def test_start_instance