newleaders/thincloud-authentication

View on GitHub
app/models/thincloud/authentication/identity.rb

Summary

Maintainability
A
0 mins
Test Coverage
module Thincloud::Authentication
  # Public: This class represents a User identity (name, email, login provider)
  class Identity < ::OmniAuth::Identity::Models::ActiveRecord
    include ActiveModel::ForbiddenAttributesProtection  # strong_parameters

    belongs_to :user

    validates :name, presence: true
    validates :email, presence: true, uniqueness: true, format: /@/
    validates :password, presence: { if: :password_required? },
      confirmation: { if: :password_confirmation_required? }

    # Ensure that a `verification_token` exists for new records.
    after_initialize do
      self.verification_token = SecureRandom.urlsafe_base64 if new_record?
      self.resetting_identity_password = false
    end

    # Only validate password if the 'provider' is 'identity'.
    before_validation do
      self.password_digest = 0 unless identity_provider?
    end

    # Public: Use a helpful attribute name when displaying errors.
    def self.human_attribute_name(attr, options={})
      attr == :password_digest ? "Password" : super
    end

    # Public: Find an `Identity` by OmniAuth parameters.
    #
    # omniauth - An instance of `OmniAuth::AuthHash`
    #
    # Returns: An instance of `Identity` or `nil`.
    def self.find_omniauth(omniauth)
      if omniauth["uid"].present?
        find_by_provider_and_uid omniauth["provider"], omniauth["uid"]
      end
    end

    # Public: Mark the `Identity` as having been verified.
    #
    # token - A String containing the `verification_token` to look up.
    #
    # Returns: An instance of the found `Identity`.
    # Raises: ActiveRecord::RecordNotFound if the `token` cannot be retrieved.
    #         ActiveRecord::RecordInvalid if the record cannot be saved.
    def self.verify!(token)
      find_by_verification_token!(token).tap do |identity|
        # ensure 'uid' exists, needed for 'identity' provider
        identity.uid = identity.id if identity.uid.blank?
        identity.verification_token = nil
        identity.verified_at = Time.zone.now
        identity.save!
      end
    end

    # Public: Shim to overcome odd behavior seen during testing with SQLite
    def uid
      read_attribute :uid
    end

    # Public: Indicate if the `Identity` has been verified.
    #
    # Returns: Boolean.
    def verified?
      verification_token.blank? && verified_at.present?
    end

    # Public: Apply attributes returned from OmniAuth.
    #
    # omniauth - An instance of `OmniAuth::AuthHash`.
    def apply_omniauth(omniauth)
      info = omniauth["info"]

      user_name = %Q(#{info["first_name"]} #{info["last_name"]})
      user_name.gsub!(/\s+/, " ").strip!

      self.provider = omniauth["provider"]
      self.uid      = omniauth["uid"]
      self.name     = user_name if self.name.blank?
      self.email    = info["email"] if info["email"] && self.email.blank?
      self
    end

    # Public: Generate a password reset token
    #
    # Returns: true
    #
    # Raises: ActiveRecord::RecordInvalid
    def generate_password_reset!
      self.password_reset_token = SecureRandom.urlsafe_base64
      self.password_reset_sent_at = Time.zone.now
      save_with_identity_password_reset!
    end

    # Public: Clear password reset fields, reset password_required? requirement
    #
    # Returns: true
    #
    # Raises: ActiveRecord::RecordInvalid
    def clear_password_reset!
      self.password_reset_token = nil
      self.password_reset_sent_at = nil
      save!
    end

    # Public: Determine if the provider is 'identity'
    #
    # Returns: true or false
    def identity_provider?
      provider == "identity"
    end

    # Public: Determine if the password must be provided
    #
    # Returns: true or false
    def password_required?
      (identity_provider? && check_identity_password?) && (
        new_record? || password_reset_token.present?
      )
    end

    # Public: Determine if the password confirmation must be provided
    #
    # Returns: true or false
    def password_confirmation_required?
      password_required? || (
        password.present? || password_confirmation.present?
      )
    end

    private

    attr_accessor :resetting_identity_password

    def check_identity_password?
      !resetting_identity_password
    end

    def save_with_identity_password_reset!
      self.resetting_identity_password = true
      save!
    ensure
      self.resetting_identity_password = false
    end

  end
end