@@ -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 => SASL ::ClientAdapter
87
90
@client = client
@@ -104,9 +107,11 @@ def initialize(client, mechanism, authenticator, sasl_ir: true)
104
107
# Unfortunately, the original error will not be the +#cause+ for the
105
108
# client error. But it will be available on #process_error.
106
109
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
110
115
rescue AuthenticationCanceled , *client . response_errors
111
116
raise # but don't drop the connection
112
117
rescue
@@ -142,11 +147,53 @@ def process(challenge)
142
147
@processed = true
143
148
return client . cancel_response if process_error
144
149
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
147
159
client . cancel_response
148
160
end
149
161
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
+
150
197
end
151
198
end
152
199
end
0 commit comments