challengable_concern.rb 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. # frozen_string_literal: true
  2. # This concern is inspired by "sudo mode" on GitHub. It
  3. # is a way to re-authenticate a user before allowing them
  4. # to see or perform an action.
  5. #
  6. # Add `before_action :require_challenge!` to actions you
  7. # want to protect.
  8. #
  9. # The user will be shown a page to enter the challenge (which
  10. # is either the password, or just the username when no
  11. # password exists). Upon passing, there is a grace period
  12. # during which no challenge will be asked from the user.
  13. #
  14. # Accessing challenge-protected resources during the grace
  15. # period will refresh the grace period.
  16. module ChallengableConcern
  17. extend ActiveSupport::Concern
  18. CHALLENGE_TIMEOUT = 1.hour.freeze
  19. def require_challenge!
  20. return if skip_challenge?
  21. if challenge_passed_recently?
  22. session[:challenge_passed_at] = Time.now.utc
  23. return
  24. end
  25. @challenge = Form::Challenge.new(return_to: request.url)
  26. if params.key?(:form_challenge)
  27. if challenge_passed?
  28. session[:challenge_passed_at] = Time.now.utc
  29. else
  30. flash.now[:alert] = I18n.t('challenge.invalid_password')
  31. render_challenge
  32. end
  33. else
  34. render_challenge
  35. end
  36. end
  37. def render_challenge
  38. @body_classes = 'lighter'
  39. render template: 'auth/challenges/new', layout: 'auth'
  40. end
  41. def challenge_passed?
  42. current_user.valid_password?(challenge_params[:current_password])
  43. end
  44. def skip_challenge?
  45. current_user.encrypted_password.blank?
  46. end
  47. def challenge_passed_recently?
  48. session[:challenge_passed_at].present? && session[:challenge_passed_at] >= CHALLENGE_TIMEOUT.ago
  49. end
  50. def challenge_params
  51. params.require(:form_challenge).permit(:current_password, :return_to)
  52. end
  53. end