Class CloudKit::OpenIDFilter

  1. lib/cloudkit/openid_filter.rb
Parent: Object

An OpenIDFilter provides OpenID authentication, listening for upstream OAuth authentication and bypassing if already authorized.

The root URI, “/”, is always bypassed. More URIs can also be bypassed using the :allow option:

use Rack::Session::Pool
use OpenIDFilter, :allow => ['/foo', '/bar']

In addition to the :allow option, a block can also be used for more complex decisions:

use Rack::Session::Pool
use OpenIDFilter, :allow => ['/foo'] do |url|
  bar(url) # some method returning true or false
end

Responds to the following URIs:

/login
/logout
/openid_complete

Included modules

  1. Util

Public class methods

new (app, options={}, &bypass_route_callback)
[show source]
    # File lib/cloudkit/openid_filter.rb, line 31
31:     def initialize(app, options={}, &bypass_route_callback)
32:       @app     = app
33:       @options = options
34:       @bypass_route_callback = bypass_route_callback || Proc.new {|url| url == '/'}
35:     end

Public instance methods

allow? (uri)
[show source]
     # File lib/cloudkit/openid_filter.rb, line 225
225:     def allow?(uri)
226:       @bypass_route_callback.call(uri) || 
227:         @options[:allow] && @options[:allow].include?(uri)
228:     end
base_url (request)
[show source]
     # File lib/cloudkit/openid_filter.rb, line 164
164:     def base_url(request)
165:       "#{request.scheme}://#{request.env['HTTP_HOST']}/"
166:     end
begin_openid_login (request)
[show source]
     # File lib/cloudkit/openid_filter.rb, line 95
 95:     def begin_openid_login(request)
 96:       begin
 97:         response = openid_consumer(request).begin(request[:openid_url])
 98:       rescue => e
 99:         request.flash[:error] = e
100:         return login_redirect(request)
101:       end
102: 
103:       redirect_url = response.redirect_url(base_url(request), full_url(request))
104:       Rack::Response.new([], 302, {'Location' => redirect_url}).finish
105:     end
bypass? (request)
[show source]
     # File lib/cloudkit/openid_filter.rb, line 230
230:     def bypass?(request)
231:       allow?(request.path_info) ||
232:         valid_auth_key?(request) ||
233:         logged_in?(request)
234:     end
call (env)
[show source]
    # File lib/cloudkit/openid_filter.rb, line 37
37:     def call(env)
38:       @@lock.synchronize do
39:         @@store = OpenIDStore.new
40:         @users  = UserStore.new
41:       end unless @@store
42: 
43:       request = Request.new(env)
44:       request.announce_auth(CLOUDKIT_OPENID_FILTER_KEY)
45: 
46:       case request
47:       when r(:get, request.login_url); request_login(request)
48:       when r(:post, request.login_url); begin_openid_login(request)
49:       when r(:get, '/openid_complete'); complete_openid_login(request)
50:       when r(:post, request.logout_url); logout(request)
51:       else
52:         if bypass?(request)
53:           @app.call(env)
54:         else
55:           if request.env[CLOUDKIT_AUTH_CHALLENGE]
56:             store_location(request)
57:             erb(
58:               request,
59:               :openid_login,
60:               request.env[CLOUDKIT_AUTH_CHALLENGE].merge('Content-Type' => 'text/html'),
61:               401)
62:           elsif !request.via.include?(CLOUDKIT_OAUTH_FILTER_KEY)
63:             store_location(request)
64:             login_redirect(request)
65:           else
66:             Rack::Response.new('server misconfigured', 500).finish
67:           end
68:         end
69:       end
70:     end
complete_openid_login (request)
[show source]
     # File lib/cloudkit/openid_filter.rb, line 107
107:     def complete_openid_login(request)
108:       begin
109:         idp_response = openid_consumer(request).complete(request.params, full_url(request))
110:       rescue => e
111:         request.flash[:error] = e
112:         return login_redirect(request)
113:       end
114: 
115:       if idp_response.is_a?(OpenID::Consumer::FailureResponse)
116:         request.flash[:error] = idp_response.message
117:         return login_redirect(request)
118:       end
119: 
120:       result = @users.get(
121:         '/cloudkit_users',
122:         # '/cloudkit_login_view',
123:         :identity_url => idp_response.endpoint.claimed_id)
124:       user_uris = result.parsed_content['uris']
125: 
126:       if user_uris.empty?
127:         json     = JSON.generate(:identity_url => idp_response.endpoint.claimed_id)
128:         result   = @users.post('/cloudkit_users', :json => json)
129:         user_uri = result.parsed_content['uri']
130:       else
131:         user_uri = user_uris.first
132:       end
133:       user_result = @users.resolve_uris([user_uri]).first
134:       user        = user_result.parsed_content
135: 
136:       if request.session['user_uri'] = user_uri
137:         request.current_user = user_uri
138:         user['remember_me_expiration'] = two_weeks_from_now
139:         user['remember_me_token'] = Base64.encode64(
140:           OpenSSL::Random.random_bytes(32)).gsub(/\W/,'')
141:         url      = request.session.delete('return_to')
142:         response = Rack::Response.new(
143:           [],
144:           302,
145:           {'Location' => (url || '/'), 'Content-Type' => 'text/html'})
146:         response.set_cookie(
147:           'remember_me', {
148:             :value   => user['remember_me_token'],
149:             :expires => Time.at(user['remember_me_expiration']).utc})
150:         json = JSON.generate(user)
151:         @users.put(user_uri, :etag => user_result.etag, :json => json)
152:         request.flash[:notice] = 'You have been logged in.'
153:         response.finish
154:       else
155:         request.flash[:error] = 'Could not log on with your OpenID.'
156:         login_redirect(request)
157:       end
158:     end
full_url (request)
[show source]
     # File lib/cloudkit/openid_filter.rb, line 168
