ruby-openid-apps-discovery 1.01 → 1.2.0

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 (2) hide show
  1. data/lib/gapps_openid.rb +71 -36
  2. metadata +21 -10
@@ -29,10 +29,17 @@ require 'base64'
29
29
  # Caching of discovery information is enabled when used with rails. In other environments,
30
30
  # a cache can be set via:
31
31
  #
32
- # OpenID::GoogleDiscovery.cache = ...
32
+ # OpenID.cache = ...
33
33
  #
34
34
  # The cache must implement methods read(key) and write(key,value)
35
35
  #
36
+ # Similarly, logging will attempt to use the default Rail's logger, but can be overriden
37
+ # by calling
38
+ #
39
+ # OpenID.logger = ...
40
+ #
41
+ # The logger must respond to warn, debug, and info methods
42
+ #
36
43
  # In some cases additional setup is required, particularly to set the location of trusted
37
44
  # root certificates for validating XRDS signatures. If standard locations don't work, additional
38
45
  # files and directories can be added via:
@@ -81,11 +88,13 @@ module OpenID
81
88
  def perform_discovery(uri)
82
89
  OpenID.logger.debug("Performing discovery for #{uri}") unless OpenID.logger.nil?
83
90
  begin
91
+ domain = uri
84
92
  parsed_uri = URI::parse(uri)
85
- if parsed_uri.scheme.nil?
86
- return discover_site(uri)
93
+ domain = parsed_uri.host unless parsed_uri.host.nil?
94
+ if site_identifier?(parsed_uri)
95
+ return discover_site(domain)
87
96
  end
88
- return discover_user(parsed_uri.host, uri)
97
+ return discover_user(domain, uri)
89
98
  rescue Exception => e
90
99
  # If we fail, just return nothing and fallback on default discovery mechanisms
91
100
  OpenID.logger.warn("Unexpected exception performing discovery for id #{uri}: #{e}") unless OpenID.logger.nil?
@@ -93,6 +102,10 @@ module OpenID
93
102
  end
94
103
  end
95
104
 
105
+ def site_identifier?(parsed_uri)
106
+ return parsed_uri.scheme.nil? || parsed_uri.path.nil? || parsed_uri.path.strip.empty?
107
+ end
108
+
96
109
  # Handles discovery for a user's claimed ID.
97
110
  def discover_user(domain, claimed_id)
98
111
  OpenID.logger.debug("Discovering user identity #{claimed_id} for domain #{domain}") unless OpenID.logger.nil?
@@ -101,13 +114,23 @@ module OpenID
101
114
  OpenID.logger.debug("#{domain} is not a Google Apps domain, aborting") unless OpenID.logger.nil?
102
115
  return nil # Not a Google Apps domain
103
116
  end
104
- xrds = fetch_xrds(domain, url)
105
- user_url, authority = get_user_xrds_url(xrds, claimed_id)
106
- user_xrds = fetch_xrds(authority, user_url, false)
107
- return if user_xrds.nil?
117
+
118
+ xrds, signed = fetch_secure_xrds(domain, url)
119
+
120
+ unless xrds.nil?
121
+ # TODO - Need to propogate secure discovery info up through stack
122
+ user_url, authority = get_user_xrds_url(xrds, claimed_id)
123
+ user_xrds, signed = fetch_secure_xrds(domain, user_url, false)
124
+
125
+ # No user xrds -- likely that identifier was just OP identifier
126
+ if user_xrds.nil?
127
+ endpoints = OpenID::OpenIDServiceEndpoint.from_xrds(domain, xrds)
128
+ return [claimed_id, OpenID.get_op_or_user_services(endpoints)]
129
+ end
108
130
 
109
- endpoints = OpenID::OpenIDServiceEndpoint.from_xrds(claimed_id, user_xrds)
110
- return [claimed_id, OpenID.get_op_or_user_services(endpoints)]
131
+ endpoints = OpenID::OpenIDServiceEndpoint.from_xrds(claimed_id, user_xrds)
132
+ return [claimed_id, OpenID.get_op_or_user_services(endpoints)]
133
+ end
111
134
  end
112
135
 
113
136
  # Handles discovery for a domain
@@ -118,10 +141,12 @@ module OpenID
118
141
  OpenID.logger.debug("#{domain} is not a Google Apps domain, aborting") unless OpenID.logger.nil?
119
142
  return nil # Not a Google Apps domain
120
143
  end
121
- xrds = fetch_xrds(domain, url)
144
+ xrds, secure = fetch_secure_xrds(domain, url)
145
+
122
146
  unless xrds.nil?
123
- endpoints = OpenID::OpenIDServiceEndpoint.from_xrds(domain, xrds)
124
- return [domain, OpenID.get_op_or_user_services(endpoints)]
147
+ # TODO - Need to propogate secure discovery info up through stack
148
+ endpoints = OpenID::OpenIDServiceEndpoint.from_xrds(domain, xrds)
149
+ return [domain, OpenID.get_op_or_user_services(endpoints)]
125
150
  end
126
151
  return nil
127
152
  end
@@ -133,11 +158,9 @@ module OpenID
133
158
  return cached_value unless cached_value.nil?
134
159
 
135
160
  host_meta_url = "https://www.google.com/accounts/o8/.well-known/host-meta?hd=#{CGI::escape(domain)}"
136
- http_resp = OpenID.fetch(host_meta_url)
137
- if http_resp.code != "200" and http_resp.code != "206"
138
- OpenID.logger.debug("Received #{http_resp.code} when fetching #{host_meta_url}") unless OpenID.logger.nil?
139
- return nil
140
- end
161
+ http_resp = fetch_url(host_meta_url)
162
+ return nil if http_resp.nil?
163
+
141
164
  matches = /Link: <(.*)>/.match( http_resp.body )
