diff options
author | Patrick J Cherry <patrick@bytemark.co.uk> | 2012-03-22 17:40:38 +0000 |
---|---|---|
committer | Patrick J Cherry <patrick@bytemark.co.uk> | 2012-03-22 17:40:38 +0000 |
commit | dc443284c4b5f59a4447797f88730d9fe1bc0b45 (patch) | |
tree | 1f82d0e9754c3239cc4ac65220ef4f54a4a5e44e | |
parent | 2622cd5d2cb322b78229d345d82076a582925ae2 (diff) |
Tidied up login authentication + tests (woo!)
-rw-r--r-- | lib/mauve/authentication.rb | 9 | ||||
-rw-r--r-- | lib/mauve/web_interface.rb | 17 | ||||
-rw-r--r-- | test/tc_mauve_web_interface.rb | 175 | ||||
-rw-r--r-- | test/test_mauve.rb | 1 | ||||
-rw-r--r-- | views/login.haml | 2 |
5 files changed, 196 insertions, 8 deletions
diff --git a/lib/mauve/authentication.rb b/lib/mauve/authentication.rb index d0d4596..54743f1 100644 --- a/lib/mauve/authentication.rb +++ b/lib/mauve/authentication.rb @@ -139,7 +139,7 @@ module Mauve client.login(login, response) return true rescue XMLRPC::FaultException => fault - logger.warn "Authentication for #{login} failed: #{fault.faultCode}: #{fault.faultString}" + logger.warn "#{self.class} for #{login} failed: #{fault.faultCode}: #{fault.faultString}" return false rescue IOError => ex logger.warn "#{ex.class} during auth for #{login} (#{ex.to_s})" @@ -164,7 +164,12 @@ module Mauve # @return [Boolean] def authenticate(login,password) super - Digest::SHA1.hexdigest(password) == Mauve::Configuration.current.people[login].password + if ( Digest::SHA1.hexdigest(password) == Mauve::Configuration.current.people[login].password ) + return true + else + logger.warn "#{self.class} for #{login} failed" + return false + end end end diff --git a/lib/mauve/web_interface.rb b/lib/mauve/web_interface.rb index b998ad8..225cc33 100644 --- a/lib/mauve/web_interface.rb +++ b/lib/mauve/web_interface.rb @@ -4,6 +4,7 @@ require 'redcloth' require 'json' require 'mauve/authentication' +require 'mauve/http_server' tilt_lib = "tilt" begin @@ -120,7 +121,6 @@ EOF unless ok_urls.include?(request.path_info) flash['error'] = "You must be logged in to access that page." - status 403 redirect "/login?next_page=#{request.path_info}" unless no_redirect_urls.any?{|u| /^#{u}/ =~ request.path_info } end end @@ -146,7 +146,9 @@ EOF if @person redirect '/' else + @username = nil @next_page = params[:next_page] || '/' + status 403 if flash['error'] haml :login end end @@ -154,7 +156,7 @@ EOF post '/login' do usr = params['username'].to_s pwd = params['password'].to_s - next_page = params['next_page'].to_s + next_page = params['next_page'] || "/" # # Make sure we don't magically logout automatically :) @@ -165,14 +167,19 @@ EOF session['username'] = usr redirect next_page else - flash['error'] = "You must be logged in to access that page." - redirect "/login?next_page=#{next_page}" + flash['error'] = "Authentication failed." + status 401 +# redirect "/login?next_page=#{next_page}" + @title += " Login" + @username = usr + @next_page = next_page + haml :login end end get '/logout' do session.delete('username') - flash['error'] = "You have logged out!" + flash['info'] = "You have logged out!" redirect '/login' end diff --git a/test/tc_mauve_web_interface.rb b/test/tc_mauve_web_interface.rb new file mode 100644 index 0000000..a120c37 --- /dev/null +++ b/test/tc_mauve_web_interface.rb @@ -0,0 +1,175 @@ +$:.unshift "../lib" + +require 'th_mauve' +require 'th_mauve_resolv' + +require 'mauve/alert' +require 'mauve/proto' +require 'mauve/server' +require 'mauve/configuration' +require 'mauve/configuration_builder' +require 'mauve/configuration_builders' + +require 'rack/test' + +ENV['RACK_ENV'] = 'test' + +class WebInterfaceTest < Mauve::UnitTest + include Rack::Test::Methods + include Mauve + + SESSION_KEY="mauvealert" + + class SessionData + def initialize(cookies) + @cookies = cookies + @data = cookies[WebInterfaceTest::SESSION_KEY] + if @data + @data = @data.unpack("m*").first + @data = Marshal.load(@data) + else + @data = {} + end + end + + def [](key) + @data[key] + end + + def []=(key, value) + @data[key] = value + session_data = Marshal.dump(@data) + session_data = [session_data].pack("m*") + @cookies.merge("#{WebInterfaceTest::SESSION_KEY}=#{Rack::Utils.escape(session_data)}", URI.parse("//example.org//")) + raise "session variable not set" unless @cookies[WebInterfaceTest::SESSION_KEY] == session_data + end + end + + def session + SessionData.new(rack_test_session.instance_variable_get(:@rack_mock_session).cookie_jar) + end + + def setup + super + setup_database + + # + # BytemarkAuth test users are: + # + # test1: ummVRu7qF + # test2: POKvBqLT7 + # + config =<<EOF +server { + hostname "localhost" + database "sqlite::memory:" + initial_sleep 0 + + web_interface { + document_root "#{File.expand_path(File.join(File.dirname(__FILE__),".."))}" + } +} + +person ("test0") { + password "#{Digest::SHA1.new.hexdigest("password")}" + all { true } +} + +person ("test1") { + password "#{Digest::SHA1.new.hexdigest("ummVRu7qF")}" + all { true } +} + +source_list "example_hosts", %w(test-1.example.com test-2.example.com www.example.com www2.example.com) + +alert_group("test") { + includes{ in_source_list?("example_hosts") } + + level LOW + + notify("test1") { + every 10.minutes + } + +} + +alert_group("default") { + level URGENT + + notify("test1") { + every 10.minutes + } +} +EOF + + Configuration.current = ConfigurationBuilder.parse(config) + Server.instance.setup + end + + def teardown + teardown_database + super + end + + def app + Rack::Session::Cookie.new(WebInterface.new, :key => WebInterfaceTest::SESSION_KEY, :secret => "testing-1234") + end + + def test_log_in + # Check we get the login page when going to "/" before logging in. + get '/' + follow_redirect! while last_response.redirect? + assert last_response.ok? + assert last_response.body.include?("Mauve: Login") + assert session['__FLASH__'].empty? + + # Check we can access this page before logging in. + get '/alerts' + assert(session['__FLASH__'].has_key?(:error),"The flash error wasn't set following forbidden access") + follow_redirect! while last_response.redirect? + assert_equal(403, last_response.status, "The HTTP status wasn't 403") + assert last_response.body.include?("Mauve: Login") + assert session['__FLASH__'].empty? + + # + # Try to falsify our login. + # + session['username'] = "test1" + get '/alerts' + assert(session['__FLASH__'].has_key?(:error),"The flash error wasn't set following forbidden access") + follow_redirect! while last_response.redirect? + assert_equal(403, last_response.status, "The HTTP status wasn't 403") + assert last_response.body.include?("Mauve: Login") + assert session['__FLASH__'].empty? + + # + # OK login with a bad password + # + post '/login', :username => 'test1', :password => 'badpassword' + assert_equal(401, last_response.status, "A bad login did not produce a 401 response") + assert(last_response.body.include?("Mauve: Login")) + assert(session['__FLASH__'].has_key?(:error),"The flash error wasn't set") + + post '/login', :username => 'test1', :password => 'ummVRu7qF' + follow_redirect! while last_response.redirect? + assert last_response.body.include?('Mauve: ') + + get '/logout' + follow_redirect! while last_response.redirect? + assert last_response.ok? + end + + def test_alerts_show_subject + post '/login', :username => 'test1', :password => 'ummVRu7qF' + follow_redirect! while last_response.redirect? + assert last_response.body.include?('Mauve: ') + + a = Alert.new(:source => "www.example.com", :alert_id => "test_raise!") + a.raise! + + get '/alerts/raised/subject' + end + +end + + diff --git a/test/test_mauve.rb b/test/test_mauve.rb index bdce3aa..ce83c6c 100644 --- a/test/test_mauve.rb +++ b/test/test_mauve.rb @@ -24,6 +24,7 @@ tc_mauve_people_list.rb tc_mauve_person.rb tc_mauve_source_list.rb tc_mauve_time.rb +tc_mauve_web_interface.rb ).each do |s| require s end diff --git a/views/login.haml b/views/login.haml index 3deee76..d1e9484 100644 --- a/views/login.haml +++ b/views/login.haml @@ -2,7 +2,7 @@ %fieldset %legend Please log in %label{:for => "username"} Username - %input{:name => 'username', :type => 'text', :autocorrect => "off", :autocapitalize => "off"}/ + %input{:name => 'username', :type => 'text', :autocorrect => "off", :autocapitalize => "off", :value => @username}/ %br %label{:for => "password", :title => "This is either your Single Sign On password or a PIN."} Password / PIN %input{:name => 'password', :type => 'password'}/ |