entitlements-app 1.2.7 → 1.2.8
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/lib/entitlements/service/ldap.rb +60 -18
- data/lib/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fdad062839c6178d6b23880ddc7c7392b5023d7f783e7b3686c3c3456392ff62
|
|
4
|
+
data.tar.gz: 924425bb54c9ffed6a6da2ff0a363e933b2351b45c2a4fa9c522ba5eb41fce39
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 74c0f06e2255e56c9a59f3a8bb6227a674b5a296ca310703c681434c78c210f658509689e41306910dfd41ae4a3247b5aeb7998654f05d17737d154fa98b453b
|
|
7
|
+
data.tar.gz: 10b6e365e40220d1c6687ded47c603b1a2b968ccdea7dc35fa61842743668d46466359f4dc954217bed2efcf13751f419cfb6133285e5eb3370a60cbc1a26f62
|
|
@@ -8,6 +8,8 @@ module Entitlements
|
|
|
8
8
|
include ::Contracts::Core
|
|
9
9
|
C = ::Contracts
|
|
10
10
|
|
|
11
|
+
MAX_CONNECTION_ATTEMPTS = 3
|
|
12
|
+
|
|
11
13
|
class ConnectionError < RuntimeError; end
|
|
12
14
|
class DuplicateEntryError < RuntimeError; end
|
|
13
15
|
class EntryError < RuntimeError; end
|
|
@@ -120,27 +122,33 @@ module Entitlements
|
|
|
120
122
|
# be consistent no matter what. LDAP is case insensitive after all!
|
|
121
123
|
downcased_attrs = attrs == "*" ? "*" : attrs.map { |a| a.downcase }
|
|
122
124
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
125
|
+
# Outside the retry block: `ldap` retries its own bind, and nesting would multiply attempts.
|
|
126
|
+
connection = ldap
|
|
127
|
+
result = with_ldap_connection_retries do
|
|
128
|
+
entries = {}
|
|
129
|
+
search_succeeded = connection.search(
|
|
130
|
+
base: base,
|
|
131
|
+
filter: filter,
|
|
132
|
+
attributes: downcased_attrs,
|
|
133
|
+
scope: scope,
|
|
134
|
+
return_result: false
|
|
135
|
+
) do |entry|
|
|
136
|
+
result_key = index == :dn ? entry.dn : entry[index]
|
|
137
|
+
unless result_key
|
|
138
|
+
raise EntryError, "#{entry.dn} has no value for #{index.inspect}"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
if entries.key?(result_key)
|
|
142
|
+
other_entry_dn = entries[result_key].dn
|
|
143
|
+
raise DuplicateEntryError, "#{entry.dn} and #{other_entry_dn} have the same value of #{index} = #{result_key.inspect}"
|
|
144
|
+
end
|
|
135
145
|
|
|
136
|
-
|
|
137
|
-
other_entry_dn = result[result_key].dn
|
|
138
|
-
raise DuplicateEntryError, "#{entry.dn} and #{other_entry_dn} have the same value of #{index} = #{result_key.inspect}"
|
|
146
|
+
entries[result_key] = entry
|
|
139
147
|
end
|
|
148
|
+
remember_existing_dn(base) if search_succeeded
|
|
140
149
|
|
|
141
|
-
|
|
150
|
+
entries
|
|
142
151
|
end
|
|
143
|
-
remember_existing_dn(base) if search_succeeded
|
|
144
152
|
|
|
145
153
|
Entitlements.logger.debug "Completed search: #{result.keys.size} result(s)"
|
|
146
154
|
|
|
@@ -248,7 +256,7 @@ module Entitlements
|
|
|
248
256
|
# Returns a Net::LDAP object configured to connect and bind for each operation.
|
|
249
257
|
Contract C::None => Net::LDAP
|
|
250
258
|
def ldap
|
|
251
|
-
@ldap ||=
|
|
259
|
+
@ldap ||= with_ldap_connection_retries do
|
|
252
260
|
uri = URI(addr)
|
|
253
261
|
|
|
254
262
|
# Construct the object
|
|
@@ -289,6 +297,40 @@ module Entitlements
|
|
|
289
297
|
end
|
|
290
298
|
end
|
|
291
299
|
|
|
300
|
+
def with_ldap_connection_retries
|
|
301
|
+
attempts = 0
|
|
302
|
+
begin
|
|
303
|
+
attempts += 1
|
|
304
|
+
yield
|
|
305
|
+
rescue SystemCallError, Net::LDAP::Error => error
|
|
306
|
+
raise unless retryable_connection_error?(error)
|
|
307
|
+
|
|
308
|
+
uri = URI(addr)
|
|
309
|
+
if attempts >= MAX_CONNECTION_ATTEMPTS
|
|
310
|
+
Entitlements.logger.error "LDAP connection to #{uri.host}:#{uri.port} failed after #{attempts} attempts: #{error.class}: #{error.message}"
|
|
311
|
+
raise
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
Entitlements.logger.warn "LDAP connection to #{uri.host}:#{uri.port} failed (attempt #{attempts} of #{MAX_CONNECTION_ATTEMPTS}): #{error.class}: #{error.message}. Retrying in #{attempts}s."
|
|
315
|
+
sleep attempts
|
|
316
|
+
retry
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# net-ldap turns single-host connection-setup failures (DNS, connect/TLS timeouts,
|
|
321
|
+
# unreachable host) into a bare Net::LDAP::Error with no cause, and the messages vary
|
|
322
|
+
# by libc. Operation errors are subclasses. Errno cases can hit mid-request; that's
|
|
323
|
+
# fine only because this never wraps writes.
|
|
324
|
+
def retryable_connection_error?(error)
|
|
325
|
+
case error
|
|
326
|
+
when Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EPIPE, Errno::ETIMEDOUT
|
|
327
|
+
true
|
|
328
|
+
when Net::LDAP::Error
|
|
329
|
+
error.instance_of?(Net::LDAP::Error) &&
|
|
330
|
+
!error.message.match?(/certificate verify failed|does not match the server certificate/)
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
292
334
|
# Create an object that does not exist. Private method intended to be called from
|
|
293
335
|
# "upsert" after determining that the object doesn't exist.
|
|
294
336
|
#
|
data/lib/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: entitlements-app
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- GitHub, Inc. Security Ops
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-11-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: concurrent-ruby
|