chorn-warden-googleapps 0.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.
- data/LICENSE +20 -0
- data/README.md +81 -0
- data/Rakefile +61 -0
- data/lib/warden-googleapps/ca-bundle.crt +2794 -0
- data/lib/warden-googleapps/devise_strategy.rb +117 -0
- data/lib/warden-googleapps/gapps_openid.rb +267 -0
- data/lib/warden-googleapps/user.rb +9 -0
- data/lib/warden-googleapps/version.rb +5 -0
- data/lib/warden-googleapps/warden_strategy.rb +92 -0
- data/lib/warden-googleapps.rb +18 -0
- metadata +101 -0
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'devise/strategies/base'
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Strategies
|
5
|
+
# Default strategy for signing in a user, based on his email and password.
|
6
|
+
# Redirects to sign_in page if it's not authenticated
|
7
|
+
class GoogleApps < Base
|
8
|
+
AxEmail = 'http://axschema.org/contact/email'
|
9
|
+
AxFirstName = 'http://axschema.org/namePerson/first'
|
10
|
+
AxLastName = 'http://axschema.org/namePerson/last'
|
11
|
+
|
12
|
+
# Need to make sure that we have a pure representation of the query string.
|
13
|
+
# Rails adds an "action" parameter which causes the openid gem to error
|
14
|
+
def params
|
15
|
+
@params ||= Rack::Utils.parse_query(request.query_string)
|
16
|
+
end
|
17
|
+
|
18
|
+
def authenticate!
|
19
|
+
if params['openid.mode']
|
20
|
+
response = consumer.complete(params, absolute_url(request, request.path))
|
21
|
+
case response.status.to_s
|
22
|
+
when 'success'
|
23
|
+
profile_data = extract_ax_profile(response)
|
24
|
+
|
25
|
+
resource = mapping.to.find_by_identity_url(response.display_identifier)
|
26
|
+
|
27
|
+
unless resource
|
28
|
+
resource = mapping.to.new
|
29
|
+
resource.first_name = profile_data[:first_name].join(' ')
|
30
|
+
resource.last_name = profile_data[:last_name].join(' ')
|
31
|
+
resource.email = profile_data[:email].join(' ')
|
32
|
+
resource.identity_url = response.display_identifier
|
33
|
+
begin
|
34
|
+
pwd = (Kernel.rand*1000000000000000).to_i
|
35
|
+
resource.password = pwd
|
36
|
+
resource.password_confirmation = pwd
|
37
|
+
rescue Error => e
|
38
|
+
puts "You probably need authenticatable set on your model."
|
39
|
+
end
|
40
|
+
resource.save!
|
41
|
+
end
|
42
|
+
|
43
|
+
success!(resource)
|
44
|
+
when 'failure', 'setup_needed', 'cancel'
|
45
|
+
fail!('Cound not log in')
|
46
|
+
else
|
47
|
+
fail!("Unknown Response Status: #{response.status.to_s}")
|
48
|
+
end
|
49
|
+
elsif params['RelayState']
|
50
|
+
raise GoogleAppsMisconfiguredError, "Warden::GoogleApps only works with OpenID Federed Login for Google Apps"
|
51
|
+
else
|
52
|
+
google_discovery = OpenID.discover(open_id_endpoint)
|
53
|
+
open_id_request = consumer.begin(google_discovery.first)
|
54
|
+
add_ax_fields(open_id_request)
|
55
|
+
redirect!(open_id_request.redirect_url(absolute_url(request), absolute_url(request)))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def consumer
|
61
|
+
@consumer ||= ::OpenID::Consumer.new(request.session, open_id_store)
|
62
|
+
end
|
63
|
+
|
64
|
+
def open_id_store
|
65
|
+
::OpenID::Store::Filesystem.new("#{Dir.tmpdir}/tmp/openid")
|
66
|
+
end
|
67
|
+
|
68
|
+
def google_apps_domain
|
69
|
+
env['warden'].config[:google_apps_domain]
|
70
|
+
end
|
71
|
+
|
72
|
+
def domain
|
73
|
+
'https://www.google.com/accounts/o8/site-xrds?hd=%s' % google_apps_domain
|
74
|
+
end
|
75
|
+
|
76
|
+
def endpoint
|
77
|
+
env['warden'].config[:google_apps_endpoint]
|
78
|
+
end
|
79
|
+
|
80
|
+
def open_id_endpoint
|
81
|
+
endpoint || domain
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_ax_fields(open_id_request)
|
85
|
+
ax_request = ::OpenID::AX::FetchRequest.new
|
86
|
+
|
87
|
+
[ AxEmail, AxFirstName, AxLastName ].each do |field|
|
88
|
+
ax_request.add(::OpenID::AX::AttrInfo.new(field, nil, true))
|
89
|
+
end
|
90
|
+
open_id_request.add_extension(ax_request)
|
91
|
+
end
|
92
|
+
|
93
|
+
def extract_ax_profile(open_id_response)
|
94
|
+
profile = { }
|
95
|
+
if ax_response = OpenID::AX::FetchResponse.from_success_response(open_id_response)
|
96
|
+
profile_data = ax_response.data
|
97
|
+
profile[:email] = profile_data[AxEmail]
|
98
|
+
profile[:last_name] = profile_data[AxLastName]
|
99
|
+
profile[:first_name] = profile_data[AxFirstName]
|
100
|
+
end
|
101
|
+
profile
|
102
|
+
end
|
103
|
+
|
104
|
+
def absolute_url(request, suffix = nil)
|
105
|
+
port_part = case request.scheme
|
106
|
+
when "http"
|
107
|
+
request.port == 80 ? "" : ":#{request.port}"
|
108
|
+
when "https"
|
109
|
+
request.port == 443 ? "" : ":#{request.port}"
|
110
|
+
end
|
111
|
+
"#{request.scheme}://#{request.host}#{port_part}#{suffix}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
Warden::Strategies.add(:devise_google_apps, Devise::Strategies::GoogleApps)
|
@@ -0,0 +1,267 @@
|
|
1
|
+
require "openid"
|
2
|
+
require "openid/fetchers"
|
3
|
+
require "openid/consumer/discovery"
|
4
|
+
require 'rexml/document'
|
5
|
+
require 'rexml/element'
|
6
|
+
require 'rexml/xpath'
|
7
|
+
require 'openssl'
|
8
|
+
require 'base64'
|
9
|
+
|
10
|
+
# Copyright 2009 Google Inc.
|
11
|
+
#
|
12
|
+
# Licensed under the Apache License, Version 2.0 (the "License")
|
13
|
+
# you may not use this file except in compliance with the License.
|
14
|
+
# You may obtain a copy of the License at
|
15
|
+
#
|
16
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
17
|
+
#
|
18
|
+
# Unless required by applicable law or agreed to in writing, software
|
19
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
20
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
21
|
+
# See the License for the specific language governing permissions and
|
22
|
+
# limitations under the License.
|
23
|
+
|
24
|
+
# Extends ruby-openid to support the discovery protocol used by Google Apps. Usage is
|
25
|
+
# generally simple. Where using ruby-openid's Consumer, add the line
|
26
|
+
#
|
27
|
+
# require 'gapps_openid'
|
28
|
+
#
|
29
|
+
# Caching of discovery information is enabled when used with rails. In other environments,
|
30
|
+
# a cache can be set via:
|
31
|
+
#
|
32
|
+
# OpenID::GoogleDiscovery.cache = ...
|
33
|
+
#
|
34
|
+
# The cache must implement methods read(key) and write(key,value)
|
35
|
+
#
|
36
|
+
# In some cases additional setup is required, particularly to set the location of trusted
|
37
|
+
# root certificates for validating XRDS signatures. If standard locations don't work, additional
|
38
|
+
# files and directories can be added via:
|
39
|
+
#
|
40
|
+
# OpenID::SimpleSign.store.add_file(path_to_cacert_pem)
|
41
|
+
#
|
42
|
+
# or
|
43
|
+
#
|
44
|
+
# OpenID::SimpleSign.store.add_path(path_to_ca_dir)
|
45
|
+
#
|
46
|
+
# TODO:
|
47
|
+
# - Memcache support for caching host-meta and site XRDS docs
|
48
|
+
# - Better packaging (gem/rails)
|
49
|
+
module OpenID
|
50
|
+
|
51
|
+
class << self
|
52
|
+
alias_method :default_discover, :discover
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.discover(uri)
|
56
|
+
# Set the CA file for the fetcher so the HTTPS requests will not issue warnings.
|
57
|
+
fetcher.ca_file = File.join(File.dirname(__FILE__), 'ca-bundle.crt')
|
58
|
+
discovery = GoogleDiscovery.new
|
59
|
+
info = discovery.perform_discovery(uri)
|
60
|
+
if not info.nil?
|
61
|
+
return info
|
62
|
+
end
|
63
|
+
return self.default_discover(uri)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Handles the bulk of Google's modified discovery prototcol
|
67
|
+
# See http://groups.google.com/group/google-federated-login-api/web/openid-discovery-for-hosted-domains
|
68
|
+
class GoogleDiscovery
|
69
|
+
|
70
|
+
begin
|
71
|
+
@@cache = RAILS_CACHE
|
72
|
+
rescue
|
73
|
+
@@cache = nil
|
74
|
+
end
|
75
|
+
|
76
|
+
NAMESPACES = {
|
77
|
+
'xrds' => 'xri://$xrd*($v*2.0)',
|
78
|
+
'xrd' => 'xri://$xrds',
|
79
|
+
'openid' => 'http://namespace.google.com/openid/xmlns'
|
80
|
+
}
|
81
|
+
|
82
|
+
# Main entry point for discovery. Attempts to detect whether or not the URI is a raw domain name ('mycompany.com')
|
83
|
+
# vs. a user's claimed ID ('http://mycompany.com/openid?id=12345') and performs the site or user discovery appropriately
|
84
|
+
def perform_discovery(uri)
|
85
|
+
begin
|
86
|
+
parsed_uri = URI::parse(uri)
|
87
|
+
if parsed_uri.scheme.nil?
|
88
|
+
return discover_site(uri)
|
89
|
+
end
|
90
|
+
return discover_user(parsed_uri.host, uri)
|
91
|
+
rescue
|
92
|
+
# If we fail, just return nothing and fallback on default discovery mechanisms
|
93
|
+
return nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Handles discovery for a user's claimed ID.
|
98
|
+
def discover_user(domain, claimed_id)
|
99
|
+
url = fetch_host_meta(domain)
|
100
|
+
if url.nil?
|
101
|
+
return nil # Not a Google Apps domain
|
102
|
+
end
|
103
|
+
xrds = fetch_xrds(domain, url)
|
104
|
+
user_url, authority = get_user_xrds_url(xrds, claimed_id)
|
105
|
+
user_xrds = fetch_xrds(authority, user_url, false)
|
106
|
+
return if user_xrds.nil?
|
107
|
+
|
108
|
+
endpoints = OpenID::OpenIDServiceEndpoint.from_xrds(claimed_id, user_xrds)
|
109
|
+
return [claimed_id, OpenID.get_op_or_user_services(endpoints)]
|
110
|
+
end
|
111
|
+
|
112
|
+
# Handles discovery for a domain
|
113
|
+
def discover_site(domain)
|
114
|
+
url = fetch_host_meta(domain)
|
115
|
+
if url.nil?
|
116
|
+
return nil # Not a Google Apps domain
|
117
|
+
end
|
118
|
+
xrds = fetch_xrds(domain, url)
|
119
|
+
unless xrds.nil?
|
120
|
+
endpoints = OpenID::OpenIDServiceEndpoint.from_xrds(domain, xrds)
|
121
|
+
return [domain, OpenID.get_op_or_user_services(endpoints)]
|
122
|
+
end
|
123
|
+
return nil
|
124
|
+
end
|
125
|
+
|
126
|
+
# Kickstart the discovery process by checking against Google's well-known location for hosted domains.
|
127
|
+
# This gives us the location of the site's XRDS doc
|
128
|
+
def fetch_host_meta(domain)
|
129
|
+
cached_value = get_cache(domain)
|
130
|
+
return cached_value unless cached_value.nil?
|
131
|
+
|
132
|
+
host_meta_url = "https://www.google.com/accounts/o8/.well-known/host-meta?hd=#{CGI::escape(domain)}"
|
133
|
+
http_resp = OpenID.fetch(host_meta_url)
|
134
|
+
if http_resp.code != "200" and http_resp.code != "206"
|
135
|
+
return nil
|
136
|
+
end
|
137
|
+
matches = /Link: <(.*)>/.match( http_resp.body )
|
138
|
+
if matches.nil?
|
139
|
+
return nil
|
140
|
+
end
|
141
|
+
put_cache(domain, matches[1])
|
142
|
+
return matches[1]
|
143
|
+
end
|
144
|
+
|
145
|
+
# Fetches the XRDS and verifies the signature and authority for the doc
|
146
|
+
def fetch_xrds(authority, url, cache=true)
|
147
|
+
return if url.nil?
|
148
|
+
|
149
|
+
cached_xrds = get_cache(url)
|
150
|
+
return cached_xrds unless cached_xrds.nil?
|
151
|
+
|
152
|
+
http_resp = OpenID.fetch(url)
|
153
|
+
return if http_resp.code != "200" and http_resp.code != "206"
|
154
|
+
|
155
|
+
body = http_resp.body
|
156
|
+
signature = http_resp["Signature"]
|
157
|
+
signed_by = SimpleSign.verify(body, signature)
|
158
|
+
if !signed_by.casecmp(authority) or !signed_by.casecmp('hosted-id.google.com')
|
159
|
+
return false # Signed, but not by the right domain.
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
# Everything is OK
|
164
|
+
if cache
|
165
|
+
put_cache(url, body)
|
166
|
+
end
|
167
|
+
return body
|
168
|
+
end
|
169
|
+
|
170
|
+
# Process the URITemplate in the XRDS to derive the location of the claimed id's XRDS
|
171
|
+
def get_user_xrds_url(xrds, claimed_id)
|
172
|
+
types_to_match = ['http://www.iana.org/assignments/relation/describedby']
|
173
|
+
services = OpenID::Yadis::apply_filter(claimed_id, xrds)
|
174
|
+
services.each do | service |
|
175
|
+
if service.match_types(types_to_match)
|
176
|
+
template = REXML::XPath.first(service.service_element, '//openid:URITemplate', NAMESPACES)
|
177
|
+
authority = REXML::XPath.first(service.service_element, '//openid:NextAuthority', NAMESPACES)
|
178
|
+
url = template.text.gsub('{%uri}', CGI::escape(claimed_id))
|
179
|
+
return [url, authority.text]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def put_cache(key, item)
|
185
|
+
return if @@cache.nil?
|
186
|
+
@@cache.write("__GAPPS_OPENID__#{key}", item)
|
187
|
+
end
|
188
|
+
|
189
|
+
def get_cache(key)
|
190
|
+
return nil if @@cache.nil?
|
191
|
+
return @@cache.read("__GAPPS_OPENID__#{key}")
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Basic implementation of the XML Simple Sign algorithm. Currently only supports
|
196
|
+
# RSA-SHA1
|
197
|
+
class SimpleSign
|
198
|
+
|
199
|
+
@@store = nil
|
200
|
+
|
201
|
+
C14N_RAW_OCTETS = 'http://docs.oasis-open.org/xri/xrd/2009/01#canonicalize-raw-octets'
|
202
|
+
SIGN_RSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
|
203
|
+
|
204
|
+
NAMESPACES = {
|
205
|
+
'ds' => 'http://www.w3.org/2000/09/xmldsig#',
|
206
|
+
'xrds' => 'xri://xrds'
|
207
|
+
}
|
208
|
+
|
209
|
+
# Initialize the store
|
210
|
+
def self.store
|
211
|
+
if @@store.nil?
|
212
|
+
ca_bundle_path = File.join(File.dirname(__FILE__), 'ca-bundle.crt')
|
213
|
+
@@store = OpenSSL::X509::Store.new
|
214
|
+
@@store.set_default_paths
|
215
|
+
@@store.add_file(ca_bundle_path)
|
216
|
+
end
|
217
|
+
return @@store
|
218
|
+
end
|
219
|
+
|
220
|
+
# Extracts the signer's certificates from the XML
|
221
|
+
def self.parse_certificates(doc)
|
222
|
+
certs = []
|
223
|
+
REXML::XPath.each(doc, "//ds:Signature/ds:KeyInfo/ds:X509Data/ds:X509Certificate", NAMESPACES ) { | encoded |
|
224
|
+
encoded = encoded.text.strip.scan(/.{1,64}/).join("\n")
|
225
|
+
encoded = "-----BEGIN CERTIFICATE-----\n#{encoded}\n-----END CERTIFICATE-----\n"
|
226
|
+
cert = OpenSSL::X509::Certificate.new(encoded)
|
227
|
+
certs << cert
|
228
|
+
}
|
229
|
+
return certs
|
230
|
+
end
|
231
|
+
|
232
|
+
# Verifies the chain of trust for the signing certificates
|
233
|
+
def self.valid_chain?(chain)
|
234
|
+
if chain.nil? or chain.empty?
|
235
|
+
return false
|
236
|
+
end
|
237
|
+
cert = chain.shift
|
238
|
+
if self.store.verify(cert)
|
239
|
+
return true
|
240
|
+
end
|
241
|
+
if chain.empty? or not cert.verify(chain.first.public_key)
|
242
|
+
return false
|
243
|
+
end
|
244
|
+
return self.valid_chain?(chain)
|
245
|
+
end
|
246
|
+
|
247
|
+
# Verifies the signature of the doc, returning the CN of the signer if valid
|
248
|
+
def self.verify(xml, signature_value)
|
249
|
+
raise "Missing signature value" if signature_value.nil?
|
250
|
+
decoded_sig = Base64.decode64(signature_value)
|
251
|
+
|
252
|
+
doc = REXML::Document.new(xml)
|
253
|
+
certs = self.parse_certificates(doc)
|
254
|
+
raise "No signature in document" if certs.nil? or certs.empty?
|
255
|
+
|
256
|
+
signing_certificate = certs.first
|
257
|
+
raise "Invalid signature" if !signing_certificate.public_key.verify(OpenSSL::Digest::SHA1.new, decoded_sig, xml)
|
258
|
+
raise "Certificate chain not valid" if !self.valid_chain?(certs)
|
259
|
+
|
260
|
+
# Signature is valid, return CN of the subject
|
261
|
+
subject = signing_certificate.subject.to_a
|
262
|
+
signed_by = subject.last[1]
|
263
|
+
return signed_by
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
Warden::Strategies.add(:google_apps) do
|
2
|
+
AxEmail = 'http://axschema.org/contact/email'
|
3
|
+
AxFirstName = 'http://axschema.org/namePerson/first'
|
4
|
+
AxLastName = 'http://axschema.org/namePerson/last'
|
5
|
+
|
6
|
+
# Need to make sure that we have a pure representation of the query string.
|
7
|
+
# Rails adds an "action" parameter which causes the openid gem to error
|
8
|
+
def params
|
9
|
+
@params ||= Rack::Utils.parse_query(request.query_string)
|
10
|
+
end
|
11
|
+
|
12
|
+
def authenticate!
|
13
|
+
if params['openid.mode']
|
14
|
+
response = consumer.complete(params, absolute_url(request, request.path))
|
15
|
+
case response.status.to_s
|
16
|
+
when 'success'
|
17
|
+
profile_data = extract_ax_profile(response)
|
18
|
+
user = Warden::GoogleApps::User.new(profile_data[:email],
|
19
|
+
profile_data[:first_name],
|
20
|
+
profile_data[:last_name],
|
21
|
+
response.display_identifier)
|
22
|
+
success!(user)
|
23
|
+
when 'failure', 'setup_needed', 'cancel'
|
24
|
+
fail!('Cound not log in')
|
25
|
+
else
|
26
|
+
fail!("Unknown Response Status: #{response.status.to_s}")
|
27
|
+
end
|
28
|
+
elsif params['RelayState']
|
29
|
+
raise GoogleAppsMisconfiguredError, "Warden::GoogleApps only works with OpenID Federed Login for Google Apps"
|
30
|
+
else
|
31
|
+
google_discovery = OpenID.discover(open_id_endpoint)
|
32
|
+
open_id_request = consumer.begin(google_discovery.first)
|
33
|
+
add_ax_fields(open_id_request)
|
34
|
+
redirect!(open_id_request.redirect_url(absolute_url(request), absolute_url(request)))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def consumer
|
40
|
+
@consumer ||= ::OpenID::Consumer.new(request.session, open_id_store)
|
41
|
+
end
|
42
|
+
|
43
|
+
def open_id_store
|
44
|
+
::OpenID::Store::Filesystem.new("#{Dir.tmpdir}/tmp/openid")
|
45
|
+
end
|
46
|
+
|
47
|
+
def google_apps_domain
|
48
|
+
env['warden'].config[:google_apps_domain]
|
49
|
+
end
|
50
|
+
|
51
|
+
def domain
|
52
|
+
'https://www.google.com/accounts/o8/site-xrds?hd=%s' % google_apps_domain
|
53
|
+
end
|
54
|
+
|
55
|
+
def endpoint
|
56
|
+
env['warden'].config[:google_apps_endpoint]
|
57
|
+
end
|
58
|
+
|
59
|
+
def open_id_endpoint
|
60
|
+
endpoint || domain
|
61
|
+
end
|
62
|
+
|
63
|
+
def add_ax_fields(open_id_request)
|
64
|
+
ax_request = ::OpenID::AX::FetchRequest.new
|
65
|
+
|
66
|
+
[ AxEmail, AxFirstName, AxLastName ].each do |field|
|
67
|
+
ax_request.add(::OpenID::AX::AttrInfo.new(field, nil, true))
|
68
|
+
end
|
69
|
+
open_id_request.add_extension(ax_request)
|
70
|
+
end
|
71
|
+
|
72
|
+
def extract_ax_profile(open_id_response)
|
73
|
+
profile = { }
|
74
|
+
if ax_response = OpenID::AX::FetchResponse.from_success_response(open_id_response)
|
75
|
+
profile_data = ax_response.data
|
76
|
+
profile[:email] = profile_data[AxEmail]
|
77
|
+
profile[:last_name] = profile_data[AxLastName]
|
78
|
+
profile[:first_name] = profile_data[AxFirstName]
|
79
|
+
end
|
80
|
+
profile
|
81
|
+
end
|
82
|
+
|
83
|
+
def absolute_url(request, suffix = nil)
|
84
|
+
port_part = case request.scheme
|
85
|
+
when "http"
|
86
|
+
request.port == 80 ? "" : ":#{request.port}"
|
87
|
+
when "https"
|
88
|
+
request.port == 443 ? "" : ":#{request.port}"
|
89
|
+
end
|
90
|
+
"#{request.scheme}://#{request.host}#{port_part}#{suffix}"
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'warden'
|
2
|
+
require 'tmpdir'
|
3
|
+
require 'openid/consumer'
|
4
|
+
require 'openid/store/filesystem'
|
5
|
+
require 'openid/extensions/sreg'
|
6
|
+
require 'openid/extensions/ax'
|
7
|
+
|
8
|
+
module Warden
|
9
|
+
module GoogleApps
|
10
|
+
class GoogleAppsMisconfiguredError < StandardError; end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require File.dirname(__FILE__)+'/warden-googleapps/user'
|
15
|
+
require File.dirname(__FILE__)+'/warden-googleapps/version'
|
16
|
+
require File.dirname(__FILE__)+'/warden-googleapps/warden_strategy'
|
17
|
+
require File.dirname(__FILE__)+'/warden-googleapps/devise_strategy' if Object.const_defined?(:Devise)
|
18
|
+
require File.dirname(__FILE__)+'/warden-googleapps/gapps_openid'
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: chorn-warden-googleapps
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Corey Donohoe
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-06-17 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: warden
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
- 10
|
31
|
+
version: "0.10"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: ruby-openid
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ~>
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
segments:
|
43
|
+
- 2
|
44
|
+
- 1
|
45
|
+
- 7
|
46
|
+
version: 2.1.7
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
description: Warden / Devise strategies to use Google's Federated OpenID with Google Apps
|
50
|
+
email: atmos@atmos.org
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files:
|
56
|
+
- LICENSE
|
57
|
+
files:
|
58
|
+
- LICENSE
|
59
|
+
- README.md
|
60
|
+
- Rakefile
|
61
|
+
- lib/warden-googleapps/ca-bundle.crt
|
62
|
+
- lib/warden-googleapps/gapps_openid.rb
|
63
|
+
- lib/warden-googleapps/warden_strategy.rb
|
64
|
+
- lib/warden-googleapps/devise_strategy.rb
|
65
|
+
- lib/warden-googleapps/user.rb
|
66
|
+
- lib/warden-googleapps/version.rb
|
67
|
+
- lib/warden-googleapps.rb
|
68
|
+
has_rdoc: true
|
69
|
+
homepage: http://github.com/chorn/warden-googleapps
|
70
|
+
licenses: []
|
71
|
+
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
version: "0"
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
requirements: []
|
94
|
+
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 1.3.7
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: Warden & Devise strategies to use Google's Federated OpenID with Google Apps
|
100
|
+
test_files: []
|
101
|
+
|