aboutsummaryrefslogtreecommitdiff
path: root/lib/mauve
diff options
context:
space:
mode:
authorPatrick J Cherry <patrick@bytemark.co.uk>2011-06-09 18:09:52 +0100
committerPatrick J Cherry <patrick@bytemark.co.uk>2011-06-09 18:09:52 +0100
commit495c44445642cfae8f23fadde299ad5307f5be58 (patch)
tree0104c9eef164235aa5ab05b126c8f63e52fb8624 /lib/mauve
parent0c88fcc91db1b003cd5d5311f62700c7867b4099 (diff)
Big commit
--HG-- rename : views/please_authenticate.haml => views/login.haml
Diffstat (limited to 'lib/mauve')
-rw-r--r--lib/mauve/alert.rb82
-rw-r--r--lib/mauve/alert_group.rb11
-rw-r--r--lib/mauve/auth_bytemark.rb10
-rw-r--r--lib/mauve/calendar_interface.rb8
-rw-r--r--lib/mauve/configuration.rb14
-rw-r--r--lib/mauve/http_server.rb16
-rw-r--r--lib/mauve/notification.rb7
-rw-r--r--lib/mauve/notifier.rb31
-rw-r--r--lib/mauve/notifiers/email.rb22
-rw-r--r--lib/mauve/notifiers/xmpp.rb462
-rw-r--r--lib/mauve/people_list.rb6
-rw-r--r--lib/mauve/person.rb45
-rw-r--r--lib/mauve/server.rb52
-rw-r--r--lib/mauve/source_list.rb2
-rw-r--r--lib/mauve/timer.rb8
-rw-r--r--lib/mauve/udp_server.rb10
-rw-r--r--lib/mauve/web_interface.rb220
17 files changed, 722 insertions, 284 deletions
diff --git a/lib/mauve/alert.rb b/lib/mauve/alert.rb
index 30d50bb..b98866c 100644
--- a/lib/mauve/alert.rb
+++ b/lib/mauve/alert.rb
@@ -103,7 +103,7 @@ module Mauve
def logger
Log4r::Logger.new(self.class.to_s)
end
-
+
def time_relative(secs)
secs = secs.to_i.abs
case secs
@@ -167,6 +167,10 @@ module Mauve
def alert_group
AlertGroup.matches(self)[0]
end
+
+ def level
+ self.alert_group.level
+ end
def subject
attribute_get(:subject) || source
@@ -174,7 +178,8 @@ module Mauve
def subject=(subject); set_changed_if_different(:subject, subject); end
def summary=(summary); set_changed_if_different(:summary, summary); end
- def detail=(detail); set_changed_if_different(:detail, detail); end
+# def detail=(detail); set_changed_if_different(:detail, detail); end
+ def detail=(detail); attribute_set(:detail, detail) ; end
protected
def set_changed_if_different(attribute, value)
@@ -252,10 +257,33 @@ module Mauve
class << self
- def all_current
- all(:cleared_at => nil)
+ def all_raised
+ all(:raised_at.not => nil, :cleared_at => nil)
end
-
+
+ def all_acknowledged
+ all(:acknowledged_at.not => nil)
+ end
+
+ def all_cleared
+ all(:cleared_at.not => nil)
+ end
+
+ # Returns a hash of all the :urgent, :normal and :low alerts.
+ #
+ # @return [Hash] A hash with the relevant alerts per level
+ def get_all ()
+ hash = Hash.new
+ hash[:urgent] = Array.new
+ hash[:normal] = Array.new
+ hash[:low] = Array.new
+ all().each do |iter|
+ next if true == iter.cleared?
+ hash[AlertGroup.matches(iter)[0].level] << iter
+ end
+ return hash
+ end
+
# Returns the next Alert that will have a timed action due on it, or nil
# if none are pending.
#
@@ -273,8 +301,9 @@ module Mauve
# Receive an AlertUpdate buffer from the wire.
#
def receive_update(update, reception_time = MauveTime.now)
- update = Proto::AlertUpdate.parse_from_string(update) unless
- update.kind_of?(Proto::AlertUpdate)
+
+ update = Proto::AlertUpdate.parse_from_string(update) unless update.kind_of?(Proto::AlertUpdate)
+
alerts_updated = []
logger.debug("Alert update received from wire: #{update.inspect.split.join(", ")}")
@@ -289,7 +318,8 @@ module Mauve
end
time_offset = (reception_time - transmission_time).round
- logger.debug("Update received from a host #{time_offset}s behind") if time_offset.abs > 0
+
+ logger.debug("Update received from a host #{time_offset}s behind") if time_offset.abs > 5
# Update each alert supplied
#
@@ -297,8 +327,17 @@ module Mauve
# Infer some actions from our pure data structure (hmm, wonder if
# this belongs in our protobuf-derived class?
#
- raise_time = alert.raise_time == 0 ? nil : MauveTime.at(alert.raise_time + time_offset)
clear_time = alert.clear_time == 0 ? nil : MauveTime.at(alert.clear_time + time_offset)
+ raise_time = alert.raise_time == 0 ? nil : MauveTime.at(alert.raise_time + time_offset)
+
+ if raise_time.nil? && clear_time.nil?
+ #
+ # Make sure that we raise if neither raise nor clear is set
+ #
+ logger.warn("No clear time or raise time set. Assuming raised!")
+
+ raise_time = reception_time
+ end
logger.debug("received at #{reception_time}, transmitted at #{transmission_time}, raised at #{raise_time}, clear at #{clear_time}")
@@ -316,10 +355,10 @@ module Mauve
##
#
- # Allow a 15s offset in timings.
+ # Allow a 5s offset in timings.
#
if raise_time
- if raise_time <= (reception_time + 15)
+ if raise_time <= (reception_time + 5)
alert_db.raised_at = raise_time
else
alert_db.will_raise_at = raise_time
@@ -327,19 +366,24 @@ module Mauve
end
if clear_time
- if clear_time <= (reception_time + 15)
+ if clear_time <= (reception_time + 5)
alert_db.cleared_at = clear_time
else
alert_db.will_clear_at = clear_time
end
end
- # re-raise
+ #
+ # Re-raise if raised_at and cleared_at are set.
+ #
if alert_db.cleared_at && alert_db.raised_at && alert_db.cleared_at < alert_db.raised_at
alert_db.cleared_at = nil
end
- if pre_cleared && alert_db.raised?
+ #
+ #
+ #
+ if (pre_raised or pre_cleared) && alert_db.raised?
alert_db.update_type = :raised
elsif pre_raised && alert_db.cleared?
alert_db.update_type = :cleared
@@ -355,8 +399,8 @@ module Mauve
# These updates happen but do not sent the alert back to the
# notification system.
#
- alert_db.importance = alert.importance if alert.importance != 0
-
+ alert_db.importance = alert.importance if alert.importance != 0
+
# FIXME: this logic ought to be clearer as it may get more complicated
#
if alert_db.update_type
@@ -369,6 +413,8 @@ module Mauve
alert_db.update_type = :changed
end
+ logger.debug "Saving #{alert_db}"
+
if !alert_db.save
if alert_db.errors.respond_to?("full_messages")
msg = alert_db.errors.full_messages
@@ -394,7 +440,9 @@ module Mauve
alerts_updated << alert_db
end
end
-
+
+ logger.debug "Got #{alerts_updated.length} alerts to notify about"
+
AlertGroup.notify(alerts_updated)
end
diff --git a/lib/mauve/alert_group.rb b/lib/mauve/alert_group.rb
index d8156fa..288b263 100644
--- a/lib/mauve/alert_group.rb
+++ b/lib/mauve/alert_group.rb
@@ -29,11 +29,14 @@ module Mauve
logger.warn "no groups found for #{alert.id}" if groups.empty?
#
- # Notify each group.
+ # Notify just the group that thinks this alert is the most urgent.
#
- groups.each do |grp|
- logger.info("notifying group #{groups[0]} of AlertID.#{alert.id}.")
- grp.notify(alert)
+ %w(urgent normal low).each do |lvl|
+ this_group = groups.find{|grp| grp.level.to_s == lvl}
+ next if this_group.nil?
+ logger.info("notifying group #{this_group} of AlertID.#{alert.id} (matching #{lvl})")
+ this_group.notify(alert)
+ break
end
end
end
diff --git a/lib/mauve/auth_bytemark.rb b/lib/mauve/auth_bytemark.rb
index 7419d10..9e0a6d1 100644
--- a/lib/mauve/auth_bytemark.rb
+++ b/lib/mauve/auth_bytemark.rb
@@ -3,7 +3,7 @@ require 'sha1'
require 'xmlrpc/client'
require 'timeout'
-class AuthSourceBytemark
+class AuthBytemark
def initialize (srv='auth.bytemark.co.uk', port=443)
raise ArgumentError.new("Server must be a String, not a #{srv.class}") if String != srv.class
@@ -11,6 +11,7 @@ class AuthSourceBytemark
@srv = srv
@port = port
@timeout = 7
+ @logger = Log4r::Logger.new(self.class.to_s)
end
## Not really needed.
@@ -33,15 +34,16 @@ class AuthSourceBytemark
raise ArgumentError.new("Login must be a string, not a #{login.class}") if String != login.class
raise ArgumentError.new("Password must be a string, not a #{password.class}") if String != password.class
raise ArgumentError.new("Login or/and password is/are empty.") if login.empty? || password.empty?
+
client = XMLRPC::Client.new(@srv,"/",@port,nil,nil,nil,nil,true,@timeout).proxy("bytemark.auth")
+
begin
challenge = client.getChallengeForUser(login)
response = Digest::SHA1.new.update(challenge).update(password).hexdigest
client.login(login, response)
- rescue XMLRPC::FaultException => fault
- return "Fault code is #{fault.faultCode} stating #{fault.faultString}"
+ rescue Exception => ex
+ return false
end
- return true
end
end
diff --git a/lib/mauve/calendar_interface.rb b/lib/mauve/calendar_interface.rb
index 08cfab3..ab2bc5b 100644
--- a/lib/mauve/calendar_interface.rb
+++ b/lib/mauve/calendar_interface.rb
@@ -27,7 +27,7 @@ module Mauve
# @return [Array] A list of all the username on support.
def self.get_users_on_support(url)
result = get_URL(url)
- logger = Log4r::Logger.new "mauve::CalendarInterface"
+ logger = Log4r::Logger.new "Mauve::CalendarInterface"
logger.debug("Cheching who is on support: #{result}")
return result
end
@@ -40,7 +40,7 @@ module Mauve
# @param [String] usr User single sign on.
# @return [Boolean] True if on support, false otherwise.
def self.is_user_on_support?(url, usr)
- logger = Log4r::Logger.new "mauve::CalendarInterface"
+ logger = Log4r::Logger.new "Mauve::CalendarInterface"
list = get_URL(url)
if true == list.include?("nobody")
logger.error("Nobody is on support thus alerts are ignored.")
@@ -63,7 +63,7 @@ module Mauve
return false if true == list.nil? or true == list.empty?
pattern = /[\d]{4}-[\d]{2}-[\d]{2}\s[\d]{2}:[\d]{2}:[\d]{2}/
result = (list[0].match(pattern))? true : false
- logger = Log4r::Logger.new "mauve::CalendarInterface"
+ logger = Log4r::Logger.new "Mauve::CalendarInterface"
logger.debug("Cheching if #{usr} is on holiday: #{result}")
return result
end
@@ -83,7 +83,7 @@ module Mauve
# @retur [Array] An array of strings, each newline creates an new item.
def self.get_URL (uri_str, limit = 11)
- logger = Log4r::Logger.new "mauve::CalendarInterface"
+ logger = Log4r::Logger.new "Mauve::CalendarInterface"
if 0 == limit
logger.warn("HTTP redirect deeper than 11 on #{uri_str}.")
diff --git a/lib/mauve/configuration.rb b/lib/mauve/configuration.rb
index b11e1b5..4b7717d 100644
--- a/lib/mauve/configuration.rb
+++ b/lib/mauve/configuration.rb
@@ -212,6 +212,7 @@ module Mauve
is_attribute "port"
is_attribute "ip"
is_attribute "document_root"
+ is_attribute "session_secret"
def builder_setup
@result = HTTPServer.instance
@@ -322,9 +323,17 @@ module Mauve
end
def holiday_url (url)
- @result.holiday_url = url
+ @result.holiday_url = url.to_s
end
-
+
+ def email(e)
+ @result.email = e.to_s
+ end
+
+ def xmpp(x)
+ @result.xmpp = x.to_s
+ end
+
def suppress_notifications_after(h)
raise ArgumentError.new("notification_threshold must be specified as e.g. (10 => 1.minute)") unless
h.kind_of?(Hash) && h.keys[0].kind_of?(Integer) && h.values[0].kind_of?(Integer)
@@ -384,7 +393,6 @@ module Mauve
# Create a new instance and adds it.
def builder_setup(label)
- pp label
@result = PeopleList.new(label)
end
diff --git a/lib/mauve/http_server.rb b/lib/mauve/http_server.rb
index 69b566b..4fd8b60 100644
--- a/lib/mauve/http_server.rb
+++ b/lib/mauve/http_server.rb
@@ -2,15 +2,15 @@
#
# Bleuurrgggggh! Bleurrrrrgghh!
#
+require 'mauve/auth_bytemark'
+require 'mauve/web_interface'
+require 'mauve/mauve_thread'
require 'digest/sha1'
require 'log4r'
require 'thin'
require 'rack'
require 'rack-flash'
require 'rack/handler/webrick'
-require 'mauve/auth_bytemark'
-require 'mauve/web_interface'
-require 'mauve/mauve_thread'
################################################################################
#
@@ -87,19 +87,19 @@ module Mauve
attr_accessor :session_secret # not used yet
def initialize
- @port = 32761
+ @port = 1288
@ip = "127.0.0.1"
- @document_root = "."
- @session_secret = rand(2**100).to_s
+ @document_root = "/usr/share/mauvealert"
+ @session_secret = "%x" % rand(2**100)
end
def main_loop
- @server = ::Thin::Server.new(@ip, @port, Rack::CommonLogger.new(Rack::Chunked.new(Rack::ContentLength.new(WebInterface.new)), RackErrorsProxy.new(logger)), :signals => false)
+ @server = ::Thin::Server.new(@ip, @port, Rack::Session::Cookie.new(WebInterface.new, {:key => "mauvealert", :secret => @session_secret, :expire_after => 691200}), :signals => false)
@server.start
end
def stop
- @server.stop
+ @server.stop if @server
super
end
end
diff --git a/lib/mauve/notification.rb b/lib/mauve/notification.rb
index 2220211..02bf6fd 100644
--- a/lib/mauve/notification.rb
+++ b/lib/mauve/notification.rb
@@ -31,7 +31,7 @@ module Mauve
@time = time
@alert = alert
@during = during || Proc.new { true }
- @logger = Log4r::Logger.new "mauve::DuringRunner"
+ @logger = Log4r::Logger.new "Mauve::DuringRunner"
end
def now?
@@ -137,6 +137,11 @@ module Mauve
#
def alert_changed(alert)
+ if people.nil? or people.empty?
+ logger.warn "No people found in for notification #{list}"
+ return
+ end
+
# Should we notificy at all?
is_relevant = DuringRunner.new(MauveTime.now, alert, &during).now?
diff --git a/lib/mauve/notifier.rb b/lib/mauve/notifier.rb
index e0692f6..0127b6b 100644
--- a/lib/mauve/notifier.rb
+++ b/lib/mauve/notifier.rb
@@ -40,11 +40,38 @@ module Mauve
def start
super
- Configuration.current.notification_methods['xmpp'].connect if Configuration.current.notification_methods['xmpp']
+ if Configuration.current.notification_methods['xmpp']
+ #
+ # Connect to XMPP server
+ #
+ xmpp = Configuration.current.notification_methods['xmpp']
+ xmpp.connect
+
+ Configuration.current.people.each do |username, person|
+ #
+ # Ignore people without XMPP stanzas.
+ #
+ next unless person.xmpp
+
+ #
+ # For each JID, either ensure they're on our roster, or that we're in
+ # that chat room.
+ #
+ jid = if xmpp.is_muc?(person.xmpp)
+ xmpp.join_muc(person.xmpp)
+ else
+ xmpp.ensure_roster_and_subscription!(person.xmpp)
+ end
+
+ Configuration.current.people[username].xmpp = jid unless jid.nil?
+ end
+ end
end
def stop
- Configuration.current.notification_methods['xmpp'].close
+ if Configuration.current.notification_methods['xmpp']
+ Configuration.current.notification_methods['xmpp'].close
+ end
super
end
diff --git a/lib/mauve/notifiers/email.rb b/lib/mauve/notifiers/email.rb
index 2c14a54..f3b9a0f 100644
--- a/lib/mauve/notifiers/email.rb
+++ b/lib/mauve/notifiers/email.rb
@@ -38,6 +38,11 @@ module Mauve
@suppressed_changed = nil
end
+ def logger
+ @logger ||= Log4r::Logger.new self.class.to_s.sub(/::Default$/,"")
+
+ end
+
def send_alert(destination, alert, all_alerts, conditions = nil)
message = prepare_message(destination, alert, all_alerts, conditions)
args = [@server, @port]
@@ -46,14 +51,11 @@ module Mauve
Net::SMTP.start(*args) do |smtp|
smtp.send_message(message, @from, destination)
end
- rescue Errno::ECONNREFUSED => e
- @logger = Log4r::Logger.new "mauve::email_send_alert"
- @logger.error("#{e.class}: #{e.message} raised. " +
- "args = #{args.inspect} "
- )
- raise e
- rescue => e
- raise e
+ true
+ rescue StandardError => ex
+ logger.error "SMTP failure: #{ex.to_s}"
+ logger.debug ex.backtrace.join("\n")
+ false
end
end
@@ -98,8 +100,8 @@ module Mauve
# FIXME: include alert.detail as multipart mime
##Thread.abort_on_exception = true
m.body += "\n" + '-'*10 + " This is the detail field " + '-'*44 + "\n\n"
- m.body += alert.get_details()
- m.body += alert.get_details_plain_text()
+ m.body += alert.detail.to_s
+#' m.body += alert.get_details_plain_text()
m.body += "\n" + '-'*80 + "\n\n"
if @suppressed_changed == true
diff --git a/lib/mauve/notifiers/xmpp.rb b/lib/mauve/notifiers/xmpp.rb
index 18df6b2..fbc9640 100644
--- a/lib/mauve/notifiers/xmpp.rb
+++ b/lib/mauve/notifiers/xmpp.rb
@@ -1,68 +1,106 @@
require 'log4r'
require 'xmpp4r'
-require 'xmpp4r/xhtml'
require 'xmpp4r/roster'
-require 'xmpp4r/muc/helper/simplemucclient'
+require 'xmpp4r/muc'
+# require 'xmpp4r/xhtml'
+# require 'xmpp4r/discovery/helper/helper'
require 'mauve/notifiers/debug'
-#Jabber::debug = true
-module Mauve
- module Notifiers
- module Xmpp
-
- class CountingMUCClient < Jabber::MUC::SimpleMUCClient
- attr_reader :participants
- def initialize(*a)
- super(*a)
- @participants = 0
- self.on_join { @participants += 1 }
- self.on_leave { @participants -= 1 }
- end
+#
+# A couple of monkey patches to fix up all this nonsense.
+#
+module Jabber
+ class Stream
+ def close
+ #
+ # Just close
+ #
+ close!
+ end
+ def close!
+ 10.times do
+ pr = 0
+ @tbcbmutex.synchronize { pr = @processing }
+ break if pr = 0
+ Thread::pass if pr > 0
+ sleep 1
end
+
+ # Order Matters here! If this method is called from within
+ # @parser_thread then killing @parser_thread first would
+ # mean the other parts of the method fail to execute.
+ # That would be bad. So kill parser_thread last
+ @tbcbmutex.synchronize { @processing = 0 }
+ @fd.close if @fd and !@fd.closed?
+ @status = DISCONNECTED
+ stop
+ end
+ end
+end
+
+
+
+
+module Mauve
+ module Notifiers
+ module Xmpp
+# class CountingMUCClient < Jabber::MUC::SimpleMUCClient
+#
+# attr_reader :participants
+#
+# def initialize(*a)
+# super(*a)
+# @participants = 0
+# self.on_join { @participants += 1 }
+# self.on_leave { @participants -= 1 }
+# end
+#
+# end
+#
class Default
include Jabber
-
+
# Atrtribute.
attr_reader :name
# Atrtribute.
attr_accessor :jid, :password
- # Atrtribute.
- attr_accessor :initial_jid
-
- # Atrtribute.
- attr_accessor :initial_messages
-
def initialize(name)
+ Jabber::logger = self.logger
+ #Jabber::debug = true
+ #Jabber::warnings = true
+
@name = name
@mucs = {}
@roster = nil
+ @closing = false
+
end
def logger
- @logger ||= Log4r::Logger.new self.class.to_s
+ # Give the logger a sane name
+ @logger ||= Log4r::Logger.new self.class.to_s.sub(/::Default$/,"")
+ end
+
+ def jid=(jid)
+ @jid = JID.new(jid)
end
+
+ def connect
+ logger.info "Jabber starting connection to #{@jid}"
- def reconnect
- if @client
- begin
- logger.debug "Jabber closing old client connection"
- @client.close
- @client = nil
- @roster = nil
- rescue Exception => ex
- logger.error "#{ex} when reconnecting"
- end
- end
+ # Make sure we're disconnected.
+ self.close if @client.is_a?(Client)
+
+ @client = Client.new(@jid)
- logger.debug "Jabber starting connection to #{@jid}"
- @client = Client.new(JID::new(@jid))
+ @closing = false
@client.connect
@client.auth_nonsasl(@password, false)
@roster = Roster::Helper.new(@client)
@@ -70,11 +108,17 @@ module Mauve
# Unconditionally accept all roster add requests, and respond with a
# roster add + subscription request of our own if we're not subscribed
# already
- @roster.add_subscription_request_callback do |ri, stanza|
+ @roster.add_subscription_request_callback do |ri, presence|
Thread.new do
- logger.debug("Accepting subscription request from #{stanza.from}")
- @roster.accept_subscription(stanza.from)
- ensure_roster_and_subscription!(stanza.from)
+ logger.debug "Known? #{is_known_contact?(presence.from).inspect}"
+ if is_known_contact?(presence.from)
+ logger.info("Accepting subscription request from #{presence.from}")
+ @roster.accept_subscription(presence.from)
+ ensure_roster_and_subscription!(presence.from)
+ else
+ logger.info("Declining subscription request from #{presence.from}")
+ @roster.decline_subscription(presence.from)
+ end
end.join
end
@@ -85,37 +129,40 @@ module Mauve
@roster.wait_for_roster
logger.debug "Jabber authenticated, setting presence"
- @client.send(Presence.new.set_type(:available))
-
- @mucs = {}
-
- logger.debug "Jabber is ready in theory"
- end
-
- def reconnect_and_retry_on_error
- @already_reconnected = false
- begin
- yield
- rescue StandardError => ex
- logger.error "#{ex} during notification\n"
- logger.debug ex.backtrace
- if !@already_reconnected
- reconnect
- @already_reconnected = true
- retry
- else
- raise ex
+ @client.send(Presence.new(nil, "Woo!").set_type(nil))
+
+ @client.on_exception do |ex, stream, where|
+ #
+ # The XMPP4R exception clauses in Stream all close the stream, so
+ # we just need to reconnect.
+ #
+ unless ex.nil? or @closing
+ logger.warn(["Caught",ex.class,ex.to_s,"during XMPP",where].join(" "))
+ connect
+ @mucs.each do |jid, muc|
+ @mucs.delete(jid)
+ join_muc(jid)
+ end
end
end
end
- def connect
- self.reconnect_and_retry_on_error { self.send_msg(@initial_jid, "Hello!") }
+ #
+ # Kills the processor thread
+ #
+ def stop
+ @client.stop
end
def close
- self.send_msg(@initial_jid, "Goodbye!")
- @client.close
+ @closing = true
+ if @client and @client.is_connected?
+ @mucs.each do |jid, muc|
+ muc.exit("Goodbye!") if muc.active?
+ end
+ @client.send(Presence.new(nil, "Goodbye!").set_type(:unavailable))
+ @client.close!
+ end
end
# Takes an alert and converts it into a message.
@@ -144,97 +191,232 @@ module Mauve
# or more of the choices - see +check_jid_has_presence+ for options.
def send_alert(destination, alert, all_alerts, conditions = nil)
- #message = Message.new(nil, alert.summary_two_lines.join("\n"))
- message = Message.new(nil, convert_alert_to_message(alert))
-
+ destination_jid = JID.new(destination)
+
if conditions
@suppressed_changed = conditions[:suppressed_changed]
end
- # MUC JIDs are prefixed with muc: - we need to strip this out.
- destination_is_muc, dest_jid = self.is_muc?(destination)
-
- begin
- xhtml = XHTML::HTML.new("<p>" +
- convert_alert_to_message(alert)+
-# alert.summary_three_lines.join("<br />") +
- #alert.summary_two_lines.join("<br />") +
- "</p>")
- message.add_element(xhtml)
- rescue REXML::ParseException => ex
- logger.warn("Can't send XMPP alert as valid XHTML-IM, falling back to plaintext")
- logger.debug(ex)
- end
-
- logger.debug "Jabber sending #{message} to #{destination}"
- reconnect unless @client
-
- ensure_roster_and_subscription!(dest_jid) unless destination_is_muc
-
- if conditions && !check_alert_conditions(dest_jid, conditions)
- logger.debug("Alert conditions not met, not sending XMPP alert to #{jid}")
+ if conditions && !check_alert_conditions(destination_jid, conditions)
+ logger.info("Alert conditions not met, not sending XMPP alert to #{destination_jid}")
return false
end
- if destination_is_muc
- if !@mucs[dest_jid]
- @mucs[dest_jid] = CountingMUCClient.new(@client)
- @mucs[dest_jid].join(JID.new(dest_jid))
- end
- reconnect_and_retry_on_error { @mucs[dest_jid].send(message, nil) ; true }
- else
- message.to = dest_jid
- reconnect_and_retry_on_error { @client.send(message) ; true }
- end
+ send_message(destination_jid, convert_alert_to_message(alert))
end
# Sends a message to the destionation.
#
- # @param [String] destionation The (full) JID to send to.
+ # @param [String] destination The (full) JID to send to.
# @param [String] msg The (formatted) message to send.
# @return [NIL] nada.
- def send_msg(destination, msg)
- reconnect unless @client
- message = Message.new(nil, msg)
- destination_is_muc, dest_jid = self.is_muc?(destination)
- if destination_is_muc
- if !@mucs[dest_jid]
- @mucs[dest_jid] = CountingMUCClient.new(@client)
- @mucs[dest_jid].join(JID.new(dest_jid))
+ def send_message(jid, msg)
+ jid = JID.new(jid) unless jid.is_a?(JID)
+
+ message = Message.new(jid)
+
+ #if msg.is_a?(XHTML::HTML)
+ # message.add_element(msg)
+ #else
+ message.body = msg
+ #end
+
+ if is_muc?(jid)
+ jid = join_muc(jid.strip)
+ muc = @mucs[jid]
+
+ if muc
+ message.to = muc.jid
+ muc.send(message)
+ true
+ else
+ logger.warn "Failed to join MUC #{jid} when trying to send a message"
+ false
end
- reconnect_and_retry_on_error { @mucs[dest_jid].send(message, nil) ; true }
else
- message.to = dest_jid
- reconnect_and_retry_on_error { @client.send(message) ; true }
+ #
+ # We aren't interested in sending things to people who aren't online.
+ #
+ ensure_roster_and_subscription!(jid)
+
+ if check_jid_has_presence(jid)
+ #
+ # We set the chat type to chat
+ #
+ message.type = :chat
+ message.to = jid
+ @client.send(message)
+ true
+ else
+ false
+ end
end
- return nil
end
- protected
+ #
+ # Joins a chat, and returns the stripped JID of the chat joined.
+ #
+ def join_muc(jid, password=nil)
+ if jid.is_a?(String)
+ jid = JID.new($1) if jid =~ /^muc:(.*)/
+ end
+
+ unless jid.is_a?(JID)
+ logger.warn "I don't think #{jid} is a MUC"
+ return
+ end
+
+ jid.resource = @client.jid.resource if jid.resource.to_s.empty?
+
+ if !@mucs[jid.strip]
+
+ logger.info("Adding new MUC client for #{jid}")
+
+ @mucs[jid.strip] = Jabber::MUC::MUCClient.new(@client)
+
+ # Add some callbacks
+ @mucs[jid.strip].add_message_callback do |m|
+ receive_message(m)
+ end
+
+ @mucs[jid.strip].add_private_message_callback do |m|
+ receive_message(m)
+ end
+
+ end
+
+ if !@mucs[jid.strip].active?
+ logger.info("Joining #{jid}")
+ #
+ # Make sure we have a resource.
+ #
+ @mucs[jid.strip].join(jid, password)
- # Checks whether the destination JID is a MUC.
- # Returns [true/false, destination]
- def is_muc?(destination)
- if /^muc:(.*)/.match(destination)
- [true, $1]
else
- [false, destination]
+ logger.debug("Already joined #{jid}.")
end
- end
+
+ #
+ # Return the JID object
+ #
+ jid.strip
+ end
+ #
+ # Checks whether the destination JID is a MUC.
+ #
+ def is_muc?(jid)
+ (jid.is_a?(JID) and @mucs.keys.include?(jid.strip)) or
+ (jid.is_a?(String) and jid =~ /^muc:(.*)/)
+
+ #
+ # It would be nice to use service discovery to determin this, but it
+ # turns out that it is shite in xmpp4r. It doesn't return straight
+ # away with an answer, making it a bit useless. Some sort of weird
+ # threading issue, I think.
+ #
+ # begin
+ # logger.warn caller.join("\n")
+ # cl = Discovery::Helper.new(@client)
+ # res = cl.get_info_for(jid.strip)
+ # @client.wait
+ # logger.warn "hello #{res.inspect}"
+ # res.is_a?(Discovery::IqQueryDiscoInfo) and res.identity.category == :conference
+ # rescue Jabber::ServerError => ex
+ # false
+ # end
+ end
+
+ #
# Checks to see if the JID is in our roster, and whether we are
# subscribed to it or not. Will add to the roster and subscribe as
# is necessary to ensure both are true.
+ #
def ensure_roster_and_subscription!(jid)
- jid = JID.new(jid)
- ri = @roster.find(jid)[jid]
- if ri.nil?
- @roster.add(jid, nil, true)
- else
- ri.subscribe unless [:to, :both, :remove].include?(ri.subscription)
- end
- rescue Exception => ex
+ return jid if is_muc?(jid)
+
+ jid = JID.new(jid) unless jid.is_a?(JID)
+
+ ri = @roster.find(jid).values.first
+ @roster.add(jid, nil, true) if ri.nil?
+
+ ri = @roster.find(jid).values.first
+ ri.subscribe unless [:to, :both, :remove].include?(ri.subscription)
+ ri.jid
+ rescue StandardError => ex
logger.error("Problem ensuring that #{jid} is subscribed and in mauve's roster: #{ex.inspect}")
+ nil
+ end
+
+ protected
+
+ def receive_message(msg)
+ # We only want to hear messages from known contacts.
+ unless is_known_contact?(msg.from)
+ # ignore message
+ logger.info "Ignoring message from unknown contact #{msg.from}"
+ return nil
+ end
+
+ case msg.type
+ when :error
+ receive_error_message(msg)
+ when :groupchat
+ receive_groupchat_message(msg)
+ else
+ receive_normal_message(msg)
+ end
+ end
+
+ def receive_error_message(msg)
+ logger.warn("Caught XMPP error #{msg}")
+ nil
+ end
+
+ def receive_normal_message(msg)
+ #
+ # Treat invites specially
+ #
+ if msg.x("jabber:x:conference")
+ #
+ # recieved an invite. Need to mangle the jid.
+ #
+ jid =JID.new(msg.x("jabber:x:conference").attribute("jid"))
+ # jid.resource = @client.jid.resource
+ logger.info "Received an invite to #{jid}"
+ unless join_muc(jid)
+ logger.warn "Failed to join MUC #{jid} following invitation"
+ return nil
+ end
+ elsif msg.body
+ #
+ # Received a message with a body.
+ #
+ jid = msg.from
+ end
+
+ #
+ # I don't have time to talk to myself!
+ #
+ if jid and jid.strip != @client.jid.strip
+ txt = File.executable?('/usr/games/fortune') ? `/usr/games/fortune -s -n 60`.chomp : "I'd love to stay and chat, but I'm really too busy."
+ send_message(jid, txt)
+ end
+ end
+
+ def receive_groupchat_message(msg)
+ #
+ # We only want group chat messages from MUCs we're already joined to,
+ # that we've not sent ourselves, that are not historical, and that
+ # match our resource or node in the body.
+ #
+ if @mucs[msg.from.strip].is_a?(MUC::MUCClient) and
+ msg.from != @mucs[msg.from.strip].jid and
+ msg.x("jabber:x:delay") == nil and
+ (msg.body =~ /\b#{Regexp.escape(@client.jid.resource)}\b/i or
+ msg.body =~ /\b#{Regexp.escape(@client.jid.node)}\b/i)
+ receive_normal_message(msg)
+ end
end
def check_alert_conditions(destination, conditions)
@@ -264,8 +446,11 @@ module Mauve
# Returns true if at least one of the presence specifiers for the jid
# is met, false otherwise. Note that if the alerter can't see the alertee's
# presence, only 'unknown' will match - generally, you'll want [:online, :unknown]
- def check_jid_has_presence(jid, presence_or_presences)
- return true if jid.match(/^muc:/)
+ def check_jid_has_presence(jid, presence_or_presences = [:online, :unknown])
+ jid = JID.new(jid) unless jid.is_a?(JID)
+
+
+ return true if is_muc?(jid)
reconnect unless @client
@@ -291,15 +476,16 @@ module Mauve
end
results.include?(true)
end
-
- end
- #
- # TODO parse message and ack as needed..? The trick is here to
- # understand what the person sending the message wants. Could be
- # difficult.
- def receive_message(message)
- @logger.debug "Received message from #{message.from}.. Ignoring for now."
+ def is_known_contact?(jid)
+ jid = JID.new(jid) unless jid.is_a?(JID)
+
+ Configuration.current.people.any? do |username, person|
+ next unless person.xmpp.is_a?(JID)
+ person.xmpp.strip == jid.strip
+ end
+ end
+
end
end
end
diff --git a/lib/mauve/people_list.rb b/lib/mauve/people_list.rb
index 2e4c737..23e0c1e 100644
--- a/lib/mauve/people_list.rb
+++ b/lib/mauve/people_list.rb
@@ -21,19 +21,21 @@ module Mauve
alias username label
def list
- self[:list]
+ self[:list] || []
end
#
# Set up the logger
def logger
- @logger ||= Log4r::Logger.new self.class
+ @logger ||= Log4r::Logger.new self.class.to_s
end
#
# Return the array of people
#
def people
+ logger.warn "No-one found in the people list for #{self.label}" if self.list.empty?
+
list.collect do |name|
Configuration.current.people.has_key?(name) ? Configuration.current.people[name] : nil
end.reject{|person| person.nil?}
diff --git a/lib/mauve/person.rb b/lib/mauve/person.rb
index da3aa13..42b6baf 100644
--- a/lib/mauve/person.rb
+++ b/lib/mauve/person.rb
@@ -3,7 +3,7 @@ require 'timeout'
require 'log4r'
module Mauve
- class Person < Struct.new(:username, :password, :holiday_url, :urgent, :normal, :low)
+ class Person < Struct.new(:username, :password, :holiday_url, :urgent, :normal, :low, :email, :xmpp, :sms)
attr_reader :notification_thresholds
@@ -24,26 +24,50 @@ module Mauve
#
class NotificationCaller
- def initialize(alert, other_alerts, notification_methods, base_conditions={})
- logger = Log4r::Logger.new "mauve::NotificationCaller"
+ def initialize(person, alert, other_alerts, notification_methods, base_conditions={})
+ @person = person
@alert = alert
@other_alerts = other_alerts
@notification_methods = notification_methods
@base_conditions = base_conditions
end
- def method_missing(name, destination, *args)
- conditions = @base_conditions.merge(args[0] ? args[0] : {})
- notification_method = @notification_methods[name.to_s]
+ def logger ; @logger ||= Log4r::Logger.new self.class.to_s ; end
+
+ #
+ # This method makes sure things liek
+ #
+ # xmpp
+ # works
+ #
+ def method_missing(name, *args)
+ #
+ # Work out the destination
+ #
+ if args.first.is_a?(String)
+ destination = args.pop
+ else
+ destination = @person.__send__(name)
+ end
- unless notification_method
- raise NoMethodError.new("#{name} not defined as a notification method")
+ if args.first.is_a?(Array)
+ conditions = @base_conditions.merge(args[0])
end
+
+ notification_method = Configuration.current.notification_methods[name.to_s]
+
+ raise NoMethodError.new("#{name} not defined as a notification method") unless notification_method
+
# Methods are expected to return true or false so the user can chain
# them together with || as fallbacks. So we have to catch exceptions
# and turn them into false.
#
- notification_method.send_alert(destination, @alert, @other_alerts, conditions)
+ res = notification_method.send_alert(destination, @alert, @other_alerts, conditions)
+ #
+ # Log the result
+ logger.debug "Notification " + (res ? "succeeded" : "failed" ) + " for #{@person.username} using notifier '#{name}' to '#{destination}'"
+
+ res
end
end
@@ -174,6 +198,7 @@ module Mauve
return if suppressed? or this_alert_suppressed
result = NotificationCaller.new(
+ self,
alert,
current_alerts,
Configuration.current.notification_methods,
@@ -197,7 +222,7 @@ module Mauve
# Returns the subset of current alerts that are relevant to this Person.
#
def current_alerts
- Alert.all_current.select do |alert|
+ Alert.all_raised.select do |alert|
my_last_update = AlertChanged.first(:person => username, :alert_id => alert.id)
my_last_update && my_last_update.update_type != :cleared
end
diff --git a/lib/mauve/server.rb b/lib/mauve/server.rb
index 30536bb..ac24da4 100644
--- a/lib/mauve/server.rb
+++ b/lib/mauve/server.rb
@@ -12,7 +12,6 @@ require 'mauve/processor'
require 'mauve/http_server'
require 'log4r'
-
module Mauve
class Server
@@ -70,7 +69,7 @@ module Mauve
#
DataMapper.setup(:default, @config[:database])
- DataObjects::Sqlite3.logger = Log4r::Logger.new("Mauve::DataMapper")
+ # DataObjects::Sqlite3.logger = Log4r::Logger.new("Mauve::DataMapper")
#
# Update any tables.
@@ -82,7 +81,7 @@ module Mauve
#
# Work out when the server was last stopped
#
- @stopped_at = self.last_heartbeat
+ # topped_at = self.last_heartbeat
end
def last_heartbeat
@@ -91,7 +90,7 @@ module Mauve
#
[ Alert.last(:order => :updated_at.asc),
AlertChanged.last(:order => :updated_at.asc) ].
- reject{|a| a.nil?}.
+ reject{|a| a.nil? or a.updated_at.nil? }.
collect{|a| a.updated_at.to_time}.
sort.
last
@@ -108,26 +107,61 @@ module Mauve
def stop
@stop = true
+ thread_list = Thread.list
+
+ thread_list.delete(Thread.current)
+
THREAD_CLASSES.reverse.each do |klass|
- klass.instance.stop
+ thread_list.delete(klass.instance)
+ klass.instance.stop unless klass.instance.nil?
end
-
+
+ thread_list.each do |t|
+ t.exit
+ end
+
@logger.info("All threads stopped")
end
def run
loop do
+ thread_list = Thread.list
+
+ thread_list.delete(Thread.current)
+
THREAD_CLASSES.each do |klass|
+ thread_list.delete(klass.instance)
+
next if @frozen or @stop
-
+
unless klass.instance.alive?
- # ugh something has died.
- klass.instance.join
+ # ugh something has died.
+ #
+ begin
+ klass.instance.join
+ rescue StandardError => ex
+ @logger.warn "Caught #{ex.to_s} whilst checking #{klass} thread"
+ @logger.debug ex.backtrace.join("\n")
+ end
+ #
+ # Start the stuff.
klass.instance.start unless @stop
end
end
+ thread_list.each do |t|
+ next unless t.alive?
+ begin
+ t.join
+ rescue StandardError => ex
+ @logger.fatal "Caught #{ex.to_s} whilst checking threads"
+ @logger.debug ex.backtrace.join("\n")
+ self.stop
+ break
+ end
+ end
+
break if @stop
sleep 1
diff --git a/lib/mauve/source_list.rb b/lib/mauve/source_list.rb
index 4ffef15..23f5ae1 100644
--- a/lib/mauve/source_list.rb
+++ b/lib/mauve/source_list.rb
@@ -27,7 +27,7 @@ module Mauve
## Default contructor.
def initialize ()
- @logger = Log4r::Logger.new "mauve::SourceList"
+ @logger = Log4r::Logger.new "Mauve::SourceList"
@hash = Hash.new
@http_head = Regexp.compile(/^http[s]?:\/\//)
@http_tail = Regexp.compile(/\/.*$/)
diff --git a/lib/mauve/timer.rb b/lib/mauve/timer.rb
index 5355dcc..60b541c 100644
--- a/lib/mauve/timer.rb
+++ b/lib/mauve/timer.rb
@@ -21,7 +21,6 @@ module Mauve
end
def main_loop
- @logger.debug "hello"
#
# Get the next alert.
#
@@ -73,7 +72,12 @@ module Mauve
#
# This is a rate-limiting step for alerts.
#
- Kernel.sleep 0.2
+ Kernel.sleep 0.1
+ #
+ # Not sure if this is needed or not. But the timer thread seems to
+ # freeze here, apparently stuck on a select() statement.
+ #
+ Thread.pass
end
return if self.should_stop? or next_to_notify.nil?
diff --git a/lib/mauve/udp_server.rb b/lib/mauve/udp_server.rb
index a570e8a..a873e77 100644
--- a/lib/mauve/udp_server.rb
+++ b/lib/mauve/udp_server.rb
@@ -68,11 +68,13 @@ module Mauve
#
# TODO: why is/isn't this non-block?
#
+ i = 0
begin
- # packet = @socket.recvfrom_nonblock(65535)
- packet = @socket.recvfrom(65535)
+ packet = @socket.recvfrom_nonblock(65535)
+# packet = @socket.recvfrom(65535)
received_at = MauveTime.now
- rescue Errno::EAGAIN, Errno::EWOULDBLOCK
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK => ex
+ puts "#{i += 1} + #{ex}"
IO.select([@socket])
retry unless self.should_stop?
end
@@ -99,7 +101,7 @@ module Mauve
#
# Triggers loop to close socket.
#
- UDPSocket.open.send("", 0, @socket.addr[2], @socket.addr[1]) unless @socket.closed?
+ UDPSocket.open.send("", 0, @socket.addr[2], @socket.addr[1]) unless @socket.nil? or @socket.closed?
super
end
diff --git a/lib/mauve/web_interface.rb b/lib/mauve/web_interface.rb
index 4569cb6..210f88a 100644
--- a/lib/mauve/web_interface.rb
+++ b/lib/mauve/web_interface.rb
@@ -1,7 +1,11 @@
# encoding: UTF-8
+require 'haml'
+require 'redcloth'
+
+require 'sinatra/tilt'
require 'sinatra/base'
require 'sinatra-partials'
-require 'haml'
+
require 'rack'
require 'rack-flash'
@@ -16,15 +20,18 @@ module Mauve
class PleaseAuthenticate < Exception; end
- use Rack::Session::Cookie, :expire_after => 604800 # 7 days in seconds
+ use Rack::CommonLogger
+ use Rack::Chunked
+ use Rack::ContentLength
+ use Rack::Flash
- enable :sessions
+# Tilt.register :textile, RedClothTemplate
- use Rack::Flash
-
- set :root, "/usr/share/mauve"
- set :views, "#{root}/views"
- set :public, "#{root}/static"
+
+ # Ugh.. hacky way to dynamically configure the document root.
+ set :root, Proc.new{ HTTPServer.instance.document_root }
+ set :views, Proc.new{ root && File.join(root, 'views') }
+ set :public, Proc.new{ root && File.join(root, 'static') }
set :static, true
set :show_exceptions, true
@@ -39,12 +46,43 @@ module Mauve
########################################################################
before do
- @person = Configuration.current.people[session['username']]
@title = "Mauve alert panel"
+ @person = nil
+ #
+ # Make sure we're authenticated.
+ #
+
+ if session.has_key?('username') and Configuration.current.people.has_key?(session['username'].to_s)
+ #
+ # Phew, we're authenticated
+ #
+ @person = Configuration.current.people[session['username']]
+
+ #
+ # A bit wasteful maybe..?
+ #
+ @alerts_raised = Alert.all_raised
+ @alerts_cleared = Alert.all_cleared
+ @alerts_ackd = Alert.all_acknowledged
+ @group_by = "subject"
+ else
+ # Uh-oh.. Intruder alert!
+ #
+ ok_urls = %w(/ /login /logout)
+
+ unless ok_urls.include?(request.path_info)
+ flash['error'] = "You must be logged in to access that page."
+ redirect "/login?next_page=#{request.path_info}"
+ end
+ end
end
get '/' do
- redirect '/alerts'
+ if @person.nil?
+ redirect '/login'
+ else
+ redirect '/alerts'
+ end
end
########################################################################
@@ -53,36 +91,69 @@ module Mauve
#
# The password can be either the SSO or a local one defined
# in the configuration file.
- #
+
+ get '/login' do
+ if @person
+ redirect '/'
+ else
+ @next_page = params[:next_page] || '/'
+ haml :login
+ end
+ end
+
post '/login' do
usr = params['username']
pwd = params['password']
- ret_sso = helper_auth_SSO(usr, pwd)
- ret_loc = helper_auth_local(usr, pwd)
- if "success" == ret_sso or "success" == ret_loc
+ next_page = params['next_page']
+ #
+ # Make sure we don't magically logout automatically :)
+ #
+ next_page = '/' if next_page == '/logout'
+
+ if auth_helper(usr, pwd)
session['username'] = usr
+ redirect next_page
else
- flash['error'] =<<__MSG
-<hr /> <img src="/images/error.png" /> <br />
-ACCESS DENIED <br />
-#{ret_sso} <br />
-#{ret_loc} <hr />
-__MSG
+ flash['error'] = "Access denied."
end
- redirect '/alerts'
end
get '/logout' do
session.delete('username')
- redirect '/alerts'
+ redirect '/login'
end
get '/alerts' do
- #now = MauveTime.now.to_f
- please_authenticate()
- find_active_alerts()
- #pp MauveTime.now.to_f - now
- haml(:alerts2)
+ redirect '/alerts/raised'
+ end
+
+ get '/alerts/:alert_type' do
+ redirect "/alerts/#{params[:alert_type]}/subject"
+ end
+
+ get '/alerts/:alert_type/:group_by' do
+ if %w(raised cleared acknowledged).include?(params[:alert_type])
+ @alert_type = params[:alert_type]
+ else
+ @alert_type = "raised"
+ end
+
+ if %w(subject source summary id alert_id level).include?(params[:group_by])
+ @group_by = params[:group_by]
+ else
+ @group_by = "subject"
+ end
+
+ case @alert_type
+ when "raised"
+ @grouped_alerts = group_by(@alerts_raised, @group_by)
+ when "cleared"
+ @grouped_alerts = group_by(@alerts_cleared, @group_by)
+ when "acknowledged"
+ @grouped_alerts = group_by(@alerts_ackd, @group_by)
+ end
+
+ haml(:alerts)
end
get '/_alert_summary' do
@@ -98,22 +169,20 @@ __MSG
partial("head")
end
- get '/alert/:id/detail' do
- please_authenticate
-
- content_type("text/html") # I think
- Alert.get(params[:id]).detail
+ get '/alert/:id/_detail' do
+ content_type "text/html"
+ alert = Alert.get(params[:id])
+
+ haml :_detail, :locals => { :alert => alert } unless alert.nil?
end
get '/alert/:id' do
- please_authenticate
find_active_alerts
@alert = Alert.get(params['id'])
haml :alert
end
post '/alert/:id/acknowledge' do
- please_authenticate
alert = Alert.get(params[:id])
if alert.acknowledged?
@@ -128,7 +197,6 @@ __MSG
# Note that :until must be in seconds.
post '/alert/acknowledge/:id/:until' do
#now = MauveTime.now.to_f
- please_authenticate
alert = Alert.get(params[:id])
alert.acknowledge!(@person, params[:until].to_i())
@@ -140,7 +208,6 @@ __MSG
post '/alert/:id/raise' do
#now = MauveTime.now.to_f
- please_authenticate
alert = Alert.get(params[:id])
alert.raise!
@@ -150,7 +217,6 @@ __MSG
end
post '/alert/:id/clear' do
- please_authenticate
alert = Alert.get(params[:id])
alert.clear!
@@ -159,7 +225,6 @@ __MSG
end
post '/alert/:id/toggleDetailView' do
- please_authenticate
alert = Alert.get(params[:id])
if nil != alert
@@ -171,8 +236,6 @@ __MSG
end
post '/alert/fold/:subject' do
- please_authenticate
-
session[:display_folding][params[:subject]] = (true == session[:display_folding][params[:subject]])? false : true
content_type("application/json")
'all is good'.to_json
@@ -181,7 +244,6 @@ __MSG
########################################################################
get '/preferences' do
- please_authenticate
find_active_alerts
haml :preferences
end
@@ -189,7 +251,6 @@ __MSG
########################################################################
get '/events' do
- please_authenticate
find_active_alerts
find_recent_alerts
haml :events
@@ -199,11 +260,21 @@ __MSG
helpers do
include Sinatra::Partials
-
- def please_authenticate
- raise PleaseAuthenticate.new unless @person
+
+ def group_by(things, meth)
+ return {} if things.empty?
+
+ raise ArgumentError.new "#{things.first.class} does not respond to #{meth}" unless things.first.respond_to?(meth)
+
+ results = Hash.new{|h,k| h[k] = Array.new}
+
+ things.each do |thing|
+ results[thing.__send__(meth)] << thing
+ end
+
+ results
end
-
+
def find_active_alerts
# FIXME: make sure alerts only appear once some better way
@@ -287,25 +358,45 @@ __MSG
## Test for authentication with SSO.
#
- def helper_auth_SSO (usr, pwd)
- auth = AuthSourceBytemark.new()
- begin
- return "success" if true == auth.authenticate(usr,pwd)
- return "SSO did not regcognise your login/password combination."
- rescue ArgumentError => ex
- return "SSO argument error: #{ex.message}"
- rescue => ex
- return "SSO generic error: #{ex.message}"
+ def auth_helper (usr, pwd)
+ # First try Bytemark
+ #
+ auth = AuthBytemark.new()
+ result = begin
+ auth.authenticate(usr,pwd)
+ rescue Exception => ex
+ @logger.debug "Caught exception during Bytemark auth for #{usr} (#{ex.to_s})"
+ false
end
- end
- ## Test for authentication with configuration file parameter.
- #
- def helper_auth_local (usr, pwd)
- person = Configuration.current.people[params['username']]
- return "I did not recognise your local login details." if !person
- return "I did not recognise your local password." if Digest::SHA1.hexdigest(params['password']) != person.password
- return "success"
+ if true == result
+ return true
+ else
+ @logger.debug "Bytemark authentication failed for #{usr}"
+ end
+
+ #
+ # OK now try local auth
+ #
+ result = begin
+ if Configuration.current.people.has_key?(usr)
+ Digest::SHA1.hexdigest(params['password']) == Configuration.current.people[usr].password
+ end
+ rescue Exception => ex
+ @logger.debug "Caught exception during local auth for #{usr} (#{ex.to_s})"
+ false
+ end
+
+ if true == result
+ return true
+ else
+ @logger.debug "Local authentication failed for #{usr}"
+ end
+ #
+ # Rate limit logins.
+ #
+ sleep 5
+ false
end
end
@@ -316,14 +407,13 @@ __MSG
status 403
session[:display_alerts] = Hash.new()
session[:display_folding] = Hash.new()
- haml :please_authenticate
end
########################################################################
# @see http://stackoverflow.com/questions/2239240/use-rackcommonlogger-in-sinatra
def call(env)
if true == @logger.nil?
- @logger = Log4r::Logger.new("mauve::Rack")
+ @logger = Log4r::Logger.new("Mauve::Rack")
end
env['rack.errors'] = RackErrorsProxy.new(@logger)
super(env)