| |   |
| 1 | | # This controller handles the login/logout function of the site. |
| 1 | require "openid" |
| 2 | require "yadis" |
| 3 | # This controller handles the login/logout function of the site. |
| 2 | 4 | class SessionsController < ApplicationController |
| 3 | 5 | # render new.rhtml |
| 4 | 6 | def new |
| 5 | 7 | end |
| 6 | 8 | |
| 7 | 9 | def create |
| 8 | | self.current_user = User.authenticate(params[:email], params[:password]) |
| 9 | | if logged_in? |
| 10 | | if params[:remember_me] == "1" |
| 11 | | self.current_user.remember_me |
| 12 | | cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at } |
| 13 | | end |
| 14 | | redirect_back_or_default('/') |
| 15 | | flash[:notice] = "Logged in successfully" |
| 10 | if using_open_id? |
| 11 | open_id_authentication(params[:openid_url]) |
| 16 | 12 | else |
| 17 | | flash.now[:error] = "Username/password didn't match, please try again." |
| 18 | | render :action => 'new' |
| 13 | password_authentication(params[:email], params[:password]) |
| 19 | 14 | end |
| 20 | 15 | end |
| 21 | 16 | |
| … | … | |
| 21 | 21 | flash[:notice] = "You have been logged out." |
| 22 | 22 | redirect_back_or_default('/') |
| 23 | 23 | end |
| 24 | |
| 25 | protected |
| 26 | |
| 27 | # if user doesn't exist, it gets created and activated, |
| 28 | # else if the user already exists with same identity_url, it just logs in |
| 29 | def open_id_authentication(openid_url) |
| 30 | authenticate_with_open_id(openid_url, :required => [:nickname, :email], :optional => [:fullname]) do |result, identity_url, registration| |
| 31 | if result.successful? |
| 32 | @user = User.find_or_initialize_by_identity_url(identity_url) |
| 33 | if @user.new_record? |
| 34 | @user.login = registration['nickname'] |
| 35 | @user.fullname = registration['fullname'] |
| 36 | @user.email = registration['email'] |
| 37 | @user.save! |
| 38 | @user.activate |
| 39 | end |
| 40 | self.current_user = @user |
| 41 | successful_login |
| 42 | else |
| 43 | failed_login result.message, 'openid' |
| 44 | end |
| 45 | end |
| 46 | rescue ActiveRecord::RecordInvalid => invalid |
| 47 | flash[:error] = "This login (<strong>#{@user.login}</strong>) already exists, please <a href="+@user.identity_url+">choose a different persona or modify the current one</a>" |
| 48 | |
| 49 | redirect_to login_path(:method => 'openid') |
| 50 | end |
| 51 | |
| 52 | def password_authentication(email, password) |
| 53 | ##self.current_user = User.authenticate(login, password) |
| 54 | self.current_user = User.authenticate(email, password) |
| 55 | if logged_in? |
| 56 | successful_login |
| 57 | else |
| 58 | failed_login("Username/password didn't match, please try again.") |
| 59 | end |
| 60 | end |
| 61 | |
| 62 | def failed_login(message = "Authentication failed.",method="") |
| 63 | if method=='' |
| 64 | flash.now[:error] = message |
| 65 | render :action => 'new' |
| 66 | else |
| 67 | redirect_to login_path(:method=>method) |
| 68 | flash[:error] = message |
| 69 | end |
| 70 | end |
| 71 | |
| 72 | def successful_login |
| 73 | if params[:remember_me] == "1" |
| 74 | self.current_user.remember_me |
| 75 | cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at } |
| 76 | end |
| 77 | redirect_back_or_default('/') |
| 78 | flash[:notice] = "Logged in successfully" |
| 79 | end |
| 80 | |
| 24 | 81 | end |
| toggle raw diff |
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -1,21 +1,16 @@
-# This controller handles the login/logout function of the site.
+ require "openid"
+ require "yadis"
+# This controller handles the login/logout function of the site.
class SessionsController < ApplicationController
# render new.rhtml
def new
end
def create
- self.current_user = User.authenticate(params[:email], params[:password])
- if logged_in?
- if params[:remember_me] == "1"
- self.current_user.remember_me
- cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
- end
- redirect_back_or_default('/')
- flash[:notice] = "Logged in successfully"
+ if using_open_id?
+ open_id_authentication(params[:openid_url])
else
- flash.now[:error] = "Username/password didn't match, please try again."
- render :action => 'new'
+ password_authentication(params[:email], params[:password])
end
end
@@ -26,4 +21,61 @@ class SessionsController < ApplicationController
flash[:notice] = "You have been logged out."
redirect_back_or_default('/')
end
+
+ protected
+
+ # if user doesn't exist, it gets created and activated,
+ # else if the user already exists with same identity_url, it just logs in
+ def open_id_authentication(openid_url)
+ authenticate_with_open_id(openid_url, :required => [:nickname, :email], :optional => [:fullname]) do |result, identity_url, registration|
+ if result.successful?
+ @user = User.find_or_initialize_by_identity_url(identity_url)
+ if @user.new_record?
+ @user.login = registration['nickname']
+ @user.fullname = registration['fullname']
+ @user.email = registration['email']
+ @user.save!
+ @user.activate
+ end
+ self.current_user = @user
+ successful_login
+ else
+ failed_login result.message, 'openid'
+ end
+ end
+ rescue ActiveRecord::RecordInvalid => invalid
+ flash[:error] = "This login (<strong>#{@user.login}</strong>) already exists, please <a href="+@user.identity_url+">choose a different persona or modify the current one</a>"
+
+ redirect_to login_path(:method => 'openid')
+ end
+
+ def password_authentication(email, password)
+ ##self.current_user = User.authenticate(login, password)
+ self.current_user = User.authenticate(email, password)
+ if logged_in?
+ successful_login
+ else
+ failed_login("Username/password didn't match, please try again.")
+ end
+ end
+
+ def failed_login(message = "Authentication failed.",method="")
+ if method==''
+ flash.now[:error] = message
+ render :action => 'new'
+ else
+ redirect_to login_path(:method=>method)
+ flash[:error] = message
+ end
+ end
+
+ def successful_login
+ if params[:remember_me] == "1"
+ self.current_user.remember_me
+ cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
+ end
+ redirect_back_or_default('/')
+ flash[:notice] = "Logged in successfully"
+ end
+
end |
| |   |
| 1 | 1 | module SessionsHelper |
| 2 | | end |
| 2 | |
| 3 | # determines which form to display for login |
| 4 | def login_method |
| 5 | if params[:method]=='openid' |
| 6 | "<script type=\"text/javascript\"> Event.observe(window, 'load', |
| 7 | function() { |
| 8 | Element.toggle(\"regular_login_fields\"); |
| 9 | Element.toggle(\"openid_login_fields\"); |
| 10 | }) |
| 11 | </script>" |
| 12 | end |
| 13 | end |
| 14 | |
| 15 | def switch_login(title, action) |
| 16 | link_to_function title do |page| |
| 17 | page.toggle "regular_login_fields" |
| 18 | page.toggle "openid_login_fields" |
| 19 | end |
| 20 | end |
| 21 | end |
| toggle raw diff |
--- a/app/helpers/sessions_helper.rb
+++ b/app/helpers/sessions_helper.rb
@@ -1,2 +1,21 @@
module SessionsHelper
-end
\ No newline at end of file
+
+ # determines which form to display for login
+ def login_method
+ if params[:method]=='openid'
+ "<script type=\"text/javascript\"> Event.observe(window, 'load',
+ function() {
+Element.toggle(\"regular_login_fields\");
+Element.toggle(\"openid_login_fields\");
+})
+ </script>"
+ end
+ end
+
+ def switch_login(title, action)
+ link_to_function title do |page|
+ page.toggle "regular_login_fields"
+ page.toggle "openid_login_fields"
+ end
+ end
+end |
| |   |
| 6 | 6 | has_many :repositories, :through => :committerships |
| 7 | 7 | has_many :ssh_keys, :order => "id desc" |
| 8 | 8 | has_many :comments |
| 9 | | |
| 9 | |
| 10 | 10 | # Virtual attribute for the unencrypted password |
| 11 | 11 | attr_accessor :password, :current_password |
| 12 | | |
| 12 | |
| 13 | 13 | attr_protected :login |
| 14 | 14 | |
| 15 | | validates_presence_of :login, :email |
| 15 | validates_presence_of :login, :email, :if => :password_required? |
| 16 | 16 | validates_format_of :login, :with => /^[a-z0-9\-_\.]+$/i |
| 17 | 17 | validates_format_of :email, :with => /^[^@\s]+@([\-a-z0-9]+\.)+[a-z]{2,}$/i |
| 18 | 18 | validates_presence_of :password, :if => :password_required? |
| … | … | |
| 22 | 22 | validates_length_of :login, :within => 3..40 |
| 23 | 23 | validates_length_of :email, :within => 3..100 |
| 24 | 24 | validates_uniqueness_of :login, :email, :case_sensitive => false |
| 25 | | |
| 25 | |
| 26 | 26 | before_save :encrypt_password |
| 27 | | before_create :make_activation_code |
| 28 | | |
| 27 | before_create :make_activation_code |
| 28 | |
| 29 | 29 | def self.find_by_login!(name) |
| 30 | 30 | find_by_login(name) || raise(ActiveRecord::RecordNotFound ) |
| 31 | 31 | end |
| 32 | | |
| 32 | |
| 33 | 33 | # Authenticates a user by their login name and unencrypted password. Returns the user or nil. |
| 34 | 34 | def self.authenticate(email, password) |
| 35 | 35 | u = find :first, :conditions => ['email = ? and activated_at IS NOT NULL', email] # need to get the salt |
| … | … | |
| 40 | 40 | def self.encrypt(password, salt) |
| 41 | 41 | Digest::SHA1.hexdigest("--#{salt}--#{password}--") |
| 42 | 42 | end |
| 43 | | |
| 43 | |
| 44 | 44 | def self.generate_random_password(password_size = 12) |
| 45 | 45 | characters = (("a".."z").to_a + ("0".."9").to_a) - %w[0 o i l 1] |
| 46 | 46 | (0...password_size).collect do |char| |
| … | … | |
| 75 | 75 | end |
| 76 | 76 | |
| 77 | 77 | def remember_token? |
| 78 | | remember_token_expires_at && Time.now.utc < remember_token_expires_at |
| 78 | remember_token_expires_at && Time.now.utc < remember_token_expires_at |
| 79 | 79 | end |
| 80 | 80 | |
| 81 | 81 | # These create and unset the fields required for remembering users between browser closes |
| … | … | |
| 98 | 98 | self.remember_token = nil |
| 99 | 99 | save(false) |
| 100 | 100 | end |
| 101 | | |
| 101 | |
| 102 | 102 | def reset_password! |
| 103 | 103 | generated = User.generate_random_password |
| 104 | 104 | self.password = generated |
| … | … | |
| 106 | 106 | self.save! |
| 107 | 107 | generated |
| 108 | 108 | end |
| 109 | | |
| 109 | |
| 110 | 110 | def can_write_to?(repository) |
| 111 | 111 | !!committerships.find_by_repository_id(repository.id) |
| 112 | 112 | end |
| 113 | | |
| 113 | |
| 114 | 114 | def to_param |
| 115 | 115 | login |
| 116 | 116 | end |
| 117 | | |
| 117 | |
| 118 | 118 | def to_xml(opts = {}) |
| 119 | 119 | super({:except => [:activation_code, :crypted_password, :remember_token, :remember_token_expires_at, :salt, :ssh_key_id]}.merge(opts)) |
| 120 | 120 | end |
| 121 | 121 | |
| 122 | 122 | protected |
| 123 | | # before filter |
| 123 | # before filter |
| 124 | 124 | def encrypt_password |
| 125 | 125 | return if password.blank? |
| 126 | 126 | self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record? |
| 127 | 127 | self.crypted_password = encrypt(password) |
| 128 | 128 | end |
| 129 | | |
| 129 | |
| 130 | 130 | def password_required? |
| 131 | | crypted_password.blank? || !password.blank? |
| 131 | not_openid? && (crypted_password.blank? || !password.blank?) |
| 132 | end |
| 133 | |
| 134 | def not_openid? |
| 135 | identity_url.blank? |
| 132 | 136 | end |
| 133 | | |
| 137 | |
| 134 | 138 | def make_activation_code |
| 135 | 139 | self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join ) |
| 136 | | end |
| 140 | end |
| 137 | 141 | end |
| toggle raw diff |
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -6,13 +6,13 @@ class User < ActiveRecord::Base
has_many :repositories, :through => :committerships
has_many :ssh_keys, :order => "id desc"
has_many :comments
-
+
# Virtual attribute for the unencrypted password
attr_accessor :password, :current_password
-
+
attr_protected :login
- validates_presence_of :login, :email
+ validates_presence_of :login, :email, :if => :password_required?
validates_format_of :login, :with => /^[a-z0-9\-_\.]+$/i
validates_format_of :email, :with => /^[^@\s]+@([\-a-z0-9]+\.)+[a-z]{2,}$/i
validates_presence_of :password, :if => :password_required?
@@ -22,14 +22,14 @@ class User < ActiveRecord::Base
validates_length_of :login, :within => 3..40
validates_length_of :email, :within => 3..100
validates_uniqueness_of :login, :email, :case_sensitive => false
-
+
before_save :encrypt_password
- before_create :make_activation_code
-
+ before_create :make_activation_code
+
def self.find_by_login!(name)
find_by_login(name) || raise(ActiveRecord::RecordNotFound )
end
-
+
# Authenticates a user by their login name and unencrypted password. Returns the user or nil.
def self.authenticate(email, password)
u = find :first, :conditions => ['email = ? and activated_at IS NOT NULL', email] # need to get the salt
@@ -40,7 +40,7 @@ class User < ActiveRecord::Base
def self.encrypt(password, salt)
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
end
-
+
def self.generate_random_password(password_size = 12)
characters = (("a".."z").to_a + ("0".."9").to_a) - %w[0 o i l 1]
(0...password_size).collect do |char|
@@ -75,7 +75,7 @@ class User < ActiveRecord::Base
end
def remember_token?
- remember_token_expires_at && Time.now.utc < remember_token_expires_at
+ remember_token_expires_at && Time.now.utc < remember_token_expires_at
end
# These create and unset the fields required for remembering users between browser closes
@@ -98,7 +98,7 @@ class User < ActiveRecord::Base
self.remember_token = nil
save(false)
end
-
+
def reset_password!
generated = User.generate_random_password
self.password = generated
@@ -106,32 +106,36 @@ class User < ActiveRecord::Base
self.save!
generated
end
-
+
def can_write_to?(repository)
!!committerships.find_by_repository_id(repository.id)
end
-
+
def to_param
login
end
-
+
def to_xml(opts = {})
super({:except => [:activation_code, :crypted_password, :remember_token, :remember_token_expires_at, :salt, :ssh_key_id]}.merge(opts))
end
protected
- # before filter
+ # before filter
def encrypt_password
return if password.blank?
self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
self.crypted_password = encrypt(password)
end
-
+
def password_required?
- crypted_password.blank? || !password.blank?
+ not_openid? && (crypted_password.blank? || !password.blank?)
+ end
+
+ def not_openid?
+ identity_url.blank?
end
-
+
def make_activation_code
self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
- end
+ end
end |
| |   |
| 1 | 1 | <h1>Login</h1> |
| 2 | <%= login_method %> |
| 2 | 3 | |
| 3 | | |
| 4 | | <% form_tag sessions_path do -%> |
| 5 | | <p> |
| 6 | | <label for="login">Email</label><br/> |
| 4 | <% form_tag sessions_path do -%> |
| 5 | <div id="regular_login_fields"> |
| 6 | <% # login_method %> |
| 7 | <p> |
| 8 | <label for="login">Email or <%= switch_login('switch to OpenID','to_openid')%></label><br/> |
| 7 | 9 | <%= text_field_tag 'email', params[:email], :class => "text" %> |
| 8 | 10 | </p> |
| 9 | 11 | |
| … | … | |
| 13 | 13 | <label for="password">Password</label><br/> |
| 14 | 14 | <%= password_field_tag 'password', '', :class => "text" %> |
| 15 | 15 | </p> |
| 16 | | |
| 16 | </div> |
| 17 | <div id="openid_login_fields" style="display:none;"> |
| 18 | <p> |
| 19 | <label for="openid">OpenID or <%= switch_login('switch to email login', 'to_email') -%></label><br/> |
| 20 | <%= text_field_tag 'openid_url', params[:openid_url], :class => "text" -%> |
| 21 | </p> |
| 22 | </div> |
| 17 | 23 | <p> |
| 18 | 24 | <label for="remember_me">Remember me:</label> |
| 19 | 25 | <%= check_box_tag 'remember_me' %> |
| toggle raw diff |
--- a/app/views/sessions/new.html.erb
+++ b/app/views/sessions/new.html.erb
@@ -1,9 +1,11 @@
<h1>Login</h1>
+<%= login_method %>
-
-<% form_tag sessions_path do -%>
- <p>
- <label for="login">Email</label><br/>
+<% form_tag sessions_path do -%>
+ <div id="regular_login_fields">
+ <% # login_method %>
+ <p>
+ <label for="login">Email or <%= switch_login('switch to OpenID','to_openid')%></label><br/>
<%= text_field_tag 'email', params[:email], :class => "text" %>
</p>
@@ -11,7 +13,13 @@
<label for="password">Password</label><br/>
<%= password_field_tag 'password', '', :class => "text" %>
</p>
-
+ </div>
+ <div id="openid_login_fields" style="display:none;">
+ <p>
+ <label for="openid">OpenID or <%= switch_login('switch to email login', 'to_email') -%></label><br/>
+ <%= text_field_tag 'openid_url', params[:openid_url], :class => "text" -%>
+ </p>
+ </div>
<p>
<label for="remember_me">Remember me:</label>
<%= check_box_tag 'remember_me' %> |
| |   |
| 1 | class AddOpenIdAuthenticationTables < ActiveRecord::Migration |
| 2 | def self.up |
| 3 | create_table :open_id_authentication_associations, :force => true do |t| |
| 4 | t.integer :issued, :lifetime |
| 5 | t.string :handle, :assoc_type |
| 6 | t.binary :server_url, :secret |
| 7 | end |
| 8 | |
| 9 | create_table :open_id_authentication_nonces, :force => true do |t| |
| 10 | t.integer :timestamp, :null => false |
| 11 | t.string :server_url, :null => true |
| 12 | t.string :salt, :null => false |
| 13 | end |
| 14 | end |
| 15 | |
| 16 | def self.down |
| 17 | drop_table :open_id_authentication_associations |
| 18 | drop_table :open_id_authentication_nonces |
| 19 | end |
| 20 | end |
| toggle raw diff |
--- /dev/null
+++ b/db/migrate/024_add_open_id_authentication_tables.rb
@@ -0,0 +1,20 @@
+class AddOpenIdAuthenticationTables < ActiveRecord::Migration
+ def self.up
+ create_table :open_id_authentication_associations, :force => true do |t|
+ t.integer :issued, :lifetime
+ t.string :handle, :assoc_type
+ t.binary :server_url, :secret
+ end
+
+ create_table :open_id_authentication_nonces, :force => true do |t|
+ t.integer :timestamp, :null => false
+ t.string :server_url, :null => true
+ t.string :salt, :null => false
+ end
+ end
+
+ def self.down
+ drop_table :open_id_authentication_associations
+ drop_table :open_id_authentication_nonces
+ end
+end |