Axe DB store, use memcache.
[gitorious:openid_auth.git] / lib / open_id_authentication.rb
1 require 'uri'
2 require 'openid'
3 require 'openid/extensions/sreg'
4 require 'openid/extensions/ax'
5 require 'openid/store/filesystem'
6
7 require 'open_id_authentication/timeout_fixes' if OpenID::VERSION == "2.0.4"
8
9 require File.dirname(__FILE__) + '/../vendor/rack-openid/lib/rack/openid'
10
11 module OpenIdAuthentication
12   OPEN_ID_AUTHENTICATION_DIR = Rails.root.join('tmp/openids')
13
14   def self.new(app)
15     ::Rack::OpenID.new(app, OpenIdAuthentication.store)
16   end
17
18   def self.store
19     @@store
20   end
21
22   def self.store=(*store_option)
23     store, *parameters = *([ store_option ].flatten)
24
25     @@store = case store
26     when :memory
27       OpenID::Store::Memory.new
28     when :file
29       OpenID::Store::Filesystem.new(OPEN_ID_AUTHENTICATION_DIR)
30     else
31       store
32     end
33   end
34
35   self.store = :memory
36
37   class Result
38     ERROR_MESSAGES = {
39       :missing      => "Sorry, the OpenID server couldn't be found",
40       :invalid      => "Sorry, but this does not appear to be a valid OpenID",
41       :canceled     => "OpenID verification was canceled",
42       :failed       => "OpenID verification failed",
43       :setup_needed => "OpenID verification needs setup"
44     }
45
46     def self.[](code)
47       new(code)
48     end
49
50     def initialize(code)
51       @code = code
52     end
53
54     def status
55       @code
56     end
57
58     ERROR_MESSAGES.keys.each { |state| define_method("#{state}?") { @code == state } }
59
60     def successful?
61       @code == :successful
62     end
63
64     def unsuccessful?
65       ERROR_MESSAGES.keys.include?(@code)
66     end
67
68     def message
69       ERROR_MESSAGES[@code]
70     end
71   end
72
73   protected
74     # The parameter name of "openid_identifier" is used rather than
75     # the Rails convention "open_id_identifier" because that's what
76     # the specification dictates in order to get browser auto-complete
77     # working across sites
78     def using_open_id?(identifier = nil) #:doc:
79       identifier ||= open_id_identifier
80       !identifier.blank?
81     end
82
83     def authenticate_with_open_id(identifier = nil, options = {}, &block) #:doc:
84       identifier ||= open_id_identifier
85
86       if request.env[Rack::OpenID::RESPONSE]
87         complete_open_id_authentication(&block)
88       else
89         begin_open_id_authentication(identifier, options, &block)
90       end
91     end
92
93   private
94     def open_id_identifier
95       params[:openid_identifier] || params[:openid_url]
96     end
97
98     def begin_open_id_authentication(identifier, options = {})
99       options[:identifier] = identifier
100       value = Rack::OpenID.build_header(options)
101       response.headers[Rack::OpenID::AUTHENTICATE_HEADER] = value
102       head :unauthorized
103     end
104
105     def complete_open_id_authentication
106       response   = request.env[Rack::OpenID::RESPONSE]
107       identifier = response.display_identifier
108
109       case response.status
110       when OpenID::Consumer::SUCCESS
111         yield Result[:successful], identifier,
112           OpenID::SReg::Response.from_success_response(response)
113       when :missing
114         yield Result[:missing], identifier, nil
115       when :invalid
116         yield Result[:invalid], identifier, nil
117       when OpenID::Consumer::CANCEL
118         yield Result[:canceled], identifier, nil
119       when OpenID::Consumer::FAILURE
120         yield Result[:failed], identifier, nil
121       when OpenID::Consumer::SETUP_NEEDED
122         yield Result[:setup_needed], response.setup_url, nil
123       end
124     end
125 end