Class: JSS::Scopable::Scope

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

Overview

TODO:

Implement simple LDAP queries using the defined LDAPServers to confirm the existance of users or groups used in limitations and exclusions. As things are now if you add invalid user or group names, you’ll get a 409 conflict error when you try to save your changes to the JSS.

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 parts of the scope’s inclusions, 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
}
LDAP_USER_KEYS =

Some things get checked in LDAP as well as the JSS

[:user, :users]
LDAP_GROUP_KEYS =
[:user_groups, :user_group]
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 }
INCLUSIONS =

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

[:buildings, :departments]
LIMITATIONS =

These can limit the inclusion list

[:network_segments, :users, :user_groups]
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 => {}
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(target_key, api_scope = nil) ⇒ Scope

If api_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 scopeing, one of TARGETS_AND_GROUPS

  • api_scope (Hash) (defaults to: nil)

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

Raises:



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/jss/api_object/scopable/scope.rb', line 202

def initialize(target_key, api_scope = nil)
  api_scope ||= DEFAULT_SCOPE
  raise JSS::InvalidDataError, "The target class of a Scope must be one of the symbols :#{TARGETS_AND_GROUPS.keys.join(', :')}" unless TARGETS_AND_GROUPS.keys.include? 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 = api_scope[@all_key]

  ### Everything gets mapped from an Array of Hashes to an Array of names (or an empty array)
  ### since names are all that really matter when submitting the scope.
  @inclusions = {}
  @inclusion_keys.each{|k| @inclusions[k] = api_scope[k] ? api_scope[k].map{|n| n[:name]} :  [] }

  @limitations = {}
  if api_scope[:limitations]
    LIMITATIONS.each{|k| @limitations[k] = api_scope[:limitations][k] ? api_scope[:limitations][k].map{|n| n[:name]} :  [] }
  end

  @exclusions = {}
  if api_scope[:exclusions]
    @exclusion_keys.each{|k| @exclusions[k] = api_scope[:exclusions][k] ? api_scope[:exclusions][k].map{|n| n[:name]} :  [] }
  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)


159
160
161
# File 'lib/jss/api_object/scopable/scope.rb', line 159

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:



129
130
131
# File 'lib/jss/api_object/scopable/scope.rb', line 129

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:



187
188
189
# File 'lib/jss/api_object/scopable/scope.rb', line 187

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:



150
151
152
# File 'lib/jss/api_object/scopable/scope.rb', line 150

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:



172
173
174
# File 'lib/jss/api_object/scopable/scope.rb', line 172

def limitations
  @limitations
end

#target_classObject (readonly)

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



136
137
138
# File 'lib/jss/api_object/scopable/scope.rb', line 136

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?



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

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)

    the name of the item being added

Raises:



512
513
514
515
516
517
518
519
520
521
522
523
524
525
# File 'lib/jss/api_object/scopable/scope.rb', line 512

def add_exclusion (key, item)
  raise JSS::InvalidDataError, "Exclusion key must be one of :#{@exclusion_keys.join(', :')}" unless @exclusion_keys.include? key
  raise JSS::InvalidDataError, "Item must be a #{key} name." unless item.kind_of? String

  return nil if @exclusions[key] and @exclusions[key].include? item

  ### check the name
  raise JSS::NoSuchItemError, "No existing #{key} with name '#{item}'" unless check_name key, item
  raise JSS::AlreadyExistsError, "Can't exclude #{key} scope to '#{item}' because it's already explicitly included." if @inclusions[key] and @inclusions[key].include? item
  raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{item}' because it's already an explicit limitation." if @limitations[key] and @limitations[key].include? item

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

#add_inclusion(key, item) ⇒ void

This method returns an undefined value.

Add a single item for this inclusion 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_inclusion(:computer, "mantis")

Parameters:

  • key (Symbol)

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

  • item (String)

    the name of the item being added

Raises:



317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'lib/jss/api_object/scopable/scope.rb', line 317

def add_inclusion (key, item)
  raise JSS::InvalidDataError, "Inclusion key must be one of :#{@inclusion_keys.join(', :')}" unless @inclusion_keys.include? key
  raise JSS::InvalidDataError, "Item must be a #{key} name." unless item.kind_of? String

  return nil if @inclusions[key] and @inclusions[key].include? item

  ### check the name
  raise JSS::NoSuchItemError, "No existing #{key} with name '#{item}'" unless check_name key, item
  raise JSS::AlreadyExistsError, "Can't set #{key} scope to '#{item}' because it's already an explicit exclusion." if @exclusions[key] and @exclusions[key].include? item


  @inclusions[key] << item
  @all_targets = false
  @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)

    the name of the item being added

Raises:



415
416
417
418
419
420
421
422
423
424
425
426
427
428
# File 'lib/jss/api_object/scopable/scope.rb', line 415

def add_limitation (key, item)
  raise JSS::InvalidDataError, "Limitation key must be one of :#{LIMITATIONS.join(', :')}" unless LIMITATIONS.include? key
  raise JSS::InvalidDataError, "Item must be a #{key} name." unless item.kind_of? String

  return nil if @limitations[key] and @limitations[key].include? item

  ### check the name
  raise JSS::NoSuchItemError, "No existing #{key} with name '#{item}'" unless check_name key, item
  raise JSS::AlreadyExistsError, "Can't set #{key} limitation for '#{name}' because it's already an explicit exclusion." if @exclusions[key] and @exclusions[key].include? item


  @limitations[key] << item
  @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?



246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/jss/api_object/scopable/scope.rb', line 246

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

#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)

    the name of the item being removed

Raises:



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