142
165
  if matches.nil?
143
166
  OpenID.logger.debug("No link tag found at #{host_meta_url}") unless OpenID.logger.nil?
@@ -147,34 +170,43 @@ module OpenID
147
170
  return matches[1]
148
171
  end
149
172
 
173
+ def fetch_url(url)
174
+ http_resp = OpenID.fetch(url)
175
+ if http_resp.code != "200" and http_resp.code != "206"
176
+ OpenID.logger.debug("Received #{http_resp.code} when fetching #{url}") unless OpenID.logger.nil?
177
+ return nil
178
+ end
179
+ return http_resp
180
+ end
181
+
150
182
  # Fetches the XRDS and verifies the signature and authority for the doc
151
- def fetch_xrds(authority, url, cache=true)
183
+ def fetch_secure_xrds(authority, url, cache=true)
152
184
  return if url.nil?
153
185
 
154
186
  OpenID.logger.debug("Retrieving XRDS from #{url}") unless OpenID.logger.nil?
155
187
 
156
- cached_xrds = get_cache(url)
188
+ cached_xrds = get_cache("XRDS_#{url}")
157
189
  return cached_xrds unless cached_xrds.nil?
158
190
 
159
- http_resp = OpenID.fetch(url)
160
- if http_resp.code != "200" and http_resp.code != "206"
161
- OpenID.logger.debug("Received #{http_resp.code} when fetching #{url}") unless OpenID.logger.nil?
162
- return nil
163
- end
191
+ http_resp = fetch_url(url)
192
+ return nil if http_resp.nil?
164
193
 
165
194
  body = http_resp.body
195
+ put_cache("XRDS_#{url}", body)
196
+
166
197
  signature = http_resp["Signature"]
167
198
  signed_by = SimpleSign.verify(body, signature)
168
- if !signed_by.casecmp(authority) or !signed_by.casecmp('hosted-id.google.com')
199
+
200
+ if signed_by.nil?
201
+ put_cache("XRDS_#{url}", body) if cache
202
+ return [body, false]
203
+ elsif signed_by.casecmp(authority) || signed_by.casecmp('hosted-id.google.com')
204
+ put_cache("XRDS_#{url}", body) if cache
205
+ return [body, true]
206
+ else
169
207
  OpenID.logger.warn("Expected signature from #{authority} but found #{signed_by}") unless OpenID.logger.nil?
170
- return false # Signed, but not by the right domain.
208
+ return nil # Signed, but not by the right domain.
171
209
  end
172
-
173
- # Everything is OK
174
- if cache
175
- put_cache(url, body)
176
- end
177
- return body
178
210
  end
179
211
 
180
212
  # Process the URITemplate in the XRDS to derive the location of the claimed id's XRDS
@@ -257,12 +289,15 @@ module OpenID
257
289
 
258
290
  # Verifies the signature of the doc, returning the CN of the signer if valid
259
291
  def self.verify(xml, signature_value)
260
- raise "Missing signature value" if signature_value.nil?
261
- decoded_sig = Base64.decode64(signature_value)
262
-
263
292
  doc = REXML::Document.new(xml)
293
+
294
+ return nil if REXML::XPath.first(doc, "//ds:Signature").nil? and signature_value.nil?
295
+
296
+ decoded_sig = Base64.decode64(signature_value)
264
297
  certs = self.parse_certificates(doc)
265
298
  raise "No signature in document" if certs.nil? or certs.empty?
299
+ raise "Missing signature value" if signature_value.nil?
300
+
266
301
 
267
302
  signing_certificate = certs.first
268
303
  raise "Invalid signature" if !signing_certificate.public_key.verify(OpenSSL::Digest::SHA1.new, decoded_sig, xml)
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-openid-apps-discovery
3
3
  version: !ruby/object:Gem::Version
4
- version: "1.01"
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 2
8
+ - 0
9
+ version: 1.2.0
5
10
  platform: ruby
6
11
  authors: []
7
12
 
@@ -9,19 +14,23 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-01-12 00:00:00 -08:00
17
+ date: 2010-05-17 00:00:00 -07:00
13
18
  default_executable:
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
21
  name: ruby-openid
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
20
24
  requirements:
21
25
  - - ">="
22
26
  - !ruby/object:Gem::Version
27
+ segments:
28
+ - 2
29
+ - 1
30
+ - 7
23
31
  version: 2.1.7
24
- version:
32
+ type: :runtime
33
+ version_requirements: *id001
25
34
  description: |
26
35
  Extension to ruby-openid that enables discovery for Google Apps domains
27
36
 
@@ -36,7 +45,7 @@ files:
36
45
  - lib/gapps_openid.rb
37
46
  - lib/ca-bundle.crt
38
47
  has_rdoc: true
39
- homepage:
48
+ homepage: http://code.google.com/p/ruby-openid-apps-discovery
40
49
  licenses: []
41
50
 
42
51
  post_install_message:
@@ -48,18 +57,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
48
57
  requirements:
49
58
  - - ">="
50
59
  - !ruby/object:Gem::Version
60
+ segments:
61
+ - 0
51
62
  version: "0"
52
- version:
53
63
  required_rubygems_version: !ruby/object:Gem::Requirement
54
64
  requirements:
55
65
  - - ">="
56
66
  - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
57
69
  version: "0"
58
- version:
59
70
  requirements: []
60
71
 
61
72
  rubyforge_project:
62
- rubygems_version: 1.3.5
73
+ rubygems_version: 1.3.6
63
74
  signing_key:
64
75
  specification_version: 3
65
76
  summary: Google Apps support for ruby-openid