lib/ssl_helpers.rb

Summary

Maintainability
A
3 hrs
Test Coverage
module SslHelpers
  def self.included(klass)
    if defined?(SystemSetting.force_https_on_restricted_pages) && SystemSetting.force_https_on_restricted_pages
      # ActionView::Base.send(:include, SslHelpers::FormTagHelper)
      ActionView::Base.send(:include, SslHelpers::PrototypeHelper)
      ActionController::UrlWriter.send(:include, SslHelpers::UrlWriter)

      klass.send :before_filter, :redirect_to_proper_protocol_if_needed

      # Ensure SSL is allowed on all controllers
      klass.class_eval do
        # We need to ensure certain actions are run with SSL requirement if applicable
        include SslRequirement
        include SslHelpers::Base
        def ssl_allowed?; true; end
      end

    end
  end

  # Uses form_tag for the nuts and bolts work
  # ActionView::Helpers::FormHelper#form_for

  # Overload for ActionView::Helpers::FormTagHelper
  # module FormTagHelper
  #
  #   def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &block)
  #
  #     if url_for_options.kind_of?(Hash)
  #       merge_into = case url_for_options[:overwrite_params]
  #         when nil
  #           url_for_options
  #         else
  #           url_for_options[:overwrite_params]
  #       end
  #
  #       # was causing problems with circular redirects
  #       # no longer all forms are under https (at least for the moment)
  #       # merge_into.merge!(:protocol => 'https://', :only_path => false)
  #     end
  #
  #     super(url_for_options, options, *parameters_for_url, &block)
  #   end
  #
  # end

  # Overload for ActionView::Helpers::PrototypeHelper
  module PrototypeHelper
    # Uses form_remote_tag for the nuts and bolts work
    # ActionView::Helpers::PrototypeHelper#remote_form_for

    def form_remote_tag(options = {}, &block)
      options[:url][:protocol] = 'https://' if options[:url].kind_of?(Hash)
      options[:html][:protocol] = 'https://' if !options[:html].nil? && options[:html].kind_of?(Hash)

      super(options, &block)
    end
  end

  # When it matters, requests are passed to ActionController::Base
  # ActionView::Helpers::UrlHelper#url_for

  # Integration::Session passes request to ActionController::Base.
  # ActionController::Integration::Session#url_for

  # Overload for ActionController::Base
  module Base
    def url_for(options = nil)
      if options.kind_of?(Hash)
        options[:protocol] = 'https://' if options[:private] == 'true'

        # If the protocol is HTTPS and we're not already there, and we haven't explicitly
        # asked for the path only, send the whole address so we're forced to HTTPS

        # There is potential for issues with this when the only the path is expected as
        # this is normally default behaviour, and we are modifying it here.
        if options[:protocol] =~ /^https/ && !request.ssl? && !options[:only_path]
          options[:only_path] = false
        end
      end

      super(options)
    end

    def polymorphic_url(record_or_hash_or_array, options = {})
      raise 'called polymorphic_url'
      options[:protocol] = 'https://' if options.kind_of?(Hash)
      super(record_or_hash_or_array, options)
    end
  end

  # Overload for ActionController::UrlWriter
  module UrlWriter
    # Used in contexts other than ActionController and ActionView
    def url_for(options)
      if options.kind_of?(Hash) && options[:private] == 'true'
        options[:protocol] = 'https://'
      end

      super(options)
    end
  end

  private

  # changed to redirect to http if not private
  def redirect_to_proper_protocol_if_needed
    return true unless request.get?
    if request.port == 80 && (params[:privacy_type] == 'private' ||
                              params[:private] == 'true')
      redirect_to params.merge(protocol: 'https')
      return false
    elsif request.port == 443 && !ssl_required? &&
          ((params[:privacy_type].blank? && params[:private].blank?) ||
            ((params[:privacy_type].present? || params[:private].present?) &&
              (params[:privacy_type].blank? || params[:privacy_type] != 'private') &&
              (params[:private].blank? || params[:private] != 'true')))

      redirect_to params.merge(protocol: 'http')
      return false
    end
    true
  end
end