aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/mauve/alert.rb125
-rw-r--r--lib/mauve/authentication.rb9
-rw-r--r--lib/mauve/mauve_resolv.rb8
-rw-r--r--lib/mauve/source_list.rb37
-rw-r--r--lib/mauve/web_interface.rb17
-rw-r--r--test/tc_mauve_web_interface.rb175
-rw-r--r--test/test_mauve.rb1
-rw-r--r--test/th_mauve.rb2
-rw-r--r--test/th_mauve_resolv.rb8
-rw-r--r--views/login.haml2
10 files changed, 322 insertions, 62 deletions
diff --git a/lib/mauve/alert.rb b/lib/mauve/alert.rb
index d867ccd..9d6ab85 100644
--- a/lib/mauve/alert.rb
+++ b/lib/mauve/alert.rb
@@ -146,15 +146,18 @@ module Mauve
#
# Find the AlertGroup by name if we've got a cached value
#
- @alert_group = AlertGroup.all.find{|a| self.cached_alert_group == a.name} if @alert_group.nil? and self.cached_alert_group
+ alert_group = AlertGroup.all.find{|a| self.cached_alert_group == a.name} if self.cached_alert_group
- #
- # If we've not found the alert group by name, or the object hasn't been
- # saved since the alert group was last resolved, look for it again.
- #
- @alert_group = AlertGroup.matches(self).first if @alert_group.nil?
+ if alert_group.nil?
+ #
+ # If we've not found the alert group by name look for it again, the
+ # proper way.
+ #
+ alert_group = AlertGroup.matches(self).first
+ self.cached_alert_group = alert_group.name unless alert_group.nil?
+ end
- @alert_group
+ alert_group
end
# Pick out the source lists that match this alert by subject.
@@ -169,32 +172,53 @@ module Mauve
# @param [String] listname
# @return [Boolean]
def in_source_list?(listname)
- list = Mauve::Configuration.current.source_lists[listname]
- return false unless list.is_a?(SourceList)
- self.subject_hosts_and_ips.any?{|host| list.includes?(host) }
- end
+ source_list = Mauve::Configuration.current.source_lists[listname]
+ return false unless source_list.is_a?(SourceList)
- def subject_hosts_and_ips
- if @subject_hosts_and_ips.nil? or @subject_hosts_and_ips_last_resolved_at.nil? or (Time.now - 1800) > @subject_hosts_and_ips_last_resolved_at
- host = self.subject
- #
- # Pick out hostnames from URIs.
- #
- if host =~ /^[a-z][a-z0-9+-]+:\/\//
- begin
- uri = URI.parse(host)
- host = uri.host unless uri.host.nil?
- rescue URI::InvalidURIError => ex
- # ugh
- logger.warn "Did not recognise URI #{host}"
- end
+ host = self.subject
+
+ #
+ # Pick out hostnames from URIs.
+ #
+ if host =~ /^[a-z][a-z0-9+-]+:\/\//
+ begin
+ uri = URI.parse(host)
+ host = uri.host unless uri.host.nil?
+ rescue URI::InvalidURIError => ex
+ # ugh
+ logger.warn "Did not recognise URI #{host}"
end
+ end
- @subject_hosts_and_ips = ([host] + MauveResolv.get_ips_for(host)).flatten
- @subject_hosts_and_ips_last_resolved_at = Time.now
+ return true if source_list.list.any? do |l|
+ case l
+ when String
+ host == l
+ when Regexp
+ host =~ l
+ when IPAddr
+ begin
+ l.include?(IPAddr.new(host))
+ rescue ArgumentError => err
+ # rescue random IPAddr argument errors
+ false
+ end
+ else
+ false
+ end
end
- @subject_hosts_and_ips
+ return false unless source_list.list.any?{|l| l.is_a?(IPAddr)}
+
+ @subject_ips ||= MauveResolv.get_ips_for(host).collect{|i| IPAddr.new(i)}
+
+ return false if @subject_ips.nil? or @subject_ips.empty?
+
+ return source_list.list.select{|i| i.is_a?(IPAddr)}.any? do |list_ip|
+ @subject_ips.any?{|ip| list_ip.include?(ip)}
+ end
+
+ return false
end
# Returns the alert level
@@ -229,6 +253,39 @@ module Mauve
#
# @return [String]
def detail; attribute_get(:detail) || "_No detail set._" ; end
+
+ #
+ # Set the subject -- this clears the cached_alert_group.
+ #
+ def subject=(s)
+ self.cached_alert_group = nil
+ @subject_ips = nil
+ attribute_set(:subject, s)
+ end
+
+ #
+ # Set the detail -- this clears the cached_alert_group.
+ #
+ def detail=(s)
+ self.cached_alert_group = nil
+ attribute_set(:detail, s)
+ end
+
+ #
+ # Set the source -- this clears the cached_alert_group.
+ #
+ def source=(s)
+ self.cached_alert_group = nil
+ attribute_set(:source, s)
+ end
+
+ #
+ # Set the summary -- this clears the cached_alert_group.
+ #
+ def summary=(s)
+ self.cached_alert_group = nil
+ attribute_set(:summary, s)
+ end
protected
@@ -326,6 +383,10 @@ module Mauve
true
end
+ #
+ #
+ #
+
# Remove all history for an alert, when an alert is destroyed.
#
#
@@ -372,9 +433,8 @@ module Mauve
#
# Re-cache the alert group.
#
- @alert_group = nil
self.cached_alert_group = nil
- self.cached_alert_group = self.alert_group.name
+ self.alert_group
unless save
logger.error("Couldn't save #{self}")
@@ -428,9 +488,8 @@ module Mauve
#
# Re-cache the alert group.
#
- @alert_group = nil
self.cached_alert_group = nil
- self.cached_alert_group = self.alert_group.name
+ self.alert_group
logger.info("Postponing raise of #{self} until #{postpone_until} as it was last updated in a prior run of Mauve.")
else
@@ -446,7 +505,7 @@ module Mauve
#
# Cache the alert group, but only if not already set.
#
- self.cached_alert_group = self.alert_group.name if self.cached_alert_group.nil?
+ self.alert_group
end
unless save
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/mauve_resolv.rb b/lib/mauve/mauve_resolv.rb
index a9c7526..8ce8969 100644
--- a/lib/mauve/mauve_resolv.rb
+++ b/lib/mauve/mauve_resolv.rb
@@ -15,12 +15,10 @@ module Mauve
# @return [Array] Array of IP addresses, as Strings.
#
def get_ips_for(host)
- pp host
ips = []
- @count ||= 0
Resolv::DNS.open do |dns|
%w(A AAAA).each do |type|
- @count += 1
+ self.count += 1 if $debug
begin
ips += dns.getresources(host, Resolv::DNS::Resource::IN.const_get(type)).collect{|a| a.address.to_s}
rescue Resolv::ResolvError, Resolv::ResolvTimeout => e
@@ -35,6 +33,10 @@ module Mauve
@count ||= 0
end
+ def count=(c)
+ @count = c
+ end
+
# @return [Log4r::Logger]
def logger
@logger ||= Log4r::Logger.new(self.to_s)
diff --git a/lib/mauve/source_list.rb b/lib/mauve/source_list.rb
index 63a7aa6..a6baa2b 100644
--- a/lib/mauve/source_list.rb
+++ b/lib/mauve/source_list.rb
@@ -19,7 +19,7 @@ module Mauve
#
class SourceList
- attr_reader :label, :list, :last_resolved_at
+ attr_reader :label, :last_resolved_at
## Default contructor.
def initialize (label)
@@ -101,6 +101,15 @@ module Mauve
@logger ||= Log4r::Logger.new self.class.to_s
end
+ def list
+ #
+ # Redo resolution every thirty minutes
+ #
+ resolve if @resolved_list.empty? or @last_resolved_at.nil? or (Time.now - 1800) > @last_resolved_at
+
+ @resolved_list
+ end
+
#
# Return whether or not a list contains a source.
#
@@ -118,11 +127,6 @@ module Mauve
# @return [Boolean]
def includes?(host)
#
- # Redo resolution every thirty minutes
- #
- resolve if @resolved_list.empty? or @last_resolved_at.nil? or (Time.now - 1800) > @last_resolved_at
-
- #
# Pick out hostnames from URIs.
#
if host =~ /^[a-z][a-z0-9+-]+:\/\//
@@ -135,7 +139,7 @@ module Mauve
end
end
- return true if @resolved_list.any? do |l|
+ return true if self.list.any? do |l|
case l
when String
host == l
@@ -153,15 +157,15 @@ module Mauve
end
end
-# return false unless @resolved_list.any?{|l| l.is_a?(IPAddr)}
-#
-# ips = MauveResolv.get_ips_for(host).collect{|i| IPAddr.new(i)}
-#
-# return false if ips.empty?
-#
-# return @resolved_list.select{|i| i.is_a?(IPAddr)}.any? do |list_ip|
-# ips.any?{|ip| list_ip.include?(ip)}
-# end
+ return false unless self.list.any?{|l| l.is_a?(IPAddr)}
+
+ ips = MauveResolv.get_ips_for(host).collect{|i| IPAddr.new(i)}
+
+ return false if ips.empty?
+
+ return self.list.select{|i| i.is_a?(IPAddr)}.any? do |list_ip|
+ ips.any?{|ip| list_ip.include?(ip)}
+ end
return false
end
@@ -181,6 +185,7 @@ module Mauve
host
end
end
+
@resolved_list = new_list.flatten
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/test/th_mauve.rb b/test/th_mauve.rb
index 7972b9d..2b97e64 100644
--- a/test/th_mauve.rb
+++ b/test/th_mauve.rb
@@ -69,7 +69,7 @@ module Mauve
@logger = Log4r::Logger.new 'Mauve'
@outputter = Mauve::TestOutputter.new("test")
@outputter.formatter = Log4r::PatternFormatter.new( :pattern => "%d %l %m" )
- @outputter.level = Log4r::WARN
+ @outputter.level = ($debug ? Log4r::DEBUG : Log4r::WARN)
@logger.outputters << @outputter
return @logger
end
diff --git a/test/th_mauve_resolv.rb b/test/th_mauve_resolv.rb
index 98b597b..10bdacc 100644
--- a/test/th_mauve_resolv.rb
+++ b/test/th_mauve_resolv.rb
@@ -14,13 +14,19 @@ module Mauve
alias_method :get_ips_for_without_testing, :get_ips_for
def get_ips_for_with_testing(host)
+
lookup = {
"test-1.example.com" => %w(1.2.3.4 2001:1:2:3::4),
"test-2.example.com" => %w(1.2.3.5 2001:1:2:3::5),
"www.example.com" => %w(1.2.3.4),
"www2.example.com" => %w(1.2.3.5 2001:2::2)
}
- lookup[host] || get_ips_for_without_testing(host)
+ if lookup.has_key?(host)
+ self.count += lookup[host].length if $debug
+ lookup[host]
+ else
+ get_ips_for_without_testing(host)
+ end
end
alias_method :get_ips_for, :get_ips_for_with_testing
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'}/