annotate .svn/pristine/b4/b49f8e8955f20d373ff50965f6541cbad8b508b5.svn-base @ 1327:287f201c2802 redmine-2.2-integration

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