Chris@1296
|
1 require 'uri'
|
Chris@1296
|
2 require 'openid'
|
Chris@1296
|
3 require 'rack/openid'
|
Chris@1296
|
4
|
Chris@1296
|
5 module OpenIdAuthentication
|
Chris@1296
|
6 def self.new(app)
|
Chris@1296
|
7 store = OpenIdAuthentication.store
|
Chris@1296
|
8 if store.nil?
|
Chris@1296
|
9 Rails.logger.warn "OpenIdAuthentication.store is nil. Using in-memory store."
|
Chris@1296
|
10 end
|
Chris@1296
|
11
|
Chris@1296
|
12 ::Rack::OpenID.new(app, OpenIdAuthentication.store)
|
Chris@1296
|
13 end
|
Chris@1296
|
14
|
Chris@1296
|
15 def self.store
|
Chris@1296
|
16 @@store
|
Chris@1296
|
17 end
|
Chris@1296
|
18
|
Chris@1296
|
19 def self.store=(*store_option)
|
Chris@1296
|
20 store, *parameters = *([ store_option ].flatten)
|
Chris@1296
|
21
|
Chris@1296
|
22 @@store = case store
|
Chris@1296
|
23 when :memory
|
Chris@1296
|
24 require 'openid/store/memory'
|
Chris@1296
|
25 OpenID::Store::Memory.new
|
Chris@1296
|
26 when :file
|
Chris@1296
|
27 require 'openid/store/filesystem'
|
Chris@1296
|
28 OpenID::Store::Filesystem.new(Rails.root.join('tmp/openids'))
|
Chris@1296
|
29 when :memcache
|
Chris@1296
|
30 require 'memcache'
|
Chris@1296
|
31 require 'openid/store/memcache'
|
Chris@1296
|
32 OpenID::Store::Memcache.new(MemCache.new(parameters))
|
Chris@1296
|
33 else
|
Chris@1296
|
34 store
|
Chris@1296
|
35 end
|
Chris@1296
|
36 end
|
Chris@1296
|
37
|
Chris@1296
|
38 self.store = nil
|
Chris@1296
|
39
|
Chris@1296
|
40 class InvalidOpenId < StandardError
|
Chris@1296
|
41 end
|
Chris@1296
|
42
|
Chris@1296
|
43 class Result
|
Chris@1296
|
44 ERROR_MESSAGES = {
|
Chris@1296
|
45 :missing => "Sorry, the OpenID server couldn't be found",
|
Chris@1296
|
46 :invalid => "Sorry, but this does not appear to be a valid OpenID",
|
Chris@1296
|
47 :canceled => "OpenID verification was canceled",
|
Chris@1296
|
48 :failed => "OpenID verification failed",
|
Chris@1296
|
49 :setup_needed => "OpenID verification needs setup"
|
Chris@1296
|
50 }
|
Chris@1296
|
51
|
Chris@1296
|
52 def self.[](code)
|
Chris@1296
|
53 new(code)
|
Chris@1296
|
54 end
|
Chris@1296
|
55
|
Chris@1296
|
56 def initialize(code)
|
Chris@1296
|
57 @code = code
|
Chris@1296
|
58 end
|
Chris@1296
|
59
|
Chris@1296
|
60 def status
|
Chris@1296
|
61 @code
|
Chris@1296
|
62 end
|
Chris@1296
|
63
|
Chris@1296
|
64 ERROR_MESSAGES.keys.each { |state| define_method("#{state}?") { @code == state } }
|
Chris@1296
|
65
|
Chris@1296
|
66 def successful?
|
Chris@1296
|
67 @code == :successful
|
Chris@1296
|
68 end
|
Chris@1296
|
69
|
Chris@1296
|
70 def unsuccessful?
|
Chris@1296
|
71 ERROR_MESSAGES.keys.include?(@code)
|
Chris@1296
|
72 end
|
Chris@1296
|
73
|
Chris@1296
|
74 def message
|
Chris@1296
|
75 ERROR_MESSAGES[@code]
|
Chris@1296
|
76 end
|
Chris@1296
|
77 end
|
Chris@1296
|
78
|
Chris@1296
|
79 # normalizes an OpenID according to http://openid.net/specs/openid-authentication-2_0.html#normalization
|
Chris@1296
|
80 def self.normalize_identifier(identifier)
|
Chris@1296
|
81 # clean up whitespace
|
Chris@1296
|
82 identifier = identifier.to_s.strip
|
Chris@1296
|
83
|
Chris@1296
|
84 # if an XRI has a prefix, strip it.
|
Chris@1296
|
85 identifier.gsub!(/xri:\/\//i, '')
|
Chris@1296
|
86
|
Chris@1296
|
87 # dodge XRIs -- TODO: validate, don't just skip.
|
Chris@1296
|
88 unless ['=', '@', '+', '$', '!', '('].include?(identifier.at(0))
|
Chris@1296
|
89 # does it begin with http? if not, add it.
|
Chris@1296
|
90 identifier = "http://#{identifier}" unless identifier =~ /^http/i
|
Chris@1296
|
91
|
Chris@1296
|
92 # strip any fragments
|
Chris@1296
|
93 identifier.gsub!(/\#(.*)$/, '')
|
Chris@1296
|
94
|
Chris@1296
|
95 begin
|
Chris@1296
|
96 uri = URI.parse(identifier)
|
Chris@1296
|
97 uri.scheme = uri.scheme.downcase if uri.scheme # URI should do this
|
Chris@1296
|
98 identifier = uri.normalize.to_s
|
Chris@1296
|
99 rescue URI::InvalidURIError
|
Chris@1296
|
100 raise InvalidOpenId.new("#{identifier} is not an OpenID identifier")
|
Chris@1296
|
101 end
|
Chris@1296
|
102 end
|
Chris@1296
|
103
|
Chris@1296
|
104 return identifier
|
Chris@1296
|
105 end
|
Chris@1296
|
106
|
Chris@1296
|
107 protected
|
Chris@1296
|
108 # The parameter name of "openid_identifier" is used rather than
|
Chris@1296
|
109 # the Rails convention "open_id_identifier" because that's what
|
Chris@1296
|
110 # the specification dictates in order to get browser auto-complete
|
Chris@1296
|
111 # working across sites
|
Chris@1296
|
112 def using_open_id?(identifier = nil) #:doc:
|
Chris@1296
|
113 identifier ||= open_id_identifier
|
Chris@1296
|
114 !identifier.blank? || request.env[Rack::OpenID::RESPONSE]
|
Chris@1296
|
115 end
|
Chris@1296
|
116
|
Chris@1296
|
117 def authenticate_with_open_id(identifier = nil, options = {}, &block) #:doc:
|
Chris@1296
|
118 identifier ||= open_id_identifier
|
Chris@1296
|
119
|
Chris@1296
|
120 if request.env[Rack::OpenID::RESPONSE]
|
Chris@1296
|
121 complete_open_id_authentication(&block)
|
Chris@1296
|
122 else
|
Chris@1296
|
123 begin_open_id_authentication(identifier, options, &block)
|
Chris@1296
|
124 end
|
Chris@1296
|
125 end
|
Chris@1296
|
126
|
Chris@1296
|
127 private
|
Chris@1296
|
128 def open_id_identifier
|
Chris@1296
|
129 params[:openid_identifier] || params[:openid_url]
|
Chris@1296
|
130 end
|
Chris@1296
|
131
|
Chris@1296
|
132 def begin_open_id_authentication(identifier, options = {})
|
Chris@1296
|
133 options[:identifier] = identifier
|
Chris@1296
|
134 value = Rack::OpenID.build_header(options)
|
Chris@1296
|
135 response.headers[Rack::OpenID::AUTHENTICATE_HEADER] = value
|
Chris@1296
|
136 head :unauthorized
|
Chris@1296
|
137 end
|
Chris@1296
|
138
|
Chris@1296
|
139 def complete_open_id_authentication
|
Chris@1296
|
140 response = request.env[Rack::OpenID::RESPONSE]
|
Chris@1296
|
141 identifier = response.display_identifier
|
Chris@1296
|
142
|
Chris@1296
|
143 case response.status
|
Chris@1296
|
144 when OpenID::Consumer::SUCCESS
|
Chris@1296
|
145 yield Result[:successful], identifier,
|
Chris@1296
|
146 OpenID::SReg::Response.from_success_response(response)
|
Chris@1296
|
147 when :missing
|
Chris@1296
|
148 yield Result[:missing], identifier, nil
|
Chris@1296
|
149 when :invalid
|
Chris@1296
|
150 yield Result[:invalid], identifier, nil
|
Chris@1296
|
151 when OpenID::Consumer::CANCEL
|
Chris@1296
|
152 yield Result[:canceled], identifier, nil
|
Chris@1296
|
153 when OpenID::Consumer::FAILURE
|
Chris@1296
|
154 yield Result[:failed], identifier, nil
|
Chris@1296
|
155 when OpenID::Consumer::SETUP_NEEDED
|
Chris@1296
|
156 yield Result[:setup_needed], response.setup_url, nil
|
Chris@1296
|
157 end
|
Chris@1296
|
158 end
|
Chris@1296
|
159 end
|