Class: Entitlements::Data::Groups::Calculated

Inherits:
Object
  • Object
show all
Includes:
Contracts::Core
Defined in:
lib/entitlements.rb,
lib/entitlements/data/groups/calculated.rb,
lib/entitlements/data/groups/calculated/base.rb,
lib/entitlements/data/groups/calculated/ruby.rb,
lib/entitlements/data/groups/calculated/text.rb,
lib/entitlements/data/groups/calculated/yaml.rb,
lib/entitlements/data/groups/calculated/rules/base.rb,
lib/entitlements/data/groups/calculated/rules/group.rb,
lib/entitlements/data/groups/calculated/filters/base.rb,
lib/entitlements/data/groups/calculated/modifiers/base.rb,
lib/entitlements/data/groups/calculated/rules/username.rb,
lib/entitlements/data/groups/calculated/modifiers/expiration.rb,
lib/entitlements/data/groups/calculated/filters/member_of_group.rb

Defined Under Namespace

Classes: Base, Filters, Modifiers, Ruby, Rules, Text, YAML

Constant Summary collapse

C =
::Contracts
FILE_EXTENSIONS =
{
  "rb"   => "Entitlements::Data::Groups::Calculated::Ruby",
  "txt"  => "Entitlements::Data::Groups::Calculated::Text",
  "yaml" => "Entitlements::Data::Groups::Calculated::YAML"
}

Class Method Summary collapse

Class Method Details

.all_groupsObject



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/entitlements/data/groups/calculated.rb', line 131

def self.all_groups
  @groups_in_ou_cache.map do |ou_key, dn_in_ou|
    if @config_cache.key?(ou_key)
      [
        ou_key,
        {
          config: @config_cache[ou_key],
          groups: dn_in_ou.sort.map { |dn| [dn, @groups_cache.fetch(dn)] }.to_h
        }
      ]
    else
      nil
    end
  end.compact.to_h
end

.filters_defaultObject

Retrieve the filters in the format used as default / starting point in other parts of the program: { “filter_name_1” => :none, “filter_name_2” => :none }

Takes no arguments.

Returns a Hash in the indicated format.



260
261
262
# File 'lib/entitlements/data/groups/calculated.rb', line 260

def self.filters_default
  @filters_index.map { |k, _| [k, :none] }.to_h
end

.filters_indexObject



250
251
252
# File 'lib/entitlements/data/groups/calculated.rb', line 250

def self.filters_index
  @filters_index
end

.read(dn) ⇒ Object



50
51
52
53
# File 'lib/entitlements/data/groups/calculated.rb', line 50

def self.read(dn)
  return @groups_cache[dn] if @groups_cache[dn]
  raise "read(#{dn.inspect}) does not support calculation at this time. Please use read_all() first to build cache."
end

.read_all(ou_key, cfg_obj, skip_broken_references: false) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/entitlements/data/groups/calculated.rb', line 65

def self.read_all(ou_key, cfg_obj, skip_broken_references: false)
  return read_mirror(ou_key, cfg_obj) if cfg_obj["mirror"]

  @config_cache[ou_key] ||= cfg_obj
  @groups_in_ou_cache[ou_key] ||= begin
    Entitlements.logger.debug "Calculating all groups for #{ou_key}"
    Entitlements.logger.debug "!!! skip_broken_references is enabled" if skip_broken_references

    result = Set.new
    Entitlements.cache[:file_objects] ||= {}

    # Iterate over all the files in the configuration directory for this OU
    path = Entitlements::Util::Util.path_for_group(ou_key)
    Dir.glob(File.join(path, "*")).each do |filename|
      # If it's a directory, skip it for now.
      if File.directory?(filename)
        next
      end

      # If the file is ignored (e.g. documentation) then skip it.
      if Entitlements::IGNORED_FILES.member?(File.basename(filename))
        next
      end

      # Determine the group DN. The CN will be the filname without its extension.
      file_without_extension = File.basename(filename).sub(/\.\w+\z/, "")
      unless file_without_extension =~ /\A[\w\-]+\z/
        raise "Illegal LDAP group name #{file_without_extension.inspect} in #{ou_key}!"
      end
      group_dn = ["cn=#{file_without_extension}", cfg_obj.fetch("base")].join(",")

      # Use the ruleset to build the group.
      options = { skip_broken_references: skip_broken_references }

      Entitlements.cache[:file_objects][filename] ||= ruleset(filename: filename, config: cfg_obj, options: options)
      @groups_cache[group_dn] = Entitlements::Models::Group.new(
        dn: group_dn,
        members: Entitlements.cache[:file_objects][filename].modified_filtered_members,
        description: Entitlements.cache[:file_objects][filename].description,
        metadata: Entitlements.cache[:file_objects][filename]..merge("_filename" => filename)
      )
      result.add group_dn
    end

    result
  end
