entp-ruby-openid 2.2

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.
Files changed (200) hide show
  1. data/CHANGELOG +215 -0
  2. data/INSTALL +47 -0
  3. data/LICENSE +210 -0
  4. data/NOTICE +2 -0
  5. data/README +85 -0
  6. data/UPGRADE +127 -0
  7. data/admin/runtests.rb +45 -0
  8. data/examples/README +32 -0
  9. data/examples/active_record_openid_store/README +58 -0
  10. data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +24 -0
  11. data/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb +26 -0
  12. data/examples/active_record_openid_store/init.rb +8 -0
  13. data/examples/active_record_openid_store/lib/association.rb +10 -0
  14. data/examples/active_record_openid_store/lib/nonce.rb +3 -0
  15. data/examples/active_record_openid_store/lib/open_id_setting.rb +4 -0
  16. data/examples/active_record_openid_store/lib/openid_ar_store.rb +57 -0
  17. data/examples/active_record_openid_store/test/store_test.rb +212 -0
  18. data/examples/discover +49 -0
  19. data/examples/rails_openid/README +153 -0
  20. data/examples/rails_openid/Rakefile +10 -0
  21. data/examples/rails_openid/app/controllers/application.rb +4 -0
  22. data/examples/rails_openid/app/controllers/consumer_controller.rb +125 -0
  23. data/examples/rails_openid/app/controllers/login_controller.rb +45 -0
  24. data/examples/rails_openid/app/controllers/server_controller.rb +265 -0
  25. data/examples/rails_openid/app/helpers/application_helper.rb +3 -0
  26. data/examples/rails_openid/app/helpers/login_helper.rb +2 -0
  27. data/examples/rails_openid/app/helpers/server_helper.rb +9 -0
  28. data/examples/rails_openid/app/views/consumer/index.rhtml +81 -0
  29. data/examples/rails_openid/app/views/layouts/server.rhtml +68 -0
  30. data/examples/rails_openid/app/views/login/index.rhtml +56 -0
  31. data/examples/rails_openid/app/views/server/decide.rhtml +26 -0
  32. data/examples/rails_openid/config/boot.rb +19 -0
  33. data/examples/rails_openid/config/database.yml +74 -0
  34. data/examples/rails_openid/config/environment.rb +54 -0
  35. data/examples/rails_openid/config/environments/development.rb +19 -0
  36. data/examples/rails_openid/config/environments/production.rb +19 -0
  37. data/examples/rails_openid/config/environments/test.rb +19 -0
  38. data/examples/rails_openid/config/routes.rb +24 -0
  39. data/examples/rails_openid/doc/README_FOR_APP +2 -0
  40. data/examples/rails_openid/public/404.html +8 -0
  41. data/examples/rails_openid/public/500.html +8 -0
  42. data/examples/rails_openid/public/dispatch.cgi +12 -0
  43. data/examples/rails_openid/public/dispatch.fcgi +26 -0
  44. data/examples/rails_openid/public/dispatch.rb +12 -0
  45. data/examples/rails_openid/public/favicon.ico +0 -0
  46. data/examples/rails_openid/public/images/openid_login_bg.gif +0 -0
  47. data/examples/rails_openid/public/javascripts/controls.js +750 -0
  48. data/examples/rails_openid/public/javascripts/dragdrop.js +584 -0
  49. data/examples/rails_openid/public/javascripts/effects.js +854 -0
  50. data/examples/rails_openid/public/javascripts/prototype.js +1785 -0
  51. data/examples/rails_openid/public/robots.txt +1 -0
  52. data/examples/rails_openid/script/about +3 -0
  53. data/examples/rails_openid/script/breakpointer +3 -0
  54. data/examples/rails_openid/script/console +3 -0
  55. data/examples/rails_openid/script/destroy +3 -0
  56. data/examples/rails_openid/script/generate +3 -0
  57. data/examples/rails_openid/script/performance/benchmarker +3 -0
  58. data/examples/rails_openid/script/performance/profiler +3 -0
  59. data/examples/rails_openid/script/plugin +3 -0
  60. data/examples/rails_openid/script/process/reaper +3 -0
  61. data/examples/rails_openid/script/process/spawner +3 -0
  62. data/examples/rails_openid/script/process/spinner +3 -0
  63. data/examples/rails_openid/script/runner +3 -0
  64. data/examples/rails_openid/script/server +3 -0
  65. data/examples/rails_openid/test/functional/login_controller_test.rb +18 -0
  66. data/examples/rails_openid/test/functional/server_controller_test.rb +18 -0
  67. data/examples/rails_openid/test/test_helper.rb +28 -0
  68. data/lib/hmac/hmac.rb +112 -0
  69. data/lib/hmac/sha1.rb +11 -0
  70. data/lib/hmac/sha2.rb +25 -0
  71. data/lib/openid.rb +22 -0
  72. data/lib/openid/association.rb +249 -0
  73. data/lib/openid/consumer.rb +395 -0
  74. data/lib/openid/consumer/associationmanager.rb +344 -0
  75. data/lib/openid/consumer/checkid_request.rb +186 -0
  76. data/lib/openid/consumer/discovery.rb +497 -0
  77. data/lib/openid/consumer/discovery_manager.rb +123 -0
  78. data/lib/openid/consumer/html_parse.rb +134 -0
  79. data/lib/openid/consumer/idres.rb +523 -0
  80. data/lib/openid/consumer/responses.rb +150 -0
  81. data/lib/openid/cryptutil.rb +115 -0
  82. data/lib/openid/dh.rb +89 -0
  83. data/lib/openid/extension.rb +39 -0
  84. data/lib/openid/extensions/ax.rb +539 -0
  85. data/lib/openid/extensions/oauth.rb +91 -0
  86. data/lib/openid/extensions/pape.rb +179 -0
  87. data/lib/openid/extensions/sreg.rb +277 -0
  88. data/lib/openid/extras.rb +11 -0
  89. data/lib/openid/fetchers.rb +258 -0
  90. data/lib/openid/kvform.rb +136 -0
  91. data/lib/openid/kvpost.rb +58 -0
  92. data/lib/openid/message.rb +553 -0
  93. data/lib/openid/protocolerror.rb +12 -0
  94. data/lib/openid/server.rb +1544 -0
  95. data/lib/openid/store.rb +10 -0
  96. data/lib/openid/store/filesystem.rb +272 -0
  97. data/lib/openid/store/interface.rb +75 -0
  98. data/lib/openid/store/memcache.rb +109 -0
  99. data/lib/openid/store/memory.rb +84 -0
  100. data/lib/openid/store/nonce.rb +68 -0
  101. data/lib/openid/trustroot.rb +349 -0
  102. data/lib/openid/urinorm.rb +75 -0
  103. data/lib/openid/util.rb +119 -0
  104. data/lib/openid/version.rb +3 -0
  105. data/lib/openid/yadis.rb +15 -0
  106. data/lib/openid/yadis/accept.rb +148 -0
  107. data/lib/openid/yadis/constants.rb +21 -0
  108. data/lib/openid/yadis/discovery.rb +153 -0
  109. data/lib/openid/yadis/filters.rb +205 -0
  110. data/lib/openid/yadis/htmltokenizer.rb +305 -0
  111. data/lib/openid/yadis/parsehtml.rb +45 -0
  112. data/lib/openid/yadis/services.rb +42 -0
  113. data/lib/openid/yadis/xrds.rb +155 -0
  114. data/lib/openid/yadis/xri.rb +90 -0
  115. data/lib/openid/yadis/xrires.rb +91 -0
  116. data/test/data/test_discover/openid_utf8.html +11 -0
  117. data/test/support/test_data_mixin.rb +127 -0
  118. data/test/support/test_util.rb +53 -0
  119. data/test/support/yadis_data.rb +131 -0
  120. data/test/support/yadis_data/accept.txt +124 -0
  121. data/test/support/yadis_data/dh.txt +29 -0
  122. data/test/support/yadis_data/example-xrds.xml +14 -0
  123. data/test/support/yadis_data/linkparse.txt +587 -0
  124. data/test/support/yadis_data/n2b64 +650 -0
  125. data/test/support/yadis_data/test1-discover.txt +137 -0
  126. data/test/support/yadis_data/test1-parsehtml.txt +152 -0
  127. data/test/support/yadis_data/test_discover/malformed_meta_tag.html +19 -0
  128. data/test/support/yadis_data/test_discover/openid.html +11 -0
  129. data/test/support/yadis_data/test_discover/openid2.html +11 -0
  130. data/test/support/yadis_data/test_discover/openid2_xrds.xml +12 -0
  131. data/test/support/yadis_data/test_discover/openid2_xrds_no_local_id.xml +11 -0
  132. data/test/support/yadis_data/test_discover/openid_1_and_2.html +11 -0
  133. data/test/support/yadis_data/test_discover/openid_1_and_2_xrds.xml +16 -0
  134. data/test/support/yadis_data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
  135. data/test/support/yadis_data/test_discover/openid_and_yadis.html +12 -0
  136. data/test/support/yadis_data/test_discover/openid_no_delegate.html +10 -0
  137. data/test/support/yadis_data/test_discover/openid_utf8.html +11 -0
  138. data/test/support/yadis_data/test_discover/yadis_0entries.xml +12 -0
  139. data/test/support/yadis_data/test_discover/yadis_2_bad_local_id.xml +15 -0
  140. data/test/support/yadis_data/test_discover/yadis_2entries_delegate.xml +22 -0
  141. data/test/support/yadis_data/test_discover/yadis_2entries_idp.xml +21 -0
  142. data/test/support/yadis_data/test_discover/yadis_another_delegate.xml +14 -0
  143. data/test/support/yadis_data/test_discover/yadis_idp.xml +12 -0
  144. data/test/support/yadis_data/test_discover/yadis_idp_delegate.xml +13 -0
  145. data/test/support/yadis_data/test_discover/yadis_no_delegate.xml +11 -0
  146. data/test/support/yadis_data/test_xrds/=j3h.2007.11.14.xrds +25 -0
  147. data/test/support/yadis_data/test_xrds/README +12 -0
  148. data/test/support/yadis_data/test_xrds/delegated-20060809-r1.xrds +34 -0
  149. data/test/support/yadis_data/test_xrds/delegated-20060809-r2.xrds +34 -0
  150. data/test/support/yadis_data/test_xrds/delegated-20060809.xrds +34 -0
  151. data/test/support/yadis_data/test_xrds/no-xrd.xml +7 -0
  152. data/test/support/yadis_data/test_xrds/not-xrds.xml +2 -0
  153. data/test/support/yadis_data/test_xrds/prefixsometimes.xrds +34 -0
  154. data/test/support/yadis_data/test_xrds/ref.xrds +109 -0
  155. data/test/support/yadis_data/test_xrds/sometimesprefix.xrds +34 -0
  156. data/test/support/yadis_data/test_xrds/spoof1.xrds +25 -0
  157. data/test/support/yadis_data/test_xrds/spoof2.xrds +25 -0
  158. data/test/support/yadis_data/test_xrds/spoof3.xrds +37 -0
  159. data/test/support/yadis_data/test_xrds/status222.xrds +9 -0
  160. data/test/support/yadis_data/test_xrds/subsegments.xrds +58 -0
  161. data/test/support/yadis_data/test_xrds/valid-populated-xrds.xml +39 -0
  162. data/test/support/yadis_data/trustroot.txt +153 -0
  163. data/test/support/yadis_data/urinorm.txt +79 -0
  164. data/test/test_accept.rb +170 -0
  165. data/test/test_association.rb +268 -0
  166. data/test/test_associationmanager.rb +918 -0
  167. data/test/test_ax.rb +690 -0
  168. data/test/test_checkid_request.rb +293 -0
  169. data/test/test_consumer.rb +260 -0
  170. data/test/test_cryptutil.rb +119 -0
  171. data/test/test_dh.rb +85 -0
  172. data/test/test_discover.rb +848 -0
  173. data/test/test_discovery_manager.rb +259 -0
  174. data/test/test_extension.rb +46 -0
  175. data/test/test_extras.rb +35 -0
  176. data/test/test_fetchers.rb +554 -0
  177. data/test/test_filters.rb +269 -0
  178. data/test/test_helper.rb +4 -0
  179. data/test/test_idres.rb +961 -0
  180. data/test/test_kvform.rb +164 -0
  181. data/test/test_kvpost.rb +64 -0
  182. data/test/test_linkparse.rb +100 -0
  183. data/test/test_message.rb +1115 -0
  184. data/test/test_nonce.rb +89 -0
  185. data/test/test_oauth.rb +176 -0
  186. data/test/test_openid_yadis.rb +177 -0
  187. data/test/test_pape.rb +248 -0
  188. data/test/test_parsehtml.rb +79 -0
  189. data/test/test_responses.rb +63 -0
  190. data/test/test_server.rb +2455 -0
  191. data/test/test_sreg.rb +479 -0
  192. data/test/test_stores.rb +292 -0
  193. data/test/test_trustroot.rb +111 -0
  194. data/test/test_urinorm.rb +34 -0
  195. data/test/test_util.rb +145 -0
  196. data/test/test_xrds.rb +167 -0
  197. data/test/test_xri.rb +48 -0
  198. data/test/test_xrires.rb +67 -0
  199. data/test/test_yadis_discovery.rb +218 -0
  200. metadata +268 -0
