entitlements-app 1.2.0 → 1.2.2
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/cli.rb +2 -0
- data/lib/entitlements/data/groups/cached.rb +2 -0
- data/lib/entitlements.rb +90 -16
- data/lib/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 18a0176e1783a420da17eee12174e9cc6f17224f980e991149743bd2366a1dbc
|
|
4
|
+
data.tar.gz: 2acf3be9e885ade8181e3fb47adfc87fbfca609bb9978ca84c9d3b6816ae0ad5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 73162232f10dca6c007c7ca5025a385d10ff06b809b1a0daace0fcc3dbcba877337b5a191963d173eeb64ff6635a4dd05f05857fb9ed37201f6cb5dfdd541ef9
|
|
7
|
+
data.tar.gz: ef9e75e2916d78607a4f963f9661c1b36c48055b97d34f0f6286c2f6ba22324f8241d7775a15f37df4bc2a9ed1d823cba5b6553fd9db6e5c9e5050e1cc1fc0a0
|
data/lib/entitlements/cli.rb
CHANGED
|
@@ -51,6 +51,8 @@ module Entitlements
|
|
|
51
51
|
Entitlements.cache[:predictive_state][:by_ou][ou] ||= {}
|
|
52
52
|
Entitlements.cache[:predictive_state][:by_ou][ou][identifier] = member_set
|
|
53
53
|
Entitlements.cache[:predictive_state][:by_dn][dn] = { members: member_set, metadata: metadata }
|
|
54
|
+
rescue => e
|
|
55
|
+
raise "Failed to load predictive state cache file #{filename}: #{e.message}"
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
Entitlements.logger.debug "Loaded #{Entitlements.cache[:predictive_state][:by_ou].keys.size} OU(s) from cache"
|
data/lib/entitlements.rb
CHANGED
|
@@ -16,9 +16,11 @@ end
|
|
|
16
16
|
# :nocov:
|
|
17
17
|
|
|
18
18
|
require "contracts"
|
|
19
|
+
require "datadog/statsd"
|
|
19
20
|
require "erb"
|
|
20
21
|
require "logger"
|
|
21
22
|
require "ostruct"
|
|
23
|
+
require "resolv"
|
|
22
24
|
require "stringio"
|
|
23
25
|
require "uri"
|
|
24
26
|
require "yaml"
|
|
@@ -89,6 +91,7 @@ module Entitlements
|
|
|
89
91
|
@config_file = nil
|
|
90
92
|
@config_path_override = nil
|
|
91
93
|
@person_extra_methods = {}
|
|
94
|
+
@statsd = nil
|
|
92
95
|
|
|
93
96
|
reset_extras!
|
|
94
97
|
Entitlements::Data::Groups::Calculated.reset!
|
|
@@ -354,6 +357,53 @@ module Entitlements
|
|
|
354
357
|
end
|
|
355
358
|
# :nocov:
|
|
356
359
|
|
|
360
|
+
def self.statsd
|
|
361
|
+
@statsd ||= build_statsd
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def self.set_statsd(statsd)
|
|
365
|
+
@statsd = statsd
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def self.close_statsd
|
|
369
|
+
@statsd&.close
|
|
370
|
+
@statsd = nil
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def self.build_statsd
|
|
374
|
+
host = Resolv.getaddress(ENV.fetch("DOGSTATSD_HOST", "localhost"))
|
|
375
|
+
port = Integer(ENV.fetch("DOGSTATSD_PORT", 28_125))
|
|
376
|
+
tags = [
|
|
377
|
+
"application:entitlements",
|
|
378
|
+
"kube_pod_name:#{ENV.fetch('KUBE_POD_NAME', 'not-on-kubernetes')}",
|
|
379
|
+
"app_env:#{ENV.fetch('APP_ENV', 'development')}",
|
|
380
|
+
"deployment_id:#{metric_deployment_id}"
|
|
381
|
+
]
|
|
382
|
+
Datadog::Statsd.new(host, port, tags: tags)
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def self.metric_deployment_id
|
|
386
|
+
ENV["HEAVEN_DEPLOYMENT_ID"] || ENV["GITHUB_RUN_ID"] || "not-in-deployment"
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def self.timed_operation(phase:, provider: nil, target: nil, span: "leaf", concurrent: false, count: nil)
|
|
390
|
+
tags = [
|
|
391
|
+
"phase:#{phase}",
|
|
392
|
+
"status:error",
|
|
393
|
+
"span:#{span}",
|
|
394
|
+
"concurrent:#{concurrent}"
|
|
395
|
+
]
|
|
396
|
+
tags << "provider:#{provider}" if provider
|
|
397
|
+
tags << "target:#{target}" if target
|
|
398
|
+
tags << "count:#{count}" if count
|
|
399
|
+
|
|
400
|
+
statsd.time("entitlements.operation.duration", tags: tags) do
|
|
401
|
+
result = yield
|
|
402
|
+
tags[1] = "status:success"
|
|
403
|
+
result
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
|
|
357
407
|
# Calculate - This runs the entitlements logic to calculate the differences, ultimately
|
|
358
408
|
# populating a cache and returning a list of actions. The cache and actions can then be
|
|
359
409
|
# consumed by `execute` to implement the changes.
|
|
@@ -363,14 +413,22 @@ module Entitlements
|
|
|
363
413
|
# Returns the array of actions.
|
|
364
414
|
Contract C::None => C::ArrayOf[Entitlements::Models::Action]
|
|
365
415
|
def self.calculate
|
|
416
|
+
timed_operation(phase: "calculate_total", span: "parent") { calculate_actions }
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def self.calculate_actions
|
|
366
420
|
# Load extras that are configured.
|
|
367
|
-
|
|
421
|
+
if Entitlements.config.key?("extras")
|
|
422
|
+
timed_operation(phase: "load_extras") { Entitlements.load_extras }
|
|
423
|
+
end
|
|
368
424
|
|
|
369
425
|
# Pre-fetch people from configured people data sources.
|
|
370
|
-
Entitlements.prefetch_people
|
|
426
|
+
timed_operation(phase: "prefetch_people") { Entitlements.prefetch_people }
|
|
371
427
|
|
|
372
428
|
# Register filters that are configured.
|
|
373
|
-
|
|
429
|
+
if Entitlements.config.key?("filters")
|
|
430
|
+
timed_operation(phase: "register_filters") { Entitlements.register_filters }
|
|
431
|
+
end
|
|
374
432
|
|
|
375
433
|
# Keep track of the total change count.
|
|
376
434
|
cache[:change_count] = 0
|
|
@@ -385,8 +443,9 @@ module Entitlements
|
|
|
385
443
|
Concurrent::Future.execute({ executor: thread_pool }) do
|
|
386
444
|
group_start = Time.now
|
|
387
445
|
logger.debug("Begin prefetch and validate for #{group_name}")
|
|
388
|
-
|
|
389
|
-
obj.
|
|
446
|
+
provider = Entitlements.config["groups"].fetch(group_name).fetch("type")
|
|
447
|
+
timed_operation(phase: "prefetch", provider: provider, target: group_name, concurrent: true) { obj.prefetch }
|
|
448
|
+
timed_operation(phase: "validate", provider: provider, target: group_name, concurrent: true) { obj.validate }
|
|
390
449
|
logger.debug("Finished prefetch and validate for #{group_name} in #{Time.now - group_start}")
|
|
391
450
|
end
|
|
392
451
|
end
|
|
@@ -398,7 +457,8 @@ module Entitlements
|
|
|
398
457
|
calc_start = Time.now
|
|
399
458
|
actions = []
|
|
400
459
|
Entitlements.child_classes.map do |group_name, obj|
|
|
401
|
-
|
|
460
|
+
provider = Entitlements.config["groups"].fetch(group_name).fetch("type")
|
|
461
|
+
timed_operation(phase: "calculate", provider: provider, target: group_name) { obj.calculate }
|
|
402
462
|
if obj.change_count > 0
|
|
403
463
|
logger.debug "Group #{group_name.inspect} contributes #{obj.change_count} change(s)."
|
|
404
464
|
cache[:change_count] += obj.change_count
|
|
@@ -422,8 +482,14 @@ module Entitlements
|
|
|
422
482
|
actions: C::ArrayOf[Entitlements::Models::Action]
|
|
423
483
|
] => nil
|
|
424
484
|
def self.execute(actions:)
|
|
485
|
+
timed_operation(phase: "execute_total", span: "parent") { execute_actions(actions: actions) }
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
def self.execute_actions(actions:)
|
|
425
489
|
# Set up auditors.
|
|
426
|
-
Entitlements.auditors.each
|
|
490
|
+
Entitlements.auditors.each do |auditor|
|
|
491
|
+
timed_operation(phase: "audit_setup", provider: auditor.provider_id) { auditor.setup }
|
|
492
|
+
end
|
|
427
493
|
|
|
428
494
|
# Track any raised exception to pass to the auditors.
|
|
429
495
|
provider_exception = nil
|
|
@@ -433,14 +499,18 @@ module Entitlements
|
|
|
433
499
|
# Sort the child classes by priority
|
|
434
500
|
begin
|
|
435
501
|
# Pre-apply changes for each class.
|
|
436
|
-
Entitlements.child_classes.each do |
|
|
437
|
-
|
|
502
|
+
Entitlements.child_classes.each do |group_name, obj|
|
|
503
|
+
provider = Entitlements.config["groups"].fetch(group_name).fetch("type")
|
|
504
|
+
timed_operation(phase: "preapply", provider: provider, target: group_name) { obj.preapply }
|
|
438
505
|
end
|
|
439
506
|
|
|
440
507
|
# Apply changes from all actions.
|
|
441
508
|
actions.each do |action|
|
|
442
509
|
obj = Entitlements.child_classes.fetch(action.ou)
|
|
443
|
-
|
|
510
|
+
provider = Entitlements.config["groups"].fetch(action.ou).fetch("type")
|
|
511
|
+
timed_operation(phase: "apply", provider: provider, target: action.ou, count: 1) do
|
|
512
|
+
obj.apply(action)
|
|
513
|
+
end
|
|
444
514
|
successful_actions.add(action.dn)
|
|
445
515
|
end
|
|
446
516
|
rescue => e
|
|
@@ -457,11 +527,13 @@ module Entitlements
|
|
|
457
527
|
logger.debug "Recording data to #{Entitlements.auditors.size} audit provider(s)"
|
|
458
528
|
Entitlements.auditors.each do |audit|
|
|
459
529
|
begin
|
|
460
|
-
audit.
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
530
|
+
timed_operation(phase: "audit_commit", provider: audit.provider_id) do
|
|
531
|
+
audit.commit(
|
|
532
|
+
actions: actions,
|
|
533
|
+
successful_actions: successful_actions,
|
|
534
|
+
provider_exception: provider_exception
|
|
535
|
+
)
|
|
536
|
+
end
|
|
465
537
|
logger.debug "Audit #{audit.description} completed successfully"
|
|
466
538
|
rescue => e
|
|
467
539
|
logger.error "Audit #{audit.description} failed: #{e.class} #{e.message}"
|
|
@@ -564,7 +636,9 @@ module Entitlements
|
|
|
564
636
|
|
|
565
637
|
objects = people_data_sources.map do |ds_name, ds_config|
|
|
566
638
|
people_obj = Entitlements::Data::People.new_from_config(ds_config)
|
|
567
|
-
|
|
639
|
+
timed_operation(phase: "prefetch_people_source", provider: ds_config.fetch("type"), target: ds_name) do
|
|
640
|
+
people_obj.read
|
|
641
|
+
end
|
|
568
642
|
[ds_name, people_obj]
|
|
569
643
|
end.to_h
|
|
570
644
|
|
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.2
|
|
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:
|
|
11
|
+
date: 2026-09-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: concurrent-ruby
|
|
@@ -30,6 +30,20 @@ dependencies:
|
|
|
30
30
|
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: 1.3.1
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: dogstatsd-ruby
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '5.7'
|
|
40
|
+
type: :runtime
|
|
41
|
+
prerelease: false
|
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '5.7'
|
|
33
47
|
- !ruby/object:Gem::Dependency
|
|
34
48
|
name: faraday
|
|
35
49
|
requirement: !ruby/object:Gem::Requirement
|