From 024092654e2bca3127b9ad572365e7f9e96064a3 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 10 Dec 2009 13:09:00 -0500 Subject: [PATCH] Require SSL for hosts controller --- app/controllers/application_controller.rb | 1 + app/controllers/hosts_controller.rb | 4 + vendor/plugins/ssl_requirement/README | 43 +++++++ .../plugins/ssl_requirement/lib/ssl_requirement.rb | 62 +++++++++ .../ssl_requirement/test/ssl_requirement_test.rb | 132 ++++++++++++++++++++ 5 files changed, 242 insertions(+), 0 deletions(-) create mode 100644 vendor/plugins/ssl_requirement/README create mode 100644 vendor/plugins/ssl_requirement/lib/ssl_requirement.rb create mode 100644 vendor/plugins/ssl_requirement/test/ssl_requirement_test.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3ff8c80..886cdc4 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -3,6 +3,7 @@ class ApplicationController < ActionController::Base protect_from_forgery # See ActionController::RequestForgeryProtection for details + include SslRequirement filter_parameter_logging :root_pass diff --git a/app/controllers/hosts_controller.rb b/app/controllers/hosts_controller.rb index cb7c107..df9e21d 100644 --- a/app/controllers/hosts_controller.rb +++ b/app/controllers/hosts_controller.rb @@ -2,6 +2,10 @@ class HostsController < ApplicationController before_filter :require_login, :except => [ :query, :externalNodes ] before_filter :find_hosts, :only => :query + def ssl_required? + true + end + helper :hosts active_scaffold :host do |config| diff --git a/vendor/plugins/ssl_requirement/README b/vendor/plugins/ssl_requirement/README new file mode 100644 index 0000000..afe5a84 --- /dev/null +++ b/vendor/plugins/ssl_requirement/README @@ -0,0 +1,43 @@ +SSL Requirement +=============== + +SSL requirement adds a declarative way of specifying that certain actions +should only be allowed to run under SSL, and if they're accessed without it, +they should be redirected. + +Example: + + class ApplicationController < ActiveRecord::Base + include SslRequirement + end + + class AccountController < ApplicationController + ssl_required :signup, :payment + ssl_allowed :index + + def signup + # Non-SSL access will be redirected to SSL + end + + def payment + # Non-SSL access will be redirected to SSL + end + + def index + # This action will work either with or without SSL + end + + def other + # SSL access will be redirected to non-SSL + end + end + +You can overwrite the protected method ssl_required? to rely on other things +than just the declarative specification. Say, only premium accounts get SSL. + +P.S.: Beware when you include the SslRequirement module. At the time of +inclusion, it'll add the before_filter that validates the declarations. Some +times you'll want to run other before_filters before that. They should then be +declared ahead of including this module. + +Copyright (c) 2005 David Heinemeier Hansson, released under the MIT license \ No newline at end of file diff --git a/vendor/plugins/ssl_requirement/lib/ssl_requirement.rb b/vendor/plugins/ssl_requirement/lib/ssl_requirement.rb new file mode 100644 index 0000000..2f155b1 --- /dev/null +++ b/vendor/plugins/ssl_requirement/lib/ssl_requirement.rb @@ -0,0 +1,62 @@ +# Copyright (c) 2005 David Heinemeier Hansson +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +module SslRequirement + def self.included(controller) + controller.extend(ClassMethods) + controller.before_filter(:ensure_proper_protocol) + end + + module ClassMethods + # Specifies that the named actions requires an SSL connection to be performed (which is enforced by ensure_proper_protocol). + def ssl_required(*actions) + write_inheritable_array(:ssl_required_actions, actions) + end + + def ssl_allowed(*actions) + write_inheritable_array(:ssl_allowed_actions, actions) + end + end + + protected + # Returns true if the current action is supposed to run as SSL + def ssl_required? + (self.class.read_inheritable_attribute(:ssl_required_actions) || []).include?(action_name.to_sym) + end + + def ssl_allowed? + (self.class.read_inheritable_attribute(:ssl_allowed_actions) || []).include?(action_name.to_sym) + end + + private + def ensure_proper_protocol + return true if ssl_allowed? + + if ssl_required? && !request.ssl? + redirect_to "https://" + request.host + request.request_uri + flash.keep + return false + elsif request.ssl? && !ssl_required? + redirect_to "http://" + request.host + request.request_uri + flash.keep + return false + end + end +end diff --git a/vendor/plugins/ssl_requirement/test/ssl_requirement_test.rb b/vendor/plugins/ssl_requirement/test/ssl_requirement_test.rb new file mode 100644 index 0000000..0dc67ed --- /dev/null +++ b/vendor/plugins/ssl_requirement/test/ssl_requirement_test.rb @@ -0,0 +1,132 @@ +begin + require 'action_controller' +rescue LoadError + if ENV['ACTIONCONTROLLER_PATH'].nil? + abort < true + end + + def b + render :nothing => true + end + + def c + render :nothing => true + end + + def d + render :nothing => true + end + + def set_flash + flash[:foo] = "bar" + end +end + +class SslRequirementTest < Test::Unit::TestCase + def setup + @controller = SslRequirementController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + end + + def test_redirect_to_https_preserves_flash + get :set_flash + get :b + assert_response :redirect + assert_equal "bar", flash[:foo] + end + + def test_not_redirecting_to_https_does_not_preserve_the_flash + get :set_flash + get :d + assert_response :success + assert_nil flash[:foo] + end + + def test_redirect_to_http_preserves_flash + get :set_flash + @request.env['HTTPS'] = "on" + get :d + assert_response :redirect + assert_equal "bar", flash[:foo] + end + + def test_not_redirecting_to_http_does_not_preserve_the_flash + get :set_flash + @request.env['HTTPS'] = "on" + get :a + assert_response :success + assert_nil flash[:foo] + end + + def test_required_without_ssl + assert_not_equal "on", @request.env["HTTPS"] + get :a + assert_response :redirect + assert_match %r{^https://}, @response.headers['Location'] + get :b + assert_response :redirect + assert_match %r{^https://}, @response.headers['Location'] + end + + def test_required_with_ssl + @request.env['HTTPS'] = "on" + get :a + assert_response :success + get :b + assert_response :success + end + + def test_disallowed_without_ssl + assert_not_equal "on", @request.env["HTTPS"] + get :d + assert_response :success + end + + def test_disallowed_with_ssl + @request.env['HTTPS'] = "on" + get :d + assert_response :redirect + assert_match %r{^http://}, @response.headers['Location'] + end + + def test_allowed_without_ssl + assert_not_equal "on", @request.env["HTTPS"] + get :c + assert_response :success + end + + def test_allowed_with_ssl + @request.env['HTTPS'] = "on" + get :c + assert_response :success + end +end \ No newline at end of file -- 1.5.4.3