diff --git a/app/models/authenticator/base.rb b/app/models/authenticator/base.rb index 5ce64ac33e8..47af92a51f2 100644 --- a/app/models/authenticator/base.rb +++ b/app/models/authenticator/base.rb @@ -55,7 +55,7 @@ def authenticate(username, password, request = nil, options = {}) authenticated = options[:authorize_only] || _authenticate(username, password, request) if authenticated - AuditEvent.success(audit.merge(:message => "User #{username} successfully validated by #{self.class.proper_name}")) + audit_success(audit.merge(:message => "User #{username} successfully validated by #{self.class.proper_name}")) if authorize? user_or_taskid = authorize_queue(username, request, options) @@ -66,17 +66,17 @@ def authenticate(username, password, request = nil, options = {}) user_or_taskid ||= autocreate_user(username) unless user_or_taskid - AuditEvent.failure(audit.merge(:message => "User #{username} authenticated but not defined in EVM")) + audit_failure(audit.merge(:message => "User #{username} authenticated but not defined in EVM")) raise MiqException::MiqEVMLoginError, _("User authenticated but not defined in EVM, please contact your EVM administrator") end end - AuditEvent.success(audit.merge(:message => "Authentication successful for user #{username}")) + audit_success(audit.merge(:message => "Authentication successful for user #{username}")) else reason = failure_reason(username, request) reason = ": #{reason}" unless reason.blank? - AuditEvent.failure(audit.merge(:message => "Authentication failed for userid #{username}#{reason}")) + audit_failure(audit.merge(:message => "Authentication failed for userid #{username}#{reason}")) raise MiqException::MiqEVMLoginError, fail_message end @@ -115,7 +115,7 @@ def authorize(taskid, username, *args) unless identity msg = "Authentication failed for userid #{username}, unable to find user object in #{self.class.proper_name}" _log.warn(msg) - AuditEvent.failure(audit.merge(:message => msg)) + audit_failure(audit.merge(:message => msg)) task.error(msg) task.state_finished return nil @@ -128,8 +128,8 @@ def authorize(taskid, username, *args) if matching_groups.empty? msg = "Authentication failed for userid #{user.userid}, unable to match user's group membership to an EVM role" - AuditEvent.failure(audit.merge(:message => msg)) _log.warn(msg) + audit_failure(audit.merge(:message => msg)) task.error(msg) task.state_finished user.save! unless user.new_record? @@ -145,7 +145,7 @@ def authorize(taskid, username, *args) user rescue Exception => err - AuditEvent.failure(audit.merge(:message => err.message)) + audit_failure(audit.merge(:message => err.message)) raise end end @@ -166,7 +166,7 @@ def authenticate_with_http_basic(username, password, request = nil, options = {} result = user && authenticate(username, password, request, options) rescue MiqException::MiqEVMLoginError end - AuditEvent.failure(:userid => username, :message => "Authentication failed for user #{username}") if result.nil? + audit_failure(:userid => username, :message => "Authentication failed for user #{username}") if result.nil? [!!result, username] end @@ -287,5 +287,14 @@ def autocreate_user(_username) def normalize_username(username) username.downcase end + + private def audit_success(options) + AuditEvent.success(options) + end + + private def audit_failure(options) + AuditEvent.failure(options) + MiqEvent.raise_evm_event_queue(MiqServer.my_server, "login_failed", options) + end end end diff --git a/db/fixtures/miq_event_definition_sets.csv b/db/fixtures/miq_event_definition_sets.csv index 95fe00fd8ed..bd4f5b877f5 100644 --- a/db/fixtures/miq_event_definition_sets.csv +++ b/db/fixtures/miq_event_definition_sets.csv @@ -1,4 +1,5 @@ name,description +authentication,Authentication Validation evm_operations,Appliance Operation ems_operations,Provider Operation host_operations,Host Operation @@ -10,6 +11,6 @@ vm_process,VM Lifecycle service_process,Service Lifecycle orchestration_process,Orchestration Lifecycle storage_operational,Datastore Operation -auth_validation,Authentication Validation +auth_validation,Authentication Validation (Provider) container_operations,Container Operation physical_server_operations,Physical Server Operation diff --git a/db/fixtures/miq_event_definitions.csv b/db/fixtures/miq_event_definitions.csv index c3f7423e1bf..0c9d4b45485 100644 --- a/db/fixtures/miq_event_definitions.csv +++ b/db/fixtures/miq_event_definitions.csv @@ -1,4 +1,9 @@ name,description,event_type,set_type +# +# Authentication +# +login_failed,Login failed,Default,authentication + # # EVM Server operations # diff --git a/spec/models/authenticator/database_spec.rb b/spec/models/authenticator/database_spec.rb index 3f3355d0273..3b1e5c94495 100644 --- a/spec/models/authenticator/database_spec.rb +++ b/spec/models/authenticator/database_spec.rb @@ -54,6 +54,10 @@ def authenticate context "with bad password" do let(:password) { 'incorrect' } + before do + EvmSpecHelper.create_guid_miq_server_zone + end + it "fails" do expect(-> { authenticate }).to raise_error(MiqException::MiqEVMLoginError, "Authentication failed") end @@ -67,11 +71,13 @@ def authenticate expect(AuditEvent).not_to receive(:success) authenticate rescue nil end + it "logs the failure" do allow($log).to receive(:warn).with(/Audit/) expect($log).to receive(:warn).with(/Authentication failed$/) authenticate rescue nil end + it "doesn't change lastlogon" do expect(-> { authenticate rescue nil }).not_to change { alice.reload.lastlogon } end @@ -80,6 +86,10 @@ def authenticate context "with unknown username" do let(:username) { 'bob' } + before do + EvmSpecHelper.create_guid_miq_server_zone + end + it "fails" do expect(-> { authenticate }).to raise_error(MiqException::MiqEVMLoginError) end @@ -93,6 +103,7 @@ def authenticate expect(AuditEvent).not_to receive(:success) authenticate rescue nil end + it "logs the failure" do allow($log).to receive(:warn).with(/Audit/) expect($log).to receive(:warn).with(/Authentication failed$/) diff --git a/spec/models/authenticator/httpd_spec.rb b/spec/models/authenticator/httpd_spec.rb index da98703107c..91f9277fd29 100644 --- a/spec/models/authenticator/httpd_spec.rb +++ b/spec/models/authenticator/httpd_spec.rb @@ -23,6 +23,8 @@ # Authenticator#uses_stored_password? whether it's allowed to do anything. allow(User).to receive(:authenticator).and_return(subject) + + EvmSpecHelper.create_guid_miq_server_zone end before do diff --git a/spec/models/authenticator/ldap_spec.rb b/spec/models/authenticator/ldap_spec.rb index 7985fe37f70..9534b462a7a 100644 --- a/spec/models/authenticator/ldap_spec.rb +++ b/spec/models/authenticator/ldap_spec.rb @@ -50,6 +50,8 @@ def normalize(dn) # Authenticator#uses_stored_password? whether it's allowed to do anything. allow(User).to receive(:authenticator).and_return(subject) + + EvmSpecHelper.create_guid_miq_server_zone end before do diff --git a/spec/models/authenticator_spec.rb b/spec/models/authenticator_spec.rb index b972d2521c9..30c35c8c28b 100644 --- a/spec/models/authenticator_spec.rb +++ b/spec/models/authenticator_spec.rb @@ -20,6 +20,10 @@ let(:task) { FactoryGirl.create(:miq_task) } let(:groups) { FactoryGirl.create_list(:miq_group, 2) } + before do + EvmSpecHelper.create_guid_miq_server_zone + end + it 'Updates the user groups when no matching groups' do expect(authenticator).to receive(:find_external_identity) .and_return([{:username => user.userid, :fullname => user.name, :domain => "example.com"}, []]) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index c6614e28784..f79565b8a44 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -178,6 +178,8 @@ stub_server_configuration(@auth_config) @miq_ldap = double('miq_ldap') allow(@miq_ldap).to receive_messages(:bind => false) + + EvmSpecHelper.create_guid_miq_server_zone end it "will fail task if user object not found in ldap" do @@ -348,6 +350,10 @@ context ".authenticate_with_http_basic" do let(:user) { FactoryGirl.create(:user, :password => "dummy") } + before do + EvmSpecHelper.create_guid_miq_server_zone + end + it "should login with good username/password" do expect(User.authenticate_with_http_basic(user.userid, user.password)).to eq([true, user.userid]) end