diff options
author | Eugen Rochko <eugen@zeonfederated.com> | 2019-09-18 16:37:27 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-09-18 16:37:27 +0200 |
commit | e1066cd4319a220d5be16e51ffaf5236a2f6e866 (patch) | |
tree | 3cac387721ffb3cefa66d96d1867ae88c9e249ce /app/controllers/concerns | |
parent | d0c2c5278391b82ba7fa2f230bf237805ff61a0c (diff) |
Add password challenge to 2FA settings, e-mail notifications (#11878)
Fix #3961
Diffstat (limited to 'app/controllers/concerns')
-rw-r--r-- | app/controllers/concerns/challengable_concern.rb | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/app/controllers/concerns/challengable_concern.rb b/app/controllers/concerns/challengable_concern.rb new file mode 100644 index 000000000..b29d90b3c --- /dev/null +++ b/app/controllers/concerns/challengable_concern.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +# This concern is inspired by "sudo mode" on GitHub. It +# is a way to re-authenticate a user before allowing them +# to see or perform an action. +# +# Add `before_action :require_challenge!` to actions you +# want to protect. +# +# The user will be shown a page to enter the challenge (which +# is either the password, or just the username when no +# password exists). Upon passing, there is a grace period +# during which no challenge will be asked from the user. +# +# Accessing challenge-protected resources during the grace +# period will refresh the grace period. +module ChallengableConcern + extend ActiveSupport::Concern + + CHALLENGE_TIMEOUT = 1.hour.freeze + + def require_challenge! + return if skip_challenge? + + if challenge_passed_recently? + session[:challenge_passed_at] = Time.now.utc + return + end + + @challenge = Form::Challenge.new(return_to: request.url) + + if params.key?(:form_challenge) + if challenge_passed? + session[:challenge_passed_at] = Time.now.utc + return + else + flash.now[:alert] = I18n.t('challenge.invalid_password') + render_challenge + end + else + render_challenge + end + end + + def render_challenge + @body_classes = 'lighter' + render template: 'auth/challenges/new', layout: 'auth' + end + + def challenge_passed? + current_user.valid_password?(challenge_params[:current_password]) + end + + def skip_challenge? + current_user.encrypted_password.blank? + end + + def challenge_passed_recently? + session[:challenge_passed_at].present? && session[:challenge_passed_at] >= CHALLENGE_TIMEOUT.ago + end + + def challenge_params + params.require(:form_challenge).permit(:current_password, :return_to) + end +end |