ruby-openid-apps-discovery 1.01 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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