-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathauth.rb
82 lines (63 loc) · 2.43 KB
/
auth.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
class Auth
attr_reader :user, :password, :auth_user, :two_factor_method, :two_factor_payload, :only_verify_password
delegate :user, to: :auth_user
attr_accessor :increase_login_failed_attempts
BRUTE_FORCE_SLEEP = 1.second
# Initializes a Auth object for the given user.
#
# @param username [String] the user name for the user object which needs an authentication.
#
# @example
# auth = Auth.new('[email protected]', 'some+password')
def initialize(username, password, two_factor_method: nil, two_factor_payload: nil, only_verify_password: false)
@auth_user = username.present? ? Auth::User.new(username) : nil
@password = password
@two_factor_payload = two_factor_payload
@two_factor_method = two_factor_method
@increase_login_failed_attempts = false
@only_verify_password = only_verify_password
return if !@two_factor_payload.is_a?(Hash)
@two_factor_payload = @two_factor_payload.symbolize_keys
end
# Validates the given credentials for the user to the configured auth backends which should
# be performed.
#
# @return true if the user was authenticated, otherwise it raises an Exception.
def valid!
if verify_credentials!
auth_user.update_last_login if !only_verify_password
return true
end
wait_and_raise Auth::Error::AuthenticationFailed
end
private
def verify_credentials!
# Wrap in a lock to synchronize concurrent requests.
auth_user&.user&.with_lock do
next false if !auth_user.can_login?
if !backends.valid?
# Failed log-in attempts are only recorded if the password backend requests so.
auth_user.increase_login_failed if increase_login_failed_attempts && !only_verify_password
next false
end
verify_two_factor!
end
end
def verify_two_factor!
return true if only_verify_password
return true if !auth_user.requires_two_factor?
if two_factor_method.blank?
raise Auth::Error::TwoFactorRequired, auth_user
end
auth_user.two_factor_payload_valid?(two_factor_method, two_factor_payload) || wait_and_raise(Auth::Error::TwoFactorFailed)
end
def wait_and_raise(...)
# Sleep for a second to avoid brute force attacks.
sleep BRUTE_FORCE_SLEEP
raise(...)
end
def backends
Auth::Backend.new(self)
end
end