simple_ldap_authenticator 1.3.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README +36 -0
- data/lib/simple_ldap_authenticator.rb +96 -23
- metadata +1 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6077b51596ff8f01277e06222f7373de619040403ef347415de226c8802a9fa7
|
|
4
|
+
data.tar.gz: f6b9491e5055a8da2a57dbdd192a9547acc194edebe95c9d0f33158e945d5133
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f426214b597d888072cd0f0cb7ec66089ae55c7330b7fafbafb098d5314760fd32854f01dd285b25f546b88f750ef86c858fdac71ecd3d644a698637651703b4
|
|
7
|
+
data.tar.gz: fb996d2dbacf455d99b8f7aee099de3e2eaccd01a77fb29f6840ffe8721c0d39fb8c71c8d268d87ac63167062fdd2e1bf9b2958999a018402c6e7a263f4f4366
|
data/README
CHANGED
|
@@ -17,4 +17,40 @@ Example Usage:
|
|
|
17
17
|
SimpleLdapAuthenticator.valid?(username, password)
|
|
18
18
|
# => true or false (or raise if there is an issue connecting to the server)
|
|
19
19
|
|
|
20
|
+
By default, a new connection object is created for each call to valid?,
|
|
21
|
+
so valid? is safe to call concurrently from multiple threads. If you only
|
|
22
|
+
use SimpleLdapAuthenticator from a single thread, you can reuse a single
|
|
23
|
+
connection object for all calls to valid?:
|
|
24
|
+
|
|
25
|
+
SimpleLdapAuthenticator.single_threaded = true
|
|
26
|
+
|
|
27
|
+
Do not enable single threaded mode if valid? may be called concurrently,
|
|
28
|
+
as that can result in invalid passwords being considered valid.
|
|
29
|
+
|
|
30
|
+
When use_ssl is true, the server's certificate and hostname are verified
|
|
31
|
+
using the default trusted certificates. You can set use_ssl to a hash of
|
|
32
|
+
TLS options to customize this, such as specifying the CA certificate(s) to
|
|
33
|
+
trust (useful for internal CAs and self-signed certificates, such as those
|
|
34
|
+
commonly used by Active Directory):
|
|
35
|
+
|
|
36
|
+
SimpleLdapAuthenticator.use_ssl = {ca_file: '/path/to/ca.pem'}
|
|
37
|
+
|
|
38
|
+
The :ca_file, :ca_path, and :verify_mode options are supported for both
|
|
39
|
+
ldap and net/ldap backends. For net/ldap, any option supported by
|
|
40
|
+
OpenSSL::SSL::SSLContext#set_params can be used. For ldap, only
|
|
41
|
+
those three options are supported. :verify_mode on ldap will default to
|
|
42
|
+
the LDAP behavior (which verifies by default unless the TLS_REQCERT
|
|
43
|
+
setting is set in the system's ldap.conf).
|
|
44
|
+
|
|
45
|
+
Before version 2, use_ssl = true did not verify the server's
|
|
46
|
+
certificate when using net/ldap. To get the previous behavior of not
|
|
47
|
+
verifying the server's certificate (not recommended, as it allows
|
|
48
|
+
man-in-the-middle attacks to capture passwords):
|
|
49
|
+
|
|
50
|
+
SimpleLdapAuthenticator.use_ssl = {verify_mode: OpenSSL::SSL::VERIFY_NONE}
|
|
51
|
+
|
|
52
|
+
Note that due to a bug in net/ldap, you should use domain names and
|
|
53
|
+
not IP addresses in the servers array, at least if you are using
|
|
54
|
+
LibreSSL (which follows the SSL spec more closely).
|
|
55
|
+
|
|
20
56
|
github: http://github.com/jeremyevans/simple_ldap_authenticator
|
|
@@ -12,9 +12,43 @@
|
|
|
12
12
|
# * login_format = '%s@domain.com' # Active Directory, OR
|
|
13
13
|
# * login_format = 'cn=%s,cn=users,o=organization,c=us' # Other LDAP servers
|
|
14
14
|
# * servers = ['dc1.domain.com', 'dc2.domain.com'] # names/addresses of LDAP servers to use
|
|
15
|
-
# * use_ssl = true # for logging in via LDAPS
|
|
15
|
+
# * use_ssl = true # for logging in via LDAPS, verifying the server certificate
|
|
16
|
+
# * use_ssl = {ca_file: '/path/to/ca.pem'} # for logging in via LDAPS, with TLS options
|
|
16
17
|
# * port = 3289 # instead of 389 for LDAP or 636 for LDAPS
|
|
17
18
|
# * logger = Logger.new($stdout) # for logging authentication successes/failures
|
|
19
|
+
# * single_threaded = true # reuse a single connection, only safe if not using threads
|
|
20
|
+
#
|
|
21
|
+
# By default, a new connection object is created for each call to valid?,
|
|
22
|
+
# so that valid? is safe to call concurrently from multiple threads. If
|
|
23
|
+
# you are only using SimpleLdapAuthenticator in a single thread, you can set
|
|
24
|
+
# single_threaded = true, which reuses a single connection object for all
|
|
25
|
+
# calls to valid?. Do not set single_threaded = true if valid? may be
|
|
26
|
+
# called concurrently, as that can result in invalid passwords being
|
|
27
|
+
# considered valid.
|
|
28
|
+
#
|
|
29
|
+
# When use_ssl is true, the server's certificate and hostname are verified
|
|
30
|
+
# using the default trusted certificates. use_ssl can also be set to a hash
|
|
31
|
+
# of TLS options, which are merged into the default TLS options. The
|
|
32
|
+
# following TLS options are supported for both libraries:
|
|
33
|
+
#
|
|
34
|
+
# :ca_file :: File containing trusted CA certificates.
|
|
35
|
+
# :ca_path :: Directory containing trusted CA certificates.
|
|
36
|
+
# :verify_mode :: Whether to verify the server certificate, should be
|
|
37
|
+
# OpenSSL::SSL::VERIFY_PEER or OpenSSL::SSL::VERIFY_NONE
|
|
38
|
+
# if given.
|
|
39
|
+
#
|
|
40
|
+
# For net/ldap, any option supported by OpenSSL::SSL::SSLContext#set_params
|
|
41
|
+
# can be used. For ldap, only the three keys given above work.
|
|
42
|
+
#
|
|
43
|
+
# Before version 2, use_ssl = true did not verify the server certificate
|
|
44
|
+
# when using net/ldap. To restore that behavior (not recommended, as it allows
|
|
45
|
+
# man-in-the-middle attacks), you can disable verification:
|
|
46
|
+
#
|
|
47
|
+
# SimpleLdapAuthenticator.use_ssl = {verify_mode: OpenSSL::SSL::VERIFY_NONE}
|
|
48
|
+
#
|
|
49
|
+
# With ldap, whether to verify the certificate by default depends on the
|
|
50
|
+
# system's libldap configuration (TLS_REQCERT in ldap.conf). The above setting
|
|
51
|
+
# also disables verification when using ldap.
|
|
18
52
|
#
|
|
19
53
|
# The class is used as a singleton, you are not supposed to create an
|
|
20
54
|
# instance of it. For example:
|
|
@@ -31,9 +65,11 @@ class SimpleLdapAuthenticator
|
|
|
31
65
|
@servers = ['127.0.0.1']
|
|
32
66
|
@use_ssl = false
|
|
33
67
|
@login_format = '%s'
|
|
68
|
+
@single_threaded = false
|
|
69
|
+
@switch_server_mutex = Mutex.new
|
|
34
70
|
|
|
35
71
|
class << self
|
|
36
|
-
attr_accessor :servers, :use_ssl, :login_format, :logger, :ldap_library
|
|
72
|
+
attr_accessor :servers, :use_ssl, :login_format, :logger, :ldap_library, :single_threaded
|
|
37
73
|
attr_writer :port, :connection
|
|
38
74
|
|
|
39
75
|
# Load the required LDAP library, either 'ldap' or 'net/ldap'
|
|
@@ -64,16 +100,46 @@ class SimpleLdapAuthenticator
|
|
|
64
100
|
servers[0]
|
|
65
101
|
end
|
|
66
102
|
|
|
67
|
-
# The connection to the LDAP server
|
|
68
|
-
# connection is
|
|
69
|
-
# invalid password.
|
|
103
|
+
# The shared connection to the LDAP server, only used in single threaded
|
|
104
|
+
# mode. A single connection is made and the connection is only changed if
|
|
105
|
+
# a server returns an error other than invalid password.
|
|
70
106
|
def connection
|
|
71
|
-
|
|
107
|
+
@connection ||= new_connection(server)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Create a new connection object for the given LDAP server.
|
|
111
|
+
def new_connection(server)
|
|
72
112
|
load_ldap_library
|
|
73
|
-
|
|
74
|
-
|
|
113
|
+
if ldap_library == 'net/ldap'
|
|
114
|
+
if use_ssl
|
|
115
|
+
tls_opts = use_ssl.is_a?(Hash) ? use_ssl.dup : {}
|
|
116
|
+
unless tls_opts[:verify_mode]
|
|
117
|
+
require "openssl"
|
|
118
|
+
tls_opts[:verify_mode] = OpenSSL::SSL::VERIFY_PEER
|
|
119
|
+
end
|
|
120
|
+
encryption_opts = {:method=>:simple_tls, :tls_options=>tls_opts}
|
|
121
|
+
end
|
|
122
|
+
Net::LDAP.new(:host=>server, :port=>port, :encryption=>encryption_opts)
|
|
123
|
+
elsif use_ssl
|
|
124
|
+
conn = LDAP::SSLConn.new(server, port)
|
|
125
|
+
if use_ssl.is_a?(Hash) && !use_ssl.empty?
|
|
126
|
+
use_ssl.each do |k, v|
|
|
127
|
+
case k
|
|
128
|
+
when :verify_mode
|
|
129
|
+
conn.set_option(LDAP::LDAP_OPT_X_TLS_REQUIRE_CERT, v)
|
|
130
|
+
when :ca_file
|
|
131
|
+
conn.set_option(LDAP::LDAP_OPT_X_TLS_CACERTFILE, v)
|
|
132
|
+
when :ca_path
|
|
133
|
+
conn.set_option(LDAP::LDAP_OPT_X_TLS_CACERTDIR, v)
|
|
134
|
+
else
|
|
135
|
+
raise ArgumentError, "unsupported TLS option for ldap library: #{k.inspect}"
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
conn.set_option(LDAP::LDAP_OPT_X_TLS_NEWCTX, 0)
|
|
139
|
+
end
|
|
140
|
+
conn
|
|
75
141
|
else
|
|
76
|
-
|
|
142
|
+
LDAP::Conn.new(server, port)
|
|
77
143
|
end
|
|
78
144
|
end
|
|
79
145
|
|
|
@@ -83,23 +149,30 @@ class SimpleLdapAuthenticator
|
|
|
83
149
|
end
|
|
84
150
|
|
|
85
151
|
# Disconnect from current LDAP server and use a different LDAP server on the
|
|
86
|
-
# next authentication attempt
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
152
|
+
# next authentication attempt. If failed_server is given, only switch
|
|
153
|
+
# servers if failed_server is still the current server, so that multiple
|
|
154
|
+
# threads failing on the same server only switch servers once.
|
|
155
|
+
def switch_server(failed_server = nil)
|
|
156
|
+
@switch_server_mutex.synchronize do
|
|
157
|
+
if failed_server.nil? || server.equal?(failed_server)
|
|
158
|
+
self.connection = nil
|
|
159
|
+
servers.rotate!
|
|
160
|
+
end
|
|
161
|
+
end
|
|
90
162
|
end
|
|
91
163
|
|
|
92
164
|
# Check the validity of a login/password combination
|
|
93
165
|
def valid?(login, password)
|
|
94
166
|
login = login.to_s
|
|
95
167
|
password = password.to_s
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
connection.
|
|
168
|
+
return false if password == '' || password.include?("\0") || login.include?("\0")
|
|
169
|
+
|
|
170
|
+
server = self.server
|
|
171
|
+
if ldap_library == 'net/ldap'
|
|
172
|
+
connection = single_threaded ? self.connection : new_connection(server)
|
|
173
|
+
auth = {:method=>:simple, :username=>login_format % login, :password=>password}
|
|
101
174
|
begin
|
|
102
|
-
if connection.bind
|
|
175
|
+
if connection.bind(auth)
|
|
103
176
|
logger.info("Authenticated #{login} by #{server}") if logger
|
|
104
177
|
true
|
|
105
178
|
else
|
|
@@ -107,15 +180,16 @@ class SimpleLdapAuthenticator
|
|
|
107
180
|
if logger
|
|
108
181
|
logger.info("Error attempting to authenticate #{login} by #{server}: #{result.code} #{result.message} #{result.error_message.to_s.strip}")
|
|
109
182
|
end
|
|
110
|
-
switch_server unless result.code == 49
|
|
183
|
+
switch_server(server) unless result.code == 49
|
|
111
184
|
false
|
|
112
185
|
end
|
|
113
186
|
rescue Net::LDAP::Error, SocketError, SystemCallError => error
|
|
114
187
|
logger.info("Error attempting to authenticate #{login} by #{server}: #{error.message}") if logger
|
|
115
|
-
switch_server
|
|
188
|
+
switch_server(server)
|
|
116
189
|
false
|
|
117
190
|
end
|
|
118
191
|
else
|
|
192
|
+
connection = (single_threaded && !(use_ssl.is_a?(Hash) && !use_ssl.empty?)) ? self.connection : new_connection(server)
|
|
119
193
|
connection.unbind if connection.bound?
|
|
120
194
|
begin
|
|
121
195
|
connection.bind(login_format % login, password)
|
|
@@ -123,9 +197,8 @@ class SimpleLdapAuthenticator
|
|
|
123
197
|
logger.info("Authenticated #{login} by #{server}") if logger
|
|
124
198
|
true
|
|
125
199
|
rescue LDAP::ResultError => error
|
|
126
|
-
connection.unbind if connection.bound?
|
|
127
200
|
logger.info("Error attempting to authenticate #{login} by #{server}: #{error.message}") if logger
|
|
128
|
-
switch_server unless error.message == 'Invalid credentials'
|
|
201
|
+
switch_server(server) unless error.message == 'Invalid credentials'
|
|
129
202
|
false
|
|
130
203
|
end
|
|
131
204
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: simple_ldap_authenticator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jeremy Evans
|
|
@@ -23,20 +23,6 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0'
|
|
26
|
-
- !ruby/object:Gem::Dependency
|
|
27
|
-
name: eventmachine
|
|
28
|
-
requirement: !ruby/object:Gem::Requirement
|
|
29
|
-
requirements:
|
|
30
|
-
- - ">="
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '0'
|
|
33
|
-
type: :development
|
|
34
|
-
prerelease: false
|
|
35
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - ">="
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '0'
|
|
40
26
|
- !ruby/object:Gem::Dependency
|
|
41
27
|
name: net-ldap
|
|
42
28
|
requirement: !ruby/object:Gem::Requirement
|