entitlements 0.1.7

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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/VERSION +1 -0
  3. data/bin/deploy-entitlements +18 -0
  4. data/lib/entitlements/auditor/base.rb +163 -0
  5. data/lib/entitlements/backend/base_controller.rb +171 -0
  6. data/lib/entitlements/backend/base_provider.rb +55 -0
  7. data/lib/entitlements/backend/dummy/controller.rb +89 -0
  8. data/lib/entitlements/backend/dummy.rb +3 -0
  9. data/lib/entitlements/backend/ldap/controller.rb +188 -0
  10. data/lib/entitlements/backend/ldap/provider.rb +128 -0
  11. data/lib/entitlements/backend/ldap.rb +4 -0
  12. data/lib/entitlements/backend/member_of/controller.rb +203 -0
  13. data/lib/entitlements/backend/member_of.rb +3 -0
  14. data/lib/entitlements/cli.rb +121 -0
  15. data/lib/entitlements/data/groups/cached.rb +120 -0
  16. data/lib/entitlements/data/groups/calculated/base.rb +478 -0
  17. data/lib/entitlements/data/groups/calculated/filters/base.rb +93 -0
  18. data/lib/entitlements/data/groups/calculated/filters/member_of_group.rb +32 -0
  19. data/lib/entitlements/data/groups/calculated/modifiers/base.rb +38 -0
  20. data/lib/entitlements/data/groups/calculated/modifiers/expiration.rb +56 -0
  21. data/lib/entitlements/data/groups/calculated/ruby.rb +137 -0
  22. data/lib/entitlements/data/groups/calculated/rules/base.rb +35 -0
  23. data/lib/entitlements/data/groups/calculated/rules/group.rb +129 -0
  24. data/lib/entitlements/data/groups/calculated/rules/username.rb +41 -0
  25. data/lib/entitlements/data/groups/calculated/text.rb +337 -0
  26. data/lib/entitlements/data/groups/calculated/yaml.rb +171 -0
  27. data/lib/entitlements/data/groups/calculated.rb +290 -0
  28. data/lib/entitlements/data/groups.rb +13 -0
  29. data/lib/entitlements/data/people/combined.rb +197 -0
  30. data/lib/entitlements/data/people/dummy.rb +71 -0
  31. data/lib/entitlements/data/people/ldap.rb +142 -0
  32. data/lib/entitlements/data/people/yaml.rb +102 -0
  33. data/lib/entitlements/data/people.rb +58 -0
  34. data/lib/entitlements/extras/base.rb +40 -0
  35. data/lib/entitlements/extras/ldap_group/base.rb +20 -0
  36. data/lib/entitlements/extras/ldap_group/filters/member_of_ldap_group.rb +50 -0
  37. data/lib/entitlements/extras/ldap_group/rules/ldap_group.rb +69 -0
  38. data/lib/entitlements/extras/orgchart/base.rb +32 -0
  39. data/lib/entitlements/extras/orgchart/logic.rb +171 -0
  40. data/lib/entitlements/extras/orgchart/person_methods.rb +55 -0
  41. data/lib/entitlements/extras/orgchart/rules/direct_report.rb +62 -0
  42. data/lib/entitlements/extras/orgchart/rules/management.rb +59 -0
  43. data/lib/entitlements/extras.rb +82 -0
  44. data/lib/entitlements/models/action.rb +82 -0
  45. data/lib/entitlements/models/group.rb +280 -0
  46. data/lib/entitlements/models/person.rb +149 -0
  47. data/lib/entitlements/plugins/dummy.rb +22 -0
  48. data/lib/entitlements/plugins/group_of_names.rb +28 -0
  49. data/lib/entitlements/plugins/posix_group.rb +46 -0
  50. data/lib/entitlements/plugins.rb +13 -0
  51. data/lib/entitlements/rule/base.rb +74 -0
  52. data/lib/entitlements/service/ldap.rb +405 -0
  53. data/lib/entitlements/util/mirror.rb +42 -0
  54. data/lib/entitlements/util/override.rb +64 -0
  55. data/lib/entitlements/util/util.rb +219 -0
  56. data/lib/entitlements.rb +606 -0
  57. metadata +343 -0
