ruby-openid2 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +136 -0
  4. data/CODE_OF_CONDUCT.md +84 -0
  5. data/CONTRIBUTING.md +54 -0
  6. data/LICENSE.txt +210 -0
  7. data/README.md +81 -0
  8. data/SECURITY.md +15 -0
  9. data/lib/hmac/hmac.rb +110 -0
  10. data/lib/hmac/sha1.rb +11 -0
  11. data/lib/hmac/sha2.rb +25 -0
  12. data/lib/openid/association.rb +246 -0
  13. data/lib/openid/consumer/associationmanager.rb +354 -0
  14. data/lib/openid/consumer/checkid_request.rb +179 -0
  15. data/lib/openid/consumer/discovery.rb +516 -0
  16. data/lib/openid/consumer/discovery_manager.rb +144 -0
  17. data/lib/openid/consumer/html_parse.rb +142 -0
  18. data/lib/openid/consumer/idres.rb +513 -0
  19. data/lib/openid/consumer/responses.rb +147 -0
  20. data/lib/openid/consumer/session.rb +36 -0
  21. data/lib/openid/consumer.rb +406 -0
  22. data/lib/openid/cryptutil.rb +112 -0
  23. data/lib/openid/dh.rb +84 -0
  24. data/lib/openid/extension.rb +38 -0
  25. data/lib/openid/extensions/ax.rb +552 -0
  26. data/lib/openid/extensions/oauth.rb +88 -0
  27. data/lib/openid/extensions/pape.rb +170 -0
  28. data/lib/openid/extensions/sreg.rb +268 -0
  29. data/lib/openid/extensions/ui.rb +49 -0
  30. data/lib/openid/fetchers.rb +277 -0
  31. data/lib/openid/kvform.rb +113 -0
  32. data/lib/openid/kvpost.rb +62 -0
  33. data/lib/openid/message.rb +555 -0
  34. data/lib/openid/protocolerror.rb +7 -0
  35. data/lib/openid/server.rb +1571 -0
  36. data/lib/openid/store/filesystem.rb +260 -0
  37. data/lib/openid/store/interface.rb +73 -0
  38. data/lib/openid/store/memcache.rb +109 -0
  39. data/lib/openid/store/memory.rb +79 -0
  40. data/lib/openid/store/nonce.rb +72 -0
  41. data/lib/openid/trustroot.rb +597 -0
  42. data/lib/openid/urinorm.rb +72 -0
  43. data/lib/openid/util.rb +119 -0
  44. data/lib/openid/version.rb +5 -0
  45. data/lib/openid/yadis/accept.rb +141 -0
  46. data/lib/openid/yadis/constants.rb +16 -0
  47. data/lib/openid/yadis/discovery.rb +151 -0
  48. data/lib/openid/yadis/filters.rb +192 -0
  49. data/lib/openid/yadis/htmltokenizer.rb +290 -0
  50. data/lib/openid/yadis/parsehtml.rb +50 -0
  51. data/lib/openid/yadis/services.rb +44 -0
  52. data/lib/openid/yadis/xrds.rb +160 -0
  53. data/lib/openid/yadis/xri.rb +86 -0
  54. data/lib/openid/yadis/xrires.rb +87 -0
  55. data/lib/openid.rb +27 -0
  56. data/lib/ruby-openid.rb +1 -0
  57. data.tar.gz.sig +0 -0
  58. metadata +331 -0
  59. metadata.gz.sig +0 -0
