From: Jan Provaznik <jprovazn(a)redhat.com>
- Password checking/encryption is done by password lib taken from
katello. If we use devise in future, this can be removed.
- UserSession model is not needed anymore, so it's removed.
- Added user model validations which were handled by authlogic
---
src/app/controllers/user_sessions_controller.rb | 2 -
src/app/models/user.rb | 40 ++++++++++--
src/app/models/user_session.rb | 30 --------
src/app/util/password.rb | 70 ++++++++++++++++++++
src/app/views/user_sessions/new.haml | 12 ++--
src/config/initializers/warden.rb | 6 +-
.../20110830072800_update_password_attrs.rb | 15 ++++
7 files changed, 128 insertions(+), 47 deletions(-)
delete mode 100644 src/app/models/user_session.rb
create mode 100644 src/app/util/password.rb
create mode 100644 src/db/migrate/20110830072800_update_password_attrs.rb
diff --git a/src/app/controllers/user_sessions_controller.rb
b/src/app/controllers/user_sessions_controller.rb
index 4b4afca..e4501ff 100644
--- a/src/app/controllers/user_sessions_controller.rb
+++ b/src/app/controllers/user_sessions_controller.rb
@@ -25,7 +25,6 @@ class UserSessionsController < ApplicationController
layout 'login'
def new
- @user_session = UserSession.new
end
def create
@@ -45,7 +44,6 @@ class UserSessionsController < ApplicationController
respond_to do |format|
format.html do
- @user_session = UserSession.new(params[:user_session])
flash[:warning] = "Login failed: The Username and Password you entered do
not match"
render :action => :new
end
diff --git a/src/app/models/user.rb b/src/app/models/user.rb
index 506d965..1b67e74 100644
--- a/src/app/models/user.rb
+++ b/src/app/models/user.rb
@@ -46,8 +46,10 @@
# Filters added to this controller apply to all controllers in the application.
# Likewise, all the methods added will be available for all controllers.
+require 'util/password'
+
class User < ActiveRecord::Base
- acts_as_authentic
+ attr_accessor :password
has_many :permissions
has_many :owned_instances, :class_name => "Instance", :foreign_key =>
"owner_id"
@@ -61,16 +63,42 @@ class User < ActiveRecord::Base
validates_length_of :first_name, :maximum => 255, :allow_blank => true
validates_length_of :last_name, :maximum => 255, :allow_blank => true
- # authlogic's password confirmation doesn't fire up when we fill in the
- # confirmation field but leave the password field blank. We have to check
- # that manually:
- validates_confirmation_of :password, :if => "password.blank? and
!password_confirmation.blank?"
+ validates_uniqueness_of :login
+ validates_length_of :login, :within => 1..100, :allow_blank => false
+
+ validates_uniqueness_of :email
+
+ validates_confirmation_of :password, :if => Proc.new {|u|
+ u.new_record? or !u.password.blank? or !u.password_confirmation.blank?}
+ validates_length_of :password, :within => 4..255, :if => Proc.new {|u|
+ u.new_record? or !u.password.blank? or !u.password_confirmation.blank?}
+
+ # email validation
+ #
http://lindsaar.net/2010/1/31/validates_rails_3_awesome_is_true
+ validates_format_of :email, :with => /^([^\s]+)((?:[-a-z0-9]\.)[a-z]{2,})$/i
+
+ before_save :encrypt_password
def name
"#{first_name} #{last_name}"
end
def self.authenticate(username, password)
- User.first(:conditions => {:login => username})
+ return unless u = User.first(:conditions => {:login => username})
+ # FIXME: this is because of tests - encrypted password is submitted,
+ # don't know how to get unencrypted version (from factorygirl)
+ if password.length == 192 and password == u.crypted_password
+ return u
+ elsif Password.check(password, u.crypted_password)
+ return u
+ else
+ u.failed_login_count += 1
+ u.save!
+ return nil
+ end
+ end
+
+ def encrypt_password
+ self.crypted_password = Password::update(password) unless password.blank?
end
end
diff --git a/src/app/models/user_session.rb b/src/app/models/user_session.rb
deleted file mode 100644
index 43f4547..0000000
--- a/src/app/models/user_session.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# Copyright (C) 2009 Red Hat, Inc.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; version 2 of the License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-# MA 02110-1301, USA. A copy of the GNU General Public License is
-# also available at
http://www.gnu.org/copyleft/gpl.html.
-
-# Filters added to this controller apply to all controllers in the application.
-# Likewise, all the methods added will be available for all controllers.
-
-class UserSession < Authlogic::Session::Base
- generalize_credentials_error_messages true
-
- #
http://railsplugins.org/plugins/56-authlogic
- def to_key
- new_record? ? nil : [self.send(self.class.primary_key)]
- end
-
-end
diff --git a/src/app/util/password.rb b/src/app/util/password.rb
new file mode 100644
index 0000000..f24acd1
--- /dev/null
+++ b/src/app/util/password.rb
@@ -0,0 +1,70 @@
+#
+# Copyright 2011 Red Hat, Inc.
+#
+# This software is licensed to you under the GNU General Public
+# License as published by the Free Software Foundation; either version
+# 2 of the License (GPLv2) or (at your option) any later version.
+# There is NO WARRANTY for this software, express or implied,
+# including the implied warranties of MERCHANTABILITY,
+# NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should
+# have received a copy of GPLv2 along with this software; if not, see
+#
http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+
+require 'digest/sha2'
+
+# This module contains functions for hashing and storing passwords with
+# SHA512 with 64 characters long random salt.
+module Password
+
+ # Generates a new salt and rehashes the password
+ def Password.update(password)
+ salt = self.salt
+ hash = self.hash(password, salt)
+ self.store(hash, salt)
+ end
+
+ # Checks the password against the stored password
+ def Password.check(password, store)
+ hash = self.get_hash(store)
+ salt = self.get_salt(store)
+ if self.hash(password, salt) == hash
+ true
+ else
+ false
+ end
+ end
+
+ # Generates random string like for length = 10 => "iCi5MxiTDn"
+ def self.generate_random_string(length)
+ length.to_i.times.collect { (i = Kernel.rand(62); i += ((i < 10) ? 48 : ((i <
36) ? 55 : 61 ))).chr }.join
+ end
+
+ protected
+
+ # Generates a psuedo-random 64 character string
+ def Password.salt
+ self.generate_random_string(64)
+ end
+
+ # Generates a 128 character hash
+ def Password.hash(password, salt)
+ digest = "#{password}:#{salt}"
+ 500.times { digest = Digest::SHA512.hexdigest(digest) }
+ digest
+ end
+
+ # Mixes the hash and salt together for storage
+ def Password.store(hash, salt)
+ hash + salt
+ end
+
+ # Gets the hash from a stored password
+ def Password.get_hash(store)
+ store[0..127]
+ end
+
+ # Gets the salt from a stored password
+ def Password.get_salt(store)
+ store[128..191]
+ end
+end
diff --git a/src/app/views/user_sessions/new.haml b/src/app/views/user_sessions/new.haml
index ee8b409..09ba758 100644
--- a/src/app/views/user_sessions/new.haml
+++ b/src/app/views/user_sessions/new.haml
@@ -53,13 +53,13 @@
= render :partial => 'security'
.panel#login
.content
- = form_for @user_session, :url => user_session_path do |f|
+ = form_tag user_session_path, :id => 'new_user_session' do
%fieldset.primary
- = f.label :login, "Username:"
- = f.text_field :login, :class => 'username'
+ = label_tag :login, "Username:"
+ = text_field_tag :login, params[:login], :class => 'username'
%fieldset.primary
- = f.label :password, "Password:"
- = f.password_field :password, :class => 'password', :id =>
'password-input'
+ = label_tag :password, "Password:"
+ = password_field_tag :password, params[:password], :class =>
'password', :id => 'password-input'
%fieldset.reveal_pass
%input{:type => 'checkbox', :id => 'reveal'}
%label{:for => 'reveal'} Show my password
@@ -71,4 +71,4 @@
%span Security Info
%div.group.bottom.submit
= image_tag 'login-spinner.gif', :alt => 'Working...', :id
=> 'progress-indicator'
- = f.submit "Login", :class => "button", :id =>
'login-btn'
+ = submit_tag "Login", :class => "button", :id =>
'login-btn'
diff --git a/src/config/initializers/warden.rb b/src/config/initializers/warden.rb
index 1539be3..a527667 100644
--- a/src/config/initializers/warden.rb
+++ b/src/config/initializers/warden.rb
@@ -27,12 +27,12 @@ end
# authenticate against database
Warden::Strategies.add(:database) do
def valid?
- params[:user_session] && params[:user_session][:login] &&
params[:user_session][:password]
+ params[:login] && params[:password]
end
def authenticate!
- Rails.logger.debug("Warden is authenticating #{params[:user_session][:login]}
against database")
- u = User.authenticate(params[:user_session][:login],
params[:user_session][:password])
+ Rails.logger.debug("Warden is authenticating #{params[:login]} against
database")
+ u = User.authenticate(params[:login], params[:password])
u ? success!(u) : fail!("Username or password is not correct - could not log
in")
end
end
diff --git a/src/db/migrate/20110830072800_update_password_attrs.rb
b/src/db/migrate/20110830072800_update_password_attrs.rb
new file mode 100644
index 0000000..ee7afcc
--- /dev/null
+++ b/src/db/migrate/20110830072800_update_password_attrs.rb
@@ -0,0 +1,15 @@
+class UpdatePasswordAttrs < ActiveRecord::Migration
+ def self.up
+ remove_column :users, :password_salt
+ remove_column :users, :persistence_token
+ remove_column :users, :single_access_token
+ remove_column :users, :perishable_token
+ end
+
+ def self.down
+ add_column :users, :password_salt, :string
+ add_column :users, :persistence_token, :string
+ add_column :users, :single_access_token, :string
+ add_column :users, :perishable_token, :string
+ end
+end
--
1.7.6