设计 - 如何在使用自定义Devise :: FailureApp重定向时获取用户

时间:2018-01-08 16:00:57

标签: ruby-on-rails devise devise-confirmable

我让用户最初登录时没有确认他们的电子邮件地址 - 但是在7天之后,如果他们还没有确认 - 我会阻止访问,直到他们确认他们的地址。

(注意 - 这是通过在Devise初始化程序中设置config.allow_unconfirmed_access_for = 7.days来实现的)

如果他们达到'宽限'限制(例如他们没有确认并且7天通过)那么我想:

  1. 将它们发送到解释发生了什么的页面(我可以这样做 部分)
  2. 自动重新发送确认电子邮件
  3. 做#2我需要访问用户才能获得电子邮件地址。

    设计显然'知道'用户是谁 - 这就是它知道他们已经通过确认到期的方式。

    如果用户刚刚尝试登录,那么我可以通过查看参数来获取此信息。 但是,如果用户已经在他们的会话中有一个实时登录令牌,那么当他们通过神奇的一周 - 他们将突然开始被设计拒绝。在这种情况下如何访问用户?

    #based on
    #https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-when-the-user-can-not-be-authenticated
    #https://stackoverflow.com/questions/9223555/devise-with-confirmable-redirect-user-to-a-custom-page-when-users-tries-to-sig
    
    class CustomFailure < Devise::FailureApp
      def redirect_url
        if warden_message == :unconfirmed
          user = User.find_by_email(params.dig(:user,:email))
          user&.send_confirmation_instructions
    
          if user.nil?
            #if the user had a valid login session when they passed the grace period, they will end up here
            !! how do I get the user in this scenario !!
          end
    
          confirmation_required_info_path(params: {found: !user.nil?})
    
        elsif warden_message == :invalid
          new_user_session_path(user:{email: params.dig(:user,:email)})
        else
          super
        end
      end
    
      # You need to override respond to eliminate recall
      def respond
        if http_auth?
          http_auth
        else
          redirect
        end
      end
    end
    

    这实现了目标#1,但如果失败是新注册的结果,它只能达到目标#2

    有没有直接的方式来访问用户有实时会话,但已经过了有效期?

    (current_user不可用,env ['warden'] .user is nil)

    谢谢

    Rails 5.0.6 设计4.2

    编辑:更新以澄清我需要帮助的示例方案:

    第0天:用户使用电子邮件/密码注册。我告诉他们没有确认他们的电子邮件。他们有7天的宽限期来确认他们的电子邮件。

    第2天:他们退出

    第7天(上午):他们再次登录

    第7天(当天晚些时候):他们采取了一些行动。他们的登录令牌仍然有效 - 设计识别它,找到他们的用户记录并检查他们是否已确认他们的电子邮件地址。他们没有 - 所以设计拒绝授权行动,给出错误:未经证实

    在这种情况下 - 他们来到失败应用程序。我会将它们重定向到一个页面,上面写着“您已经过了7天的宽限期,现在确实需要确认您的电子邮件地址”。

    在失败的应用程序中,我想知道他们的电子邮件地址是什么,以便我可以自动重新发送确认电子邮件。 我如何得到这个?

    注意 - 在这种情况下,设计已拒绝授权。 current_user是零。然而,Devise清楚地“知道”用户是谁 - 因为它能够在数据库中查找他们的记录,并检查他们是否已经超过了未经证实的电子邮件地址的宽限期。我如何访问相同的“知识”

1 个答案:

答案 0 :(得分:1)

我认为有更好的方法可以在不创建Devise::FailureApp的情况下实现相同的结果:

这可以通过覆盖confirmed? module中存在的Devise资源扩展程序中的Confirmable方法来实现。

一个简单的例子是:

  1. 通过迁移将delayed_confirmation_expiry_date日期时间字段添加到您的模型表中。
  2. 当用户首次注册到您的应用程序时,此字段将用于存储到期日期时间。您必须覆盖SessionsController#create方法,因此可以调用资源上的#delay_confirmation!方法。

    1. User等效模型中添加:
    2. # Will update the field you have added with the new temporary expiration access datetime
      def delay_confirmation!(expiry_datetime=7.days.from_now)
        self.delayed_confirmation_expiry_date = expiry_datetime
        self.save
      end
      
      # Override that will make sure that, once the user is confirmed, the delayed confirmation information is cleared
      def confirm(args={})
        clear_delay_confirmation!
        super
      end
      
      # Self-explanatory
      def clear_delay_confirmation!
        self.delayed_confirmation_expiry_date = nil
        self.save
      end
      
      # Used on your controllers to show messages to the user warning him about the presence of the confirmation delay
      def confirmation_is_delayed?
        self.confirmed? && !self.confirmed_at.present?
      end
      
      # Overrides the default implementation to allow temporary access for users who haven't confirmed their accounts within the time limit
      def confirmed?
        if !self.confirmation_is_delayed?
          super
        else
          self.delayed_confirmation_expiry_date >= DateTime.now.in_time_zone
        end
      end