def remove_exclusion (key, item)
  raise JSS::InvalidDataError, "Exclusion key must be one of :#{@exclusion_keys.join(', :')}" unless @exclusion_keys.include? key
  raise JSS::InvalidDataError, "Item must be a #{key} name." unless item.kind_of? String

  return nil unless @exclusions[key] and @exclusions[key].include? item

  @exclusions[key] -= [item]
  @container.should_update if @container
end

#remove_inclusion(key, item) ⇒ void

This method returns an undefined value.

Remove a single item for this scope.

Examples:

remove_inclusion(:computer, "mantis")

Parameters:

  • key (Symbol)

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

  • item (String)

    the name of the item being removed

Raises:



345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/jss/api_object/scopable/scope.rb', line 345

def remove_inclusion (key, item)
  raise JSS::InvalidDataError, "Inclusion key must be one of :#{@inclusion_keys.join(', :')}" unless @inclusion_keys.include? key
  raise JSS::InvalidDataError, "Item must be a #{key} name." unless item.kind_of? String

  return nil unless @inclusions[key] and @inclusions[key].include? item

  @inclusions[key] -= [item]

  # if ALL the @inclusion keys are empty, then set all targets to true.
  @all_targets =  @inclusions.values.reject{|a| a.nil? or a.empty?}.empty?

  @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)

    the name of the item being removed

Raises:



444
445
446
447
448
449
450
451
452
# File 'lib/jss/api_object/scopable/scope.rb', line 444

def remove_limitation( key, item)
  raise JSS::InvalidDataError, "Limitation key must be one of :#{LIMITATIONS.join(', :')}" unless LIMITATIONS.include? key
  raise JSS::InvalidDataError, "Item must be a #{key} name." unless item.kind_of? String

  return nil unless @limitations[key] and @limitations[key].include? item

  @limitations[key] -= [item]
  @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)


556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
# File 'lib/jss/api_object/scopable/scope.rb', line 556

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

  @inclusions.each do |klass,list|
    list_as_hash = list.map{|i| {:name => i} }
    scope << SCOPING_CLASSES[klass].xml_list( list_as_hash, :name)
  end

  limitations = scope.add_element('limitations')
  @limitations.each do |klass,list|
    list_as_hash = list.map{|i| {:name => i} }
    limitations << SCOPING_CLASSES[klass].xml_list( list_as_hash, :name)
  end

  exclusions = scope.add_element('exclusions')
  @exclusions.each do |klass,list|
    list_as_hash = list.map{|i| {:name => i} }
    exclusions << SCOPING_CLASSES[klass].xml_list( list_as_hash, :name)
  end
  return 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 names of the items being added

Raises:



471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
# File 'lib/jss/api_object/scopable/scope.rb', line 471

def set_exclusion (key, list)
  raise JSS::InvalidDataError, "Exclusion key must be one of :#{@exclusion_keys.join(', :')}" unless @exclusion_keys.include? key
  raise JSS::InvalidDataError, "List must be an Array of #{key} names, it may be empty." unless list.kind_of? Array
  return nil if list.sort == @exclusions[key].sort

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

  ### check the names
  list.each do |name|
    raise JSS::NoSuchItemError, "No existing #{key} with name '#{name}'" unless check_name key, name
    case key
      when *@inclusion_keys
        raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{name}' because it's already explicitly included." if  @inclusions[key] and @inclusions[key].include? name
      when *LIMITATIONS
        raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{name}' because it's already an explicit limitation." if @limitations[key] and @limitations[key].include? name
    end

  end # each

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

#set_inclusion(key, list) ⇒ void

This method returns an undefined value.

Replace a list of item names for inclusion 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.

Examples:

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

Parameters:

  • key (Symbol)

    the key from #SCOPING_CLASSES for the kind of items being included, :computer, :building, etc…

  • list (Array)

    the names of the items being added

Raises:



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/jss/api_object/scopable/scope.rb', line 276

def set_inclusion(key, list)
  raise JSS::InvalidDataError, "Inclusion key must be one of :#{@inclusion_keys.join(', :')}" unless @inclusion_keys.include? key
  raise JSS::InvalidDataError, "List must be an Array of #{key} names, it may be empty." unless list.kind_of? Array

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

  # emptying the list?
  if list.empty?
    @inclusion[key] = list
    # if ALL the @inclusion keys are empty, then set all targets to true.
    @all_targets =  @inclusions.values.reject{|a| a.nil? or a.empty?}.empty?
    @container.should_update if @container
    return list
  end

  ### check the names
  list.each do |name|
    raise JSS::NoSuchItemError, "No existing #{key} with name '#{name}'" unless check_name key, name
    raise JSS::AlreadyExistsError, "Can't set #{key} scope to '#{name}' because it's already an explicit exclusion." if @exclusions[key] and @exclusions[key].include? name
  end # each

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

#set_limitation(key, list) ⇒ void

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','bar'])

Parameters:

  • key (Symbol)

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

  • list (Array)

    the names of the items being set as limitations

Raises:



377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/jss/api_object/scopable/scope.rb', line 377

def set_limitation (key, list)
  raise JSS::InvalidDataError, "Limitation key must be one of :#{LIMITATIONS.join(', :')}" unless LIMITATIONS.include? key
  raise JSS::InvalidDataError, "List must be an Array of #{key} names, it may be empty." unless list.kind_of? Array
  return nil if list.sort == @limitations[key].sort

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

  ### check the names
  list.each do |name|
    raise JSS::NoSuchItemError, "No existing #{key} with name '#{name}'" unless check_name key,  name
    raise JSS::AlreadyExistsError, "Can't set #{key} limitation for '#{name}' because it's already an explicit exclusion." if @exclusions[key] and @exclusions[key].include? name
  end # each

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