Skip to content

Commit

Permalink
Raise an event on failed login attempt
Browse files Browse the repository at this point in the history
  • Loading branch information
Fryguy committed Jun 4, 2018
1 parent c174d0b commit b690828
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 9 deletions.
25 changes: 17 additions & 8 deletions app/models/authenticator/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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?
Expand All @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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
3 changes: 2 additions & 1 deletion db/fixtures/miq_event_definition_sets.csv
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name,description
authentication,Authentication Validation
evm_operations,Appliance Operation
ems_operations,Provider Operation
host_operations,Host Operation
Expand All @@ -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
5 changes: 5 additions & 0 deletions db/fixtures/miq_event_definitions.csv
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
name,description,event_type,set_type
#
# Authentication
#
login_failed,Login failed,Default,authentication

#
# EVM Server operations
#
Expand Down
11 changes: 11 additions & 0 deletions spec/models/authenticator/database_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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$/)
Expand Down
2 changes: 2 additions & 0 deletions spec/models/authenticator/httpd_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions spec/models/authenticator/ldap_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions spec/models/authenticator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"}, []])
Expand Down
6 changes: 6 additions & 0 deletions spec/models/user_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit b690828

Please sign in to comment.