annotate .svn/pristine/bc/bcf601e4c2eab267682f5366299899e20f6d08d2.svn-base @ 1298:4f746d8966dd redmine_2.3_integration

Merge from redmine-2.3 branch to create new branch redmine-2.3-integration
author Chris Cannam
date Fri, 14 Jun 2013 09:28:30 +0100
parents 622f24f53b42
children
rev   line source
Chris@1295 1 OpenIdAuthentication
Chris@1295 2 ====================
Chris@1295 3
Chris@1295 4 Provides a thin wrapper around the excellent ruby-openid gem from JanRan. Be sure to install that first:
Chris@1295 5
Chris@1295 6 gem install ruby-openid
Chris@1295 7
Chris@1295 8 To understand what OpenID is about and how it works, it helps to read the documentation for lib/openid/consumer.rb
Chris@1295 9 from that gem.
Chris@1295 10
Chris@1295 11 The specification used is http://openid.net/specs/openid-authentication-2_0.html.
Chris@1295 12
Chris@1295 13
Chris@1295 14 Prerequisites
Chris@1295 15 =============
Chris@1295 16
Chris@1295 17 OpenID authentication uses the session, so be sure that you haven't turned that off.
Chris@1295 18
Chris@1295 19 Alternatively, you can use the file-based store, which just relies on on tmp/openids being present in RAILS_ROOT. But be aware that this store only works if you have a single application server. And it's not safe to use across NFS. It's recommended that you use the database store if at all possible. To use the file-based store, you'll also have to add this line to your config/environment.rb:
Chris@1295 20
Chris@1295 21 OpenIdAuthentication.store = :file
Chris@1295 22
Chris@1295 23 This particular plugin also relies on the fact that the authentication action allows for both POST and GET operations.
Chris@1295 24 If you're using RESTful authentication, you'll need to explicitly allow for this in your routes.rb.
Chris@1295 25
Chris@1295 26 The plugin also expects to find a root_url method that points to the home page of your site. You can accomplish this by using a root route in config/routes.rb:
Chris@1295 27
Chris@1295 28 map.root :controller => 'articles'
Chris@1295 29
Chris@1295 30 This plugin relies on Rails Edge revision 6317 or newer.
Chris@1295 31
Chris@1295 32
Chris@1295 33 Example
Chris@1295 34 =======
Chris@1295 35
Chris@1295 36 This example is just to meant to demonstrate how you could use OpenID authentication. You might well want to add
Chris@1295 37 salted hash logins instead of plain text passwords and other requirements on top of this. Treat it as a starting point,
Chris@1295 38 not a destination.
Chris@1295 39
Chris@1295 40 Note that the User model referenced in the simple example below has an 'identity_url' attribute. You will want to add the same or similar field to whatever
Chris@1295 41 model you are using for authentication.
Chris@1295 42
Chris@1295 43 Also of note is the following code block used in the example below:
Chris@1295 44
Chris@1295 45 authenticate_with_open_id do |result, identity_url|
Chris@1295 46 ...
Chris@1295 47 end
Chris@1295 48
Chris@1295 49 In the above code block, 'identity_url' will need to match user.identity_url exactly. 'identity_url' will be a string in the form of 'http://example.com' -
Chris@1295 50 If you are storing just 'example.com' with your user, the lookup will fail.
Chris@1295 51
Chris@1295 52 There is a handy method in this plugin called 'normalize_url' that will help with validating OpenID URLs.
Chris@1295 53
Chris@1295 54 OpenIdAuthentication.normalize_url(user.identity_url)
Chris@1295 55
Chris@1295 56 The above will return a standardized version of the OpenID URL - the above called with 'example.com' will return 'http://example.com/'
Chris@1295 57 It will also raise an InvalidOpenId exception if the URL is determined to not be valid.
Chris@1295 58 Use the above code in your User model and validate OpenID URLs before saving them.
Chris@1295 59
Chris@1295 60 config/routes.rb
Chris@1295 61
Chris@1295 62 map.root :controller => 'articles'
Chris@1295 63 map.resource :session
Chris@1295 64
Chris@1295 65
Chris@1295 66 app/views/sessions/new.erb
Chris@1295 67
Chris@1295 68 <% form_tag(session_url) do %>
Chris@1295 69 <p>
Chris@1295 70 <label for="name">Username:</label>
Chris@1295 71 <%= text_field_tag "name" %>
Chris@1295 72 </p>
Chris@1295 73
Chris@1295 74 <p>
Chris@1295 75 <label for="password">Password:</label>
Chris@1295 76 <%= password_field_tag %>
Chris@1295 77 </p>
Chris@1295 78
Chris@1295 79 <p>
Chris@1295 80 ...or use:
Chris@1295 81 </p>
Chris@1295 82
Chris@1295 83 <p>
Chris@1295 84 <label for="openid_identifier">OpenID:</label>
Chris@1295 85 <%= text_field_tag "openid_identifier" %>
Chris@1295 86 </p>
Chris@1295 87
Chris@1295 88 <p>
Chris@1295 89 <%= submit_tag 'Sign in', :disable_with => "Signing in&hellip;" %>
Chris@1295 90 </p>
Chris@1295 91 <% end %>
Chris@1295 92
Chris@1295 93 app/controllers/sessions_controller.rb
Chris@1295 94 class SessionsController < ApplicationController
Chris@1295 95 def create
Chris@1295 96 if using_open_id?
Chris@1295 97 open_id_authentication
Chris@1295 98 else
Chris@1295 99 password_authentication(params[:name], params[:password])
Chris@1295 100 end
Chris@1295 101 end
Chris@1295 102
Chris@1295 103
Chris@1295 104 protected
Chris@1295 105 def password_authentication(name, password)
Chris@1295 106 if @current_user = @account.users.authenticate(params[:name], params[:password])
Chris@1295 107 successful_login
Chris@1295 108 else
Chris@1295 109 failed_login "Sorry, that username/password doesn't work"
Chris@1295 110 end
Chris@1295 111 end
Chris@1295 112
Chris@1295 113 def open_id_authentication
Chris@1295 114 authenticate_with_open_id do |result, identity_url|
Chris@1295 115 if result.successful?
Chris@1295 116 if @current_user = @account.users.find_by_identity_url(identity_url)
Chris@1295 117 successful_login
Chris@1295 118 else
Chris@1295 119 failed_login "Sorry, no user by that identity URL exists (#{identity_url})"
Chris@1295 120 end
Chris@1295 121 else
Chris@1295 122 failed_login result.message
Chris@1295 123 end
Chris@1295 124 end
Chris@1295 125 end
Chris@1295 126
Chris@1295 127
Chris@1295 128 private
Chris@1295 129 def successful_login
Chris@1295 130 session[:user_id] = @current_user.id
Chris@1295 131 redirect_to(root_url)
Chris@1295 132 end
Chris@1295 133
Chris@1295 134 def failed_login(message)
Chris@1295 135 flash[:error] = message
Chris@1295 136 redirect_to(new_session_url)
Chris@1295 137 end
Chris@1295 138 end
Chris@1295 139
Chris@1295 140
Chris@1295 141
Chris@1295 142 If you're fine with the result messages above and don't need individual logic on a per-failure basis,
Chris@1295 143 you can collapse the case into a mere boolean:
Chris@1295 144
Chris@1295 145 def open_id_authentication
Chris@1295 146 authenticate_with_open_id do |result, identity_url|
Chris@1295 147 if result.successful? && @current_user = @account.users.find_by_identity_url(identity_url)
Chris@1295 148 successful_login
Chris@1295 149 else
Chris@1295 150 failed_login(result.message || "Sorry, no user by that identity URL exists (#{identity_url})")
Chris@1295 151 end
Chris@1295 152 end
Chris@1295 153 end
Chris@1295 154
Chris@1295 155
Chris@1295 156 Simple Registration OpenID Extension
Chris@1295 157 ====================================
Chris@1295 158
Chris@1295 159 Some OpenID Providers support this lightweight profile exchange protocol. See more: http://www.openidenabled.com/openid/simple-registration-extension
Chris@1295 160
Chris@1295 161 You can support it in your app by changing #open_id_authentication
Chris@1295 162
Chris@1295 163 def open_id_authentication(identity_url)
Chris@1295 164 # Pass optional :required and :optional keys to specify what sreg fields you want.
Chris@1295 165 # Be sure to yield registration, a third argument in the #authenticate_with_open_id block.
Chris@1295 166 authenticate_with_open_id(identity_url,
Chris@1295 167 :required => [ :nickname, :email ],
Chris@1295 168 :optional => :fullname) do |result, identity_url, registration|
Chris@1295 169 case result.status
Chris@1295 170 when :missing
Chris@1295 171 failed_login "Sorry, the OpenID server couldn't be found"
Chris@1295 172 when :invalid
Chris@1295 173 failed_login "Sorry, but this does not appear to be a valid OpenID"
Chris@1295 174 when :canceled
Chris@1295 175 failed_login "OpenID verification was canceled"
Chris@1295 176 when :failed
Chris@1295 177 failed_login "Sorry, the OpenID verification failed"
Chris@1295 178 when :successful
Chris@1295 179 if @current_user = @account.users.find_by_identity_url(identity_url)
Chris@1295 180 assign_registration_attributes!(registration)
Chris@1295 181
Chris@1295 182 if current_user.save
Chris@1295 183 successful_login
Chris@1295 184 else
Chris@1295 185 failed_login "Your OpenID profile registration failed: " +
Chris@1295 186 @current_user.errors.full_messages.to_sentence
Chris@1295 187 end
Chris@1295 188 else
Chris@1295 189 failed_login "Sorry, no user by that identity URL exists"
Chris@1295 190 end
Chris@1295 191 end
Chris@1295 192 end
Chris@1295 193 end
Chris@1295 194
Chris@1295 195 # registration is a hash containing the valid sreg keys given above
Chris@1295 196 # use this to map them to fields of your user model
Chris@1295 197 def assign_registration_attributes!(registration)
Chris@1295 198 model_to_registration_mapping.each do |model_attribute, registration_attribute|
Chris@1295 199 unless registration[registration_attribute].blank?
Chris@1295 200 @current_user.send("#{model_attribute}=", registration[registration_attribute])
Chris@1295 201 end
Chris@1295 202 end
Chris@1295 203 end
Chris@1295 204
Chris@1295 205 def model_to_registration_mapping
Chris@1295 206 { :login => 'nickname', :email => 'email', :display_name => 'fullname' }
Chris@1295 207 end
Chris@1295 208
Chris@1295 209 Attribute Exchange OpenID Extension
Chris@1295 210 ===================================
Chris@1295 211
Chris@1295 212 Some OpenID providers also support the OpenID AX (attribute exchange) protocol for exchanging identity information between endpoints. See more: http://openid.net/specs/openid-attribute-exchange-1_0.html
Chris@1295 213
Chris@1295 214 Accessing AX data is very similar to the Simple Registration process, described above -- just add the URI identifier for the AX field to your :optional or :required parameters. For example:
Chris@1295 215
Chris@1295 216 authenticate_with_open_id(identity_url,
Chris@1295 217 :required => [ :email, 'http://schema.openid.net/birthDate' ]) do |result, identity_url, registration|
Chris@1295 218
Chris@1295 219 This would provide the sreg data for :email, and the AX data for 'http://schema.openid.net/birthDate'
Chris@1295 220
Chris@1295 221
Chris@1295 222
Chris@1295 223 Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license