end

.read_mirror(ou_key, cfg_obj) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/entitlements/data/groups/calculated.rb', line 156

def self.read_mirror(ou_key, cfg_obj)
  @groups_in_ou_cache[ou_key] ||= begin
    Entitlements.logger.debug "Mirroring #{ou_key} from #{cfg_obj['mirror']}"

    unless @groups_in_ou_cache[cfg_obj["mirror"]]
      raise "Cannot read_mirror on #{ou_key.inspect} because read_all has not occurred on #{cfg_obj['mirror'].inspect}!"
    end

    result = Set.new
    @groups_in_ou_cache[cfg_obj["mirror"]].each do |source_dn|
      source_group = @groups_cache[source_dn]
      unless source_group
        raise "No group has been calculated for #{source_dn.inspect}!"
      end

      new_dn = ["cn=#{source_group.cn}", cfg_obj["base"]].join(",")
      @groups_cache[new_dn] ||= source_group.copy_of(new_dn)
      result.add new_dn
    end

    result
  end
end

.register_filter(filter_name, filter_cfg) ⇒ Object



284
285
286
# File 'lib/entitlements/data/groups/calculated.rb', line 284

def self.register_filter(filter_name, filter_cfg)
  @filters_index[filter_name] = filter_cfg
end

.register_rule(rule_name, clazz) ⇒ Object



272
273
274
# File 'lib/entitlements/data/groups/calculated.rb', line 272

def self.register_rule(rule_name, clazz)
  @rules_index[rule_name] = clazz
end

.reset!Object

Reset all module state

Takes no arguments



32
33
34
35
36
37
38
39
40
41
42
# File 'lib/entitlements/data/groups/calculated.rb', line 32

def self.reset!
  @rules_index = {
    "group"    => Entitlements::Data::Groups::Calculated::Rules::Group,
    "username" => Entitlements::Data::Groups::Calculated::Rules::Username
  }

  @filters_index = {}
  @groups_in_ou_cache = {}
  @groups_cache = {}
  @config_cache = {}
end

.rules_indexObject



240
241
242
# File 'lib/entitlements/data/groups/calculated.rb', line 240

def self.rules_index
  @rules_index
end

.ruleset(filename:, config:, options: {}) ⇒ Object



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/entitlements/data/groups/calculated.rb', line 194

def self.ruleset(filename:, config:, options: {})
  unless filename =~ /\.(\w+)\z/
    raise ArgumentError, "Unable to determine the extension on #{filename.inspect}!"
  end
  ext = Regexp.last_match(1)

  unless FILE_EXTENSIONS[ext]
    Entitlements.logger.fatal "Unable to map filename #{filename.inspect} to a ruleset object!"
    raise ArgumentError, "Unable to map filename #{filename.inspect} to a ruleset object!"
  end

  if config.key?("allowed_types")
    unless config["allowed_types"].is_a?(Array)
      Entitlements.logger.fatal "Configuration error: allowed_types should be an Array, got #{config['allowed_types'].inspect}"
      raise ArgumentError, "Configuration error: allowed_types should be an Array, got #{config['allowed_types'].class}!"
    end

    unless config["allowed_types"].include?(ext)
      allowed_join = config["allowed_types"].join(",")
      Entitlements.logger.fatal "Files with extension #{ext.inspect} are not allowed in this OU! Allowed: #{allowed_join}!"
      raise ArgumentError, "Files with extension #{ext.inspect} are not allowed in this OU! Allowed: #{allowed_join}!"
    end
  end

  clazz = Kernel.const_get(FILE_EXTENSIONS[ext])
  clazz.new(filename: filename, config: config, options: options)
end

.to_hObject



120
121
122
# File 'lib/entitlements/data/groups/calculated.rb', line 120

def self.to_h
  @groups_cache
end