@@ -82,6 +82,9 @@ def self.build(client, mechanism, *args, sasl_ir: true, **kwargs, &block)
8282 # An exception that has been raised by <tt>authenticator.process</tt>.
8383 attr_reader :process_error
8484
85+ # An exception that represents an error response from the server.
86+ attr_reader :response_error
87+
8588 def initialize ( client , mechanism , authenticator , sasl_ir : true )
8689 @client = client
8790 @mechanism = Authenticators . normalize_name ( mechanism )
@@ -103,9 +106,11 @@ def initialize(client, mechanism, authenticator, sasl_ir: true)
103106 # Unfortunately, the original error will not be the +#cause+ for the
104107 # client error. But it will be available on #process_error.
105108 def authenticate
106- client . run_command ( mechanism , initial_response ) { process _1 }
107- . tap { raise process_error if process_error }
108- . tap { raise AuthenticationIncomplete , _1 unless done? }
109+ handle_cancellation do
110+ client . run_command ( mechanism , initial_response ) { process _1 }
111+ . tap { raise process_error if process_error }
112+ . tap { raise AuthenticationIncomplete , _1 unless done? }
113+ end
109114 rescue AuthenticationCanceled , *client . response_errors
110115 raise # but don't drop the connection
111116 rescue
@@ -141,11 +146,53 @@ def process(challenge)
141146 @processed = true
142147 return client . cancel_response if process_error
143148 client . encode authenticator . process client . decode challenge
144- rescue => process_error
145- @process_error = process_error
149+ rescue AuthenticationCanceled => error
150+ @process_error = error
151+ client . cancel_response
152+ rescue => error
153+ @process_error = begin
154+ raise AuthenticationError , "error while processing server challenge"
155+ rescue
156+ $!
157+ end
146158 client . cancel_response
147159 end
148160
161+ # | process | response | => result |
162+ # |---------|----------|------------------------------------------|
163+ # | success | success | success |
164+ # | success | error | reraise response error |
165+ # | error | success | raise incomplete error (cause = process) |
166+ # | error | error | raise canceled error (cause = process) |
167+ def handle_cancellation
168+ result = begin
169+ yield
170+ rescue *client . response_errors => error
171+ @response_error = error
172+ raise unless process_error
173+ end
174+ raise_mutual_cancellation! if process_error && response_error
175+ raise_incomplete_cancel! ( result ) if process_error && !response_error
176+ result
177+ end
178+
179+ def raise_mutual_cancellation!
180+ raise process_error # sets the cause
181+ rescue
182+ raise AuthenticationCanceled . new (
183+ "authentication canceled (see error #cause and #response)" ,
184+ response : response_error
185+ )
186+ end
187+
188+ def raise_incomplete_cancellation!
189+ raise process_error # sets the cause
190+ rescue
191+ raise AuthenticationIncomplete . new (
192+ response_error , "server ignored canceled authentication"
193+ )
194+ end
195+
149196 end
150197 end
151198 end
0 commit comments