@@ -0,0 +1,147 @@
1
+ module OpenID
2
+ class Consumer
3
+ # Code returned when either the of the
4
+ # OpenID::OpenIDConsumer.begin_auth or OpenID::OpenIDConsumer.complete_auth
5
+ # methods return successfully.
6
+ SUCCESS = :success
7
+
8
+ # Code OpenID::OpenIDConsumer.complete_auth
9
+ # returns when the value it received indicated an invalid login.
10
+ FAILURE = :failure
11
+
12
+ # Code returned by OpenIDConsumer.complete_auth when the user
13
+ # cancels the operation from the server.
14
+ CANCEL = :cancel
15
+
16
+ # Code returned by OpenID::OpenIDConsumer.complete_auth when the
17
+ # OpenIDConsumer instance is in immediate mode and ther server sends back a
18
+ # URL for the user to login with.
19
+ SETUP_NEEDED = :setup_needed
20
+
21
+ module Response
22
+ attr_reader :endpoint
23
+
24
+ def status
25
+ self.class::STATUS
26
+ end
27
+
28
+ # The identity URL that has been authenticated; the Claimed Identifier.
29
+ # See also display_identifier.
30
+ def identity_url
31
+ @endpoint ? @endpoint.claimed_id : nil
32
+ end
33
+
34
+ # The display identifier is related to the Claimed Identifier, but the
35
+ # two are not always identical. The display identifier is something the
36
+ # user should recognize as what they entered, whereas the response's
37
+ # claimed identifier (in the identity_url attribute) may have extra
38
+ # information for better persistence.
39
+ #
40
+ # URLs will be stripped of their fragments for display. XRIs will
41
+ # display the human-readable identifier (i-name) instead of the
42
+ # persistent identifier (i-number).
43
+ #
44
+ # Use the display identifier in your user interface. Use identity_url
45
+ # for querying your database or authorization server, or other
46
+ # identifier equality comparisons.
47
+ def display_identifier
48
+ @endpoint ? @endpoint.display_identifier : nil
49
+ end
50
+ end
51
+
52
+ # A successful acknowledgement from the OpenID server that the
53
+ # supplied URL is, indeed controlled by the requesting agent.
54
+ class SuccessResponse
55
+ include Response
56
+
57
+ STATUS = SUCCESS
58
+
59
+ attr_reader :message, :signed_fields
60
+
61
+ def initialize(endpoint, message, signed_fields)
62
+ # Don't use :endpoint=, because endpoint should never be nil
63
+ # for a successfull transaction.
64
+ @endpoint = endpoint
65
+ @identity_url = endpoint.claimed_id
66
+ @message = message
67
+ @signed_fields = signed_fields
68
+ end
69
+
70
+ # Was this authentication response an OpenID 1 authentication
71
+ # response?
72
+ def is_openid1
73
+ @message.is_openid1
74
+ end
75
+
76
+ # Return whether a particular key is signed, regardless of its
77
+ # namespace alias
78
+ def signed?(ns_uri, ns_key)
79
+ @signed_fields.member?(@message.get_key(ns_uri, ns_key))
80
+ end
81
+
82
+ # Return the specified signed field if available, otherwise
83
+ # return default
84
+ def get_signed(ns_uri, ns_key, default = nil)
85
+ return @message.get_arg(ns_uri, ns_key, default) if signed?(ns_uri, ns_key)
86
+
87
+ default
88
+ end
89
+
90
+ # Get signed arguments from the response message. Return a dict
91
+ # of all arguments in the specified namespace. If any of the
92
+ # arguments are not signed, return nil.
93
+ def get_signed_ns(ns_uri)
94
+ msg_args = @message.get_args(ns_uri)
95
+ msg_args.each_key do |key|
96
+ return nil unless signed?(ns_uri, key)
97
+ end
98
+ msg_args
99
+ end
100
+
101
+ # Return response arguments in the specified namespace.
102
+ # If require_signed is true and the arguments are not signed,
103
+ # return nil.
104
+ def extension_response(namespace_uri, require_signed)
105
+ if require_signed
106
+ get_signed_ns(namespace_uri)
107
+ else
108
+ @message.get_args(namespace_uri)
109
+ end
110
+ end
111
+ end
112
+
113
+ class FailureResponse
114
+ include Response
115
+ STATUS = FAILURE
116
+
117
+ attr_reader :message, :contact, :reference
118
+
119
+ def initialize(endpoint, message, contact = nil, reference = nil)
120
+ @endpoint = endpoint
121
+ @message = message
122
+ @contact = contact
123
+ @reference = reference
124
+ end
125
+ end
126
+
127
+ class CancelResponse
128
+ include Response
129
+ STATUS = CANCEL
130
+ def initialize(endpoint)
131
+ @endpoint = endpoint
132
+ end
133
+ end
134
+
135
+ class SetupNeededResponse
136
+ include Response
137
+ STATUS = SETUP_NEEDED
138
+
139
+ attr_reader :setup_url
140
+
141
+ def initialize(endpoint, setup_url)
142
+ @endpoint = endpoint
143
+ @setup_url = setup_url
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,36 @@
1
+ module OpenID
2
+ class Consumer
3
+ class Session
4
+ def initialize(session, decode_klass = nil)
5
+ @session = session
6
+ @decode_klass = decode_klass
7
+ end
8
+
9
+ def [](key)
10
+ val = @session[key]
11
+ @decode_klass ? @decode_klass.from_session_value(val) : val
12
+ end
13
+
14
+ def []=(key, val)
15
+ @session[key] = to_session_value(val)
16
+ end
17
+
18
+ def keys
19
+ @session.keys
20
+ end
21
+
22
+ private
23
+
24
+ def to_session_value(val)
25
+ case val
26
+ when Array
27
+ val.map { |ele| to_session_value(ele) }
28
+ when Hash
29
+ Hash[*val.flat_map { |k, v| [k, to_session_value(v)] }]
30
+ else
31
+ val.respond_to?(:to_session_value) ? val.to_session_value : val
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,406 @@
1
+ require_relative "consumer/idres"
2
+ require_relative "consumer/checkid_request"
3
+ require_relative "consumer/associationmanager"
4
+ require_relative "consumer/responses"
5
+ require_relative "consumer/session"
6
+ require_relative "consumer/discovery_manager"
7
+ require_relative "consumer/discovery"
8
+ require_relative "message"
9
+ require_relative "yadis/discovery"
10
+ require_relative "store/nonce"
11
+
12
+ module OpenID
13
+ # OpenID support for Relying Parties (aka Consumers).
14
+ #
15
+ # This module documents the main interface with the OpenID consumer
16
+ # library. The only part of the library which has to be used and
17
+ # isn't documented in full here is the store required to create an
18
+ # Consumer instance.
19
+ #
20
+ # = OVERVIEW
21
+ #
22
+ # The OpenID identity verification process most commonly uses the
23
+ # following steps, as visible to the user of this library:
24
+ #
25
+ # 1. The user enters their OpenID into a field on the consumer's
26
+ # site, and hits a login button.
27
+ #
28
+ # 2. The consumer site discovers the user's OpenID provider using
29
+ # the Yadis protocol.
30
+ #
31
+ # 3. The consumer site sends the browser a redirect to the OpenID
32
+ # provider. This is the authentication request as described in
33
+ # the OpenID specification.
34
+ #
35
+ # 4. The OpenID provider's site sends the browser a redirect back to
36
+ # the consumer site. This redirect contains the provider's
37
+ # response to the authentication request.
38
+ #
39
+ # The most important part of the flow to note is the consumer's site
40
+ # must handle two separate HTTP requests in order to perform the
41
+ # full identity check.
42
+ #
43
+ # = LIBRARY DESIGN
44
+ #
45
+ # This consumer library is designed with that flow in mind. The
46
+ # goal is to make it as easy as possible to perform the above steps
47
+ # securely.
48
+ #
49
+ # At a high level, there are two important parts in the consumer
50
+ # library. The first important part is this module, which contains
51
+ # the interface to actually use this library. The second is
52
+ # openid/store/interface.rb, which describes the interface to use if
53
+ # you need to create a custom method for storing the state this
54
+ # library needs to maintain between requests.
55
+ #
56
+ # In general, the second part is less important for users of the
57
+ # library to know about, as several implementations are provided
58
+ # which cover a wide variety of situations in which consumers may
59
+ # use the library.
60
+ #
61
+ # The Consumer class has methods corresponding to the actions
62
+ # necessary in each of steps 2, 3, and 4 described in the overview.
63
+ # Use of this library should be as easy as creating an Consumer
64
+ # instance and calling the methods appropriate for the action the
65
+ # site wants to take.
66
+ #
67
+ # This library automatically detects which version of the OpenID
68
+ # protocol should be used for a transaction and constructs the
69
+ # proper requests and responses. Users of this library do not need
70
+ # to worry about supporting multiple protocol versions; the library
71
+ # supports them implicitly. Depending on the version of the
72
+ # protocol in use, the OpenID transaction may be more secure. See
73
+ # the OpenID specifications for more information.
74
+ #
75
+ # = SESSIONS, STORES, AND STATELESS MODE
76
+ #
77
+ # The Consumer object keeps track of two types of state:
78
+ #
79
+ # 1. State of the user's current authentication attempt. Things
80
+ # like the identity URL, the list of endpoints discovered for
81
+ # that URL, and in case where some endpoints are unreachable, the
82
+ # list of endpoints already tried. This state needs to be held
83
+ # from Consumer.begin() to Consumer.complete(), but it is only
84
+ # applicable to a single session with a single user agent, and at
85
+ # the end of the authentication process (i.e. when an OP replies
86
+ # with either <tt>id_res</tt>. or <tt>cancel</tt> it may be
87
+ # discarded.
88
+ #
89
+ # 2. State of relationships with servers, i.e. shared secrets
90
+ # (associations) with servers and nonces seen on signed messages.
91
+ # This information should persist from one session to the next
92
+ # and should not be bound to a particular user-agent.
93
+ #
94
+ # These two types of storage are reflected in the first two
95
+ # arguments of Consumer's constructor, <tt>session</tt> and
96
+ # <tt>store</tt>. <tt>session</tt> is a dict-like object and we
97
+ # hope your web framework provides you with one of these bound to
98
+ # the user agent. <tt>store</tt> is an instance of Store.
99
+ #
100
+ # Since the store does hold secrets shared between your application
101
+ # and the OpenID provider, you should be careful about how you use
102
+ # it in a shared hosting environment. If the filesystem or database
103
+ # permissions of your web host allow strangers to read from them, do
104
+ # not store your data there! If you have no safe place to store
105
+ # your data, construct your consumer with nil for the store, and it
106
+ # will operate only in stateless mode. Stateless mode may be
107
+ # slower, put more load on the OpenID provider, and trusts the
108
+ # provider to keep you safe from replay attacks.
109
+ #
110
+ # Several store implementation are provided, and the interface is
111
+ # fully documented so that custom stores can be used as well. See
112
+ # the documentation for the Consumer class for more information on
113
+ # the interface for stores. The implementations that are provided
114
+ # allow the consumer site to store the necessary data in several
115
+ # different ways, including several SQL databases and normal files
116
+ # on disk.
117
+ #
118
+ # = IMMEDIATE MODE
119
+ #
120
+ # In the flow described above, the user may need to confirm to the
121
+ # OpenID provider that it's ok to disclose his or her identity. The
122
+ # provider may draw pages asking for information from the user
123
+ # before it redirects the browser back to the consumer's site. This
124
+ # is generally transparent to the consumer site, so it is typically
125
+ # ignored as an implementation detail.
126
+ #
127
+ # There can be times, however, where the consumer site wants to get
128
+ # a response immediately. When this is the case, the consumer can
129
+ # put the library in immediate mode. In immediate mode, there is an
130
+ # extra response possible from the server, which is essentially the
131
+ # server reporting that it doesn't have enough information to answer
132
+ # the question yet.
133
+ #
134
+ # = USING THIS LIBRARY
135
+ #
136
+ # Integrating this library into an application is usually a
137
+ # relatively straightforward process. The process should basically
138
+ # follow this plan:
139
+ #
140
+ # Add an OpenID login field somewhere on your site. When an OpenID
141
+ # is entered in that field and the form is submitted, it should make
142
+ # a request to the site that includes that OpenID URL.
143
+ #
144
+ # First, the application should instantiate a Consumer with a
145
+ # session for per-user state and store for shared state using the
146
+ # store of choice.
147
+ #
148
+ # Next, the application should call the <tt>begin</tt> method of
149
+ # Consumer instance. This method takes the OpenID URL as entered by
150
+ # the user. The <tt>begin</tt> method returns a CheckIDRequest
151
+ # object.
152
+ #
153
+ # Next, the application should call the redirect_url method on the
154
+ # CheckIDRequest object. The parameter <tt>return_to</tt> is the
155
+ # URL that the OpenID server will send the user back to after
156
+ # attempting to verify his or her identity. The <tt>realm</tt>
157
+ # parameter is the URL (or URL pattern) that identifies your web
158
+ # site to the user when he or she is authorizing it. Send a
159
+ # redirect to the resulting URL to the user's browser.
160
+ #
161
+ # That's the first half of the authentication process. The second
162
+ # half of the process is done after the user's OpenID Provider sends
163
+ # the user's browser a redirect back to your site to complete their
164
+ # login.
165
+ #
166
+ # When that happens, the user will contact your site at the URL
167
+ # given as the <tt>return_to</tt> URL to the redirect_url call made
168
+ # above. The request will have several query parameters added to
169
+ # the URL by the OpenID provider as the information necessary to
170
+ # finish the request.
171
+ #
172
+ # Get a Consumer instance with the same session and store as before
173
+ # and call its complete() method, passing in all the received query
174
+ # arguments and URL currently being handled.
175
+ #
176
+ # There are multiple possible return types possible from that
177
+ # method. These indicate the whether or not the login was
178
+ # successful, and include any additional information appropriate for
179
+ # their type.
180
+ class Consumer
181
+ attr_accessor :session_key_prefix
182
+
183
+ # Initialize a Consumer instance.
184
+ #
185
+ # You should create a new instance of the Consumer object with
186
+ # every HTTP request that handles OpenID transactions.
187
+ #
188
+ # session: the session object to use to store request information.
189
+ # The session should behave like a hash.
190
+ #
191
+ # store: an object that implements the interface in Store.
192
+ def initialize(session, store)
193
+ @origin_session = session
194
+ @session = Session.new(session, OpenID::OpenIDServiceEndpoint)
195
+ @store = store
196
+ @session_key_prefix = "OpenID::Consumer::"
197
+ end
198
+
199
+ # Start the OpenID authentication process. See steps 1-2 in the
200
+ # overview for the Consumer class.
201
+ #
202
+ # user_url: Identity URL given by the user. This method performs a
203
+ # textual transformation of the URL to try and make sure it is
204
+ # normalized. For example, a user_url of example.com will be
205
+ # normalized to http://example.com/ normalizing and resolving any
206
+ # redirects the server might issue.
207
+ #
208
+ # anonymous: A boolean value. Whether to make an anonymous
209
+ # request of the OpenID provider. Such a request does not ask for
210
+ # an authorization assertion for an OpenID identifier, but may be
211
+ # used with extensions to pass other data. e.g. "I don't care who
212
+ # you are, but I'd like to know your time zone."
213
+ #
214
+ # Returns a CheckIDRequest object containing the discovered
215
+ # information, with a method for building a redirect URL to the
216
+ # server, as described in step 3 of the overview. This object may
217
+ # also be used to add extension arguments to the request, using
218
+ # its add_extension_arg method.
219
+ #
220
+ # Raises DiscoveryFailure when no OpenID server can be found for
221
+ # this URL.
222
+ def begin(openid_identifier, anonymous = false)
223
+ manager = discovery_manager(openid_identifier)
224
+ service = manager.get_next_service(&method(:discover))
225
+
226
+ if service.nil?
227
+ raise DiscoveryFailure.new(
228
+ "No usable OpenID services were found " \
229
+ "for #{openid_identifier.inspect}",
230
+ nil,
231
+ )
232
+ else
233
+ begin_without_discovery(service, anonymous)
234
+ end
235
+ end
236
+
237
+ # Start OpenID verification without doing OpenID server
238
+ # discovery. This method is used internally by Consumer.begin()
239
+ # after discovery is performed, and exists to provide an interface
240
+ # for library users needing to perform their own discovery.
241
+ #
242
+ # service: an OpenID service endpoint descriptor. This object and
243
+ # factories for it are found in the openid/consumer/discovery.rb
244
+ # module.
245
+ #
246
+ # Returns an OpenID authentication request object.
247
+ def begin_without_discovery(service, anonymous)
248
+ assoc = association_manager(service).get_association
249
+ checkid_request = CheckIDRequest.new(assoc, service)
250
+ checkid_request.anonymous = anonymous
251
+
252
+ if service.compatibility_mode
253
+ rt_args = checkid_request.return_to_args
254
+ rt_args[Consumer.openid1_return_to_nonce_name] = Nonce.mk_nonce
255
+ rt_args[Consumer.openid1_return_to_claimed_id_name] =
256
+ service.claimed_id
257
+ end
258
+
259
+ self.last_requested_endpoint = service
260
+ checkid_request
261
+ end
262
+
263
+ # Called to interpret the server's response to an OpenID
264
+ # request. It is called in step 4 of the flow described in the
265
+ # Consumer overview.
266
+ #
267
+ # query: A hash of the query parameters for this HTTP request.
268
+ # Note that in rails, this is <b>not</b> <tt>params</tt> but
269
+ # <tt>params.reject{|k,v|request.path_parameters[k]}</tt>
270
+ # because <tt>controller</tt> and <tt>action</tt> and other
271
+ # "path parameters" are included in params.
272
+ #
273
+ # current_url: Extract the URL of the current request from your
274
+ # application's web request framework and specify it here to have it
275
+ # checked against the openid.return_to value in the response. Do not
276
+ # just pass <tt>args['openid.return_to']</tt> here; that will defeat the
277
+ # purpose of this check. (See OpenID Authentication 2.0 section 11.1.)
278
+ #
279
+ # If the return_to URL check fails, the status of the completion will be
280
+ # FAILURE.
281
+
282
+ #
283
+ # Returns a subclass of Response. The type of response is
284
+ # indicated by the status attribute, which will be one of
285
+ # SUCCESS, CANCEL, FAILURE, or SETUP_NEEDED.
286
+ def complete(query, current_url)
287
+ message = Message.from_post_args(query)
288
+ mode = message.get_arg(OPENID_NS, "mode", "invalid")
289
+ begin
290
+ meth = method("complete_" + mode)
291
+ rescue NameError
292
+ meth = method(:complete_invalid)
293
+ end
294
+ response = meth.call(message, current_url)
295
+ cleanup_last_requested_endpoint
296
+ cleanup_session if [SUCCESS, CANCEL].member?(response.status)
297
+ response
298
+ end
299
+
300
+ protected
301
+
302
+ def session_get(name)
303
+ @session[session_key(name)]
304
+ end
305
+
306
+ def session_set(name, val)
307
+ @session[session_key(name)] = val
308
+ end
309
+
310
+ def session_key(suffix)
311
+ @session_key_prefix + suffix
312
+ end
313
+
314
+ def last_requested_endpoint
315
+ session_get("last_requested_endpoint")
316
+ end
317
+
318
+ def last_requested_endpoint=(endpoint)
319
+ session_set("last_requested_endpoint", endpoint)
320
+ end
321
+
322
+ def cleanup_last_requested_endpoint
323
+ @session[session_key("last_requested_endpoint")] = nil
324
+ end
325
+
326
+ def discovery_manager(openid_identifier)
327
+ DiscoveryManager.new(@origin_session, openid_identifier, @session_key_prefix)
328
+ end
329
+
330
+ def cleanup_session
331
+ discovery_manager(nil).cleanup(true)
332
+ end
333
+
334
+ def discover(identifier)
335
+ OpenID.discover(identifier)
336
+ end
337
+
338
+ def negotiator
339
+ DefaultNegotiator
340
+ end
341
+
342
+ def association_manager(service)
343
+ AssociationManager.new(
344
+ @store,
345
+ service.server_url,
346
+ service.compatibility_mode,
347
+ negotiator,
348
+ )
349
+ end
350
+
351
+ def handle_idres(message, current_url)
352
+ IdResHandler.new(message, current_url, @store, last_requested_endpoint)
353
+ end
354
+
355
+ def complete_invalid(message, _unused_return_to)
356
+ mode = message.get_arg(OPENID_NS, "mode", "<No mode set>")
357
+ FailureResponse.new(
358
+ last_requested_endpoint,
359
+ "Invalid openid.mode: #{mode}",
360
+ )
361
+ end
362
+
363
+ def complete_cancel(_unused_message, _unused_return_to)
364
+ CancelResponse.new(last_requested_endpoint)
365
+ end
366
+
367
+ def complete_error(message, _unused_return_to)
368
+ error = message.get_arg(OPENID_NS, "error")
369
+ contact = message.get_arg(OPENID_NS, "contact")
370
+ reference = message.get_arg(OPENID_NS, "reference")
371
+
372
+ FailureResponse.new(
373
+ last_requested_endpoint,
374
+ error,
375
+ contact,
376
+ reference,
377
+ )
378
+ end
379
+
380
+ def complete_setup_needed(message, _unused_return_to)
381
+ return complete_invalid(message, nil) if message.is_openid1
382
+
383
+ setup_url = message.get_arg(OPENID2_NS, "user_setup_url")
384
+ SetupNeededResponse.new(last_requested_endpoint, setup_url)
385
+ end
386
+
387
+ def complete_id_res(message, current_url)
388
+ if message.is_openid1
389
+ setup_url = message.get_arg(OPENID_NS, "user_setup_url")
390
+ return SetupNeededResponse.new(last_requested_endpoint, setup_url) unless setup_url.nil?
391
+ end
392
+
393
+ begin
394
+ idres = handle_idres(message, current_url)
395
+ rescue OpenIDError => e
396
+ FailureResponse.new(last_requested_endpoint, e.message)
397
+ else
398
+ SuccessResponse.new(
399
+ idres.endpoint,
400
+ message,
401
+ idres.signed_fields,
402
+ )
403
+ end
404
+ end
405
+ end
406
+ end
@@ -0,0 +1,112 @@
1
+ # stdlib
2
+ require "digest/sha1"
3
+ require "digest/sha2"
4
+ begin
5
+ require "openssl"
6
+ rescue LoadError
7
+ begin
8
+ # Try loading the ruby-hmac files if they exist
9
+ require "hmac-sha1"
10
+ require "hmac-sha2"
11
+ rescue LoadError
12
+ # Nothing exists use included hmac files
13
+ require_relative "../hmac/sha1"
14
+ require_relative "../hmac/sha2"
15
+ end
16
+ end
17
+
18
+ # This library
19
+ require_relative "util"
20
+
21
+ module OpenID
22
+ # This module contains everything needed to perform low-level
23
+ # cryptograph and data manipulation tasks.
24
+ module CryptUtil
25
+ # Generate a random number, doing a little extra work to make it
26
+ # more likely that it's suitable for cryptography. If your system
27
+ # doesn't have /dev/urandom then this number is not
28
+ # cryptographically safe. See
29
+ # <http://www.cosine.org/2007/08/07/security-ruby-kernel-rand/>
30
+ # for more information. max is the largest possible value of such
31
+ # a random number, where the result will be less than max.
32
+ def self.rand(max)
33
+ Kernel.srand
34
+ Kernel.rand(max)
35
+ end
36
+
37
+ def self.sha1(text)
38
+ Digest::SHA1.digest(text)
39
+ end
40
+
41
+ def self.hmac_sha1(key, text)
42
+ return HMAC::SHA1.digest(key, text) unless defined? OpenSSL
43
+
44
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new("SHA1"), key, text)
45
+ end
46
+
47
+ def self.sha256(text)
48
+ Digest::SHA256.digest(text)
49
+ end
50
+
51
+ def self.hmac_sha256(key, text)
52
+ return HMAC::SHA256.digest(key, text) unless defined? OpenSSL
53
+
54
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new("SHA256"), key, text)
55
+ end
56
+
57
+ # Generate a random string of the given length, composed of the
58
+ # specified characters. If chars is nil, generate a string
59
+ # composed of characters in the range 0..255.
60
+ def self.random_string(length, chars = nil)
61
+ s = ""
62
+
63
+ if chars.nil?
64
+ length.times { s << rand(256).chr }
65
+ else
66
+ length.times { s << chars[rand(chars.length)] }
67
+ end
68
+ s
69
+ end
70
+
71
+ # Convert a number to its binary representation; return a string
72
+ # of bytes.
73
+ def self.num_to_binary(n)
74
+ bits = n.to_s(2)
75
+ prepend = (8 - bits.length % 8)
76
+ bits = ("0" * prepend) + bits
77
+ [bits].pack("B*")
78
+ end
79
+
80
+ # Convert a string of bytes into a number.
81
+ def self.binary_to_num(s)
82
+ # taken from openid-ruby 0.0.1
83
+ s = "\000" * (4 - (s.length % 4)) + s
84
+ num = 0
85
+ s.unpack("N*").each do |x|
86
+ num <<= 32
87
+ num |= x
88
+ end
89
+ num
90
+ end
91
+
92
+ # Encode a number as a base64-encoded byte string.
93
+ def self.num_to_base64(l)
94
+ OpenID::Util.to_base64(num_to_binary(l))
95
+ end
96
+
97
+ # Decode a base64 byte string to a number.
98
+ def self.base64_to_num(s)
99
+ binary_to_num(OpenID::Util.from_base64(s))
100
+ end
101
+
102
+ def self.const_eq(s1, s2)
103
+ return false if s1.length != s2.length
104
+
105
+ result = true
106
+ s1.length.times do |i|
107
+ result &= (s1[i] == s2[i])
108
+ end
109
+ result
110
+ end
111
+ end
112
+ end