Class: JSS::Scopable::Scope

Inherits:
Object show all
Defined in:
lib/jss/api_object/scopable/scope.rb,
lib/jss.rb

Overview

This class represents a Scope in the JSS, as can be applied to Scopable objects like Policies, Profiles, etc. Instances of this class are generally used as the value of the @scope attribute of those objects.

Scope data comes from the API as a hash within the overall object data. The main keys of the hash define the included targets of the scope. A sub-hash defines limitations on those inclusions, and another sub-hash defines explicit exclusions.

This class provides methods for adding, removing, or fully replacing the various items in scope’s realms: targets, limitations, and exclusions.

See Also:

Constant Summary collapse

SCOPING_CLASSES =

These are the classes that Scopes can use for defining a scope, keyed by appropriate symbols.

{
  computers: JSS::Computer,
  computer: JSS::Computer,
  computer_groups: JSS::ComputerGroup,
  computer_group: JSS::ComputerGroup,
  mobile_devices: JSS::MobileDevice,
  mobile_device: JSS::MobileDevice,
  mobile_device_groups: JSS::MobileDeviceGroup,
  mobile_device_group: JSS::MobileDeviceGroup,
  buildings: JSS::Building,
  building: JSS::Building,
  departments: JSS::Department,
  department: JSS::Department,
  network_segments: JSS::NetworkSegment,
  network_segment: JSS::NetworkSegment,
  users: JSS::User,
  user: JSS::User,
  user_groups: JSS::UserGroup,
  user_group: JSS::UserGroup
}.freeze
LDAP_USER_KEYS =

Some things get checked in LDAP as well as the JSS

i[user users].freeze
LDAP_GROUP_KEYS =
i[user_groups user_group].freeze
CHECK_LDAP_KEYS =
LDAP_USER_KEYS + LDAP_GROUP_KEYS
TARGETS_AND_GROUPS =

This hash maps the availble Scope Target keys from SCOPING_CLASSES to their corresponding target group keys from SCOPING_CLASSES.

{ computers: :computer_groups, mobile_devices: :mobile_device_groups }.freeze
ESS =

added to the ends of singular key names if needed, e.g. computer_group => computer_groups

's'.freeze
INCLUSIONS =

These can be part of the base inclusion list of the scope, along with the appropriate target and target group keys

i[buildings departments].freeze
LIMITATIONS =

These can limit the inclusion list

i[network_segments users user_groups].freeze
EXCLUSIONS =

any of them can be excluded

INCLUSIONS + LIMITATIONS
DEFAULT_SCOPE =

Here’s a default scope as it might come from the API.

