Add hCaptcha support (#25019)
This commit is contained in:
		
							parent
							
								
									e60414792d
								
							
						
					
					
						commit
						bec6a1cad4
					
				
					 12 changed files with 146 additions and 0 deletions
				
			
		|  | @ -1,21 +1,63 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class Auth::ConfirmationsController < Devise::ConfirmationsController | ||||
|   include CaptchaConcern | ||||
| 
 | ||||
|   layout 'auth' | ||||
| 
 | ||||
|   before_action :set_body_classes | ||||
|   before_action :set_confirmation_user!, only: [:show, :confirm_captcha] | ||||
|   before_action :require_unconfirmed! | ||||
| 
 | ||||
|   before_action :extend_csp_for_captcha!, only: [:show, :confirm_captcha] | ||||
|   before_action :require_captcha_if_needed!, only: [:show] | ||||
| 
 | ||||
|   skip_before_action :require_functional! | ||||
| 
 | ||||
|   def show | ||||
|     old_session_values = session.to_hash | ||||
|     reset_session | ||||
|     session.update old_session_values.except('session_id') | ||||
| 
 | ||||
|     super | ||||
|   end | ||||
| 
 | ||||
|   def new | ||||
|     super | ||||
| 
 | ||||
|     resource.email = current_user.unconfirmed_email || current_user.email if user_signed_in? | ||||
|   end | ||||
| 
 | ||||
|   def confirm_captcha | ||||
|     check_captcha! do |message| | ||||
|       flash.now[:alert] = message | ||||
|       render :captcha | ||||
|       return | ||||
|     end | ||||
| 
 | ||||
|     show | ||||
|   end | ||||
| 
 | ||||
|   private | ||||
| 
 | ||||
|   def require_captcha_if_needed! | ||||
|     render :captcha if captcha_required? | ||||
|   end | ||||
| 
 | ||||
|   def set_confirmation_user! | ||||
|     # We need to reimplement looking up the user because | ||||
|     # Devise::ConfirmationsController#show looks up and confirms in one | ||||
|     # step. | ||||
|     confirmation_token = params[:confirmation_token] | ||||
|     return if confirmation_token.nil? | ||||
| 
 | ||||
|     @confirmation_user = User.find_first_by_auth_conditions(confirmation_token: confirmation_token) | ||||
|   end | ||||
| 
 | ||||
|   def captcha_user_bypass? | ||||
|     return true if @confirmation_user.nil? || @confirmation_user.confirmed? | ||||
|   end | ||||
| 
 | ||||
|   def require_unconfirmed! | ||||
|     if user_signed_in? && current_user.confirmed? && current_user.unconfirmed_email.blank? | ||||
|       redirect_to(current_user.approved? ? root_path : edit_user_registration_path) | ||||
|  |  | |||
							
								
								
									
										59
									
								
								app/controllers/concerns/captcha_concern.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								app/controllers/concerns/captcha_concern.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module CaptchaConcern | ||||
|   extend ActiveSupport::Concern | ||||
|   include Hcaptcha::Adapters::ViewMethods | ||||
| 
 | ||||
|   included do | ||||
|     helper_method :render_captcha | ||||
|   end | ||||
| 
 | ||||
|   def captcha_available? | ||||
|     ENV['HCAPTCHA_SECRET_KEY'].present? && ENV['HCAPTCHA_SITE_KEY'].present? | ||||
|   end | ||||
| 
 | ||||
|   def captcha_enabled? | ||||
|     captcha_available? && Setting.captcha_enabled | ||||
|   end | ||||
| 
 | ||||
|   def captcha_user_bypass? | ||||
|     false | ||||
|   end | ||||
| 
 | ||||
|   def captcha_required? | ||||
|     captcha_enabled? && !captcha_user_bypass? | ||||
|   end | ||||
| 
 | ||||
|   def check_captcha! | ||||
|     return true unless captcha_required? | ||||
| 
 | ||||
|     if verify_hcaptcha | ||||
|       true | ||||
|     else | ||||
|       if block_given? | ||||
|         message = flash[:hcaptcha_error] | ||||
|         flash.delete(:hcaptcha_error) | ||||
|         yield message | ||||
|       end | ||||
|       false | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def extend_csp_for_captcha! | ||||
|     policy = request.content_security_policy | ||||
|     return unless captcha_required? && policy.present? | ||||
| 
 | ||||
|     %w(script_src frame_src style_src connect_src).each do |directive| | ||||
|       values = policy.send(directive) | ||||
|       values << 'https://hcaptcha.com' unless values.include?('https://hcaptcha.com') || values.include?('https:') | ||||
|       values << 'https://*.hcaptcha.com' unless values.include?('https://*.hcaptcha.com') || values.include?('https:') | ||||
|       policy.send(directive, *values) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def render_captcha | ||||
|     return unless captcha_required? | ||||
| 
 | ||||
|     hcaptcha_tags | ||||
|   end | ||||
| end | ||||
		Reference in a new issue