From 84b1abf30fe79032209cb0fcd0bfa9d6aaf37721 Mon Sep 17 00:00:00 2001 From: Patrick J Cherry Date: Sat, 21 Apr 2012 13:37:12 +0100 Subject: Overhaul of authentication. * Added new configuration options: ** bytemark_calendar_url ** bytemark_auth_url ** remote_https_verify_mode ** remote_http_timeout ** failed_login_delay * Added authentication tests * Removed remote auth tests in from the web interface tests. * If no bytemark_auth_url is set, then no remote auth takes place. * SSL peer cert validation now takes place by default. * Removed old logic tests * Tidied the way tests take place a little. --- lib/mauve/authentication.rb | 71 ++++++++-------------- lib/mauve/configuration_builders/server.rb | 27 +++++++- lib/mauve/server.rb | 98 ++++++++++++++++++++++++++++-- 3 files changed, 146 insertions(+), 50 deletions(-) (limited to 'lib') diff --git a/lib/mauve/authentication.rb b/lib/mauve/authentication.rb index 0fc6823..c467a1d 100644 --- a/lib/mauve/authentication.rb +++ b/lib/mauve/authentication.rb @@ -1,7 +1,6 @@ # encoding: UTF-8 require 'sha1' require 'xmlrpc/client' -require 'timeout' # # This allows poking of the SSL attributes of the http client. @@ -28,8 +27,6 @@ module Mauve 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? - return false unless Mauve::Configuration.current.people.has_key?(login) - false end @@ -73,7 +70,7 @@ module Mauve unless true == result logger.info "Authentication for #{login} failed" # Rate limit - sleep 5 + sleep Server.instance.failed_login_delay end result @@ -88,45 +85,6 @@ module Mauve Mauve::Authentication::ORDER << self - # Set up the Bytemark authenticator - # - # @todo allow configuration of where the server is. - # - # @param [String] srv Authentication server name - # @param [String] port Port overwhich authentication should take place - # - # @return [Mauve::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 - raise ArgumentError.new("Port must be a Fixnum, not a #{port.class}") if Fixnum != port.class - @srv = srv - @port = port - @timeout = 7 - - self - end - - # Tests to see if a server is alive, alive-o. - # - # @deprecated Not really needed. - # - # @return [Boolean] - def ping - begin - Timeout.timeout(@timeout) do - s = TCPSocket.open(@srv, @port) - s.close() - return true - end - rescue Timeout::Error => ex - return false - rescue => ex - return false - end - return false - end - # Authenticate against the Bytemark server # # @param [String] login @@ -136,14 +94,27 @@ module Mauve def authenticate(login, password) super - client = XMLRPC::Client.new(@srv,"/",@port,nil,nil,nil,nil,true,@timeout) + # + # Don't bother checking if no auth_url has been set. + # + return false unless Server.instance.bytemark_auth_url.is_a?(URI) + + # + # Don't bother checking if the person doesn't exist. + # + return false unless Mauve::Configuration.current.people.has_key?(login) + + uri = Server.instance.bytemark_auth_url + timeout = Server.instance.remote_http_timeout + # host=nil, path=nil, port=nil, proxy_host=nil, proxy_port=nil, user=nil, password=nil, use_ssl=nil, timeout=nil) + client = XMLRPC::Client.new(uri.host, uri.path, uri.port, nil, nil, uri.user, uri.password, uri.scheme == "https", timeout) # # Make sure we verify our peer before attempting login. # if client.http.use_ssl? client.http.ca_path = "/etc/ssl/certs/" - client.http.verify_mode = OpenSSL::SSL::VERIFY_PEER + client.http.verify_mode = Server.instance.remote_https_verify_mode end begin @@ -179,6 +150,16 @@ module Mauve # @return [Boolean] def authenticate(login,password) super + # + # Don't bother checking if the person doesn't exist. + # + return false unless Mauve::Configuration.current.people.has_key?(login) + + # + # Don't bother checking if no password has been set. + # + return false if Mauve::Configuration.current.people[login].password.nil? + if ( Digest::SHA1.hexdigest(password) == Mauve::Configuration.current.people[login].password ) return true else diff --git a/lib/mauve/configuration_builders/server.rb b/lib/mauve/configuration_builders/server.rb index feb75b8..d22ed87 100644 --- a/lib/mauve/configuration_builders/server.rb +++ b/lib/mauve/configuration_builders/server.rb @@ -170,7 +170,32 @@ module Mauve # is_attribute "use_packet_buffer" is_attribute "use_notification_buffer" - + + # + # This is where the calendar is located. The request paths are hard-coded. + # + is_attribute "bytemark_calendar_url" + + # + # This is where the Bytemark authentication server is located. + # + is_attribute "bytemark_auth_url" + + # + # This is the level of SSL verification used when making external HTTPS connections. + # + is_attribute "remote_https_verify_mode" + + # + # This is the default timeout when making remote HTTP requests + # + is_attribute "remote_http_timeout" + + # + # This is the default sleep time after an authentication attempt has failed. + # + is_attribute "failed_login_delay" + def builder_setup @result = Mauve::Server.instance end diff --git a/lib/mauve/server.rb b/lib/mauve/server.rb index db5fda6..2b0e101 100644 --- a/lib/mauve/server.rb +++ b/lib/mauve/server.rb @@ -27,6 +27,7 @@ module Mauve attr_reader :hostname, :database, :initial_sleep attr_reader :packet_buffer, :notification_buffer, :started_at + attr_reader :bytemark_auth_url, :bytemark_calendar_url, :remote_http_timeout, :remote_https_verify_mode, :failed_login_delay include Singleton @@ -48,6 +49,23 @@ module Mauve @packet_buffer = [] @notification_buffer = [] + # + # Set the auth/calendar URLs + # + @bytemark_auth_url = nil + @bytemark_calendar_url = nil + + # + # Set a couple of params for remote HTTP requests. + # + @remote_http_timeout = 5 + @remote_https_verify_mode = OpenSSL::SSL::VERIFY_PEER + + # + # Rate limit login attempts to limit the success of brute-forcing. + # + @failed_login_delay = 1 + # # Set up a blank config. # @@ -70,14 +88,12 @@ module Mauve @database = d end - # # Sets up the packet buffer (or not). The argument can be "false" or "no" # or a FalseClass object for no. Anything else makes no change. # # @param [String] arg # @return [Array or nil] def use_packet_buffer=(arg) - logger.debug(arg) if arg.is_a?(FalseClass) or arg =~ /^(n(o)?|f(alse)?)$/i @packet_buffer = nil end @@ -85,14 +101,12 @@ module Mauve @packet_buffer end - # # Sets up the notification buffer (or not). The argument can be "false" or # "no" or a FalseClass object for no. Anything else makes no change. # # @param [String] arg # @return [Array or nil] def use_notification_buffer=(arg) - logger.debug(arg) if arg.is_a?(FalseClass) or arg =~ /^(n(o)?|f(alse)?)$/i @notification_buffer = nil end @@ -100,6 +114,82 @@ module Mauve @notification_buffer end + # Set the calendar URL. + # + # @param [String] arg + # @return [URI] + def bytemark_calendar_url=(arg) + raise ArgumentError, "bytemark_calendar_url must be a string" unless arg.is_a?(String) + + @bytemark_calendar_url = URI.parse(arg) + + # + # Make sure we get an HTTP URL. + # + raise ArgumentError, "bytemark_calendar_url must be an HTTP(S) URL." unless %w(http https).include?(@bytemark_calendar_url.scheme) + + # + # Set a default request path, if none was given + # + @bytemark_calendar_url.path="/" if @bytemark_calendar_url.path.empty? + + @bytemark_calendar_url + end + + # Set the Bytemark Authentication URL + # + # @param [String] arg + # @return [URI] + def bytemark_auth_url=(arg) + raise ArgumentError, "bytemark_auth_url must be a string" unless arg.is_a?(String) + + @bytemark_auth_url = URI.parse(arg) + # + # Make sure we get an HTTP URL. + # + raise ArgumentError, "bytemark_auth_url must be an HTTP(S) URL." unless %w(http https).include?(@bytemark_auth_url.scheme) + + # + # Set a default request path, if none was given + # + @bytemark_auth_url.path="/" if @bytemark_auth_url.path.empty? + + @bytemark_auth_url + end + + # Sets the timeout when making remote HTTP requests + # + # @param [Integer] arg + # @return [Integer] + def remote_http_timeout=(arg) + raise ArgumentError, "initial_sleep must be an integer" unless s.is_a?(Integer) + @remote_http_timeout = arg + end + + # Sets the SSL verification mode when makeing remote HTTPS requests + # + # @param [String] arg must be one of "none" or "peer" + # @return [Constant] + def remote_https_verify_mode=(arg) + @remote_https_verify_mode = case arg + when "peer" + OpenSSL::SSL::VERIFY_PEER + when "none" + OpenSSL::SSL::VERIFY_NONE + else + raise ArgumentError, "remote_https_verify_mode must be either 'peer' or 'none'" + end + end + + # Set the delay added following a failed login attempt. + # + # @param [Numeric] arg Number of seconds to delay following a failed login attempt + # @return [Numeric] + # + def failed_login_delay=(arg) + raise ArgumentError, "initial_sleep must be numeric" unless arg.is_a?(Numeric) + @failed_login_delay = arg + end # Set the sleep period during which notifications about old alerts are # suppressed. -- cgit v1.2.1