@@ -82,6 +82,9 @@ def self.build(client, mechanism, *args, sasl_ir: true, **kwargs, &block)
82
82
# An exception that has been raised by <tt>authenticator.process</tt>.
83
83
attr_reader :process_error
84
84
85
+ # An exception that represents an error response from the server.
86
+ attr_reader :response_error
87
+
85
88
def initialize ( client , mechanism , authenticator , sasl_ir : true )
86
89
@client = client
87
90
@mechanism = Authenticators . normalize_name ( mechanism )
@@ -103,9 +106,11 @@ def initialize(client, mechanism, authenticator, sasl_ir: true)
103
106
# Unfortunately, the original error will not be the +#cause+ for the
104
107
# client error. But it will be available on #process_error.
105
108
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
109
114
rescue AuthenticationCanceled , *client . response_errors
110
115
raise # but don't drop the connection
111
116
rescue
@@ -141,11 +146,53 @@ def process(challenge)
141
146
@processed = true
142
147
return client . cancel_response if process_error
143
148
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
146
158
client . cancel_response
147
159
end
148
160
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
+
149
196
end
150
197
end
151
198
end
0 commit comments