ruby-jss 0.8.2 → 0.9.0.b1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of ruby-jss might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGES.md +37 -1
- data/README.md +15 -15
- data/bin/cgrouper +1 -1
- data/bin/netseg-update +1 -1
- data/lib/jss/api_connection.rb +307 -25
- data/lib/jss/api_object.rb +123 -54
- data/lib/jss/api_object/account.rb +6 -1
- data/lib/jss/api_object/advanced_search.rb +12 -12
- data/lib/jss/api_object/computer.rb +570 -141
- data/lib/jss/api_object/computer_invitation.rb +9 -0
- data/lib/jss/api_object/creatable.rb +1 -1
- data/lib/jss/api_object/distribution_point.rb +1 -1
- data/lib/jss/api_object/extension_attribute.rb +6 -6
- data/lib/jss/api_object/group.rb +1 -1
- data/lib/jss/api_object/matchable.rb +1 -1
- data/lib/jss/api_object/mobile_device.rb +690 -304
- data/lib/jss/api_object/mobile_device_application.rb +11 -1
- data/lib/jss/api_object/package.rb +1 -1
- data/lib/jss/api_object/policy.rb +206 -2
- data/lib/jss/api_object/restricted_software.rb +126 -42
- data/lib/jss/api_object/scopable/scope.rb +20 -21
- data/lib/jss/api_object/self_servable.rb +9 -6
- data/lib/jss/api_object/updatable.rb +1 -1
- data/lib/jss/api_object/uploadable.rb +1 -1
- data/lib/jss/server.rb +57 -56
- data/lib/jss/utility.rb +4 -0
- data/lib/jss/version.rb +1 -1
- metadata +4 -24
data/lib/jss/api_object.rb
CHANGED
@@ -108,24 +108,18 @@ module JSS
|
|
108
108
|
# *NOTE* Some API objects have data broken into subsections, in which case the
|
109
109
|
# VALID_DATA_KEYS are expected in the section :general.
|
110
110
|
#
|
111
|
+
#
|
112
|
+
# === Optional Constants
|
113
|
+
#
|
114
|
+
# ==== OTHER_LOOKUP_KEYS = [Hash{Symbol=>Hash}] Every object can be looked up by
|
115
|
+
# :id and :name, but some have other uniq identifiers that can also be used,
|
116
|
+
# e.g. :serial_number, :mac_address, and so on. This Hash, if defined,
|
117
|
+
# speficies those other keys for the subclass
|
118
|
+
# For more details about this hash, see {APIObject::DEFAULT_LOOKUP_KEYS},
|
119
|
+
# {APIObject.fetch}, and {APIObject#lookup_object_data}
|
120
|
+
#
|
111
121
|
class APIObject
|
112
122
|
|
113
|
-
# Mix-Ins
|
114
|
-
#####################################
|
115
|
-
|
116
|
-
# Class Variables
|
117
|
-
#####################################
|
118
|
-
|
119
|
-
# This Hash holds the most recent API query for a list of all items in any subclass,
|
120
|
-
# keyed by the subclass's RSRC_LIST_KEY. See the self.all class method.
|
121
|
-
#
|
122
|
-
# When the .all method is called without an argument, and this hash has
|
123
|
-
# a matching value, the value is returned, rather than requerying the
|
124
|
-
# API. The first time a class calls .all, or whnever refresh is
|
125
|
-
# not false, the API is queried and the value in this hash is updated.
|
126
|
-
#
|
127
|
-
@@all_items = {}
|
128
|
-
|
129
123
|
# Class Methods
|
130
124
|
#####################################
|
131
125
|
|
@@ -148,7 +142,7 @@ module JSS
|
|
148
142
|
# class methods for accessing those other values as mapped Arrays,
|
149
143
|
# e.g. JSS::Computer.all_udids
|
150
144
|
#
|
151
|
-
# The results of the first query for each subclass is stored in
|
145
|
+
# The results of the first query for each subclass is stored in JSS.api.object_list_cache
|
152
146
|
# and returned at every future call, so as to not requery the server every time.
|
153
147
|
#
|
154
148
|
# To force requerying to get updated data, provided a non-false argument.
|
@@ -161,9 +155,9 @@ module JSS
|
|
161
155
|
#
|
162
156
|
def self.all(refresh = false)
|
163
157
|
raise JSS::UnsupportedError, '.all can only be called on subclasses of JSS::APIObject' if self == JSS::APIObject
|
164
|
-
|
165
|
-
return
|
166
|
-
|
158
|
+
JSS.api.object_list_cache[self::RSRC_LIST_KEY] = nil if refresh
|
159
|
+
return JSS.api.object_list_cache[self::RSRC_LIST_KEY] if JSS.api.object_list_cache[self::RSRC_LIST_KEY]
|
160
|
+
JSS.api.object_list_cache[self::RSRC_LIST_KEY] = JSS.api.get_rsrc(self::RSRC_BASE)[self::RSRC_LIST_KEY]
|
167
161
|
end
|
168
162
|
|
169
163
|
# Returns an Array of the JSS id numbers of all the members
|
@@ -174,7 +168,7 @@ module JSS
|
|
174
168
|
#
|
175
169
|
# @param refresh[Boolean] should the data be re-queried from the API?
|
176
170
|
#
|
177
|
-
# @return [Array<Integer>] the ids of all
|
171
|
+
# @return [Array<Integer>] the ids of all it1ems of this subclass in the JSS
|
178
172
|
#
|
179
173
|
def self.all_ids(refresh = false)
|
180
174
|
all(refresh).map { |i| i[:id] }
|
@@ -238,9 +232,9 @@ module JSS
|
|
238
232
|
# @return [Hash{Integer => Object}] the objects requested
|
239
233
|
def self.all_objects(refresh = false)
|
240
234
|
objects_key = "#{self::RSRC_LIST_KEY}_objects".to_sym
|
241
|
-
|
242
|
-
return
|
243
|
-
|
235
|
+
JSS.api.object_list_cache[objects_key] = nil if refresh
|
236
|
+
return JSS.api.object_list_cache[objects_key] if JSS.api.object_list_cache[objects_key]
|
237
|
+
JSS.api.object_list_cache[objects_key] = all(refresh).map { |o| new id: o[:id] }
|
244
238
|
end
|
245
239
|
|
246
240
|
# Return true or false if an object of this subclass
|
@@ -368,6 +362,44 @@ module JSS
|
|
368
362
|
end
|
369
363
|
end
|
370
364
|
|
365
|
+
# What are all the lookup keys available for this class?
|
366
|
+
#
|
367
|
+
# @return [Array<Symbol>] the DEFAULT_LOOKUP_KEYS plus any OTHER_LOOKUP_KEYS
|
368
|
+
# defined for this class
|
369
|
+
#
|
370
|
+
def self.lookup_keys
|
371
|
+
return DEFAULT_LOOKUP_KEYS.keys unless defined? self::OTHER_LOOKUP_KEYS
|
372
|
+
DEFAULT_LOOKUP_KEYS.keys + self::OTHER_LOOKUP_KEYS.keys
|
373
|
+
end
|
374
|
+
|
375
|
+
# @return [Hash] the available lookup keys mapped to the appropriate
|
376
|
+
# resource key for building a REST url to retrieve an object.
|
377
|
+
#
|
378
|
+
def self.rsrc_keys
|
379
|
+
hash = {}
|
380
|
+
all_keys = if defined?(self::OTHER_LOOKUP_KEYS)
|
381
|
+
DEFAULT_LOOKUP_KEYS.merge self::OTHER_LOOKUP_KEYS
|
382
|
+
else
|
383
|
+
DEFAULT_LOOKUP_KEYS
|
384
|
+
end
|
385
|
+
all_keys.each { |key, deets| hash[key] = deets[:rsrc_key]}
|
386
|
+
hash
|
387
|
+
end
|
388
|
+
|
389
|
+
# @return [Hash] the available lookup keys mapped to the appropriate
|
390
|
+
# list class method (e.g. id: :all_ids )
|
391
|
+
#
|
392
|
+
def self.lookup_key_list_methods
|
393
|
+
hash = {}
|
394
|
+
all_keys = if defined?(self::OTHER_LOOKUP_KEYS)
|
395
|
+
DEFAULT_LOOKUP_KEYS.merge self::OTHER_LOOKUP_KEYS
|
396
|
+
else
|
397
|
+
DEFAULT_LOOKUP_KEYS
|
398
|
+
end
|
399
|
+
all_keys.each { |key, deets| hash[key] = deets[:list]}
|
400
|
+
hash
|
401
|
+
end
|
402
|
+
|
371
403
|
# Retrieve an object from the API.
|
372
404
|
#
|
373
405
|
# This is the preferred way to retrieve existing objects from the JSS.
|
@@ -382,11 +414,26 @@ module JSS
|
|
382
414
|
#
|
383
415
|
# @return [APIObject] The ruby-instance of a JSS object
|
384
416
|
#
|
385
|
-
def self.fetch(
|
417
|
+
def self.fetch(arg)
|
386
418
|
raise JSS::UnsupportedError, 'JSS::APIObject cannot be instantiated' if self.class == JSS::APIObject
|
387
|
-
|
388
|
-
|
389
|
-
|
419
|
+
|
420
|
+
# if given a hash (or a colletion of named params)
|
421
|
+
# pass to .new
|
422
|
+
if arg.is_a? Hash
|
423
|
+
raise ArgumentError, 'Use .create to create new JSS objects' if arg[:id] == :new
|
424
|
+
return new arg
|
425
|
+
end
|
426
|
+
|
427
|
+
# loop thru the lookup_key list methods for this class
|
428
|
+
# and if it's result includes the desired value,
|
429
|
+
# the pass they key and arg to .new
|
430
|
+
lookup_key_list_methods.each do |key, method_name|
|
431
|
+
return new({key => arg}) if self.send(method_name).include? arg
|
432
|
+
end # each key
|
433
|
+
|
434
|
+
# if we're here, we couldn't find a matching object
|
435
|
+
raise NoSuchItemError, "No #{self::RSRC_OBJECT_KEY} found matching '#{arg}'"
|
436
|
+
end # fetch
|
390
437
|
|
391
438
|
# Make a ruby instance of a not-yet-existing APIObject.
|
392
439
|
#
|
@@ -404,14 +451,13 @@ module JSS
|
|
404
451
|
#
|
405
452
|
# @return [APIObject] The un-created ruby-instance of a JSS object
|
406
453
|
#
|
407
|
-
def self.make(
|
454
|
+
def self.make(args = {})
|
408
455
|
raise JSS::UnsupportedError, 'JSS::APIObject cannot be instantiated' if self.class == JSS::APIObject
|
409
456
|
raise ArgumentError, "Use '#{self.class}.fetch id: xx' to retrieve existing JSS objects" if args[:id]
|
410
457
|
args[:id] = :new
|
411
458
|
new args
|
412
459
|
end
|
413
460
|
|
414
|
-
|
415
461
|
### Class Constants
|
416
462
|
#####################################
|
417
463
|
|
@@ -420,13 +466,37 @@ module JSS
|
|
420
466
|
#
|
421
467
|
REQUIRED_DATA_KEYS = [:id, :name].freeze
|
422
468
|
|
423
|
-
# By
|
424
|
-
#
|
425
|
-
# as the second argument to super(initialize)
|
426
|
-
# The keys must be Symbols that match the keyname in the resource url.
|
427
|
-
# e.g. :serialnumber for JSSResource/computers/serialnumber/xxxxx
|
469
|
+
# All API objects have an id and a name. As such By these keys are available
|
470
|
+
# for object lookups.
|
428
471
|
#
|
429
|
-
|
472
|
+
# Others can be defined by subclasses in their OTHER_LOOKUP_KEYS constant
|
473
|
+
# which has the same format, described here:
|
474
|
+
#
|
475
|
+
# The merged Hashes DEFAULT_LOOKUP_KEYS and OTHER_LOOKUP_KEYS
|
476
|
+
# define what unique identifiers can be passed as parameters to the
|
477
|
+
# fetch method for retrieving an object from the API.
|
478
|
+
# They also define the class methods that return a list (Array) of all such
|
479
|
+
# identifiers for the class (e.g. the :all_ids class method returns an array
|
480
|
+
# of all id's for an APIObject subclass)
|
481
|
+
#
|
482
|
+
# Since there's often a discrepency between the name of the identifier as
|
483
|
+
# an attribute (e.g. serial_number) and the REST resource key for
|
484
|
+
# retrieving that object (e.g. ../computers/serialnumber/xxxxx) this hash
|
485
|
+
# also explicitly provides the REST resource key for a given lookup key, so
|
486
|
+
# e.g. both serialnumber and serial_number can be used, and both will have
|
487
|
+
# the resource key 'serialnumber' and the list method ':all_serial_numbers'
|
488
|
+
#
|
489
|
+
# Here's how the Hash is structured, using serialnumber as an example:
|
490
|
+
#
|
491
|
+
# LOOKUP_KEYS = {
|
492
|
+
# serialnumber: {rsrc_key: :serialnumber, list: :all_serial_numbers},
|
493
|
+
# serial_number: {rsrc_key: :serialnumber, list: :all_serial_numbers}
|
494
|
+
# }
|
495
|
+
#
|
496
|
+
DEFAULT_LOOKUP_KEYS = {
|
497
|
+
id: {rsrc_key: :id, list: :all_ids},
|
498
|
+
name: {rsrc_key: :name, list: :all_names}
|
499
|
+
}.freeze
|
430
500
|
|
431
501
|
# Attributes
|
432
502
|
#####################################
|
@@ -461,20 +531,13 @@ module JSS
|
|
461
531
|
# @option args :name[String] the name to look up
|
462
532
|
#
|
463
533
|
# @option args :data[Hash] the JSON output of a separate {JSS::APIConnection} query
|
534
|
+
# NOTE: This arg is deprecated and will be removed in a future release.
|
464
535
|
#
|
465
|
-
# @param other_lookup_keys[Array<Symbol>] Hash keys other than :id and :name, by which an API
|
466
|
-
# lookup may be performed.
|
467
536
|
#
|
468
|
-
def initialize(args = {}
|
469
|
-
args[:other_lookup_keys] ||= other_lookup_keys
|
537
|
+
def initialize(args = {})
|
470
538
|
|
471
539
|
raise JSS::UnsupportedError, 'JSS::APIObject cannot be instantiated' if self.class == JSS::APIObject
|
472
540
|
|
473
|
-
### what lookup key are we using, if any?
|
474
|
-
lookup_keys = DEFAULT_LOOKUP_KEYS
|
475
|
-
lookup_keys += self.class::OTHER_LOOKUP_KEYS if defined? self.class::OTHER_LOOKUP_KEYS
|
476
|
-
lookup_key = (lookup_keys & args.keys)[0]
|
477
|
-
|
478
541
|
####### Previously looked-up JSON data
|
479
542
|
# DEPRECATED: pre-lookedup data is never used
|
480
543
|
# and support for it will be going away.
|
@@ -486,9 +549,9 @@ module JSS
|
|
486
549
|
|
487
550
|
###### Make a new one in the JSS, but only if we've included the Creatable module
|
488
551
|
elsif args[:id] == :new
|
489
|
-
|
490
552
|
validate_init_for_creation(args)
|
491
553
|
setup_object_for_creation(args)
|
554
|
+
|
492
555
|
return
|
493
556
|
|
494
557
|
###### Look up the data via the API
|
@@ -496,6 +559,7 @@ module JSS
|
|
496
559
|
@init_data = look_up_object_data(args)
|
497
560
|
end ## end arg parsing
|
498
561
|
|
562
|
+
|
499
563
|
parse_init_data
|
500
564
|
@need_to_update = false
|
501
565
|
end # init
|
@@ -596,7 +660,7 @@ module JSS
|
|
596
660
|
#
|
597
661
|
def delete
|
598
662
|
return nil unless @in_jss
|
599
|
-
JSS
|
663
|
+
JSS.api.delete_rsrc @rest_rsrc
|
600
664
|
@rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name}"
|
601
665
|
@id = nil
|
602
666
|
@in_jss = false
|
@@ -668,16 +732,20 @@ module JSS
|
|
668
732
|
#
|
669
733
|
def look_up_object_data(args)
|
670
734
|
# what lookup key are we using?
|
671
|
-
|
672
|
-
lookup_key = (
|
735
|
+
lookup_keys = self.class.lookup_keys
|
736
|
+
lookup_key = (self.class.lookup_keys & args.keys)[0]
|
737
|
+
raise JSS::MissingDataError, "Args must include a lookup key, one of: :#{lookup_keys.join(', :')}" unless lookup_key
|
738
|
+
rsrc_key = self.class.rsrc_keys[lookup_key]
|
673
739
|
|
674
|
-
|
740
|
+
rsrc = "#{self.class::RSRC_BASE}/#{rsrc_key}/#{args[lookup_key]}"
|
675
741
|
|
676
|
-
|
742
|
+
# if needed, a non-standard object key can be passed by a subclass.
|
743
|
+
# e.g. User when loookup is by email.
|
744
|
+
rsrc_object_key = args[:rsrc_object_key] ? args[:rsrc_object_key] : self.class::RSRC_OBJECT_KEY
|
677
745
|
|
678
|
-
return JSS
|
746
|
+
return JSS.api.get_rsrc(rsrc)[rsrc_object_key]
|
679
747
|
rescue RestClient::ResourceNotFound
|
680
|
-
raise NoSuchItemError, "No #{self.class::RSRC_OBJECT_KEY} found matching: #{
|
748
|
+
raise NoSuchItemError, "No #{self.class::RSRC_OBJECT_KEY} found matching: #{rsrc_key}/#{args[lookup_key]}"
|
681
749
|
end
|
682
750
|
|
683
751
|
# Start examining the @init_data recieved from the API
|
@@ -685,6 +753,7 @@ module JSS
|
|
685
753
|
# @return [void]
|
686
754
|
#
|
687
755
|
def parse_init_data
|
756
|
+
@init_data ||= {}
|
688
757
|
# set empty strings to nil
|
689
758
|
@init_data.jss_nillify! '', :recurse
|
690
759
|
|
@@ -802,7 +871,7 @@ module JSS
|
|
802
871
|
def setup_object_for_creation(args)
|
803
872
|
# NOTE: subclasses may want to pre-populate more keys in @init_data when :id == :new
|
804
873
|
# then parse them into attributes later.
|
805
|
-
@init_data =
|
874
|
+
@init_data = args
|
806
875
|
@name = args[:name]
|
807
876
|
@in_jss = false
|
808
877
|
@rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name}"
|
@@ -61,7 +61,12 @@ module JSS
|
|
61
61
|
RSRC_OBJECT_KEY = :account
|
62
62
|
|
63
63
|
# these keys, as well as :id and :name, can be used to look up objects of this class in the JSS
|
64
|
-
OTHER_LOOKUP_KEYS =
|
64
|
+
OTHER_LOOKUP_KEYS = {
|
65
|
+
userid: {rsrc_key: :userid, list: :all_user_ids},
|
66
|
+
username: {rsrc_key: :username, list: :all_user_names},
|
67
|
+
groupid: {rsrc_key: :groupid, list: :all_group_ids},
|
68
|
+
groupname: {rsrc_key: :groupname, list: :all_group_names}
|
69
|
+
}.freeze
|
65
70
|
|
66
71
|
# Class Methods
|
67
72
|
#####################################
|
@@ -167,11 +167,11 @@ module JSS
|
|
167
167
|
raise JSS::InvalidDataError, 'JSS::Criteriable::Criteria instance required' unless @criteria.is_a? JSS::Criteriable::Criteria
|
168
168
|
raise JSS::InvalidDataError, 'display_fields must be an Array.' unless @display_fields.is_a? Array
|
169
169
|
|
170
|
-
orig_timeout = JSS
|
171
|
-
JSS
|
170
|
+
orig_timeout = JSS.api_connection.cnx.options[:timeout]
|
171
|
+
JSS.api_connection.timeout = 1800
|
172
172
|
super()
|
173
173
|
requery_search_results if get_results
|
174
|
-
JSS
|
174
|
+
JSS.api_connection.timeout = orig_timeout
|
175
175
|
|
176
176
|
@id # remember to return the id
|
177
177
|
end
|
@@ -185,11 +185,11 @@ module JSS
|
|
185
185
|
# @return [Integer] the id of the updated search
|
186
186
|
#
|
187
187
|
def update(get_results = false)
|
188
|
-
orig_timeout = JSS
|
189
|
-
JSS
|
188
|
+
orig_timeout = JSS.api_connection.cnx.options[:timeout]
|
189
|
+
JSS.api_connection.timeout = 1800
|
190
190
|
super()
|
191
191
|
requery_search_results if get_results
|
192
|
-
JSS
|
192
|
+
JSS.api_connection.timeout = orig_timeout
|
193
193
|
|
194
194
|
@id # remember to return the id
|
195
195
|
end
|
@@ -201,17 +201,17 @@ module JSS
|
|
201
201
|
# @return [Array<Hash>] the new search results
|
202
202
|
#
|
203
203
|
def requery_search_results
|
204
|
-
orig_open_timeout = JSS
|
205
|
-
orig_timeout = JSS
|
206
|
-
JSS
|
207
|
-
JSS
|
204
|
+
orig_open_timeout = JSS.api_connection.cnx.options[:open_timeout]
|
205
|
+
orig_timeout = JSS.api_connection.cnx.options[:timeout]
|
206
|
+
JSS.api_connection.timeout = 1800
|
207
|
+
JSS.api_connection.open_timeout = 1800
|
208
208
|
begin
|
209
209
|
requery = self.class.new(id: @id)
|
210
210
|
@search_results = requery.search_results
|
211
211
|
@result_display_keys = requery.result_display_keys
|
212
212
|
ensure
|
213
|
-
JSS
|
214
|
-
JSS
|
213
|
+
JSS.api_connection.timeout = orig_timeout
|
214
|
+
JSS.api_connection.open_timeout = orig_open_timeout
|
215
215
|
end
|
216
216
|
end
|
217
217
|
|
@@ -26,24 +26,14 @@
|
|
26
26
|
###
|
27
27
|
module JSS
|
28
28
|
|
29
|
-
# Module Constants
|
30
|
-
#####################################
|
31
|
-
|
32
|
-
# Module Variables
|
33
|
-
#####################################
|
34
|
-
|
35
|
-
# Module Methods
|
36
|
-
#####################################
|
37
|
-
|
38
29
|
# This class represents a Computer in the JSS.
|
39
30
|
#
|
40
|
-
# ===Adding Computers to the JSS
|
31
|
+
# === Adding Computers to the JSS
|
41
32
|
#
|
42
|
-
#
|
43
|
-
#
|
33
|
+
# At the moment, this class cannot be used to add new Computers to the JSS.
|
34
|
+
# Please use other methods (like the Recon App or QuickAdd package)
|
44
35
|
#
|
45
|
-
#
|
46
|
-
# ===Editing values
|
36
|
+
# === Editing values
|
47
37
|
#
|
48
38
|
# Any data that arrives in the JSS via an "inventory update"
|
49
39
|
# (a.k.a. 'recon') cannot be modified through this class, or the API.
|
@@ -66,27 +56,91 @@ module JSS
|
|
66
56
|
# After making any changes, you must call #update to send those
|
67
57
|
# changes to the server.
|
68
58
|
#
|
69
|
-
# ---
|
70
59
|
# === MDM Commands
|
71
60
|
#
|
72
|
-
#
|
73
|
-
#
|
61
|
+
# The following methods can be used to send an APNS command to the
|
62
|
+
# computer represented by an instance of JSS::Computer, equivalent to
|
63
|
+
# clicking one of the buttons on the Management Commands section of the
|
64
|
+
# Management tab of the Computer details page in the JSS UI.
|
65
|
+
#
|
66
|
+
# - {#blank_push} (aliases blank, noop, send_blank_push)
|
67
|
+
# - {#device_lock} (aliases lock, lock_device)
|
68
|
+
# - {#erase_device} (aliases wipe)
|
69
|
+
# - {#remove_mdm_profile}
|
70
|
+
#
|
71
|
+
# To send an MDM command without making a Computer instance, use the class
|
72
|
+
# {JSS::Computer.send_mdm_command} which can take multiple computer
|
73
|
+
# identifiers at once.
|
74
|
+
#
|
75
|
+
# NOTE: the poorly named 'UnmanageDevice' command via the API is implemented
|
76
|
+
# as the {#remove_mdm_profile} method (which is its name in the webUI).
|
77
|
+
# Calling that method will NOT unmanage the machine from the JSS's point
|
78
|
+
# of view, it will just remove the mdm management profile from the machine
|
79
|
+
# and all configuration profiles that were installed via the JSS. Those
|
80
|
+
# profiles may be re-installed automatically later if the computer is still in
|
81
|
+
# scope for them
|
82
|
+
#
|
83
|
+
# The {#make_unmanaged} method also removes the mdm profile, but actually
|
84
|
+
# does make the machine unmanged by the JSS, setting the management acct to
|
85
|
+
# nil, and requring re-enrollment.
|
86
|
+
#
|
87
|
+
# === Computer History
|
88
|
+
#
|
89
|
+
# Computer instances can now retrieve their management history from the JSS.
|
90
|
+
#
|
91
|
+
# The full history data is available from the {#history} method, but beware that
|
92
|
+
# it is very large.
|
93
|
+
#
|
94
|
+
# Subsets of that history have their own methods, which are faster and only retrieve
|
95
|
+
# the subset requested. See {#usage_logs}, {#audits}, {#policy_los},
|
96
|
+
# {#completed_policies}, {#failed_polices}, {#casper_remote_logs},
|
97
|
+
# {#screen_sharing_logs}, {#casper_imaging_logs}, {#commands},
|
98
|
+
# {#user_location_history},and {#app_store_app_history}
|
99
|
+
#
|
100
|
+
# When any of the history methods is used the first time, the data is read
|
101
|
+
# from the API and cached internally, and that data is
|
102
|
+
# used for all future calls.. To re-read the data from the API and re-cache it,
|
103
|
+
# provide any non-false parameter to the subset methods , or `refresh: true`
|
104
|
+
# to the main {#history} method.
|
105
|
+
#
|
106
|
+
# === Appication Usage History
|
107
|
+
#
|
108
|
+
# Computer Instances now have access to their Application Usage history
|
109
|
+
# via the {#application_usage} method.
|
110
|
+
# Call the method with a start-date value (either a String or a Time object)
|
111
|
+
# and an optional end-date value. If you omite the end-date, the start-date
|
112
|
+
# is used and you'll see usage for just that day.
|
113
|
+
#
|
114
|
+
# See {#application_usage} for details about the data returned.
|
74
115
|
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
116
|
+
# NOTE: your JSS must be gathering Appication Usage data in order
|
117
|
+
# for any data to be returned, and the usage history will only go back as
|
118
|
+
# far as your setting for flushing of Application Usage Logs.
|
78
119
|
#
|
79
|
-
#
|
80
|
-
# - #blank_push (aliases blank, noop, send_blank_push)
|
81
|
-
# - #device_lock (aliases lock, lock_device)
|
82
|
-
# - #erase_device (aliases wipe)
|
120
|
+
# === Management Data
|
83
121
|
#
|
84
|
-
#
|
122
|
+
# The computers 'manamgement data', as presented on the 'Management' tab of
|
123
|
+
# the computer's detail page in the JSS web UI, is available from the
|
124
|
+
# {#management_data} method. That method may return a large dataset,
|
125
|
+
# unless a subset is requested.
|
85
126
|
#
|
86
|
-
#
|
127
|
+
# Subsets of management data have their own methods, which are faster and
|
128
|
+
# only retrieve the subset requested. See {#smart_groups}, {#static_groups},
|
129
|
+
# {#policies}, {#configuration_profiles}, {#ebooks}, {#app_store_apps},
|
130
|
+
# {#restricted_software}, and {#patch_titles}
|
87
131
|
#
|
88
|
-
#
|
89
|
-
#
|
132
|
+
# The subset methods can take an 'only:' parameter, which is a symbol specifying
|
133
|
+
# the value you care to see. For example {#smart_groups} returns an array
|
134
|
+
# of hashes, one for each smart_group the computer is in. Those hashes
|
135
|
+
# have two keys, :name, and :id. However if you only want an array of
|
136
|
+
# names, you can call `smart_groups only: :name`
|
137
|
+
#
|
138
|
+
# When any of the manamgement data methods are used the first time, the data
|
139
|
+
# is read from the API and cached internally, the cache is then
|
140
|
+
# used for all future calls. To re-read the data from the API and re-cache it,
|
141
|
+
# provide `refresh: true` to any of the manamgement data methods.
|
142
|
+
#
|
143
|
+
# === Other Methods
|
90
144
|
#
|
91
145
|
# - {#set_management_to} change the management acct and passwd for this computer, aliased to #make_managed
|
92
146
|
# - requires calling #update to push changes to the server
|
@@ -118,14 +172,185 @@ module JSS
|
|
118
172
|
|
119
173
|
extend JSS::Matchable
|
120
174
|
|
121
|
-
# Class
|
175
|
+
# Class Constants
|
122
176
|
#####################################
|
123
177
|
|
124
|
-
|
178
|
+
# The base for REST resources of this class
|
179
|
+
RSRC_BASE = 'computers'.freeze
|
180
|
+
|
181
|
+
# The (temporary?) list-resource
|
182
|
+
LIST_RSRC = "#{RSRC_BASE}/subset/basic".freeze
|
183
|
+
|
184
|
+
# the hash key used for the JSON list output of all objects in the JSS
|
185
|
+
RSRC_LIST_KEY = :computers
|
186
|
+
|
187
|
+
# The hash key used for the JSON object output.
|
188
|
+
# It's also used in various error messages
|
189
|
+
RSRC_OBJECT_KEY = :computer
|
190
|
+
|
191
|
+
# these keys, as well as :id and :name, are present in valid API JSON data for this class
|
192
|
+
# DEPRECATED, with be removed in a future release.
|
193
|
+
VALID_DATA_KEYS = %i[sus distribution_point alt_mac_address].freeze
|
194
|
+
|
195
|
+
# these keys, as well as :id and :name, can be used to look up objects of this class in the JSS
|
196
|
+
OTHER_LOOKUP_KEYS = {
|
197
|
+
udid: { rsrc_key: :udid, list: :all_udids },
|
198
|
+
serialnumber: { rsrc_key: :serialnumber, list: :all_serial_numbers },
|
199
|
+
serial_number: { rsrc_key: :serialnumber, list: :all_serial_numbers },
|
200
|
+
macaddress: { rsrc_key: :macaddress, list: :all_mac_addresses },
|
201
|
+
mac_address: { rsrc_key: :macaddress, list: :all_mac_addresses }
|
202
|
+
}.freeze
|
203
|
+
|
204
|
+
# This class lets us seach for computers
|
205
|
+
SEARCH_CLASS = JSS::AdvancedComputerSearch
|
206
|
+
|
207
|
+
# This is the class for relevant Extension Attributes
|
208
|
+
EXT_ATTRIB_CLASS = JSS::ComputerExtensionAttribute
|
209
|
+
|
210
|
+
# Boot partitions are noted with the string "(Boot Partition)" at the end
|
211
|
+
BOOT_FLAG = ' (Boot Partition)'.freeze
|
212
|
+
|
213
|
+
# file uploads can send attachments to the JSS using :computers as the sub-resource.
|
214
|
+
UPLOAD_TYPES = { attachment: :computers }.freeze
|
215
|
+
|
216
|
+
# The base REST resource for sending computer MDM commands
|
217
|
+
COMPUTER_MDM_RSRC = 'computercommands/command'.freeze
|
218
|
+
|
219
|
+
# A mapping of Symbols available to the send_mdm_command class method, to
|
220
|
+
# the String commands actuallly sent via the API.
|
221
|
+
COMPUTER_MDM_COMMANDS = {
|
222
|
+
blank_push: 'BlankPush',
|
223
|
+
blankpush: 'BlankPush',
|
224
|
+
send_blank_push: 'BlankPush',
|
225
|
+
blank: 'BlankPush',
|
226
|
+
noop: 'BlankPush',
|
227
|
+
device_lock: 'DeviceLock',
|
228
|
+
devicelock: 'DeviceLock',
|
229
|
+
lock: 'DeviceLock',
|
230
|
+
lock_device: 'DeviceLock',
|
231
|
+
erase_device: 'EraseDevice',
|
232
|
+
erasedevice: 'EraseDevice',
|
233
|
+
erase: 'EraseDevice',
|
234
|
+
wipe: 'EraseDevice',
|
235
|
+
unmanage_device: 'UnmanageDevice',
|
236
|
+
unmanagedevice: 'UnmanageDevice',
|
237
|
+
unmanage: 'UnmanageDevice'
|
238
|
+
}.freeze
|
239
|
+
|
240
|
+
# these MDM commands require a passcode
|
241
|
+
COMPUTER_MDM_COMMANDS_NEEDING_PASSCODE = %w[DeviceLock EraseDevice].freeze
|
242
|
+
|
243
|
+
# The API resource for app usage
|
244
|
+
APPLICATION_USAGE_RSRC = 'computerapplicationusage'.freeze
|
245
|
+
|
246
|
+
# The date format for retrieving usage data
|
247
|
+
APPLICATION_USAGE_DATE_FMT = '%Y-%m-%d'.freeze
|
248
|
+
|
249
|
+
# The classes that can be used with the date format
|
250
|
+
APPLICATION_USAGE_DATE_CLASSES = [Time, DateTime, Date].freeze
|
251
|
+
|
252
|
+
# The top-level hash key of the raw app usage data
|
253
|
+
APPLICATION_USAGE_KEY = :computer_application_usage
|
254
|
+
|
255
|
+
# The API resource for computer_management data
|
256
|
+
MGMT_DATA_RSRC = 'computermanagement'.freeze
|
257
|
+
|
258
|
+
# The top-level hash key of the computer_management data
|
259
|
+
MGMT_DATA_KEY = :computer_management
|
260
|
+
|
261
|
+
# Thes are both the subset names in the resrouce URLS (when
|
262
|
+
# converted to strings) and the second-level hash key of the
|
263
|
+
# returned subset data.
|
264
|
+
MGMT_DATA_SUBSETS = %i[
|
265
|
+
smart_groups
|
266
|
+
static_groups
|
267
|
+
mac_app_store_apps
|
268
|
+
policies
|
269
|
+
ebooks
|
270
|
+
os_x_configuration_profiles
|
271
|
+
restricted_software
|
272
|
+
patch_reporting_software_titles
|
273
|
+
].freeze
|
274
|
+
|
275
|
+
# The API Resource for the computer checkin settings
|
276
|
+
CHECKIN_RSRC = 'computercheckin'.freeze
|
277
|
+
|
278
|
+
# The top-level hash key for the checkin settings
|
279
|
+
CHECKIN_KEY = :computer_check_in
|
280
|
+
|
281
|
+
# The API Resource for the computer inventory collection settings
|
282
|
+
INV_COLLECTION_RSRC = 'computerinventorycollection'.freeze
|
283
|
+
|
284
|
+
# The top-level hash key for the inventory collection settings
|
285
|
+
INV_COLLECTION_KEY = :computer_inventory_collection
|
286
|
+
|
287
|
+
# The API Resource for the computer history data
|
288
|
+
HISTORY_RSRC = 'computerhistory'.freeze
|
289
|
+
|
290
|
+
# The top-level hash key for the history data
|
291
|
+
HISTORY_KEY = :computer_history
|
292
|
+
|
293
|
+
# The keys are both the subset names in the resrouce URLS (when
|
294
|
+
# converted to strings) and the second-level hash key of the
|
295
|
+
# returned subset data.
|
296
|
+
#
|
297
|
+
# The values are the key within each history item that contains the
|
298
|
+
# 'epoch' timestamp, for conver
|
299
|
+
HISTORY_SUBSETS = %i[
|
300
|
+
computer_usage_logs
|
301
|
+
audits
|
302
|
+
policy_logs
|
303
|
+
casper_remote_logs
|
304
|
+
screen_sharing_logs
|
305
|
+
casper_imaging_logs
|
306
|
+
commands
|
307
|
+
user_location
|
308
|
+
mac_app_store_applications
|
309
|
+
].freeze
|
310
|
+
|
311
|
+
# HISTORY_SUBSETS = %i(
|
312
|
+
# computer_usage_logs date_time_epoch
|
313
|
+
# audits
|
314
|
+
# policy_logs date_completed_epoch
|
315
|
+
# casper_remote_logs date_time_epoch
|
316
|
+
# screen_sharing_logs date_time_epoch
|
317
|
+
# casper_imaging_logs
|
318
|
+
# commands completed_epoch
|
319
|
+
# user_location
|
320
|
+
# mac_app_store_applications
|
321
|
+
# ).freeze
|
322
|
+
|
323
|
+
POLICY_STATUS_COMPLETED = 'Completed'.freeze
|
324
|
+
|
325
|
+
POLICY_STATUS_FAILED = 'Failed'.freeze
|
326
|
+
|
327
|
+
POLICY_STATUS_PENDING = 'Pending'.freeze
|
125
328
|
|
126
329
|
# Class Methods
|
127
330
|
#####################################
|
128
331
|
|
332
|
+
# Display the current Computer CheckIn settings in the JSS.
|
333
|
+
# Currently this is read-only in ruby-jss, even tho the API
|
334
|
+
# allows updating.
|
335
|
+
#
|
336
|
+
# @return [Hash] the Computer Checkin Settings from the
|
337
|
+
# currently connected JSS.
|
338
|
+
#
|
339
|
+
def self.checkin_settings
|
340
|
+
JSS.api_connection.get_rsrc(CHECKIN_RSRC)[CHECKIN_KEY]
|
341
|
+
end
|
342
|
+
|
343
|
+
# Display the current Computer Inventory Collection settings in the JSS.
|
344
|
+
# Currently this is read-only in ruby-jss, even tho the API
|
345
|
+
# allows updating.
|
346
|
+
#
|
347
|
+
# @return [Hash] the Computer Inventpry Collection Settings from the
|
348
|
+
# currently connected JSS.
|
349
|
+
#
|
350
|
+
def self.inventory_collection_settings
|
351
|
+
JSS.api_connection.get_rsrc(INV_COLLECTION_RSRC)[INV_COLLECTION_KEY]
|
352
|
+
end
|
353
|
+
|
129
354
|
# A larger set of info about the computers in the JSS.
|
130
355
|
#
|
131
356
|
# Casper 9.4 introduced the API Resource /computers/subset/basic
|
@@ -143,9 +368,9 @@ module JSS
|
|
143
368
|
# @return [Array<Hash{:name=>String, :id=> Integer}>]
|
144
369
|
#
|
145
370
|
def self.all(refresh = false)
|
146
|
-
|
147
|
-
return
|
148
|
-
|
371
|
+
JSS.api.object_list_cache[RSRC_LIST_KEY] = nil if refresh
|
372
|
+
return JSS.api.object_list_cache[RSRC_LIST_KEY] if JSS.api.object_list_cache[RSRC_LIST_KEY]
|
373
|
+
JSS.api.object_list_cache[RSRC_LIST_KEY] = JSS.api_connection.get_rsrc(self::LIST_RSRC)[self::RSRC_LIST_KEY]
|
149
374
|
end
|
150
375
|
|
151
376
|
# @return [Array<String>] all computer serial numbers in the jss
|
@@ -170,7 +395,7 @@ module JSS
|
|
170
395
|
|
171
396
|
# @return [Array<Hash>] all unmanaged computers in the jss
|
172
397
|
def self.all_unmanaged(refresh = false)
|
173
|
-
all(refresh).
|
398
|
+
all(refresh).reject { |d| d[:managed] }
|
174
399
|
end
|
175
400
|
|
176
401
|
# @return [Array<Hash>] all laptop computers in the jss
|
@@ -200,7 +425,7 @@ module JSS
|
|
200
425
|
|
201
426
|
# @return [Array<Hash>] all desktop macs in the jss
|
202
427
|
def self.all_desktops(refresh = false)
|
203
|
-
all(refresh).
|
428
|
+
all(refresh).reject { |d| d[:model] =~ /serve|book/i }
|
204
429
|
end
|
205
430
|
|
206
431
|
# @return [Array<Hash>] all imacs in the jss
|
@@ -218,88 +443,53 @@ module JSS
|
|
218
443
|
all(refresh).select { |d| d[:model] =~ /^macpro/i }
|
219
444
|
end
|
220
445
|
|
221
|
-
# Send an MDM command to
|
222
|
-
#
|
223
|
-
# @param computer[String,Integer] the name or id of the computer to recieve the command
|
224
|
-
# @param command[Symbol] the command to send, one of the keys of COMPUTER_MDM_COMMANDS
|
225
|
-
#
|
226
|
-
# @return [true] if the command was sent
|
227
|
-
#
|
228
|
-
|
229
|
-
# Not functional until I get more docs from JAMF
|
446
|
+
# Send an MDM command to one or more managed computers by id or name
|
230
447
|
#
|
231
|
-
# def self.send_mdm_command(computer,command)
|
232
448
|
#
|
233
|
-
#
|
449
|
+
# @param targets[String,Integer,Array<String,Integer>]
|
450
|
+
# the name or id of the computer to receive the command, or
|
451
|
+
# an array of such names or ids, or a comma-separated string
|
452
|
+
# of them.
|
453
|
+
# @param command[Symbol] the command to send, one of the keys
|
454
|
+
# of COMPUTER_MDM_COMMANDS
|
234
455
|
#
|
235
|
-
#
|
236
|
-
# the_id = nil
|
456
|
+
# @param passcode[String] some commands require a 6-character passcode
|
237
457
|
#
|
238
|
-
#
|
239
|
-
#
|
240
|
-
# else
|
241
|
-
# the_id = self.map_all_ids_to(:name).invert[computer]
|
242
|
-
# end
|
458
|
+
# @return [String] The uuid of the MDM command sent, if applicable
|
459
|
+
# (blank pushes do not generate uuids)
|
243
460
|
#
|
244
|
-
|
245
|
-
|
246
|
-
# response =~ %r{<notification_sent>(.+)</notification_sent>}
|
247
|
-
# return ($1 and $1 == "true")
|
248
|
-
# end
|
249
|
-
# raise JSS::UnmanagedError, "Cannot send command to unknown/unmanaged computer '#{computer}'"
|
250
|
-
# end
|
461
|
+
def self.send_mdm_command(targets, command, passcode = nil)
|
462
|
+
raise JSS::NoSuchItemError, "Unknown command '#{command}'" unless COMPUTER_MDM_COMMANDS.keys.include? command
|
251
463
|
|
252
|
-
|
253
|
-
|
464
|
+
command = COMPUTER_MDM_COMMANDS[command]
|
465
|
+
cmd_rsrc = "#{COMPUTER_MDM_RSRC}/#{command}"
|
254
466
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
# the hash key used for the JSON list output of all objects in the JSS
|
262
|
-
RSRC_LIST_KEY = :computers
|
263
|
-
|
264
|
-
# The hash key used for the JSON object output.
|
265
|
-
# It's also used in various error messages
|
266
|
-
RSRC_OBJECT_KEY = :computer
|
467
|
+
if COMPUTER_MDM_COMMANDS_NEEDING_PASSCODE.include? command
|
468
|
+
unless passcode && passcode.is_a?(String) && passcode.length == 6
|
469
|
+
raise JSS::MissingDataError, "Command '#{command}' requires a 6-character passcode"
|
470
|
+
end
|
471
|
+
cmd_rsrc << "/passcode/#{passcode}"
|
472
|
+
end
|
267
473
|
|
268
|
-
|
269
|
-
# DEPRECATED, with be removed in a future release.
|
270
|
-
VALID_DATA_KEYS = [:sus, :distribution_point, :alt_mac_address].freeze
|
474
|
+
targets = JSS.to_s_and_a(targets.to_s)[:arrayform] unless targets.is_a? Array
|
271
475
|
|
272
|
-
|
273
|
-
|
476
|
+
# make sure its an array of ids
|
477
|
+
targets.map! do |comp|
|
478
|
+
if all_ids.include? comp.to_i
|
479
|
+
comp.to_i
|
480
|
+
elsif all_names.include? comp
|
481
|
+
map_all_ids_to(:name).invert[comp]
|
482
|
+
else
|
483
|
+
raise JSS::NoSuchItemError, "No computer found matching '#{comp}'"
|
484
|
+
end # if
|
485
|
+
end # map!
|
274
486
|
|
275
|
-
|
276
|
-
SEARCH_CLASS = JSS::AdvancedComputerSearch
|
487
|
+
cmd_rsrc << "/id/#{targets.join ','}"
|
277
488
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
#
|
282
|
-
BOOT_FLAG = ' (Boot Partition)'.freeze
|
283
|
-
|
284
|
-
# file uploads can send attachments to the JSS using :computers as the sub-resource.
|
285
|
-
UPLOAD_TYPES = { attachment: :computers }.freeze
|
286
|
-
|
287
|
-
# A mapping of Symbols available to the send_mdm_command class method, to
|
288
|
-
# the String commands actuallly sent via the API.
|
289
|
-
COMPUTER_MDM_COMMANDS = {
|
290
|
-
blank_push: 'BlankPush',
|
291
|
-
send_blank_push: 'BlankPush',
|
292
|
-
blank: 'BlankPush',
|
293
|
-
noop: 'BlankPush',
|
294
|
-
device_lock: 'DeviceLock',
|
295
|
-
lock: 'DeviceLock',
|
296
|
-
lock_device: 'DeviceLock',
|
297
|
-
erase_device: 'EraseDevice',
|
298
|
-
erase: 'EraseDevice',
|
299
|
-
wipe: 'EraseDevice',
|
300
|
-
unmanage_device: 'UnmanageDevice',
|
301
|
-
unmanage: 'UnmanageDevice'
|
302
|
-
}.freeze
|
489
|
+
result = JSS::API.post_rsrc cmd_rsrc, nil
|
490
|
+
result =~ %r{<command_uuid>(.*)</command_uuid>}
|
491
|
+
Regexp.last_match(1)
|
492
|
+
end # send mdm command
|
303
493
|
|
304
494
|
# Attributes
|
305
495
|
#####################################
|
@@ -526,7 +716,7 @@ module JSS
|
|
526
716
|
# As well as :id and :name, computers can be queried using :udid, :serialnumber, and :mac_address
|
527
717
|
#
|
528
718
|
def initialize(args = {})
|
529
|
-
super args
|
719
|
+
super args
|
530
720
|
|
531
721
|
# now we have raw @init_data with something in it, so fill out the instance vars
|
532
722
|
@alt_mac_address = @init_data[:general][:alt_mac_address]
|
@@ -621,6 +811,234 @@ module JSS
|
|
621
811
|
@software[:licensed_software]
|
622
812
|
end
|
623
813
|
|
814
|
+
# Get application usage data for this computer
|
815
|
+
# for a given date range.
|
816
|
+
#
|
817
|
+
# @param start_date [String,Date,DateTime,Time]
|
818
|
+
#
|
819
|
+
# @param end_date [String,Date,DateTime,Time] Defaults to start_date
|
820
|
+
#
|
821
|
+
# @return [Hash{Date=>Array<Hash>}] For each day in the range, an Array
|
822
|
+
# with one Hash per application used. The hash keys are:
|
823
|
+
# :name => String, the name of the app
|
824
|
+
# :version => String ,the version of the app
|
825
|
+
# :foreground => Integer, the minutes it was in the foreground
|
826
|
+
# :open => Integer, the minutes it was running.
|
827
|
+
#
|
828
|
+
def application_usage(start_date, end_date = nil)
|
829
|
+
end_date ||= start_date
|
830
|
+
start_date = Time.parse start_date if start_date.is_a? String
|
831
|
+
end_date = Time.parse end_date if end_date.is_a? String
|
832
|
+
unless ([start_date.class, end_date.class] - APPLICATION_USAGE_DATE_CLASSES).empty?
|
833
|
+
raise JSS::InvalidDataError, 'Invalid Start or End Date'
|
834
|
+
end
|
835
|
+
start_date = start_date.strftime APPLICATION_USAGE_DATE_FMT
|
836
|
+
end_date = end_date.strftime APPLICATION_USAGE_DATE_FMT
|
837
|
+
data = JSS.api_connection.get_rsrc(APPLICATION_USAGE_RSRC + "/id/#{@id}/#{start_date}_#{end_date}")
|
838
|
+
parsed_data = {}
|
839
|
+
data[APPLICATION_USAGE_KEY].each do |day_hash|
|
840
|
+
date = Date.parse day_hash[:date]
|
841
|
+
parsed_data[date] = day_hash[:apps]
|
842
|
+
end
|
843
|
+
parsed_data
|
844
|
+
end # app usage
|
845
|
+
|
846
|
+
# The 'computer management' data for this computer, looked up on the fly.
|
847
|
+
#
|
848
|
+
# Without specifying a subset:, the entire dataset is returned as a hash of
|
849
|
+
# arrays, one per subset
|
850
|
+
# If a subset is given then only that array is returned, and it contains
|
851
|
+
# hashes with data about each item (usually :name and :id)
|
852
|
+
#
|
853
|
+
# If the only: param is provided with a subset, it is used as a hash-key to
|
854
|
+
# map the array to just those values, so subset: :smart_groups, only: :name
|
855
|
+
# will return an array of names of smartgroups that contain this computer.
|
856
|
+
#
|
857
|
+
# @param subset[Symbol] Fetch only a subset of data, as an array.
|
858
|
+
# must be one of the symbols in MGMT_DATA_SUBSETS
|
859
|
+
#
|
860
|
+
# @param only[Symbol] When fetching a subset, only return one value
|
861
|
+
# per item in the array. meaningless without a subset.
|
862
|
+
#
|
863
|
+
# @param refresh[Boolean] should the data be re-cached from the API?
|
864
|
+
#
|
865
|
+
# @return [Hash] Without a subset:, a hash of all subsets, each of which is
|
866
|
+
# an Array
|
867
|
+
#
|
868
|
+
# @return [Array] With a subset:, an array of items in that subset.
|
869
|
+
#
|
870
|
+
def management_data(subset: nil, only: nil, refresh: false)
|
871
|
+
@management_data ||= {}
|
872
|
+
if subset
|
873
|
+
management_data_subset(subset: subset, only: only, refresh: refresh)
|
874
|
+
else
|
875
|
+
full_management_data refresh
|
876
|
+
end
|
877
|
+
end
|
878
|
+
|
879
|
+
def full_management_data(refresh = false)
|
880
|
+
@management_data[:full] = nil if refresh
|
881
|
+
return @management_data[:full] if @management_data[:full]
|
882
|
+
mgmt_rsrc = MGMT_DATA_RSRC + "/id/#{@id}"
|
883
|
+
@management_data[:full] = JSS.api.get_rsrc(mgmt_rsrc)[MGMT_DATA_KEY]
|
884
|
+
@management_data[:full]
|
885
|
+
end
|
886
|
+
private :full_management_data
|
887
|
+
|
888
|
+
def management_data_subset(subset: nil, only: nil, refresh: false)
|
889
|
+
raise "Subset must be one of :#{MGMT_DATA_SUBSETS.join ', :'}" unless MGMT_DATA_SUBSETS.include? subset
|
890
|
+
@management_data[subset] = nil if refresh
|
891
|
+
return @management_data[subset] if @management_data[subset]
|
892
|
+
subset_rsrc = MGMT_DATA_RSRC + "/id/#{@id}/subset/#{subset}"
|
893
|
+
@management_data[subset] = JSS.api.get_rsrc(subset_rsrc)[MGMT_DATA_KEY]
|
894
|
+
return @management_data[subset] unless only
|
895
|
+
@management_data[subset].map { |d| d[only] }
|
896
|
+
end
|
897
|
+
private :management_data_subset
|
898
|
+
|
899
|
+
# A shortcut for 'management_data subset: :smart_groups'
|
900
|
+
#
|
901
|
+
def smart_groups(only: nil, refresh: false)
|
902
|
+
management_data subset: :smart_groups, only: only, refresh: refresh
|
903
|
+
end
|
904
|
+
|
905
|
+
# A shortcut for 'management_data subset: :static_groups'
|
906
|
+
#
|
907
|
+
def static_groups(only: nil, refresh: false)
|
908
|
+
management_data subset: :static_groups, only: only, refresh: refresh
|
909
|
+
end
|
910
|
+
|
911
|
+
# A shortcut for 'management_data subset: :policies'
|
912
|
+
#
|
913
|
+
def policies(only: nil, refresh: false)
|
914
|
+
management_data subset: :policies, only: only, refresh: refresh
|
915
|
+
end
|
916
|
+
|
917
|
+
# A shortcut for 'management_data subset: :os_x_configuration_profiles'
|
918
|
+
#
|
919
|
+
def configuration_profiles(only: nil, refresh: false)
|
920
|
+
management_data subset: :os_x_configuration_profiles, only: only, refresh: refresh
|
921
|
+
end
|
922
|
+
|
923
|
+
# A shortcut for 'management_data subset: :ebooks'
|
924
|
+
#
|
925
|
+
def ebooks(only: nil, refresh: false)
|
926
|
+
management_data subset: :ebooks, only: only, refresh: refresh
|
927
|
+
end
|
928
|
+
|
929
|
+
# A shortcut for 'management_data subset: :mac_app_store_apps'
|
930
|
+
#
|
931
|
+
def app_store_apps(only: nil, refresh: false)
|
932
|
+
management_data subset: :mac_app_store_apps, only: only, refresh: refresh
|
933
|
+
end
|
934
|
+
|
935
|
+
# A shortcut for 'management_data subset: :restricted_software'
|
936
|
+
#
|
937
|
+
def restricted_software(only: nil, refresh: false)
|
938
|
+
management_data subset: :restricted_software, only: only, refresh: refresh
|
939
|
+
end
|
940
|
+
|
941
|
+
# A shortcut for 'management_data subset: :patch_reporting_software_titles'
|
942
|
+
#
|
943
|
+
def patch_titles(only: nil, refresh: false)
|
944
|
+
management_data subset: :patch_reporting_software_titles, only: only, refresh: refresh
|
945
|
+
end
|
946
|
+
|
947
|
+
# Return this computer's history.
|
948
|
+
# WARNING! Its huge, better to use a subset a
|
949
|
+
# nd one of the shortcut methods.
|
950
|
+
#
|
951
|
+
# @param subset[Symbol] the subset to return, rather than full history.
|
952
|
+
#
|
953
|
+
# @param refresh[Boolean] should we re-cache the data from the API?
|
954
|
+
#
|
955
|
+
# @return [Hash] The full history
|
956
|
+
#
|
957
|
+
# @return [Array] The history subset requested
|
958
|
+
#
|
959
|
+
def history(subset: nil, refresh: false)
|
960
|
+
@history ||= {}
|
961
|
+
if subset
|
962
|
+
history_subset(subset: subset, refresh: refresh)
|
963
|
+
else
|
964
|
+
full_history refresh
|
965
|
+
end
|
966
|
+
end
|
967
|
+
|
968
|
+
def full_history(refresh = false)
|
969
|
+
@history[:full] = nil if refresh
|
970
|
+
return @history[:full] if @history[:full]
|
971
|
+
history_rsrc = HISTORY_RSRC + "/id/#{@id}"
|
972
|
+
@history[:full] = JSS.api.get_rsrc(history_rsrc)[HISTORY_KEY]
|
973
|
+
@history[:full]
|
974
|
+
end
|
975
|
+
private :full_history
|
976
|
+
|
977
|
+
def history_subset(subset: nil, refresh: false)
|
978
|
+
raise "Subset must be one of :#{HISTORY_SUBSETS.join ', :'}" unless HISTORY_SUBSETS.include? subset
|
979
|
+
@history[subset] = nil if refresh
|
980
|
+
return @history[subset] if @history[subset]
|
981
|
+
subset_rsrc = HISTORY_RSRC + "/id/#{@id}/subset/#{subset}"
|
982
|
+
@history[subset] = JSS.api.get_rsrc(subset_rsrc)[HISTORY_KEY]
|
983
|
+
@history[subset]
|
984
|
+
end
|
985
|
+
private :history_subset
|
986
|
+
|
987
|
+
# Shortcut for history(:computer_usage_logs)
|
988
|
+
def usage_logs(refresh = false)
|
989
|
+
history(subset: :computer_usage_logs, refresh: refresh)
|
990
|
+
end
|
991
|
+
|
992
|
+
# Shortcut for history(:audits)
|
993
|
+
def audits(refresh = false)
|
994
|
+
history(subset: :audits, refresh: refresh)
|
995
|
+
end
|
996
|
+
|
997
|
+
# Shortcut for history(:policy_logs)
|
998
|
+
def policy_logs(refresh = false)
|
999
|
+
history(subset: :policy_logs, refresh: refresh)
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
# Shortcut for history(:policy_logs), but just the completed policies
|
1003
|
+
def completed_policies(refresh = false)
|
1004
|
+
policy_logs(refresh).select { |pl| pl[:status] == POLICY_STATUS_COMPLETED }
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
# Shortcut for history(:policy_logs), but just the failes policies
|
1008
|
+
def failed_policies(refresh = false)
|
1009
|
+
policy_log(refresh).select { |pl| pl[:status] == POLICY_STATUS_FAILED }
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
# Shortcut for history(:casper_remote_logs)
|
1013
|
+
def casper_remote_logs(refresh = false)
|
1014
|
+
history(subset: :casper_remote_logs, refresh: refresh)
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
# Shortcut for history(:screen_sharing_logs)
|
1018
|
+
def screen_sharing_logs(refresh = false)
|
1019
|
+
history(subset: :screen_sharing_logs, refresh: refresh)
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
# Shortcut for history(:casper_imaging_logs)
|
1023
|
+
def casper_imaging_logs(refresh = false)
|
1024
|
+
history(subset: :casper_imaging_logs, refresh: refresh)
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
# Shortcut for history(:commands)
|
1028
|
+
def commands(refresh = false)
|
1029
|
+
history(subset: :commands, refresh: refresh)
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
# Shortcut for history(:user_location)
|
1033
|
+
def user_location_history(refresh = false)
|
1034
|
+
history(subset: :user_location, refresh: refresh)
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
# Shortcut for history(:mac_app_store_applications)
|
1038
|
+
def app_store_app_history(refresh = false)
|
1039
|
+
history(subset: :mac_app_store_applications, refresh: refresh)
|
1040
|
+
end
|
1041
|
+
|
624
1042
|
# Set or unset management acct and password for this computer
|
625
1043
|
#
|
626
1044
|
# @param name[String] the name of the management acct.
|
@@ -629,7 +1047,7 @@ module JSS
|
|
629
1047
|
#
|
630
1048
|
# @return [void]
|
631
1049
|
#
|
632
|
-
# The changes will need to be pushed to the server with #update
|
1050
|
+
# The changes will need to be pushed to the server with {#update}
|
633
1051
|
# before they take effect.
|
634
1052
|
#
|
635
1053
|
# CAUTION: this does nothing to confirm the name and password
|
@@ -656,10 +1074,8 @@ module JSS
|
|
656
1074
|
def make_unmanaged
|
657
1075
|
return nil unless managed?
|
658
1076
|
set_management_to(nil, nil)
|
659
|
-
|
660
|
-
|
661
|
-
rescue
|
662
|
-
end
|
1077
|
+
return unless mdm_capable
|
1078
|
+
self.class.send_mdm_command(@id, :unmanage_device)
|
663
1079
|
end
|
664
1080
|
|
665
1081
|
#
|
@@ -751,34 +1167,47 @@ module JSS
|
|
751
1167
|
@software = nil
|
752
1168
|
end # delete
|
753
1169
|
|
754
|
-
#
|
755
|
-
#
|
756
|
-
#
|
757
|
-
#
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
#
|
765
|
-
#
|
766
|
-
#
|
767
|
-
#
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
#
|
775
|
-
#
|
776
|
-
#
|
777
|
-
#
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
1170
|
+
# Send a blank_push MDM command
|
1171
|
+
#
|
1172
|
+
# See JSS::Computer.send_mdm_command
|
1173
|
+
#
|
1174
|
+
def blank_push
|
1175
|
+
self.class.send_mdm_command @id, :blank_push
|
1176
|
+
end
|
1177
|
+
alias noop blank_push
|
1178
|
+
alias send_blank_push blank_push
|
1179
|
+
|
1180
|
+
# Send a device_lock MDM command
|
1181
|
+
#
|
1182
|
+
# See JSS::Computer.send_mdm_command
|
1183
|
+
#
|
1184
|
+
def device_lock(passcode)
|
1185
|
+
self.class.send_mdm_command @id, :device_lock, passcode
|
1186
|
+
end
|
1187
|
+
alias lock device_lock
|
1188
|
+
alias lock_device device_lock
|
1189
|
+
|
1190
|
+
# Send an erase_device MDM command
|
1191
|
+
#
|
1192
|
+
# See JSS::Computer.send_mdm_command
|
1193
|
+
#
|
1194
|
+
def erase_device(passcode)
|
1195
|
+
self.class.send_mdm_command @id, :erase_device, passcode
|
1196
|
+
end
|
1197
|
+
alias erase erase_device
|
1198
|
+
alias wipe erase_device
|
1199
|
+
|
1200
|
+
# Remove MDM management profile without
|
1201
|
+
# un-enrolling from the JSS or
|
1202
|
+
# resetting the JSS management acct.
|
1203
|
+
#
|
1204
|
+
# To do those things as well, see {#make_unmanaged}
|
1205
|
+
#
|
1206
|
+
# See JSS::Computer.send_mdm_command
|
1207
|
+
#
|
1208
|
+
def remove_mdm_profile
|
1209
|
+
self.class.send_mdm_command(@id, :unmanage_device)
|
1210
|
+
end
|
782
1211
|
|
783
1212
|
# aliases
|
784
1213
|
alias alt_macaddress alt_mac_address
|
@@ -830,7 +1259,7 @@ module JSS
|
|
830
1259
|
computer << purchasing_xml if has_purchasing?
|
831
1260
|
|
832
1261
|
doc.to_s
|
833
|
-
end
|
1262
|
+
end # rest_xml
|
834
1263
|
|
835
1264
|
end # class Computer
|
836
1265
|
|