Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- require 'digest/sha1'
- class User < ActiveRecord::Base
- ###########################
- # state
- ###########################
- state_machine :state_email, :initial => 'fresh' do
- before_transition :to => 'unverified', :do => :make_email_verification_code
- before_transition :to => 'verified', :do => :set_email_verified_attributes
- event :unverify do
- transition :to => 'unverified', :from => %w(unverified verified fresh)
- end
- event :verify do
- transition :to => 'verified', :from => %w(unverified verified fresh)
- end
- end
- state_machine :state_password, :initial => 'known' do
- before_transition :to => 'forgotten', :do => :make_forgot_password_code
- before_transition :to => 'known', :do => :clear_forgot_password_code
- event :forgets_password do
- transition :to => 'forgotten', :from => 'known'
- end
- event :remembers_password do
- transition :to => 'known', :from => 'forgotten'
- end
- end
- def verified?
- state_email == 'verified'
- end
- def unverified?
- state_email == 'unverified'
- end
- def forgets_password?
- state_password == 'forgotten'
- end
- def remembers_password?
- state_password == 'known'
- end
- ###########################
- # attributes
- ###########################
- # Virtual attribute for the unencrypted password
- attr_accessor :password, :terms_and_conditions
- attr_accessible :login,
- :email,
- :password,
- :password_confirmation,
- :terms_and_conditions,
- :news,
- :gender,
- :display_name,
- :year
- ###########################
- # validations
- ###########################
- validates_presence_of :email, :message => "missing email"
- validates_length_of :login, :within => 3..40, :allow_blank => true, :message => "login must be between 3 and 40 characters"
- validates_length_of :email, :within => 3..100, :allow_blank => true, :message => "email must be between 3 and 100 characters"
- validates_uniqueness_of :login, :allow_blank => true, :message => "login is taken"
- validates_uniqueness_of :email, :allow_blank => true, :message => "email is taken; please contact us if this seems like a mistake"
- validates_format_of :login, :with => /^[^@]+$/, :allow_blank => true, :message => "invalid login (cannot be an email address)"
- validates_format_of :email, :with => /(\S+)@(\S+)/, :allow_blank => true, :message => "invalid email"
- validates_presence_of :password, :if => :password_required?, :message => "missing password"
- validates_presence_of :password_confirmation, :if => :password_required?, :message => "missing password confirmation"
- validates_length_of :password, :within => 4..40, :if => :password_required?, :missing => "password must be between 4 and 40 characters"
- validates_confirmation_of :password, :if => :password_required?, :message => "passwords do not match"
- validates_presence_of :terms_and_conditions, :on => :create, :message => "please read and agree to the terms and conditions"
- validates_format_of :terms_and_conditions, :with => /^1$/, :on => :create, :message => "please read and agree to the terms and conditions"
- validates_inclusion_of :news, :in => [true,false], :allow_blank => true
- validates_inclusion_of :gender, :in => %w(m f), :allow_blank => true, :message => 'must select m or f'
- validates_format_of :year, :with => /^\d{4}$/, :allow_blank => true, :message => 'must select a year'
- ###########################
- # callbacks
- ###########################
- before_save :encrypt_password, :nullify_blanks, :check_for_new_unverified_email
- ###########################
- # public methods
- ###########################
- # Updates attributes while also marking that the email address has been
- # verified.
- def verify_email_and_update_password_attributes(attributes)
- self.verify(false) # the false flag tells SM not to save
- self.remembers_password(false) # the false flag tells SM not to save
- self.update_attributes(attributes.delete_if {|key, val| key.to_sym != :password && key.to_sym != :password_confirmation})
- end
- # Authenticates a user by their login name and unencrypted password. Returns
- # the user or nil. Also calls remembers_password in CASE 1 of
- # forgot_password?. This is the main (non-fatal) flaw of the combined
- # email-verification/forgotten password system.
- def self.authenticate(login_or_email, password)
- u = find :first, :conditions => ["#{login_or_email =~ /@/ ? 'email' : 'login'} = ?", login_or_email]
- if u && u.authenticated?(password)
- u.remembers_password
- u
- end
- end
- # Encrypts some data with the salt.
- def self.encrypt(password, salt)
- Digest::SHA1.hexdigest("--#{salt}--#{password}--")
- end
- # Encrypts the password with the user salt
- def encrypt(password)
- self.class.encrypt(password, salt)
- end
- def authenticated?(password)
- crypted_password == encrypt(password)
- end
- # A customized attribute writer that sets whether or not the user is
- # changing the email address.
- def email=(new_email)
- unless (self.email == new_email) || new_email.blank?
- @email_updated = true
- write_attribute(:email, new_email)
- end
- end
- ###########################
- # protected methods
- ###########################
- protected
- # Before filter that encrypts a plain text password into a crypted_password.
- 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
- # Before filter that ensures "NULL" values instead of blanks in DB.
- def nullify_blanks
- self.login = nil if login.blank?
- write_attribute(:email, nil) if email.blank?
- end
- # If there's an email address and it's different than the one that existed
- # when the record was loaded we need to validate it again.
- def check_for_new_unverified_email
- unverify(false) if @email_updated && !self.unverified?
- end
- def make_email_verification_code
- self.multipurpose_code = new_multipurpose_code
- self.email_verified_at = nil
- end
- def make_forgot_password_code
- self.multipurpose_code = new_multipurpose_code
- end
- def clear_forgot_password_code
- self.multipurpose_code = nil if self.verified?
- end
- # Creates a random hash code that has oh so many uses.
- def new_multipurpose_code
- Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
- end
- # Called to set the attributes associated with an email verification.
- def set_email_verified_attributes
- self.email_verified_at = Time.now.utc
- self.multipurpose_code = nil
- end
- # Some helper methods used in validation.
- def present_email?
- !email.blank?
- end
- def present_login?
- !login.blank?
- end
- def blank_email?
- email.blank?
- end
- def blank_login?
- login.blank?
- end
- def password_required?
- crypted_password.blank? || !password.blank?
- end
- end
Add Comment
Please, Sign In to add comment