168:     def full_url(request)
169:       base_url(request) + 'openid_complete'
170:     end
logged_in? (request)
[show source]
     # File lib/cloudkit/openid_filter.rb, line 172
172:     def logged_in?(request)
173:       logged_in = user_in_session?(request) || valid_remember_me_token?(request)
174:       request.current_user = request.session['user_uri'] if logged_in
175:       logged_in
176:     end
login_redirect (request)
[show source]
     # File lib/cloudkit/openid_filter.rb, line 160
160:     def login_redirect(request)
161:       Rack::Response.new([], 302, {'Location' => request.login_url}).finish
162:     end
logout (request)
[show source]
    # File lib/cloudkit/openid_filter.rb, line 72
72:     def logout(request)
73:       user_uri = request.session.delete('user_uri')
74:       result   = @users.get(user_uri)
75:       user     = result.parsed_content
76:       user.delete('remember_me_token')
77:       user.delete('remember_me_expiration')
78:       json = JSON.generate(user)
79:       @users.put(user_uri, :etag => result.etag, :json => json)
80: 
81:       request.env[CLOUDKIT_AUTH_KEY] = nil
82:       request.flash['info'] = 'You have been logged out.'
83:       response = Rack::Response.new(
84:         [],
85:         302,
86:         {'Location' => request.login_url, 'Content-Type' => 'text/html'})
87:       response.delete_cookie('remember_me')
88:       response.finish
89:     end
openid_consumer (request)
[show source]
     # File lib/cloudkit/openid_filter.rb, line 194
194:     def openid_consumer(request)
195:       @openid_consumer ||= OpenID::Consumer.new(
196:         request.session, OpenIDStore.new)
197:     end
request_login (request)
[show source]
    # File lib/cloudkit/openid_filter.rb, line 91
91:     def request_login(request)
92:       erb(request, :openid_login)
93:     end
root_request? (request)
[show source]
     # File lib/cloudkit/openid_filter.rb, line 186
186:     def root_request?(request)
187:       request.path_info == '/' || request.path_info == '/favicon.ico'
188:     end
store_location (request)
[show source]
     # File lib/cloudkit/openid_filter.rb, line 182
182:     def store_location(request)
183:       request.session['return_to'] = request.url
184:     end
two_weeks_from_now ()
[show source]
     # File lib/cloudkit/openid_filter.rb, line 221
221:     def two_weeks_from_now
222:       Time.now.to_i+1209600
223:     end
user_in_session? (request)
[show source]
     # File lib/cloudkit/openid_filter.rb, line 178
178:     def user_in_session?(request)
179:       request.session['user_uri'] != nil
180:     end
valid_auth_key? (request)
[show source]
     # File lib/cloudkit/openid_filter.rb, line 190
190:     def valid_auth_key?(request)
191:       request.env[CLOUDKIT_AUTH_KEY] && request.env[CLOUDKIT_AUTH_KEY] != ''
192:     end
valid_remember_me_token? (request)
[show source]
     # File lib/cloudkit/openid_filter.rb, line 199
199:     def valid_remember_me_token?(request)
200:       return false unless token = request.cookies['remember_me']
201: 
202:       # result = @users.get('/cloudkit_login_view', :remember_me_token => token)
203:       result = @users.get('/cloudkit_users', :remember_me_token => token)
204:       return false unless result.status == 200
205: 
206:       user_uris = result.parsed_content['uris']
207:       return false unless user_uris.try(:size) == 1
208: 
209:       user_uri    = user_uris.first
210:       user_result = @users.resolve_uris([user_uri]).first
211:       user        = user_result.parsed_content
212:       return false unless Time.now.to_i < user['remember_me_expiration']
213: 
214:       user['remember_me_expiration'] = two_weeks_from_now
215:       json = JSON.generate(user)
216:       @users.put(user_uri, :etag => user_result.etag, :json => json)
217:       request.session['user_uri'] = user_uri
218:       true
219:     end