ruby-openid 1.1.4 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/INSTALL +0 -9
- data/README +21 -22
- data/UPGRADE +117 -0
- data/admin/runtests.rb +36 -0
- data/examples/README +13 -21
- data/examples/active_record_openid_store/README +8 -3
- data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +4 -8
- data/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb +26 -0
- data/examples/active_record_openid_store/lib/association.rb +2 -0
- data/examples/active_record_openid_store/lib/openid_ar_store.rb +22 -47
- data/examples/active_record_openid_store/test/store_test.rb +78 -48
- data/examples/discover +46 -0
- data/examples/{rails_server → rails_openid}/README +0 -0
- data/examples/{rails_server → rails_openid}/Rakefile +0 -0
- data/examples/{rails_server → rails_openid}/app/controllers/application.rb +0 -0
- data/examples/rails_openid/app/controllers/consumer_controller.rb +115 -0
- data/examples/{rails_server → rails_openid}/app/controllers/login_controller.rb +10 -2
- data/examples/rails_openid/app/controllers/server_controller.rb +265 -0
- data/examples/{rails_server → rails_openid}/app/helpers/application_helper.rb +0 -0
- data/examples/{rails_server → rails_openid}/app/helpers/login_helper.rb +0 -0
- data/examples/{rails_server → rails_openid}/app/helpers/server_helper.rb +0 -0
- data/examples/rails_openid/app/views/consumer/index.rhtml +81 -0
- data/examples/rails_openid/app/views/consumer/start.rhtml +8 -0
- data/examples/{rails_server → rails_openid}/app/views/layouts/server.rhtml +0 -0
- data/examples/{rails_server → rails_openid}/app/views/login/index.rhtml +1 -1
- data/examples/rails_openid/app/views/server/decide.rhtml +26 -0
- data/examples/{rails_server → rails_openid}/config/boot.rb +0 -0
- data/examples/{rails_server → rails_openid}/config/database.yml +0 -0
- data/examples/{rails_server → rails_openid}/config/environment.rb +0 -0
- data/examples/{rails_server → rails_openid}/config/environments/development.rb +0 -0
- data/examples/{rails_server → rails_openid}/config/environments/production.rb +0 -0
- data/examples/{rails_server → rails_openid}/config/environments/test.rb +0 -0
- data/examples/{rails_server → rails_openid}/config/routes.rb +2 -1
- data/examples/{rails_server → rails_openid}/doc/README_FOR_APP +0 -0
- data/examples/{rails_server → rails_openid}/public/404.html +0 -0
- data/examples/{rails_server → rails_openid}/public/500.html +0 -0
- data/examples/{rails_server → rails_openid}/public/dispatch.cgi +0 -0
- data/examples/{rails_server → rails_openid}/public/dispatch.fcgi +0 -0
- data/examples/{rails_server → rails_openid}/public/dispatch.rb +0 -0
- data/examples/{rails_server → rails_openid}/public/favicon.ico +0 -0
- data/examples/rails_openid/public/images/openid_login_bg.gif +0 -0
- data/examples/{rails_server → rails_openid}/public/javascripts/controls.js +0 -0
- data/examples/{rails_server → rails_openid}/public/javascripts/dragdrop.js +0 -0
- data/examples/{rails_server → rails_openid}/public/javascripts/effects.js +0 -0
- data/examples/{rails_server → rails_openid}/public/javascripts/prototype.js +0 -0
- data/examples/{rails_server → rails_openid}/public/robots.txt +0 -0
- data/examples/{rails_server → rails_openid}/script/about +0 -0
- data/examples/{rails_server → rails_openid}/script/breakpointer +0 -0
- data/examples/{rails_server → rails_openid}/script/console +0 -0
- data/examples/{rails_server → rails_openid}/script/destroy +0 -0
- data/examples/{rails_server → rails_openid}/script/generate +0 -0
- data/examples/{rails_server → rails_openid}/script/performance/benchmarker +0 -0
- data/examples/{rails_server → rails_openid}/script/performance/profiler +0 -0
- data/examples/{rails_server → rails_openid}/script/plugin +0 -0
- data/examples/{rails_server → rails_openid}/script/process/reaper +0 -0
- data/examples/{rails_server → rails_openid}/script/process/spawner +0 -0
- data/examples/{rails_server → rails_openid}/script/process/spinner +0 -0
- data/examples/{rails_server → rails_openid}/script/runner +0 -0
- data/examples/{rails_server → rails_openid}/script/server +0 -0
- data/examples/{rails_server → rails_openid}/test/functional/login_controller_test.rb +0 -0
- data/examples/{rails_server → rails_openid}/test/functional/server_controller_test.rb +0 -0
- data/examples/{rails_server → rails_openid}/test/test_helper.rb +0 -0
- data/lib/{hmac.rb → hmac/hmac.rb} +0 -0
- data/lib/{hmac-sha1.rb → hmac/sha1.rb} +1 -1
- data/lib/{hmac-sha2.rb → hmac/sha2.rb} +1 -1
- data/lib/openid/association.rb +213 -73
- data/lib/openid/consumer/associationmanager.rb +338 -0
- data/lib/openid/consumer/checkid_request.rb +175 -0
- data/lib/openid/consumer/discovery.rb +480 -0
- data/lib/openid/consumer/discovery_manager.rb +123 -0
- data/lib/openid/consumer/html_parse.rb +136 -0
- data/lib/openid/consumer/idres.rb +525 -0
- data/lib/openid/consumer/responses.rb +133 -0
- data/lib/openid/consumer.rb +280 -807
- data/lib/openid/cryptutil.rb +85 -0
- data/lib/openid/dh.rb +60 -23
- data/lib/openid/extension.rb +31 -0
- data/lib/openid/extensions/ax.rb +506 -0
- data/lib/openid/extensions/pape.rb +182 -0
- data/lib/openid/extensions/sreg.rb +275 -0
- data/lib/openid/extras.rb +11 -0
- data/lib/openid/fetchers.rb +132 -93
- data/lib/openid/kvform.rb +133 -0
- data/lib/openid/kvpost.rb +56 -0
- data/lib/openid/message.rb +534 -0
- data/lib/openid/protocolerror.rb +6 -0
- data/lib/openid/server.rb +1215 -666
- data/lib/openid/store/filesystem.rb +271 -0
- data/lib/openid/store/interface.rb +75 -0
- data/lib/openid/store/memory.rb +84 -0
- data/lib/openid/store/nonce.rb +68 -0
- data/lib/openid/trustroot.rb +314 -87
- data/lib/openid/urinorm.rb +37 -34
- data/lib/openid/util.rb +42 -220
- data/lib/openid/yadis/accept.rb +148 -0
- data/lib/openid/yadis/constants.rb +21 -0
- data/lib/openid/yadis/discovery.rb +153 -0
- data/lib/openid/yadis/filters.rb +205 -0
- data/lib/openid/{htmltokenizer.rb → yadis/htmltokenizer.rb} +1 -54
- data/lib/openid/yadis/parsehtml.rb +36 -0
- data/lib/openid/yadis/services.rb +42 -0
- data/lib/openid/yadis/xrds.rb +171 -0
- data/lib/openid/yadis/xri.rb +90 -0
- data/lib/openid/yadis/xrires.rb +106 -0
- data/lib/openid.rb +1 -4
- data/test/data/accept.txt +124 -0
- data/test/data/dh.txt +29 -0
- data/test/data/example-xrds.xml +14 -0
- data/test/data/linkparse.txt +587 -0
- data/test/data/n2b64 +650 -0
- data/test/data/test1-discover.txt +137 -0
- data/test/data/test1-parsehtml.txt +128 -0
- data/test/data/test_discover/openid.html +11 -0
- data/test/data/test_discover/openid2.html +11 -0
- data/test/data/test_discover/openid2_xrds.xml +12 -0
- data/test/data/test_discover/openid2_xrds_no_local_id.xml +11 -0
- data/test/data/test_discover/openid_1_and_2.html +11 -0
- data/test/data/test_discover/openid_1_and_2_xrds.xml +16 -0
- data/test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
- data/test/data/test_discover/openid_and_yadis.html +12 -0
- data/test/data/test_discover/openid_no_delegate.html +10 -0
- data/test/data/test_discover/yadis_0entries.xml +12 -0
- data/test/data/test_discover/yadis_2_bad_local_id.xml +15 -0
- data/test/data/test_discover/yadis_2entries_delegate.xml +22 -0
- data/test/data/test_discover/yadis_2entries_idp.xml +21 -0
- data/test/data/test_discover/yadis_another_delegate.xml +14 -0
- data/test/data/test_discover/yadis_idp.xml +12 -0
- data/test/data/test_discover/yadis_idp_delegate.xml +13 -0
- data/test/data/test_discover/yadis_no_delegate.xml +11 -0
- data/test/data/test_xrds/=j3h.2007.11.14.xrds +25 -0
- data/test/data/test_xrds/README +12 -0
- data/test/data/test_xrds/delegated-20060809-r1.xrds +34 -0
- data/test/data/test_xrds/delegated-20060809-r2.xrds +34 -0
- data/test/data/test_xrds/delegated-20060809.xrds +34 -0
- data/test/data/test_xrds/no-xrd.xml +7 -0
- data/test/data/test_xrds/not-xrds.xml +2 -0
- data/test/data/test_xrds/prefixsometimes.xrds +34 -0
- data/test/data/test_xrds/ref.xrds +109 -0
- data/test/data/test_xrds/sometimesprefix.xrds +34 -0
- data/test/data/test_xrds/spoof1.xrds +25 -0
- data/test/data/test_xrds/spoof2.xrds +25 -0
- data/test/data/test_xrds/spoof3.xrds +37 -0
- data/test/data/test_xrds/status222.xrds +9 -0
- data/test/data/test_xrds/valid-populated-xrds.xml +39 -0
- data/test/data/trustroot.txt +147 -0
- data/test/discoverdata.rb +131 -0
- data/test/test_accept.rb +170 -0
- data/test/test_association.rb +266 -0
- data/test/test_associationmanager.rb +899 -0
- data/test/test_ax.rb +587 -0
- data/test/test_checkid_request.rb +297 -0
- data/test/test_consumer.rb +257 -0
- data/test/test_cryptutil.rb +117 -0
- data/test/test_dh.rb +86 -0
- data/test/test_discover.rb +772 -0
- data/test/test_discovery_manager.rb +262 -0
- data/test/test_extras.rb +35 -0
- data/test/test_fetchers.rb +472 -0
- data/test/test_filters.rb +270 -0
- data/test/test_idres.rb +816 -0
- data/test/test_kvform.rb +165 -0
- data/test/test_kvpost.rb +65 -0
- data/test/test_linkparse.rb +101 -0
- data/test/test_message.rb +1058 -0
- data/test/test_nonce.rb +89 -0
- data/test/test_openid_yadis.rb +178 -0
- data/test/test_pape.rb +233 -0
- data/test/test_parsehtml.rb +80 -0
- data/test/test_responses.rb +63 -0
- data/test/test_server.rb +2270 -0
- data/test/test_sreg.rb +479 -0
- data/test/test_stores.rb +269 -0
- data/test/test_trustroot.rb +112 -0
- data/test/{urinorm.rb → test_urinorm.rb} +6 -3
- data/test/test_util.rb +144 -0
- data/test/test_xrds.rb +160 -0
- data/test/test_xri.rb +48 -0
- data/test/test_xrires.rb +63 -0
- data/test/test_yadis_discovery.rb +207 -0
- data/test/testutil.rb +116 -0
- data/test/util.rb +47 -50
- metadata +233 -143
- data/examples/consumer.rb +0 -290
- data/examples/rails_openid_login_generator/openid_login_generator-0.1.gem +0 -0
- data/examples/rails_server/app/controllers/server_controller.rb +0 -190
- data/examples/rails_server/app/views/server/decide.rhtml +0 -11
- data/examples/rails_server/public/images/rails.png +0 -0
- data/lib/hmac-md5.rb +0 -11
- data/lib/hmac-rmd160.rb +0 -11
- data/lib/openid/discovery.rb +0 -122
- data/lib/openid/filestore.rb +0 -315
- data/lib/openid/parse.rb +0 -23
- data/lib/openid/service.rb +0 -147
- data/lib/openid/stores.rb +0 -178
- data/test/assoc.rb +0 -38
- data/test/consumer.rb +0 -376
- data/test/data/brian.xrds +0 -16
- data/test/data/brianellin.mylid.xrds +0 -42
- data/test/dh.rb +0 -20
- data/test/extensions.rb +0 -30
- data/test/linkparse.rb +0 -305
- data/test/runtests.rb +0 -22
- data/test/server2.rb +0 -1053
- data/test/service.rb +0 -47
- data/test/storetestcase.rb +0 -172
- data/test/teststore.rb +0 -47
- data/test/trustroot.rb +0 -117
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
require 'openid/extension'
|
|
2
|
+
require 'openid/util'
|
|
3
|
+
require 'openid/message'
|
|
4
|
+
|
|
5
|
+
module OpenID
|
|
6
|
+
module SReg
|
|
7
|
+
DATA_FIELDS = {
|
|
8
|
+
'fullname'=>'Full Name',
|
|
9
|
+
'nickname'=>'Nickname',
|
|
10
|
+
'dob'=>'Date of Birth',
|
|
11
|
+
'email'=>'E-mail Address',
|
|
12
|
+
'gender'=>'Gender',
|
|
13
|
+
'postcode'=>'Postal Code',
|
|
14
|
+
'country'=>'Country',
|
|
15
|
+
'language'=>'Language',
|
|
16
|
+
'timezone'=>'Time Zone',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
NS_URI_1_0 = 'http://openid.net/sreg/1.0'
|
|
20
|
+
NS_URI_1_1 = 'http://openid.net/extensions/sreg/1.1'
|
|
21
|
+
NS_URI = NS_URI_1_1
|
|
22
|
+
|
|
23
|
+
begin
|
|
24
|
+
Message.register_namespace_alias(NS_URI_1_1, 'sreg')
|
|
25
|
+
rescue NamespaceAliasRegistrationError => e
|
|
26
|
+
Util.log(e)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# raise ArgumentError if fieldname is not in the defined sreg fields
|
|
30
|
+
def OpenID.check_sreg_field_name(fieldname)
|
|
31
|
+
unless DATA_FIELDS.member? fieldname
|
|
32
|
+
raise ArgumentError, "#{fieldname} is not a defined simple registration field"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Does the given endpoint advertise support for simple registration?
|
|
37
|
+
def OpenID.supports_sreg?(endpoint)
|
|
38
|
+
endpoint.uses_extension(NS_URI_1_1) || endpoint.uses_extension(NS_URI_1_0)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Extract the simple registration namespace URI from the given
|
|
42
|
+
# OpenID message. Handles OpenID 1 and 2, as well as both sreg
|
|
43
|
+
# namespace URIs found in the wild, as well as missing namespace
|
|
44
|
+
# definitions (for OpenID 1)
|
|
45
|
+
def OpenID.get_sreg_ns(message)
|
|
46
|
+
[NS_URI_1_1, NS_URI_1_0].each{|ns|
|
|
47
|
+
if message.namespaces.get_alias(ns)
|
|
48
|
+
return ns
|
|
49
|
+
end
|
|
50
|
+
}
|
|
51
|
+
# try to add an alias, since we didn't find one
|
|
52
|
+
ns = NS_URI_1_1
|
|
53
|
+
begin
|
|
54
|
+
message.namespaces.add_alias(ns, 'sreg')
|
|
55
|
+
rescue IndexError
|
|
56
|
+
raise NamespaceError
|
|
57
|
+
end
|
|
58
|
+
return ns
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# The simple registration namespace was not found and could not
|
|
62
|
+
# be created using the expected name (there's another extension
|
|
63
|
+
# using the name 'sreg')
|
|
64
|
+
#
|
|
65
|
+
# This is not <em>illegal</em>, for OpenID 2, although it probably
|
|
66
|
+
# indicates a problem, since it's not expected that other extensions
|
|
67
|
+
# will re-use the alias that is in use for OpenID 1.
|
|
68
|
+
#
|
|
69
|
+
# If this is an OpenID 1 request, then there is no recourse. This
|
|
70
|
+
# should not happen unless some code has modified the namespaces for
|
|
71
|
+
# the message that is being processed.
|
|
72
|
+
class NamespaceError < ArgumentError
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# An object to hold the state of a simple registration request.
|
|
76
|
+
class Request < Extension
|
|
77
|
+
attr_reader :optional, :required, :ns_uri
|
|
78
|
+
attr_accessor :policy_url
|
|
79
|
+
def initialize(required = nil, optional = nil, policy_url = nil, ns_uri = NS_URI)
|
|
80
|
+
super()
|
|
81
|
+
|
|
82
|
+
@policy_url = policy_url
|
|
83
|
+
@ns_uri = ns_uri
|
|
84
|
+
@ns_alias = 'sreg'
|
|
85
|
+
@required = []
|
|
86
|
+
@optional = []
|
|
87
|
+
|
|
88
|
+
if required
|
|
89
|
+
request_fields(required, true, true)
|
|
90
|
+
end
|
|
91
|
+
if optional
|
|
92
|
+
request_fields(optional, false, true)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Create a simple registration request that contains the
|
|
97
|
+
# fields that were requested in the OpenID request with the
|
|
98
|
+
# given arguments
|
|
99
|
+
# Takes an OpenID::CheckIDRequest, returns an OpenID::Sreg::Request
|
|
100
|
+
# return nil if the extension was not requested.
|
|
101
|
+
def self.from_openid_request(request)
|
|
102
|
+
# Since we're going to mess with namespace URI mapping, don't
|
|
103
|
+
# mutate the object that was passed in.
|
|
104
|
+
message = request.message.copy
|
|
105
|
+
ns_uri = OpenID::get_sreg_ns(message)
|
|
106
|
+
args = message.get_args(ns_uri)
|
|
107
|
+
return nil if args == {}
|
|
108
|
+
req = new(nil,nil,nil,ns_uri)
|
|
109
|
+
req.parse_extension_args(args)
|
|
110
|
+
return req
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Parse the unqualified simple registration request
|
|
114
|
+
# parameters and add them to this object.
|
|
115
|
+
#
|
|
116
|
+
# This method is essentially the inverse of
|
|
117
|
+
# getExtensionArgs. This method restores the serialized simple
|
|
118
|
+
# registration request fields.
|
|
119
|
+
#
|
|
120
|
+
# If you are extracting arguments from a standard OpenID
|
|
121
|
+
# checkid_* request, you probably want to use fromOpenIDRequest,
|
|
122
|
+
# which will extract the sreg namespace and arguments from the
|
|
123
|
+
# OpenID request. This method is intended for cases where the
|
|
124
|
+
# OpenID server needs more control over how the arguments are
|
|
125
|
+
# parsed than that method provides.
|
|
126
|
+
def parse_extension_args(args, strict = false)
|
|
127
|
+
required_items = args['required']
|
|
128
|
+
unless required_items.nil? or required_items.empty?
|
|
129
|
+
required_items.split(',').each{|field_name|
|
|
130
|
+
begin
|
|
131
|
+
request_field(field_name, true, strict)
|
|
132
|
+
rescue ArgumentError
|
|
133
|
+
raise if strict
|
|
134
|
+
end
|
|
135
|
+
}
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
optional_items = args['optional']
|
|
139
|
+
unless optional_items.nil? or optional_items.empty?
|
|
140
|
+
optional_items.split(',').each{|field_name|
|
|
141
|
+
begin
|
|
142
|
+
request_field(field_name, false, strict)
|
|
143
|
+
rescue ArgumentError
|
|
144
|
+
raise if strict
|
|
145
|
+
end
|
|
146
|
+
}
|
|
147
|
+
end
|
|
148
|
+
@policy_url = args['policy_url']
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# A list of all of the simple registration fields that were
|
|
152
|
+
# requested, whether they were required or optional.
|
|
153
|
+
def all_requested_fields
|
|
154
|
+
@required + @optional
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Have any simple registration fields been requested?
|
|
158
|
+
def were_fields_requested?
|
|
159
|
+
!all_requested_fields.empty?
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Request the specified field from the OpenID user
|
|
163
|
+
# field_name: the unqualified simple registration field name
|
|
164
|
+
# required: whether the given field should be presented
|
|
165
|
+
# to the user as being a required to successfully complete
|
|
166
|
+
# the request
|
|
167
|
+
# strict: whether to raise an exception when a field is
|
|
168
|
+
# added to a request more than once
|
|
169
|
+
# Raises ArgumentError if the field_name is not a simple registration
|
|
170
|
+
# field, or if strict is set and a field is added more than once
|
|
171
|
+
def request_field(field_name, required=false, strict=false)
|
|
172
|
+
OpenID::check_sreg_field_name(field_name)
|
|
173
|
+
|
|
174
|
+
if strict
|
|
175
|
+
if (@required + @optional).member? field_name
|
|
176
|
+
raise ArgumentError, 'That field has already been requested'
|
|
177
|
+
end
|
|
178
|
+
else
|
|
179
|
+
return if @required.member? field_name
|
|
180
|
+
if @optional.member? field_name
|
|
181
|
+
if required
|
|
182
|
+
@optional.delete field_name
|
|
183
|
+
else
|
|
184
|
+
return
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
if required
|
|
189
|
+
@required << field_name
|
|
190
|
+
else
|
|
191
|
+
@optional << field_name
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Add the given list of fields to the request.
|
|
196
|
+
def request_fields(field_names, required = false, strict = false)
|
|
197
|
+
raise ArgumentError unless field_names[0].is_a?(String)
|
|
198
|
+
field_names.each{|fn|request_field(fn, required, strict)}
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Get a hash of unqualified simple registration arguments
|
|
202
|
+
# representing this request.
|
|
203
|
+
# This method is essentially the inverse of parse_extension_args.
|
|
204
|
+
# This method serializes the simple registration request fields.
|
|
205
|
+
def get_extension_args
|
|
206
|
+
args = {}
|
|
207
|
+
args['required'] = @required.join(',') unless @required.empty?
|
|
208
|
+
args['optional'] = @optional.join(',') unless @optional.empty?
|
|
209
|
+
args['policy_url'] = @policy_url unless @policy_url.nil?
|
|
210
|
+
return args
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def member?(field_name)
|
|
214
|
+
all_requested_fields.member?(field_name)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Represents the data returned in a simple registration response
|
|
220
|
+
# inside of an OpenID id_res response. This object will be
|
|
221
|
+
# created by the OpenID server, added to the id_res response
|
|
222
|
+
# object, and then extracted from the id_res message by the Consumer.
|
|
223
|
+
class Response < Extension
|
|
224
|
+
attr_reader :ns_uri, :data
|
|
225
|
+
|
|
226
|
+
def initialize(data = {}, ns_uri=NS_URI)
|
|
227
|
+
@ns_alias = 'sreg'
|
|
228
|
+
@data = data
|
|
229
|
+
@ns_uri = ns_uri
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Take a Request and a hash of simple registration
|
|
233
|
+
# values and create a Response object containing that data.
|
|
234
|
+
def self.extract_response(request, data)
|
|
235
|
+
arf = request.all_requested_fields
|
|
236
|
+
resp_data = data.reject{|k,v| !arf.member?(k) || v.nil? }
|
|
237
|
+
new(resp_data, request.ns_uri)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Create an Response object from an
|
|
241
|
+
# OpenID::Consumer::SuccessResponse from consumer.complete
|
|
242
|
+
# If you set the signed_only parameter to false, unsigned data from
|
|
243
|
+
# the id_res message from the server will be processed.
|
|
244
|
+
def self.from_success_response(success_response, signed_only = true)
|
|
245
|
+
ns_uri = OpenID::get_sreg_ns(success_response.message)
|
|
246
|
+
if signed_only
|
|
247
|
+
args = success_response.get_signed_ns(ns_uri)
|
|
248
|
+
else
|
|
249
|
+
args = success_response.message.get_args(ns_uri)
|
|
250
|
+
end
|
|
251
|
+
args.reject!{|k,v| !DATA_FIELDS.member?(k) }
|
|
252
|
+
new(args, ns_uri)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Get the fields to put in the simple registration namespace
|
|
256
|
+
# when adding them to an id_res message.
|
|
257
|
+
def get_extension_args
|
|
258
|
+
return @data
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Read-only hashlike interface.
|
|
262
|
+
# Raises an exception if the field name is bad
|
|
263
|
+
def [](field_name)
|
|
264
|
+
OpenID::check_sreg_field_name(field_name)
|
|
265
|
+
data[field_name]
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def empty?
|
|
269
|
+
@data.empty?
|
|
270
|
+
end
|
|
271
|
+
# XXX is there more to a hashlike interface I should add?
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
data/lib/openid/fetchers.rb
CHANGED
|
@@ -1,20 +1,15 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require 'net/http'
|
|
2
|
+
require 'openid'
|
|
3
|
+
require 'openid/util'
|
|
3
4
|
|
|
4
|
-
# Try to use net/https, falling back to no SSL support if it is not available
|
|
5
5
|
begin
|
|
6
6
|
require 'net/https'
|
|
7
7
|
rescue LoadError
|
|
8
|
-
OpenID::Util.log('WARNING: no SSL support found. Will not be able
|
|
9
|
-
|
|
10
|
-
require 'net/http'
|
|
11
|
-
else
|
|
12
|
-
HAS_OPENSSL = true
|
|
8
|
+
OpenID::Util.log('WARNING: no SSL support found. Will not be able ' +
|
|
9
|
+
'to fetch HTTPS URLs!')
|
|
10
|
+
require 'net/http'
|
|
13
11
|
end
|
|
14
12
|
|
|
15
|
-
# Not all versions of Ruby 1.8.4 have the version of post_connection_check
|
|
16
|
-
# that properly handles wildcard hostnames. This version of
|
|
17
|
-
# post_connection_check is copied from post April 2006 release of 1.8.4.
|
|
18
13
|
module Net
|
|
19
14
|
class HTTP
|
|
20
15
|
def post_connection_check(hostname)
|
|
@@ -47,114 +42,158 @@ module Net
|
|
|
47
42
|
end
|
|
48
43
|
|
|
49
44
|
module OpenID
|
|
45
|
+
# Our HTTPResponse class extends Net::HTTPResponse with an additional
|
|
46
|
+
# method, final_url.
|
|
47
|
+
class HTTPResponse
|
|
48
|
+
attr_accessor :final_url
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
class Fetcher
|
|
50
|
+
attr_accessor :_response
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
def self._from_net_response(response, final_url, headers=nil)
|
|
53
|
+
me = self.new
|
|
54
|
+
me._response = response
|
|
55
|
+
me.final_url = final_url
|
|
56
|
+
return me
|
|
58
57
|
end
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
|
|
59
|
+
def method_missing(method, *args)
|
|
60
|
+
@_response.send(method, *args)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def body=(s)
|
|
64
|
+
@_response.instance_variable_set('@body', s)
|
|
65
|
+
# XXX Hack to work around ruby's HTTP library behavior. @body
|
|
66
|
+
# is only returned if it has been read from the response
|
|
67
|
+
# object's socket, but since we're not using a socket in this
|
|
68
|
+
# case, we need to set the @read flag to true to avoid a bug in
|
|
69
|
+
# Net::HTTPResponse.stream_check when @socket is nil.
|
|
70
|
+
@_response.instance_variable_set('@read', true)
|
|
64
71
|
end
|
|
65
|
-
|
|
66
72
|
end
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
|
|
74
|
+
class FetchingError < OpenIDError
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class HTTPRedirectLimitReached < FetchingError
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
class SSLFetchingError < FetchingError
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
@fetcher = nil
|
|
84
|
+
|
|
85
|
+
def self.fetch(url, body=nil, headers=nil,
|
|
86
|
+
redirect_limit=StandardFetcher::REDIRECT_LIMIT)
|
|
87
|
+
return fetcher.fetch(url, body, headers, redirect_limit)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def self.fetcher
|
|
91
|
+
if @fetcher.nil?
|
|
92
|
+
@fetcher = StandardFetcher.new
|
|
77
93
|
end
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
94
|
+
|
|
95
|
+
return @fetcher
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def self.fetcher=(fetcher)
|
|
99
|
+
@fetcher = fetcher
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
class StandardFetcher
|
|
103
|
+
|
|
104
|
+
USER_AGENT = "ruby-openid/#{OpenID::VERSION} (#{PLATFORM})"
|
|
105
|
+
|
|
106
|
+
REDIRECT_LIMIT = 5
|
|
107
|
+
|
|
108
|
+
attr_accessor :ca_file
|
|
109
|
+
|
|
110
|
+
def initialize
|
|
111
|
+
@ca_file = nil
|
|
86
112
|
end
|
|
87
|
-
|
|
88
|
-
def
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
113
|
+
|
|
114
|
+
def supports_ssl?(conn)
|
|
115
|
+
return conn.respond_to?(:use_ssl=)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def make_http(uri)
|
|
119
|
+
Net::HTTP.new(uri.host, uri.port)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def set_verified(conn, verify)
|
|
123
|
+
if verify
|
|
124
|
+
conn.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
96
125
|
else
|
|
97
|
-
|
|
126
|
+
conn.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
98
127
|
end
|
|
99
128
|
end
|
|
100
129
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
130
|
+
def make_connection(uri)
|
|
131
|
+
conn = make_http(uri)
|
|
132
|
+
|
|
133
|
+
if !conn.is_a?(Net::HTTP)
|
|
134
|
+
raise RuntimeError, sprintf("Expected Net::HTTP object from make_http; got %s",
|
|
135
|
+
conn.class)
|
|
136
|
+
end
|
|
108
137
|
|
|
109
138
|
if uri.scheme == 'https'
|
|
139
|
+
if supports_ssl?(conn)
|
|
110
140
|
|
|
111
|
-
|
|
112
|
-
http.use_ssl = true
|
|
141
|
+
conn.use_ssl = true
|
|
113
142
|
|
|
114
|
-
if @
|
|
115
|
-
|
|
116
|
-
|
|
143
|
+
if @ca_file
|
|
144
|
+
set_verified(conn, true)
|
|
145
|
+
conn.ca_file = @ca_file
|
|
117
146
|
else
|
|
118
|
-
|
|
119
|
-
|
|
147
|
+
Util.log("WARNING: making https request to #{uri} without verifying " +
|
|
148
|
+
"server certificate; no CA path was specified.")
|
|
149
|
+
set_verified(conn, false)
|
|
120
150
|
end
|
|
121
|
-
|
|
151
|
+
else
|
|
152
|
+
raise RuntimeError, "SSL support not found; cannot fetch #{uri}"
|
|
122
153
|
end
|
|
123
|
-
|
|
124
154
|
end
|
|
125
155
|
|
|
126
|
-
return
|
|
156
|
+
return conn
|
|
127
157
|
end
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
158
|
+
|
|
159
|
+
def fetch(url, body=nil, headers=nil, redirect_limit=REDIRECT_LIMIT)
|
|
160
|
+
unparsed_url = url.dup
|
|
161
|
+
url = URI::parse(url)
|
|
162
|
+
|
|
163
|
+
headers ||= {}
|
|
164
|
+
headers['User-agent'] ||= USER_AGENT
|
|
165
|
+
|
|
166
|
+
conn = make_connection(url)
|
|
167
|
+
response = nil
|
|
168
|
+
|
|
135
169
|
begin
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
170
|
+
response = conn.start {
|
|
171
|
+
# Check the certificate against the URL's hostname
|
|
172
|
+
if supports_ssl?(conn) and conn.use_ssl?
|
|
173
|
+
conn.post_connection_check(url.host)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
if body.nil?
|
|
177
|
+
conn.request_get(url.request_uri, headers)
|
|
178
|
+
else
|
|
179
|
+
headers["Content-type"] ||= "application/x-www-form-urlencoded"
|
|
180
|
+
conn.request_post(url.request_uri, body, headers)
|
|
143
181
|
end
|
|
144
182
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
183
|
+
rescue OpenSSL::SSL::SSLError => why
|
|
184
|
+
raise SSLFetchingError, "Error connecting to SSL URL #{url}: #{why}"
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
case response
|
|
188
|
+
when Net::HTTPRedirection
|
|
189
|
+
if redirect_limit <= 0
|
|
190
|
+
raise HTTPRedirectLimitReached.new(
|
|
191
|
+
"Too many redirects, not fetching #{response['location']}")
|
|
154
192
|
end
|
|
193
|
+
return fetch(response['location'], body, headers, redirect_limit - 1)
|
|
194
|
+
else
|
|
195
|
+
return HTTPResponse._from_net_response(response, unparsed_url)
|
|
155
196
|
end
|
|
156
197
|
end
|
|
157
|
-
|
|
158
198
|
end
|
|
159
|
-
|
|
160
199
|
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
|
|
2
|
+
module OpenID
|
|
3
|
+
|
|
4
|
+
module Util
|
|
5
|
+
|
|
6
|
+
def Util.seq_to_kv(seq, strict=false)
|
|
7
|
+
# Represent a sequence of pairs of strings as newline-terminated
|
|
8
|
+
# key:value pairs. The pairs are generated in the order given.
|
|
9
|
+
#
|
|
10
|
+
# @param seq: The pairs
|
|
11
|
+
#
|
|
12
|
+
# returns a string representation of the sequence
|
|
13
|
+
err = lambda { |msg|
|
|
14
|
+
msg = "seq_to_kv warning: #{msg}: #{seq.inspect}"
|
|
15
|
+
if strict
|
|
16
|
+
raise ArgumentError, msg
|
|
17
|
+
else
|
|
18
|
+
Util.log(msg)
|
|
19
|
+
end
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
lines = []
|
|
23
|
+
seq.each { |k, v|
|
|
24
|
+
if !k.is_a?(String)
|
|
25
|
+
err.call("Converting key to string: #{k.inspect}")
|
|
26
|
+
k = k.to_s
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if !k.index("\n").nil?
|
|
30
|
+
raise ArgumentError, "Invalid input for seq_to_kv: key contains newline: #{k.inspect}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if !k.index(":").nil?
|
|
34
|
+
raise ArgumentError, "Invalid input for seq_to_kv: key contains colon: #{k.inspect}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if k.strip() != k
|
|
38
|
+
err.call("Key has whitespace at beginning or end: #{k.inspect}")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if !v.is_a?(String)
|
|
42
|
+
err.call("Converting value to string: #{v.inspect}")
|
|
43
|
+
v = v.to_s
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if !v.index("\n").nil?
|
|
47
|
+
raise ArgumentError, "Invalid input for seq_to_kv: value contains newline: #{v.inspect}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
if v.strip() != v
|
|
51
|
+
err.call("Value has whitespace at beginning or end: #{v.inspect}")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
lines << k + ":" + v + "\n"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return lines.join("")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def Util.kv_to_seq(data, strict=false)
|
|
61
|
+
# After one parse, seq_to_kv and kv_to_seq are inverses, with no
|
|
62
|
+
# warnings:
|
|
63
|
+
#
|
|
64
|
+
# seq = kv_to_seq(s)
|
|
65
|
+
# seq_to_kv(kv_to_seq(seq)) == seq
|
|
66
|
+
err = lambda { |msg|
|
|
67
|
+
msg = "kv_to_seq warning: #{msg}: #{data.inspect}"
|
|
68
|
+
if strict
|
|
69
|
+
raise ArgumentError, msg
|
|
70
|
+
else
|
|
71
|
+
Util.log(msg)
|
|
72
|
+
end
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
lines = data.split("\n")
|
|
76
|
+
if data.length == 0
|
|
77
|
+
return []
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
if data[-1].chr != "\n"
|
|
81
|
+
err.call("Does not end in a newline")
|
|
82
|
+
# We don't expect the last element of lines to be an empty
|
|
83
|
+
# string because split() doesn't behave that way.
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
pairs = []
|
|
87
|
+
line_num = 0
|
|
88
|
+
lines.each { |line|
|
|
89
|
+
line_num += 1
|
|
90
|
+
|
|
91
|
+
# Ignore blank lines
|
|
92
|
+
if line.strip() == ""
|
|
93
|
+
next
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
pair = line.split(':', 2)
|
|
97
|
+
if pair.length == 2
|
|
98
|
+
k, v = pair
|
|
99
|
+
k_s = k.strip()
|
|
100
|
+
if k_s != k
|
|
101
|
+
msg = "In line #{line_num}, ignoring leading or trailing whitespace in key #{k.inspect}"
|
|
102
|
+
err.call(msg)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
if k_s.length == 0
|
|
106
|
+
err.call("In line #{line_num}, got empty key")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
v_s = v.strip()
|
|
110
|
+
if v_s != v
|
|
111
|
+
msg = "In line #{line_num}, ignoring leading or trailing whitespace in value #{v.inspect}"
|
|
112
|
+
err.call(msg)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
pairs << [k_s, v_s]
|
|
116
|
+
else
|
|
117
|
+
err.call("Line #{line_num} does not contain a colon")
|
|
118
|
+
end
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return pairs
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def Util.dict_to_kv(d)
|
|
125
|
+
return seq_to_kv(d.entries.sort)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def Util.kv_to_dict(s)
|
|
129
|
+
seq = kv_to_seq(s)
|
|
130
|
+
return Hash[*seq.flatten]
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|