@@ -0,0 +1,91 @@
1
+ # An implementation of the OpenID OAuth Extension
2
+ # Extension 1.0
3
+ # see: http://openid.net/specs/
4
+
5
+ require 'openid/extension'
6
+
7
+ module OpenID
8
+
9
+ module OAuth
10
+ NS_URI = "http://specs.openid.net/extensions/oauth/1.0"
11
+ # An OAuth token request, sent from a relying
12
+ # party to a provider
13
+ class Request < Extension
14
+ attr_accessor :consumer, :scope, :ns_alias, :ns_uri
15
+ def initialize(consumer=nil, scope=nil)
16
+ @ns_alias = 'oauth'
17
+ @ns_uri = NS_URI
18
+ @consumer = consumer
19
+ @scope = scope
20
+ end
21
+
22
+
23
+ def get_extension_args
24
+ ns_args = {}
25
+ ns_args['consumer'] = @consumer if @consumer
26
+ ns_args['scope'] = @scope if @scope
27
+ return ns_args
28
+ end
29
+
30
+ # Instantiate a Request object from the arguments in a
31
+ # checkid_* OpenID message
32
+ # return nil if the extension was not requested.
33
+ def self.from_openid_request(oid_req)
34
+ oauth_req = new
35
+ args = oid_req.message.get_args(NS_URI)
36
+ if args == {}
37
+ return nil
38
+ end
39
+ oauth_req.parse_extension_args(args)
40
+ return oauth_req
41
+ end
42
+
43
+ # Set the state of this request to be that expressed in these
44
+ # OAuth arguments
45
+ def parse_extension_args(args)
46
+ @consumer = args["consumer"]
47
+ @scope = args["scope"]
48
+ end
49
+
50
+ end
51
+
52
+ # A OAuth request token response, sent from a provider
53
+ # to a relying party
54
+ class Response < Extension
55
+ attr_accessor :request_token, :scope
56
+ def initialize(request_token=nil, scope=nil)
57
+ @ns_alias = 'oauth'
58
+ @ns_uri = NS_URI
59
+ @request_token = request_token
60
+ @scope = scope
61
+ end
62
+
63
+ # Create a Response object from an OpenID::Consumer::SuccessResponse
64
+ def self.from_success_response(success_response)
65
+ args = success_response.get_signed_ns(NS_URI)
66
+ return nil if args.nil?
67
+ oauth_resp = new
68
+ oauth_resp.parse_extension_args(args)
69
+ return oauth_resp
70
+ end
71
+
72
+ # parse the oauth request arguments into the
73
+ # internal state of this object
74
+ # if strict is specified, raise an exception when bad data is
75
+ # encountered
76
+ def parse_extension_args(args, strict=false)
77
+ @request_token = args["request_token"]
78
+ @scope = args["scope"]
79
+ end
80
+
81
+ def get_extension_args
82
+ ns_args = {}
83
+ ns_args['request_token'] = @request_token if @request_token
84
+ ns_args['scope'] = @scope if @scope
85
+ return ns_args
86
+ end
87
+
88
+ end
89
+ end
90
+
91
+ end
@@ -0,0 +1,179 @@
1
+ # An implementation of the OpenID Provider Authentication Policy
2
+ # Extension 1.0
3
+ # see: http://openid.net/specs/
4
+
5
+ require 'openid/extension'
6
+
7
+ module OpenID
8
+
9
+ module PAPE
10
+ NS_URI = "http://specs.openid.net/extensions/pape/1.0"
11
+ AUTH_MULTI_FACTOR_PHYSICAL =
12
+ 'http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical'
13
+ AUTH_MULTI_FACTOR =
14
+ 'http://schemas.openid.net/pape/policies/2007/06/multi-factor'
15
+ AUTH_PHISHING_RESISTANT =
16
+ 'http://schemas.openid.net/pape/policies/2007/06/phishing-resistant'
17
+ TIME_VALIDATOR = /\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ/
18
+ # A Provider Authentication Policy request, sent from a relying
19
+ # party to a provider
20
+ class Request < Extension
21
+ attr_accessor :preferred_auth_policies, :max_auth_age, :ns_alias, :ns_uri
22
+ def initialize(preferred_auth_policies=[], max_auth_age=nil)
23
+ @ns_alias = 'pape'
24
+ @ns_uri = NS_URI
25
+ @preferred_auth_policies = preferred_auth_policies
26
+ @max_auth_age = max_auth_age
27
+ end
28
+
29
+ # Add an acceptable authentication policy URI to this request
30
+ # This method is intended to be used by the relying party to add
31
+ # acceptable authentication types to the request.
32
+ def add_policy_uri(policy_uri)
33
+ unless @preferred_auth_policies.member? policy_uri
34
+ @preferred_auth_policies << policy_uri
35
+ end
36
+ end
37
+
38
+ def get_extension_args
39
+ ns_args = {
40
+ 'preferred_auth_policies' => @preferred_auth_policies.join(' ')
41
+ }
42
+ ns_args['max_auth_age'] = @max_auth_age.to_s if @max_auth_age
43
+ return ns_args
44
+ end
45
+
46
+ # Instantiate a Request object from the arguments in a
47
+ # checkid_* OpenID message
48
+ # return nil if the extension was not requested.
49
+ def self.from_openid_request(oid_req)
50
+ pape_req = new
51
+ args = oid_req.message.get_args(NS_URI)
52
+ if args == {}
53
+ return nil
54
+ end
55
+ pape_req.parse_extension_args(args)
56
+ return pape_req
57
+ end
58
+
59
+ # Set the state of this request to be that expressed in these
60
+ # PAPE arguments
61
+ def parse_extension_args(args)
62
+ @preferred_auth_policies = []
63
+ policies_str = args['preferred_auth_policies']
64
+ if policies_str
65
+ policies_str.split(' ').each{|uri|
66
+ add_policy_uri(uri)
67
+ }
68
+ end
69
+
70
+ max_auth_age_str = args['max_auth_age']
71
+ if max_auth_age_str
72
+ @max_auth_age = max_auth_age_str.to_i
73
+ else
74
+ @max_auth_age = nil
75
+ end
76
+ end
77
+
78
+ # Given a list of authentication policy URIs that a provider
79
+ # supports, this method returns the subset of those types
80
+ # that are preferred by the relying party.
81
+ def preferred_types(supported_types)
82
+ @preferred_auth_policies.select{|uri| supported_types.member? uri}
83
+ end
84
+ end
85
+
86
+ # A Provider Authentication Policy response, sent from a provider
87
+ # to a relying party
88
+ class Response < Extension
89
+ attr_accessor :ns_alias, :auth_policies, :auth_time, :nist_auth_level
90
+ def initialize(auth_policies=[], auth_time=nil, nist_auth_level=nil)
91
+ @ns_alias = 'pape'
92
+ @ns_uri = NS_URI
93
+ @auth_policies = auth_policies
94
+ @auth_time = auth_time
95
+ @nist_auth_level = nist_auth_level
96
+ end
97
+
98
+ # Add a policy URI to the response
99
+ # see http://openid.net/specs/openid-provider-authentication-policy-extension-1_0-01.html#auth_policies
100
+ def add_policy_uri(policy_uri)
101
+ @auth_policies << policy_uri unless @auth_policies.member?(policy_uri)
102
+ end
103
+
104
+ # Create a Response object from an OpenID::Consumer::SuccessResponse
105
+ def self.from_success_response(success_response)
106
+ args = success_response.get_signed_ns(NS_URI)
107
+ return nil if args.nil?
108
+ pape_resp = new
109
+ pape_resp.parse_extension_args(args)
110
+ return pape_resp
111
+ end
112
+
113
+ # parse the provider authentication policy arguments into the
114
+ # internal state of this object
115
+ # if strict is specified, raise an exception when bad data is
116
+ # encountered
117
+ def parse_extension_args(args, strict=false)
118
+ policies_str = args['auth_policies']
119
+ if policies_str and policies_str != 'none'
120
+ @auth_policies = policies_str.split(' ')
121
+ end
122
+
123
+ nist_level_str = args['nist_auth_level']
124
+ if nist_level_str
125
+ # special handling of zero to handle to_i behavior
126
+ if nist_level_str.strip == '0'
127
+ nist_level = 0
128
+ else
129
+ nist_level = nist_level_str.to_i
130
+ # if it's zero here we have a bad value
131
+ if nist_level == 0
132
+ nist_level = nil
133
+ end
134
+ end
135
+ if nist_level and nist_level >= 0 and nist_level < 5
136
+ @nist_auth_level = nist_level
137
+ elsif strict
138
+ raise ArgumentError, "nist_auth_level must be an integer 0 through 4, not #{nist_level_str.inspect}"
139
+ end
140
+ end
141
+
142
+ auth_time_str = args['auth_time']
143
+ if auth_time_str
144
+ # validate time string
145
+ if auth_time_str =~ TIME_VALIDATOR
146
+ @auth_time = auth_time_str
147
+ elsif strict
148
+ raise ArgumentError, "auth_time must be in RFC3339 format"
149
+ end
150
+ end
151
+ end
152
+
153
+ def get_extension_args
154
+ ns_args = {}
155
+ if @auth_policies.empty?
156
+ ns_args['auth_policies'] = 'none'
157
+ else
158
+ ns_args['auth_policies'] = @auth_policies.join(' ')
159
+ end
160
+ if @nist_auth_level
161
+ unless (0..4).member? @nist_auth_level
162
+ raise ArgumentError, "nist_auth_level must be an integer 0 through 4, not #{@nist_auth_level.inspect}"
163
+ end
164
+ ns_args['nist_auth_level'] = @nist_auth_level.to_s
165
+ end
166
+
167
+ if @auth_time
168
+ unless @auth_time =~ TIME_VALIDATOR
169
+ raise ArgumentError, "auth_time must be in RFC3339 format"
170
+ end
171
+ ns_args['auth_time'] = @auth_time
172
+ end
173
+ return ns_args
174
+ end
175
+
176
+ end
177
+ end
178
+
179
+ end
@@ -0,0 +1,277 @@
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.respond_to?(:each) and
198
+ field_names[0].is_a?(String)
199
+ field_names.each{|fn|request_field(fn, required, strict)}
200
+ end
201
+
202
+ # Get a hash of unqualified simple registration arguments
203
+ # representing this request.
204
+ # This method is essentially the inverse of parse_extension_args.
205
+ # This method serializes the simple registration request fields.
206
+ def get_extension_args
207
+ args = {}
208
+ args['required'] = @required.join(',') unless @required.empty?
209
+ args['optional'] = @optional.join(',') unless @optional.empty?
210
+ args['policy_url'] = @policy_url unless @policy_url.nil?
211
+ return args
212
+ end
213
+
214
+ def member?(field_name)
215
+ all_requested_fields.member?(field_name)
216
+ end
217
+
218
+ end
219
+
220
+ # Represents the data returned in a simple registration response
221
+ # inside of an OpenID id_res response. This object will be
222
+ # created by the OpenID server, added to the id_res response
223
+ # object, and then extracted from the id_res message by the Consumer.
224
+ class Response < Extension
225
+ attr_reader :ns_uri, :data
226
+
227
+ def initialize(data = {}, ns_uri=NS_URI)
228
+ @ns_alias = 'sreg'
229
+ @data = data
230
+ @ns_uri = ns_uri
231
+ end
232
+
233
+ # Take a Request and a hash of simple registration
234
+ # values and create a Response object containing that data.
235
+ def self.extract_response(request, data)
236
+ arf = request.all_requested_fields
237
+ resp_data = data.reject{|k,v| !arf.member?(k) || v.nil? }
238
+ new(resp_data, request.ns_uri)
239
+ end
240
+
241
+ # Create an Response object from an
242
+ # OpenID::Consumer::SuccessResponse from consumer.complete
243
+ # If you set the signed_only parameter to false, unsigned data from
244
+ # the id_res message from the server will be processed.
245
+ def self.from_success_response(success_response, signed_only = true)
246
+ ns_uri = OpenID::get_sreg_ns(success_response.message)
247
+ if signed_only
248
+ args = success_response.get_signed_ns(ns_uri)
249
+ return nil if args.nil? # No signed args, so fail
250
+ else
251
+ args = success_response.message.get_args(ns_uri)
252
+ end
253
+ args.reject!{|k,v| !DATA_FIELDS.member?(k) }
254
+ new(args, ns_uri)
255
+ end
256
+
257
+ # Get the fields to put in the simple registration namespace
258
+ # when adding them to an id_res message.
259
+ def get_extension_args
260
+ return @data
261
+ end
262
+
263
+ # Read-only hashlike interface.
264
+ # Raises an exception if the field name is bad
265
+ def [](field_name)
266
+ OpenID::check_sreg_field_name(field_name)
267
+ data[field_name]
268
+ end
269
+
270
+ def empty?
271
+ @data.empty?
272
+ end
273
+ # XXX is there more to a hashlike interface I should add?
274
+ end
275
+ end
276
+ end
277
+