Skip to content

Commit 999e5ac

Browse files
committed
🥅 Cancel AUTHENTICATE if process raises an error
The exception will be re-raised after the protocol cancel response has been sent.
1 parent 4ba5f94 commit 999e5ac

File tree

1 file changed

+17
-4
lines changed

1 file changed

+17
-4
lines changed

lib/net/imap/sasl/authentication_exchange.rb

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ module SASL
66

77
# This API is *experimental*, and may change.
88
#
9-
# TODO: catch exceptions in #process and send #cancel_response.
10-
# TODO: raise an error if the command succeeds after being canceled.
119
# TODO: use with more clients, to verify the API can accommodate them.
1210
#
1311
# An AuthenticationExchange represents a single attempt to authenticate
@@ -79,6 +77,9 @@ def self.build(client, mechanism, *args, sasl_ir: true, **kwargs, &block)
7977

8078
attr_reader :mechanism, :authenticator
8179

80+
# An exception that has been raised by <tt>authenticator.process</tt>.
81+
attr_reader :process_error
82+
8283
def initialize(client, mechanism, authenticator, sasl_ir: true)
8384
client => SASL::ClientAdapter
8485
@client = client
@@ -92,8 +93,17 @@ def initialize(client, mechanism, authenticator, sasl_ir: true)
9293
# using #authenticator. Authentication failures will raise an
9394
# exception. Any exceptions other than AuthenticationCanceled or those
9495
# in <tt>client.response_errors</tt> will drop the connection.
96+
#
97+
# When <tt>authenticator.process</tt> raises any StandardError
98+
# (including AuthenticationCanceled), the authentication exchange will
99+
# be canceled before re-raising the exception. The server will usually
100+
# respond with an error response, and the client will most likely raise
101+
# that error. This client error will supercede the original error.
102+
# Unfortunately, the original error will not be the +#cause+ for the
103+
# client error. But it will be available on #process_error.
95104
def authenticate
96105
client.run_command(mechanism, initial_response) { process _1 }
106+
.tap { raise process_error if process_error }
97107
.tap { raise AuthenticationIncomplete, _1 unless done? }
98108
rescue AuthenticationCanceled, *client.response_errors
99109
raise # but don't drop the connection
@@ -127,9 +137,12 @@ def initial_response
127137
end
128138

129139
def process(challenge)
130-
client.encode authenticator.process client.decode challenge
131-
ensure
132140
@processed = true
141+
return client.cancel_response if process_error
142+
client.encode authenticator.process client.decode challenge
143+
rescue => process_error
144+
@process_error = process_error
145+
client.cancel_response
133146
end
134147

135148
end

0 commit comments

Comments
 (0)