@@ -0,0 +1,606 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Hey there! With our use of the "contracts" module, load order is important.
4
+
5
+ # Load third party dependencies first.
6
+ require "concurrent"
7
+ require "contracts"
8
+ require "erb"
9
+ require "logger"
10
+ require "ostruct"
11
+ require "set"
12
+ require "stringio"
13
+ require "uri"
14
+ require "yaml"
15
+
16
+ # Next, pre-declare any classes that are referenced from contracts.
17
+ module Entitlements
18
+ class Auditor
19
+ class Base; end
20
+ end
21
+ class Data
22
+ class Groups
23
+ class Cached; end
24
+ class Calculated
25
+ class Base; end
26
+ class Ruby < Base; end
27
+ class Text < Base; end
28
+ class YAML < Base; end
29
+ end
30
+ end
31
+ class People
32
+ class Combined; end
33
+ class Dummy; end
34
+ class LDAP; end
35
+ class YAML; end
36
+ end
37
+ end
38
+ module Extras; end
39
+ class Models
40
+ class Action; end
41
+ class Group; end
42
+ class Person; end
43
+ class RuleSet
44
+ class Base; end
45
+ class Ruby < Base; end
46
+ class YAML < Base; end
47
+ end
48
+ end
49
+ class Service
50
+ class GitHub; end
51
+ class LDAP; end
52
+ end
53
+ end
54
+
55
+ module Entitlements
56
+ include ::Contracts::Core
57
+ C = ::Contracts
58
+
59
+ IGNORED_FILES = Set.new(%w[README.md PR_TEMPLATE.md])
60
+
61
+ # Allows interpretation of ERB for the configuration file to make things less hokey.
62
+ class ERB < OpenStruct
63
+ def self.render_from_hash(template, hash)
64
+ new(hash).render(template)
65
+ end
66
+
67
+ def render(template)
68
+ ::ERB.new(template, nil, "-").result(binding)
69
+ end
70
+ end
71
+
72
+ # Reset all Entitlements state
73
+ #
74
+ # Takes no arguments
75
+ def self.reset!
76
+ @cache = nil
77
+ @child_classes = nil
78
+ @config = nil
79
+ @config_file = nil
80
+ @config_path_override = nil
81
+ @person_extra_methods = {}
82
+
83
+ reset_extras!
84
+ Entitlements::Data::Groups::Calculated.reset!
85
+ end
86
+
87
+ def self.reset_extras!
88
+ extras_loaded = @extras_loaded
89
+ if extras_loaded
90
+ extras_loaded.each { |clazz| clazz.reset! if clazz.respond_to?(:reset!) }
91
+ end
92
+ @extras_loaded = nil
93
+ end
94
+
95
+ # Set up a dummy logger.
96
+ #
97
+ # Returns a Logger.
98
+ Contract C::None => Logger
99
+ def self.dummy_logger
100
+ # :nocov:
101
+ Logger.new(StringIO.new)
102
+ # :nocov:
103
+ end
104
+
105
+ # Read the configuration file and return it as a hash.
106
+ #
107
+ # Takes no arguments.
108
+ #
109
+ # Returns a Hash.
110
+ Contract C::None => C::HashOf[String => C::Any]
111
+ def self.config
112
+ @config ||= begin
113
+ content = ERB.render_from_hash(File.read(config_file), {})
114
+ ::YAML.safe_load(content)
115
+ end
116
+ end
117
+
118
+ # Set the configuration directly to a Hash.
119
+ #
120
+ # config_hash - Desired value for the configuration.
121
+ #
122
+ # Returns the supplied configuration.
123
+ Contract C::HashOf[String => C::Any] => C::HashOf[String => C::Any]
124
+ def self.config=(config_hash)
125
+ @config = config_hash
126
+ end
127
+
128
+ # Determine the configuration file location. Gets the default if
129
+ # it is called before explicitly set.
130
+ #
131
+ # Returns a String.
132
+ Contract C::None => String
133
+ def self.config_file
134
+ @config_file || File.expand_path("../config/entitlements/config.yaml", File.dirname(__FILE__))
135
+ end
136
+
137
+ # Allow an alternate configuration file to be set. When this is set, it
138
+ # clears @config so it gets read upon the next invocation.
139
+ #
140
+ # path - Path to config file.
141
+ Contract String => C::Any
142
+ def self.config_file=(path)
143
+ unless File.file?(path)
144
+ raise "Specified config file = #{path.inspect} but it does not exist!"
145
+ end
146
+
147
+ @config_file = path
148
+ @config = nil
149
+ end
150
+
151
+ # Get the configuration path for the groups. This is based on the relative
152
+ # location to the configuration file if it doesn't start with a "/".
153
+ #
154
+ # Takes no arguments.
155
+ #
156
+ # Returns a String with the config path.
157
+ def self.config_path
158
+ return @config_path_override if @config_path_override
159
+ base = config.fetch("configuration_path")
160
+ return base if base.start_with?("/")
161
+ File.expand_path(base, File.dirname(config_file))
162
+ end
163
+
164
+ # Set the configuration path for the groups. This will override the automatically
165
+ # calculated config_path that respects the algorithm noted above.
166
+ #
167
+ # path - Path to the base directory of groups.
168
+ #
169
+ # Returns the config_path that was set.
170
+
171
+ Contract String => C::Any
172
+ def self.config_path=(path)
173
+ unless path.start_with?("/")
174
+ raise ArgumentError, "Path must be absolute when setting config_path!"
175
+ end
176
+
177
+ unless File.directory?(path)
178
+ raise Errno::ENOENT, "config_path #{path.inspect} is not a directory!"
179
+ end
180
+
181
+ @config["configuration_path"] = path if @config
182
+ @config_path_override = path
183
+ end
184
+
185
+ # Keep track of backends that are registered when backends are loaded.
186
+ #
187
+ # identifier - A String with the identifier for the backend as it appears in the configuration file
188
+ # clazz - A Class reference to the backend
189
+ # priority - An Integer with the order of execution (smaller = first)
190
+ #
191
+ # Returns nothing.
192
+ Contract String, Class, Integer, C::Maybe[C::Bool] => C::Any
193
+ def self.register_backend(identifier, clazz, priority)
194
+ @backends ||= {}
195
+ @backends[identifier] = { class: clazz, priority: priority }
196
+ end
197
+
198
+ # Return the registered backends.
199
+ #
200
+ # Takes no arguments.
201
+ #
202
+ # Returns a Hash of backend identifier => class and priority.
203
+ Contract C::None => C::HashOf[String => C::HashOf[Symbol, C::Any]]
204
+ def self.backends
205
+ @backends || {}
206
+ end
207
+
208
+ # Load all extras configured by the "extras" key in the entitlements configuration.
209
+ #
210
+ # Takes no arguments.
211
+ #
212
+ # Returns nothing.
213
+ Contract C::None => nil
214
+ def self.load_extras
215
+ Entitlements.config.fetch("extras", {}).each do |extra_name, extra_cfg|
216
+ path = extra_cfg.key?("path") ? Entitlements::Util::Util.absolute_path(extra_cfg["path"]) : nil
217
+ logger.debug "Loading extra #{extra_name} (path = #{path || 'default'})"
218
+ Entitlements::Extras.load_extra(extra_name, path)
219
+ end
220
+ nil
221
+ end
222
+
223
+ # Handle a callback from Entitlements::Extras.load_extra to add a class to the tracker of loaded extra classes.
224
+ #
225
+ # clazz - Class that was loaded.
226
+ #
227
+ # Returns nothing.
228
+ Contract Class => C::Any
229
+ def self.record_loaded_extra(clazz)
230
+ @extras_loaded ||= Set.new
231
+ @extras_loaded.add(clazz)
232
+ end
233
+
234
+ # Register all filters configured by the "filters" key in the entitlements configuration.
235
+ #
236
+ # Takes no arguments.
237
+ #
238
+ # Returns nothing.
239
+ Contract C::None => nil
240
+ def self.register_filters
241
+ Entitlements.config.fetch("filters", {}).each do |filter_name, filter_cfg|
242
+ filter_class = filter_cfg.fetch("class")
243
+ filter_clazz = Kernel.const_get(filter_class)
244
+ filter_config = filter_cfg.fetch("config", {})
245
+
246
+ logger.debug "Registering filter #{filter_name} (class: #{filter_class})"
247
+ Entitlements::Data::Groups::Calculated.register_filter(filter_name, { class: filter_clazz, config: filter_config })
248
+ end
249
+ nil
250
+ end
251
+
252
+ @person_extra_methods = {}
253
+
254
+ # Register a method on the Entitlements::Models::Person objects. Methods are registered at
255
+ # a class level by extras. This updates @person_methods with a Hash of method_name => reference.
256
+ #
257
+ # method_name - A String with the extra method name to register.
258
+ # method_ref - A reference to the method within the appropriate class.
259
+ #
260
+ # Returns nothing.
261
+ Contract String, C::Any => C::Any
262
+ def self.register_person_extra_method(method_name, method_class_ref)
263
+ @person_extra_methods[method_name.to_sym] = method_class_ref
264
+ end
265
+
266
+ # Get the current entries in @person_methods as a hash.
267
+ #
268
+ # Takes no arguments.
269
+ #
270
+ # Returns a Hash of method_name => reference.
271
+ Contract C::None => C::HashOf[Symbol => C::Any]
272
+ def self.person_extra_methods
273
+ @person_extra_methods
274
+ end
275
+
276
+ # Return array of all registered child classes.
277
+ #
278
+ # Takes no arguments.
279
+ #
280
+ # Returns a Hash of instantiated Class objects, indexed by group name, sorted by priority.
281
+ Contract C::None => C::HashOf[C::Or[Symbol, String] => Object]
282
+ def self.child_classes
283
+ @child_classes ||= begin
284
+ backend_obj = Entitlements.config["groups"].map do |group_name, data|
285
+ [group_name, Entitlements.backends[data["type"]][:class].new(group_name)]
286
+ end.compact.to_h
287
+
288
+ # Sort first by priority, then by whether this is a mirror or not (mirrors go last), and
289
+ # finally by the length of the OU name from shortest to longest.
290
+ backend_obj.sort_by do |k, v|
291
+ [
292
+ v.priority,
293
+ Entitlements.config["groups"][k] && Entitlements.config["groups"][k].key?("mirror") ? 1 : 0,
294
+ k.length
295
+ ]
296
+ end.to_h
297
+ end
298
+ end
299
+
300
+ # Method to access the configured auditors.
301
+ #
302
+ # Takes no arguments.
303
+ #
304
+ # Returns an Array of Entitlements::Auditor::* objects.
305
+ Contract C::None => C::ArrayOf[Entitlements::Auditor::Base]
306
+ def self.auditors
307
+ @auditors ||= begin
308
+ if Entitlements.config.key?("auditors")
309
+ Entitlements.config["auditors"].map do |auditor|
310
+ unless auditor.is_a?(Hash)
311
+ # :nocov:
312
+ raise ArgumentError, "Configuration error: Expected auditor to be a hash, got #{auditor.inspect}!"
313
+ # :nocov:
314
+ end
315
+
316
+ auditor_class = auditor.fetch("auditor_class")
317
+
318
+ begin
319
+ clazz = Kernel.const_get("Entitlements::Auditor::#{auditor_class}")
320
+ rescue NameError
321
+ raise ArgumentError, "Auditor class #{auditor_class.inspect} is invalid"
322
+ end
323
+
324
+ clazz.new(logger, auditor)
325
+ end
326
+ else
327
+ []
328
+ end
329
+ end
330
+ end
331
+
332
+ # Global logger for this run of Entitlements.
333
+ #
334
+ # Takes no arguments.
335
+ #
336
+ # Returns a Logger.
337
+ # :nocov:
338
+ def self.logger
339
+ @logger ||= dummy_logger
340
+ end
341
+
342
+ def self.set_logger(logger)
343
+ @logger = logger
344
+ end
345
+ # :nocov:
346
+
347
+ # Calculate - This runs the entitlements logic to calculate the differences, ultimately
348
+ # populating a cache and returning a list of actions. The cache and actions can then be
349
+ # consumed by `execute` to implement the changes.
350
+ #
351
+ # Takes no arguments.
352
+ #
353
+ # Returns the array of actions.
354
+ Contract C::None => C::ArrayOf[Entitlements::Models::Action]
355
+ def self.calculate
356
+ # Load extras that are configured.
357
+ Entitlements.load_extras if Entitlements.config.key?("extras")
358
+
359
+ # Pre-fetch people from configured people data sources.
360
+ Entitlements.prefetch_people
361
+
362
+ # Register filters that are configured.
363
+ Entitlements.register_filters if Entitlements.config.key?("filters")
364
+
365
+ # Keep track of the total change count.
366
+ cache[:change_count] = 0
367
+
368
+ max_parallelism = Entitlements.config["max_parallelism"] || 1
369
+
370
+ # Calculate old and new membership in each group.
371
+ thread_pool = Concurrent::FixedThreadPool.new(max_parallelism)
372
+ logger.debug("Begin prefetch and validate for all groups")
373
+ prep_start = Time.now
374
+ futures = Entitlements.child_classes.map do |group_name, obj|
375
+ Concurrent::Future.execute({ executor: thread_pool }) do
376
+ group_start = Time.now
377
+ logger.debug("Begin prefetch and validate for #{group_name}")
378
+ obj.prefetch
379
+ obj.validate
380
+ logger.debug("Finished prefetch and validate for #{group_name} in #{Time.now - group_start}")
381
+ end
382
+ end
383
+
384
+ futures.each(&:value!)
385
+ logger.debug("Finished all prefetch and validate in #{Time.now - prep_start}")
386
+
387
+ logger.debug("Begin all calculations")
388
+ calc_start = Time.now
389
+ actions = []
390
+ Entitlements.child_classes.map do |group_name, obj|
391
+ obj.calculate
392
+ if obj.change_count > 0
393
+ logger.debug "Group #{group_name.inspect} contributes #{obj.change_count} change(s)."
394
+ cache[:change_count] += obj.change_count
395
+ end
396
+ actions.concat(obj.actions)
397
+ end
398
+ logger.debug("Finished all calculations in #{Time.now - calc_start}")
399
+ logger.debug("Finished all prefetch, validate, and calculation in #{Time.now - prep_start}")
400
+
401
+ actions
402
+ end
403
+
404
+ # Method to execute all of the actions and run the auditors. Returns an Array of the exceptions
405
+ # raised by auditors. Any exceptions raised by providers will be raised once the auditors are
406
+ # executed.
407
+ #
408
+ # actions - An Array of Entitlements::Models::Action
409
+ #
410
+ # Returns nothing.
411
+ Contract C::KeywordArgs[
412
+ actions: C::ArrayOf[Entitlements::Models::Action]
413
+ ] => nil
414
+ def self.execute(actions:)
415
+ # Set up auditors.
416
+ Entitlements.auditors.each { |auditor| auditor.setup }
417
+
418
+ # Track any raised exception to pass to the auditors.
419
+ provider_exception = nil
420
+ audit_exceptions = []
421
+ successful_actions = Set.new
422
+
423
+ # Sort the child classes by priority
424
+ begin
425
+ # Pre-apply changes for each class.
426
+ Entitlements.child_classes.each do |_, obj|
427
+ obj.preapply
428
+ end
429
+
430
+ # Apply changes from all actions.
431
+ actions.each do |action|
432
+ obj = Entitlements.child_classes.fetch(action.ou)
433
+ obj.apply(action)
434
+ successful_actions.add(action.dn)
435
+ end
436
+ rescue => e
437
+ # Populate 'provider_exception' for the auditors and then raise the exception.
438
+ provider_exception = e
439
+ raise e
440
+ ensure
441
+ # Run the audit "commit" action for each auditor. This needs to happen despite any failures that
442
+ # may occur when pre-applying or applying actions, because actions might have been applied despite
443
+ # any failures raised. Run each audit, even if one fails, and batch up the exceptions for the end.
444
+ # If there was an original exception from one of the providers, this block will be executed and then
445
+ # that original exception will be raised.
446
+ if Entitlements.auditors.any?
447
+ logger.debug "Recording data to #{Entitlements.auditors.size} audit provider(s)"
448
+ Entitlements.auditors.each do |audit|
449
+ begin
450
+ audit.commit(
451
+ actions: actions,
452
+ successful_actions: successful_actions,
453
+ provider_exception: provider_exception
454
+ )
455
+ logger.debug "Audit #{audit.description} completed successfully"
456
+ rescue => e
457
+ logger.error "Audit #{audit.description} failed: #{e.class} #{e.message}"
458
+ e.backtrace.each { |line| logger.error line }
459
+ audit_exceptions << e
460
+ end
461
+ end
462
+ end
463
+ end
464
+
465
+ # If we get here there were no provider exceptions. If there were audit exceptions raise them here.
466
+ # If there were multiple exceptions we can only raise the first one, but log a message indicating this.
467
+ return if audit_exceptions.empty?
468
+
469
+ if audit_exceptions.size > 1
470
+ logger.warn "There were #{audit_exceptions.size} audit exceptions. Only the first one is raised."
471
+ end
472
+ raise audit_exceptions.first
473
+ end
474
+
475
+ # Validate the configuration file.
476
+ #
477
+ # Takes no input.
478
+ #
479
+ # Returns nothing.
480
+ Contract C::None => nil
481
+ def self.validate_configuration_file!
482
+ # Required attributes
483
+ spec = {
484
+ "configuration_path" => { required: true, type: String },
485
+ "backends" => { required: false, type: Hash },
486
+ "people" => { required: true, type: Hash },
487
+ "people_data_source" => { required: true, type: String },
488
+ "groups" => { required: true, type: Hash },
489
+ "auditors" => { required: false, type: Array },
490
+ "filters" => { required: false, type: Hash },
491
+ "extras" => { required: false, type: Hash },
492
+ "max_parallelism" => { required: false, type: Integer },
493
+ }
494
+
495
+ Entitlements::Util::Util.validate_attr!(spec, Entitlements.config, "Entitlements configuration file")
496
+
497
+ # Make sure each group has a valid type, and then forward the validator to the child class.
498
+ # If a named backend is chosen, merge the parameters from the backend with the parameters given
499
+ # for the class configuration, and then remove all indication that a backend was used.
500
+ Entitlements.config["groups"].each do |key, data|
501
+ if data.key?("backend")
502
+ unless Entitlements.config["backends"] && Entitlements.config["backends"].key?(data["backend"])
503
+ raise "Entitlements configuration group #{key.inspect} references non-existing backend #{data['backend'].inspect}!"
504
+ end
505
+
506
+ backend = Entitlements.config["backends"].fetch(data["backend"])
507
+ unless backend.key?("type")
508
+ raise "Entitlements backend #{data['backend'].inspect} is missing a type!"
509
+ end
510
+
511
+ # Priority in the merge is given to the specific OU configured. Backend data is filled
512
+ # in only as default values when not otherwise defined.
513
+ Entitlements.config["groups"][key] = backend.merge(data)
514
+ Entitlements.config["groups"][key].delete("backend")
515
+ data = Entitlements.config["groups"][key]
516
+ end
517
+
518
+ unless data["type"].is_a?(String)
519
+ raise "Entitlements configuration group #{key.inspect} does not properly declare a type!"
520
+ end
521
+
522
+ unless Entitlements.backends.key?(data["type"])
523
+ raise "Entitlements configuration group #{key.inspect} has invalid type (#{data['type'].inspect})"
524
+ end
525
+ end
526
+
527
+ # Good if nothing is raised by here.
528
+ nil
529
+ end
530
+
531
+ # Method to go through each person data source and retrieve the list of people from it. Populates
532
+ # Entitlements.cache[:people][<datasource>] with the objects that can be subsequently `read` from
533
+ # with no penalty.
534
+ #
535
+ # Takes no arguments.
536
+ #
537
+ # Returns the Entitlements::Data::People::* object.
538
+ Contract C::None => C::Any
539
+ def self.prefetch_people
540
+ Entitlements.cache[:people_obj] ||= begin
541
+ people_data_sources = Entitlements.config.fetch("people", [])
542
+ if people_data_sources.empty?
543
+ raise ArgumentError, "At least one data source for people must be specified in the Entitlements configuration!"
544
+ end
545
+
546
+ # TODO: In the future, have separate data sources per group.
547
+ people_data_source_name = Entitlements.config.fetch("people_data_source", "")
548
+ if people_data_source_name.empty?
549
+ raise ArgumentError, "The Entitlements configuration must define a people_data_source!"
550
+ end
551
+ unless people_data_sources.key?(people_data_source_name)
552
+ raise ArgumentError, "The people_data_source #{people_data_source_name.inspect} is invalid!"
553
+ end
554
+
555
+ objects = people_data_sources.map do |ds_name, ds_config|
556
+ people_obj = Entitlements::Data::People.new_from_config(ds_config)
557
+ people_obj.read
558
+ [ds_name, people_obj]
559
+ end.to_h
560
+
561
+ objects.fetch(people_data_source_name)
562
+ end
563
+ end
564
+
565
+ # This is a global cache for the whole run of entitlements. To avoid passing objects around, since Entitlements
566
+ # by its nature is a run-once-upon-demand application.
567
+ #
568
+ # Takes no arguments.
569
+ #
570
+ # Returns a Hash that contains the cache.
571
+ #
572
+ # Note: Since this is hit a lot, to avoid the performance penalty, Contracts is not used here.
573
+ # :nocov:
574
+ def self.cache
575
+ @cache ||= {
576
+ calculated: {},
577
+ file_objects: {}
578
+ }
579
+ end
580
+ # :nocov:
581
+ end
582
+
583
+ # Finally, load everything else. Order should be unimportant here.
584
+ require_relative "entitlements/auditor/base"
585
+ require_relative "entitlements/backend/base_controller"
586
+ require_relative "entitlements/backend/base_provider"
587
+ require_relative "entitlements/backend/dummy"
588
+ require_relative "entitlements/backend/ldap"
589
+ require_relative "entitlements/backend/member_of"
590
+ require_relative "entitlements/cli"
591
+ require_relative "entitlements/data/groups"
592
+ require_relative "entitlements/data/people"
593
+ require_relative "entitlements/extras"
594
+ require_relative "entitlements/extras/base"
595
+ require_relative "entitlements/models/action"
596
+ require_relative "entitlements/models/group"
597
+ require_relative "entitlements/models/person"
598
+ require_relative "entitlements/plugins"
599
+ require_relative "entitlements/plugins/dummy"
600
+ require_relative "entitlements/plugins/group_of_names"
601
+ require_relative "entitlements/plugins/posix_group"
602
+ require_relative "entitlements/rule/base"
603
+ require_relative "entitlements/service/ldap"
604
+ require_relative "entitlements/util/mirror"
605
+ require_relative "entitlements/util/override"
606
+ require_relative "entitlements/util/util"