public
Description: master merb branch
Homepage: http://www.merbivore.com
Clone URL: git://github.com/wycats/merb.git
100644 153 lines (138 sloc) 5.485 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# The openid strategy attempts to login users based on the OpenID protocol
# http://openid.net/
#
# Overwrite the on_sucess!, on_failure!, on_setup_needed!, and on_cancel! to customize events.
#
# Overwite the required_reg_fields method to require different sreg fields. Default is email and nickname
#
# Overwrite the openid_store method to customize your session store
#
# == Requirments
#
# === Routes:
# :openid - an action that is accessilbe via http GET and protected via ensure_authenticated
# :signup - a url accessed via GET that takes a user to a signup form (overwritable)
#
# === Attributes
# :identity_url - A string for holding the identity_url associated with this user (overwritable)
#
# install the ruby-openid gem
 
require 'openid'
require 'openid/store/filesystem'
require 'openid/extensions/sreg'
 
class Merb::Authentication
  module Strategies
    module Basic
      class OpenID < Merb::Authentication::Strategy
        def run!
          if request.params[:'openid.mode']
            response = consumer.complete(request.send(:query_params), "#{request.protocol}://#{request.host}" + request.path)
            case response.status.to_s
            when 'success'
              sreg_response = ::OpenID::SReg::Response.from_success_response(response)
              result = on_success!(response, sreg_response)
              Merb.logger.info "\n\n#{result.inspect}\n\n"
              result
            when 'failure'
              on_failure!(response)
            when 'setup_needed'
              on_setup_needed!(response)
            when 'cancel'
              on_cancel!(response)
            end
          elsif identity_url = params[:openid_url]
            begin
              openid_request = consumer.begin(identity_url)
              openid_reg = ::OpenID::SReg::Request.new
              openid_reg.request_fields(required_reg_fields, true)
              openid_reg.request_fields(optional_reg_fields)
              openid_request.add_extension(openid_reg)
              customize_openid_request!(openid_request)
              redirect!(openid_request.redirect_url("#{request.protocol}://#{request.host}", openid_callback_url))
            rescue ::OpenID::OpenIDError => e
              request.session.authentication.errors.clear!
              request.session.authentication.errors.add(:openid, 'The OpenID verification failed')
              nil
            end
          end
        end # run!
        
        
        # Overwrite this to add extra options to the OpenID request before it is made.
        #
        # @example request.return_to_args["remember_me"] = 1 # remember_me=1 is added when returning from the OpenID provider.
        #
        # @api overwritable
        def customize_openid_request!(openid_request)
        end
        
        # Used to define the callback url for the openid provider. By default it
        # is set to the named :openid route.
        #
        # @api overwritable
        def openid_callback_url
          "#{request.protocol}://#{request.host}#{Merb::Router.url(:openid)}"
        end
        
        # Overwrite the on_success! method with the required behavior for successful logins
        #
        # @api overwritable
        def on_success!(response, sreg_response)
          if user = find_user_by_identity_url(response.identity_url)
            user
          else
            request.session[:'openid.url'] = response.identity_url
            required_reg_fields.each do |f|
              session[:"openid.#{f}"] = sreg_response.data[f] if sreg_response.data[f]
            end if sreg_response
            redirect!(Merb::Router.url(:signup))
          end
        end
        
        # Overwrite the on_failure! method with the required behavior for failed logins
        #
        # @api overwritable
        def on_failure!(response)
          session.authentication.errors.clear!
          session.authentication.errors.add(:openid, 'OpenID verification failed, maybe the provider is down? Or the session timed out')
          nil
        end
        
        #
        # @api overwritable
        def on_setup_needed!(response)
          request.session.authentication.errors.clear!
          request.session.authentication.errors.add(:openid, 'OpenID does not seem to be configured correctly')
          nil
        end
        
         #
         # @api overwritable
        def on_cancel!(response)
          request.session.authentication.errors.clear!
          request.session.authentication.errors.add(:openid, 'OpenID rejected our request')
          nil
        end
        
        #
        # @api overwritable
        def required_reg_fields
          ['nickname', 'email']
        end
        
        #
        # @api overwritable
        def optional_reg_fields
          ['fullname']
        end
        
        # Overwrite this to support an ORM other than DataMapper
        #
        # @api overwritable
        def find_user_by_identity_url(url)
          user_class.first(:identity_url => url)
        end
        
        # Overwrite this method to set your store
        #
        # @api overwritable
        def openid_store
          ::OpenID::Store::Filesystem.new("#{Merb.root}/tmp/openid")
        end
        
        private
        def consumer
          @consumer ||= ::OpenID::Consumer.new(request.session, openid_store)
        end
              
      end # OpenID
    end # Basic
  end # Strategies
end # Merb::Authentication