@@ -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 => SASL ::ClientAdapter
8790 @client = client
@@ -104,9 +107,11 @@ def initialize(client, mechanism, authenticator, sasl_ir: true)
104107 # Unfortunately, the original error will not be the +#cause+ for the
105108 # client error. But it will be available on #process_error.
106109 def authenticate
107- client . run_command ( mechanism , initial_response ) { process _1 }
108- . tap { raise process_error if process_error }
109- . tap { raise AuthenticationIncomplete , _1 unless done? }
110+ handle_cancellation do
111+ client . run_command ( mechanism , initial_response ) { process _1 }
112+ . tap { raise process_error if process_error }
113+ . tap { raise AuthenticationIncomplete , _1 unless done? }
114+ end
110115 rescue AuthenticationCanceled , *client . response_errors
111116 raise # but don't drop the connection
112117 rescue
@@ -142,11 +147,53 @@ def process(challenge)
142147 @processed = true
143148 return client . cancel_response if process_error
144149 client . encode authenticator . process client . decode challenge
145- rescue => process_error
146- @process_error = process_error
150+ rescue AuthenticationCanceled => error
151+ @process_error = error
152+ client . cancel_response
153+ rescue => error
154+ @process_error = begin
155+ raise AuthenticationError , "error while processing server challenge"
156+ rescue
157+ $!
158+ end
147159 client . cancel_response
148160 end
149161
162+ # | process | response | => result |
163+ # |---------|----------|------------------------------------------|
164+ # | success | success | success |
165+ # | success | error | reraise response error |
166+ # | error | success | raise incomplete error (cause = process) |
167+ # | error | error | raise canceled error (cause = process) |
168+ def handle_cancellation
169+ result = begin
170+ yield
171+ rescue *client . response_errors => error
172+ @response_error = error
173+ raise unless process_error
174+ end
175+ raise_mutual_cancellation! if process_error && response_error
176+ raise_incomplete_cancel! ( result ) if process_error && !response_error
177+ result
178+ end
179+
180+ def raise_mutual_cancellation!
181+ raise process_error # sets the cause
182+ rescue
183+ raise AuthenticationCanceled . new (
184+ "authentication canceled (see error #cause and #response)" ,
185+ response : response_error
186+ )
187+ end
188+
189+ def raise_incomplete_cancellation!
190+ raise process_error # sets the cause
191+ rescue
192+ raise AuthenticationIncomplete . new (
193+ response_error , "server ignored canceled authentication"
194+ )
195+ end
196+
150197 end
151198 end
152199 end
0 commit comments