Created
March 30, 2010 20:57
-
-
Save joshk/349585 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module Devise | |
| module Models | |
| # Authenticable module. Holds common settings for authentication. | |
| # | |
| # Configuration: | |
| # | |
| # You can overwrite configuration values by setting in globally in Devise, | |
| # using devise method or overwriting the respective instance method. | |
| # | |
| # authentication_keys: parameters used for authentication. By default [:email]. | |
| # | |
| # http_authenticatable: if this model allows http authentication. By default true. | |
| # | |
| module Authenticatable | |
| extend ActiveSupport::Concern | |
| # Yields the given block. This method is overwritten by other modules to provide | |
| # hooks around authentication. | |
| def valid_for_authentication? | |
| case before_auth = before_authentication | |
| when Symbol, false | |
| return before_auth | |
| end | |
| result = yield | |
| return result if skip_authentication_callbacks | |
| if result | |
| successful_authentication(result) | |
| else | |
| failed_authentication(result) | |
| end | |
| end | |
| def before_authentication; true; end | |
| def skip_authentication_callbacks; false; end | |
| def successful_authentication(result); result; end | |
| def failed_authentication(result); result; end | |
| module ClassMethods | |
| Devise::Models.config(self, :authentication_keys, :http_authenticatable) | |
| alias :http_authenticatable? :http_authenticatable | |
| # Find first record based on conditions given (ie by the sign in form). | |
| # Overwrite to add customized conditions, create a join, or maybe use a | |
| # namedscope to filter records while authenticating. | |
| # Example: | |
| # | |
| # def self.find_for_authentication(conditions={}) | |
| # conditions[:active] = true | |
| # super | |
| # end | |
| # | |
| def find_for_authentication(conditions) | |
| find(:first, :conditions => conditions) | |
| end | |
| end | |
| end | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| require 'devise/models/activatable' | |
| module Devise | |
| module Models | |
| # Handles blocking a user access after a certain number of attempts. | |
| # Lockable accepts two different strategies to unlock a user after it's | |
| # blocked: email and time. The former will send an email to the user when | |
| # the lock happens, containing a link to unlock it's account. The second | |
| # will unlock the user automatically after some configured time (ie 2.hours). | |
| # It's also possible to setup lockable to use both email and time strategies. | |
| # | |
| # Configuration: | |
| # | |
| # maximum_attempts: how many attempts should be accepted before blocking the user. | |
| # unlock_strategy: unlock the user account by :time, :email, :both or :none. | |
| # unlock_in: the time you want to lock the user after to lock happens. Only | |
| # available when unlock_strategy is :time or :both. | |
| # | |
| module Lockable | |
| extend ActiveSupport::Concern | |
| include Devise::Models::Activatable | |
| # Lock an user setting it's locked_at to actual time. | |
| def lock_access! | |
| self.locked_at = Time.now | |
| if self.class.unlock_strategy_enabled?(:email) | |
| generate_unlock_token | |
| send_unlock_instructions | |
| end | |
| save(:validate => false) | |
| end | |
| # Unlock an user by cleaning locket_at and failed_attempts. | |
| def unlock_access! | |
| if_access_locked do | |
| self.locked_at = nil | |
| self.failed_attempts = 0 if self.respond_to?(:failed_attempts=) | |
| self.unlock_token = nil if self.respond_to?(:unlock_token=) | |
| save(:validate => false) | |
| end | |
| end | |
| # Verifies whether a user is locked or not. | |
| def access_locked? | |
| locked_at && !lock_expired? | |
| end | |
| # Send unlock instructions by email | |
| def send_unlock_instructions | |
| ::Devise::Mailer.unlock_instructions(self).deliver | |
| end | |
| # Resend the unlock instructions if the user is locked. | |
| def resend_unlock_token | |
| if_access_locked { send_unlock_instructions } | |
| end | |
| # Overwrites active? from Devise::Models::Activatable for locking purposes | |
| # by verifying whether an user is active to sign in or not based on locked? | |
| def active? | |
| super && !access_locked? | |
| end | |
| # Overwrites invalid_message from Devise::Models::Authenticatable to define | |
| # the correct reason for blocking the sign in. | |
| def inactive_message | |
| access_locked? ? :locked : super | |
| end | |
| # Overwrites valid_for_authentication? from Devise::Models::Authenticatable | |
| # for verifying whether an user is allowed to sign in or not. If the user | |
| # is locked, it should never be allowed. | |
| def before_authentication | |
| access_locked? ? :locked : true | |
| end | |
| def successful_authentication(result) | |
| self.failed_attempts = 0 | |
| save(:validate => false) if changed? | |
| result | |
| end | |
| def failed_authentication(result) | |
| self.failed_attempts += 1 | |
| if attempts_exceeded? | |
| lock_access! | |
| return :locked | |
| end | |
| save(:validate => false) if changed? | |
| result | |
| end | |
| def skip_authentication_callbacks | |
| !persisted? || self.class.unlock_strategy == :none | |
| end | |
| protected | |
| def attempts_exceeded? | |
| self.failed_attempts > self.class.maximum_attempts | |
| end | |
| # Generates unlock token | |
| def generate_unlock_token | |
| self.unlock_token = self.class.unlock_token | |
| end | |
| # Tells if the lock is expired if :time unlock strategy is active | |
| def lock_expired? | |
| if self.class.unlock_strategy_enabled?(:time) | |
| locked_at && locked_at < self.class.unlock_in.ago | |
| else | |
| false | |
| end | |
| end | |
| # Checks whether the record is locked or not, yielding to the block | |
| # if it's locked, otherwise adds an error to email. | |
| def if_access_locked | |
| if access_locked? | |
| yield | |
| else | |
| self.errors.add(:email, :not_locked) | |
| false | |
| end | |
| end | |
| module ClassMethods | |
| # Attempt to find a user by it's email. If a record is found, send new | |
| # unlock instructions to it. If not user is found, returns a new user | |
| # with an email not found error. | |
| # Options must contain the user email | |
| def send_unlock_instructions(attributes={}) | |
| lockable = find_or_initialize_with_error_by(:email, attributes[:email], :not_found) | |
| lockable.resend_unlock_token if lockable.persisted? | |
| lockable | |
| end | |
| # Find a user by it's unlock token and try to unlock it. | |
| # If no user is found, returns a new user with an error. | |
| # If the user is not locked, creates an error for the user | |
| # Options must have the unlock_token | |
| def unlock_access_by_token(unlock_token) | |
| lockable = find_or_initialize_with_error_by(:unlock_token, unlock_token) | |
| lockable.unlock_access! if lockable.persisted? | |
| lockable | |
| end | |
| # Is the unlock enabled for the given unlock strategy? | |
| def unlock_strategy_enabled?(strategy) | |
| [:both, strategy].include?(self.unlock_strategy) | |
| end | |
| def unlock_token | |
| Devise.friendly_token | |
| end | |
| Devise::Models.config(self, :maximum_attempts, :unlock_strategy, :unlock_in) | |
| end | |
| end | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment