Skip to content
This repository
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 159 lines (135 sloc) 4.355 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
require 'digest/sha1'

module Clearance
  module User
    extend ActiveSupport::Concern

    # Hook for all Clearance::User modules.
    #
    # If you need to override parts of Clearance::User,
    # extend and include à la carte.
    #
    # @example
    # include Clearance::User::Callbacks
    #
    # @see Validations
    # @see Callbacks
    included do
      attr_accessor :password_changing
      attr_reader :password

      include Validations
      include Callbacks

      include (Clearance.configuration.password_strategy || Clearance::PasswordStrategies::SHA1)
    end

    module ClassMethods
      # Authenticate with email and password.
      #
      # @param [String, String] email and password
      # @return [User, nil] authenticated user or nil
      # @example
      # User.authenticate("email@example.com", "password")
      def authenticate(email, password)
        return nil unless user = find_by_email(email.to_s.downcase)
        return user if user.authenticated?(password)
      end
    end

    module Validations
      extend ActiveSupport::Concern

      # Hook for validations.
      #
      # :email must be present, unique, formatted
      #
      # If password is required,
      # :password must be present, confirmed
      included do
        validates_presence_of :email, :unless => :email_optional?
        validates_uniqueness_of :email, :allow_blank => true
        validates_format_of :email, :with => %r{^[a-z0-9!#\$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$}i, :allow_blank => true

        validates_presence_of :password, :unless => :password_optional?
      end
    end

    module Callbacks
      extend ActiveSupport::Concern

      # Hook for callbacks.
      #
      # salt, token, password encryption are handled before_save.
      included do
        before_validation :downcase_email
        before_create :generate_remember_token
      end
    end

    # Set the remember token.
    #
    # @deprecated Use {#reset_remember_token!} instead
    def remember_me!
      warn "[DEPRECATION] remember_me!: use reset_remember_token! instead"
      reset_remember_token!
    end

    # Reset the remember token.
    #
    # @example
    # user.reset_remember_token!
    def reset_remember_token!
      generate_remember_token
      save(:validate => false)
    end

    # Mark my account as forgotten password.
    #
    # @example
    # user.forgot_password!
    def forgot_password!
      generate_confirmation_token
      save(:validate => false)
    end

    # Update my password.
    #
    # @return [true, false] password was updated or not
    # @example
    # user.update_password('new-password')
    def update_password(new_password)
      self.password_changing = true
      self.password = new_password
      if valid?
        self.confirmation_token = nil
        generate_remember_token
      end
      save
    end

    def password=(unencrypted_password)
      @password = unencrypted_password
      encrypt_password
    end

    protected

    def generate_random_code(length = 20)
      if RUBY_VERSION >= '1.9'
        SecureRandom.hex(length).encode('UTF-8')
      else
        SecureRandom.hex(length)
      end
    end

    def generate_remember_token
      self.remember_token = generate_random_code
    end

    def generate_confirmation_token
      self.confirmation_token = generate_random_code
    end

    # Always false. Override to allow other forms of authentication
    # (username, facebook, etc).
    # @return [Boolean] true if the email field be left blank for this user
    def email_optional?
      false
    end

    # True if the password has been set and the password is not being
    # updated and we are not updating the password. Override to allow
    # other forms of authentication (username, facebook, etc).
    # @return [Boolean] true if the password field can be left blank for this user
    def password_optional?
      encrypted_password.present? && password.blank? && password_changing.blank?
    end

    def password_required?
      # warn "[DEPRECATION] password_required?: use !password_optional? instead"
      !password_optional?
    end

    def downcase_email
      self.email = email.to_s.downcase
    end
  end
end
Something went wrong with that request. Please try again.