Skip to content

Commit

Permalink
Add forward-compatible #auth for SASL mechanisms
Browse files Browse the repository at this point in the history
A fully SASL-compatible API must first find the authenticator for the
mechanism and then delegate any and all arbitrary parameters to that
authenticator.  Practically, that means that the mechanism name must
either be the first positional parameter or a keyword parameter, and
then every other parameter can be forwarded.  `Net::SMTP#auth` does this.

For backward compatibility, `Net::SMTP#authenticate` is unchanged.
  • Loading branch information
nevans committed Oct 11, 2023
1 parent de0ebb1 commit 7f2ff25
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 0 deletions.
23 changes: 23 additions & 0 deletions lib/net/smtp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,29 @@ def authenticate(user, secret, authtype = DEFAULT_AUTH_TYPE)
critical { authenticator.auth(user, secret) }
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 = nil, *args, type: nil, **kwargs, &blk)
type ||= authtype || DEFAULT_AUTH_TYPE
authenticator = Authenticator.auth_class(type).new(self)
if kwargs.empty?
# TODO: remove this, unless it is needed for 2.6/2.7/3.0 compatibility
critical { authenticator.auth(*args, &blk) }
else
critical { authenticator.auth(*args, **kwargs, &blk) }
end
end

private

def check_auth_method(type)
Expand Down
25 changes: 25 additions & 0 deletions test/net/smtp/test_smtp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -120,10 +135,20 @@ 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").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?

server = FakeServer.start(auth: 'login')
smtp = Net::SMTP.start 'localhost', server.port
assert smtp.auth("LOGIN", username: "account", secret: "password").success?
end

def test_unsucessful_auth_login
Expand Down

0 comments on commit 7f2ff25

Please sign in to comment.