entitlements-app 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
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"