{
  all_computers: true,
  all_mobile_devices: true,
  limitations: {},
  exclusions: {}
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(target_key, raw_scope = nil) ⇒ Scope

If raw_scope is empty, a default scope, scoped to all targets, is created, and can be modified as needed.

Parameters:

  • target_key (Symbol)

    the kind of thing we’re scoping, one of TARGETS_AND_GROUPS

  • raw_scope (Hash) (defaults to: nil)

    the JSON :scope data from an API query that is scopable, e.g. a Policy.

Raises:



180
181
182
183
184
185
186
187
188
189
190
191
192
193
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/jss/api_object/scopable/scope.rb', line 180

def initialize(target_key, raw_scope = nil)
  raw_scope ||= DEFAULT_SCOPE.dup
  raise JSS::InvalidDataError, "The target class of a Scope must be one of the symbols :#{TARGETS_AND_GROUPS.keys.join(', :')}" unless TARGETS_AND_GROUPS.key?(target_key)

  @target_key = target_key
  @target_class = SCOPING_CLASSES[@target_key]
  @group_key = TARGETS_AND_GROUPS[@target_key]
  @group_class = SCOPING_CLASSES[@group_key]

  @inclusion_keys = [@target_key, @group_key] + INCLUSIONS
  @exclusion_keys = [@target_key, @group_key] + EXCLUSIONS

  @all_key = "all_#{target_key}".to_sym
  @all_targets = raw_scope[@all_key]

  # Everything gets mapped from an Array of Hashes to
  # an Array of ids
  @inclusions = {}
  @inclusion_keys.each do |k|
    raw_scope[k] ||= []
    @inclusions[k] = raw_scope[k].compact.map { |n| n[:id].to_i  }
  end # @inclusion_keys.each do |k|

  @limitations = {}
  if raw_scope[:limitations]
    LIMITATIONS.each do |k|
      raw_scope[:limitations][k] ||= []
      @limitations[k] = raw_scope[:limitations][k].compact.map { |n| n[:id].to_i }
    end # LIMITATIONS.each do |k|
  end # if raw_scope[:limitations]

  @exclusions = {}
  if raw_scope[:exclusions]
    @exclusion_keys.each do |k|
      raw_scope[:exclusions][k] ||= []
      @exclusions[k] = raw_scope[:exclusions][k].compact.map { |n| n[:id].to_i }
    end
  end

  @container = nil
end

Instance Attribute Details

#all_targetsBoolean (readonly) Also known as: all_targets?

Does this scope cover all targets?

If this is true, the @inclusions Hash is ignored, and all targets in the JSS form the base scope.

Returns:

  • (Boolean)


142
143
144
# File 'lib/jss/api_object/scopable/scope.rb', line 142

def all_targets
  @all_targets
end

#containerJSS::APIObject subclass

A reference to the object that contains this Scope

For telling it when a change is made and an update needed

Returns:



112
113
114
# File 'lib/jss/api_object/scopable/scope.rb', line 112

def container
  @container
end

#exclusionsHash<Array> (readonly)

The items in these arrays are the exclusions applied to targets in the @inclusions .

The arrays of names are:

  • :targets

  • :target_groups

  • :departments

  • :buildings

  • :network_segments

  • :users

  • :user_groups

Returns:



168
169
170
# File 'lib/jss/api_object/scopable/scope.rb', line 168

def exclusions
  @exclusions
end

#inclusionsHash<Array> (readonly)

The items which form the base scope of included targets

This is the group of targets to which the limitations and exclusions apply. they keys are:

  • :targets

  • :target_groups

  • :departments

  • :buildings

and the values are Arrays of names of those things.

Returns:



133
134
135
# File 'lib/jss/api_object/scopable/scope.rb', line 133

def inclusions
  @inclusions
end

#limitationsHash<Array> (readonly)

The items in these arrays are the limitations applied to targets in the @inclusions .

The arrays of names are:

  • :network_segments

  • :users

  • :user_groups

Returns:



153
154
155
# File 'lib/jss/api_object/scopable/scope.rb', line 153

def limitations
  @limitations
end

#target_classObject (readonly)

what type of target is this scope for? Computers or Mobiledevices?



119
120
121
# File 'lib/jss/api_object/scopable/scope.rb', line 119

def target_class
  @target_class
end

#unable_to_verify_ldap_entriesBoolean

Returns should we expect a potential 409 Conflict if we can’t connect to LDAP servers for verification?.

Returns:

  • (Boolean)

    should we expect a potential 409 Conflict if we can’t connect to LDAP servers for verification?



116
117
118
# File 'lib/jss/api_object/scopable/scope.rb', line 116

def unable_to_verify_ldap_entries
  @unable_to_verify_ldap_entries
end

Instance Method Details

#add_exclusion(key, item) ⇒ void

This method returns an undefined value.

Add a single item for exclusions of this scope.

The item name will be checked for existence in the JSS, and an exception raised if the item doesn’t exist.

Examples:

add_exclusion(:network_segments, "foo")

Parameters:

  • key (Symbol)

    the type of item being added to the exclusions, :computer, :building, etc…

  • item (String, integer)

    a valid identifier of the item being added

Raises:



468
469
470
471
472
473
474
475
476
477
# File 'lib/jss/api_object/scopable/scope.rb', line 468

def add_exclusion(key, item)
  key = pluralize_key(key)
  item_id = validate_item(:exclusion, key, item)
  return if @exclusions[key] && @exclusions[key].include?(item_id)
  raise JSS::AlreadyExistsError, "Can't exclude #{key} scope to '#{item}' because it's already explicitly included." if @inclusions[key] && @inclusions[key].include?(item)
  raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{item}' because it's already an explicit limitation." if @limitations[key] && @limitations[key].include?(item)

  @exclusions[key] << item_id
  @container.should_update if @container
end

#add_limitation(key, item) ⇒ void

TODO:

handle ldap user/group lookups

This method returns an undefined value.

Add a single item for limiting this scope.

The item name will be checked for existence in the JSS, and an exception raised if the item doesn’t exist.

Examples:

add_limitation(:network_segments, "foo")

Parameters:

  • key (Symbol)

    the type of item being added, :computer, :building, etc…

  • item (String, integer)

    a valid identifier of the item being added

Raises:



386
387
388
389
390
391
392
393
394
395
# File 'lib/jss/api_object/scopable/scope.rb', line 386

def add_limitation(key, item)
  key = pluralize_key(key)
  item_id = validate_item(:limitation, key, item)
  return nil if @limitations[key] && @limitations[key].include?(item_id)

  raise JSS::AlreadyExistsError, "Can't set #{key} limitation for '#{name}' because it's already an explicit exclusion." if @exclusions[key] && @exclusions[key].include?(item_id)

  @limitations[key] << item_id
  @container.should_update if @container
end

#add_target(key, item) ⇒ void Also known as: add_inclusion

This method returns an undefined value.

Add a single item as a target in this scope.

The item name will be checked for existence in the JSS, and an exception

raised if the item doesn't exist.

Examples:

add_target(:computers, "mantis")
add_target(:computer_groups, 2342)

Parameters:

  • key (Symbol)

    the key from #SCOPING_CLASSES for the kind of item being added, :computer, :building, etc…

  • item (String, integer)

    a valid identifier of the item being added

Raises:



303
304
305
306
307
308
309
310
311
312
313
# File 'lib/jss/api_object/scopable/scope.rb', line 303

def add_target(key, item)
  key = pluralize_key(key)
  item_id = validate_item(:target, key, item)
  return if @inclusions[key] && @inclusions[key].include?(item_id)

  raise JSS::AlreadyExistsError, "Can't set #{key} target to '#{item}' because it's already an explicit exclusion." if @exclusions[key] && @exclusions[key].include?(item_id)

  @inclusions[key] << item_id
  @all_targets = false
  @container.should_update if @container
end

#include_all(clear = false) ⇒ void

This method returns an undefined value.

Set the scope’s inclusions to all targets.

By default, the limitations and exclusions remain. If a non-false parameter is provided, they will be removed also.

Parameters:

  • clear (Boolean) (defaults to: false)

    Should the limitations and exclusions be removed also?



231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/jss/api_object/scopable/scope.rb', line 231

def include_all(clear = false)
  @inclusions = {}
  @inclusion_keys.each { |k| @inclusions[k] = [] }
  @all_targets = true
  if clear
    @limitations = {}
    LIMITATIONS.each { |k| @limitations[k] = [] }

    @exclusions = {}
    @exclusion_keys.each { |k| @exclusions[k] = [] }
  end
  @container.should_update if @container
end

#pretty_print_instance_variablesArray

Remove the init_data and api object from the instance_variables used to create pretty-print (pp) output.

Returns:

  • (Array)

    the desired instance_variables



539
540
541
542
543
# File 'lib/jss/api_object/scopable/scope.rb', line 539

def pretty_print_instance_variables
  vars = instance_variables.sort
  vars.delete :@container
  vars
end

#remove_exclusion(key, item) ⇒ void

This method returns an undefined value.

Remove a single item for exclusions of this scope

Examples:

remove_exclusion(:network_segments, "foo")

Parameters:

  • key (Symbol)

    the type of item being removed from the excludions, :computer, :building, etc…

  • item (String, integer)

    a valid identifier of the item being removed



490
491
492
493
494
495
496
# File 'lib/jss/api_object/scopable/scope.rb', line 490

def remove_exclusion(key, item)
  key = pluralize_key(key)
  item_id = validate_item :exclusion, key, item, error_if_not_found: false
  return unless @exclusions[key] && @exclusions[key].include?(item_id)
  @exclusions[key].delete item_id
  @container.should_update if @container
end

#remove_limitation(key, item) ⇒ void

TODO:

handle ldap user/group lookups

This method returns an undefined value.

Remove a single item for limiting this scope.

Examples:

remove_limitation(:network_segments, "foo")

Parameters:

  • key (Symbol)

    the type of item being removed, :computer, :building, etc…

  • item (String, integer)

    a valid identifier of the item being removed



410
411
412
413
414
415
416
417
# File 'lib/jss/api_object/scopable/scope.rb', line 410

def remove_limitation(key, item)
  key = pluralize_key(key)
  item_id = validate_item :limitation, key, item, error_if_not_found: false
  return unless item_id
  return unless @limitations[key] && @limitations[key].include?(item_id)
  @limitations[key].delete item_id
  @container.should_update if @container
end

#remove_target(key, item) ⇒ void Also known as: remove_inclusion

This method returns an undefined value.

Remove a single item as a target for this scope.

Examples:

remove_target(:computer, "mantis")

Parameters:

  • key (Symbol)

    the key from #SCOPING_CLASSES for the kind of item being removed, :computer, :building, etc…

  • item (String, integer)

    a valid identifier of the item being removed



327
328
329
330
331
332
333
334
# File 'lib/jss/api_object/scopable/scope.rb', line 327

def remove_target(key, item)
  key = pluralize_key(key)
  item_id = validate_item :target, key, item, error_if_not_found: false
  return unless item_id
  return unless @inclusions[key] && @inclusions[key].include?(item_id)
  @inclusions[key].delete item_id
  @container.should_update if @container
end

#scope_xmlREXML::Element

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return a REXML Element containing the current state of the Scope for adding into the XML of the container.

Returns:

  • (REXML::Element)


504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
# File 'lib/jss/api_object/scopable/scope.rb', line 504

def scope_xml
  scope = REXML::Element.new 'scope'
  scope.add_element(@all_key.to_s).text = @all_targets

  @inclusions.each do |klass, list|
    list.compact!
    list.delete 0
    list_as_hash = list.map { |i| { id: i } }
    scope << SCOPING_CLASSES[klass].xml_list(list_as_hash, :id)
  end

  limitations = scope.add_element('limitations')
  @limitations.each do |klass, list|
    list.compact!
    list.delete 0
    list_as_hash = list.map { |i| { id: i } }
    limitations << SCOPING_CLASSES[klass].xml_list(list_as_hash, :id)
  end

  exclusions = scope.add_element('exclusions')
  @exclusions.each do |klass, list|
    list.compact!
    list.delete 0
    list_as_hash = list.map { |i| { id: i } }
    exclusions << SCOPING_CLASSES[klass].xml_list(list_as_hash, :id)
  end
  scope
end

#set_exclusion(key, list) ⇒ void

This method returns an undefined value.

Replace an exclusion list for this scope

The list must be an Array of names of items of the Class being excluded from the scope Each will be checked for existence in the JSS, and an exception raised if the item doesn’t exist.

Examples:

set_exclusion(:network_segments, ['foo','bar'])

Parameters:

  • key (Symbol)

    the type of item being excluded, :computer, :building, etc…

  • list (Array)

    the identifiers of the items being set

Raises:



433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
# File 'lib/jss/api_object/scopable/scope.rb', line 433

def set_exclusion(key, list)
  key = pluralize_key(key)
  raise JSS::InvalidDataError, "List must be an Array of #{key} identifiers, it may be empty." unless list.is_a? Array

  # check the idents
  list.map! do |ident|
    item_id = validate_item(:exclusion, key, ident)
    case key
    when *@inclusion_keys
      raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{ident}' because it's already explicitly included." if @inclusions[key] && @inclusions[key].include?(item_id)
    when *LIMITATIONS
      raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{ident}' because it's already an explicit limitation." if @limitations[key] && @limitations[key].include?(item_id)
    end
    item_id
  end # each

  return nil if list.sort == @exclusions[key].sort

  @exclusions[key] = list
  @container.should_update if @container
end

#set_limitation(key, list) ⇒ void Also known as: set_limitations

TODO:

handle ldap user group lookups

This method returns an undefined value.

Replace a limitation list for this scope.

The list must be an Array of names of items of the Class represented by the key. Each will be checked for existence in the JSS, and an exception raised if the item doesn’t exist.

Examples:

set_limitation(:network_segments, ['foo',231])

Parameters:

  • key (Symbol)

    the type of items being set as limitations, :network_segments, :users, etc…

  • list (Array)

    the identifiers of the items being set as limitations

Raises:



353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/jss/api_object/scopable/scope.rb', line 353

def set_limitation(key, list)
  key = pluralize_key(key)
  raise JSS::InvalidDataError, "List must be an Array of #{key} identifiers, it may be empty." unless list.is_a? Array

  # check the idents
  list.map! do |ident|
    item_id = validate_item(:limitation, key, ident)
    raise JSS::AlreadyExistsError, "Can't set #{key} limitation for '#{name}' because it's already an explicit exclusion." if @exclusions[key] && @exclusions[key].include?(item_id)
    item_id
  end # each

  return nil if list.sort == @limitations[key].sort

  @limitations[key] = list
  @container.should_update if @container
end

#set_targets(key, list) ⇒ void Also known as: set_target, set_inclusion, set_inclusions

This method returns an undefined value.

Replace a list of item names for as targets in this scope.

The list must be an Array of names of items of the Class represented by the key. Each will be checked for existence in the JSS, and an exception raised if the item doesn’t exist.

being included, :computer, :building, etc…

Examples:

set_targets(:computers, ['kimchi','mantis'])

Parameters:

  • key (Symbol)

    the key from #SCOPING_CLASSES for the kind of items

  • list (Array)

    identifiers of the items being added

Raises:



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/jss/api_object/scopable/scope.rb', line 262

def set_targets(key, list)
  key = pluralize_key(key)
  raise JSS::InvalidDataError, "List must be an Array of #{key} identifiers, it may be empty." unless list.is_a? Array

  # check the idents
  list.map! do |ident|
    item_id = validate_item(:target, key, ident)
    if @exclusions[key] && @exclusions[key].include?(item_id)
      raise JSS::AlreadyExistsError, \
        "Can't set #{key} target to '#{ident}' because it's already an explicit exclusion."
    end
    item_id
  end # each

  return nil if list.sort == @inclusions[key].sort

  @inclusions[key] = list
  @all_targets = false
  @container.should_update if @container
end