ruby-jss 1.3.3 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ruby-jss might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGES.md +47 -0
- data/lib/jamf/api/connection.rb +1 -1
- data/lib/jamf/api/connection/token.rb +21 -1
- data/lib/jamf/api/resources/collection_resources/department.rb +1 -1
- data/lib/jamf/api/resources/collection_resources/device_enrollment.rb +3 -3
- data/lib/jamf/version.rb +1 -1
- data/lib/jss/api_object.rb +7 -1
- data/lib/jss/api_object/advanced_search.rb +27 -26
- data/lib/jss/api_object/app_store_country_codes.rb +298 -0
- data/lib/jss/api_object/ebook.rb +1 -2
- data/lib/jss/api_object/extension_attribute.rb +4 -3
- data/lib/jss/api_object/mac_application.rb +107 -8
- data/lib/jss/api_object/policy.rb +112 -3
- data/lib/jss/api_object/scopable/scope.rb +366 -51
- data/lib/jss/api_object/self_servable.rb +17 -9
- data/lib/jss/api_object/uploadable.rb +1 -1
- data/lib/jss/api_object/user.rb +42 -1
- data/lib/jss/api_object/vpp_account.rb +209 -0
- data/lib/jss/api_object/vppable.rb +169 -13
- data/lib/jss/validate.rb +53 -10
- data/lib/jss/version.rb +1 -1
- metadata +4 -2
@@ -51,12 +51,12 @@ module JSS
|
|
51
51
|
# - :security
|
52
52
|
#
|
53
53
|
# Additionally, items that apper in macOS Slf Svc have these keys:
|
54
|
-
# - :self_service_display_name
|
54
|
+
# - :self_service_display_name (but not JSS::MacApplication)
|
55
55
|
# - :install_button_text
|
56
|
-
# - :reinstall_button_text
|
56
|
+
# - :reinstall_button_text (but not JSS::MacApplication)
|
57
57
|
# - :force_users_to_view_description
|
58
58
|
# - :notification
|
59
|
-
# - :notification_location # PENDING API FIX
|
59
|
+
# - :notification_location # PENDING API FIX, and also, not JSS::MacApplication
|
60
60
|
# - :notification_subject
|
61
61
|
# - :notification_message
|
62
62
|
#
|
@@ -178,15 +178,14 @@ module JSS
|
|
178
178
|
notification_reminders: true
|
179
179
|
},
|
180
180
|
JSS::MacApplication => {
|
181
|
-
|
182
|
-
# Jamf Product Issue [PI-003773]
|
183
|
-
in_self_service_data_path: [:general, :deployment_type],
|
181
|
+
in_self_service_data_path: %i[general deployment_type],
|
184
182
|
in_self_service: MAKE_AVAILABLE,
|
185
183
|
not_in_self_service: AUTO_INSTALL_OR_PROMPT,
|
186
184
|
targets: [:macos],
|
187
185
|
payload: :app,
|
188
186
|
can_display_in_categories: true,
|
189
187
|
can_feature_in_categories: true,
|
188
|
+
notifications_supported: :ssvc_and_nctr,
|
190
189
|
url_entity: 'app'
|
191
190
|
# OTHER BUG: no notification options seem to be changable via the API
|
192
191
|
},
|
@@ -329,6 +328,7 @@ module JSS
|
|
329
328
|
#
|
330
329
|
def self_service_view_url
|
331
330
|
return nil unless @self_service_data_config[:url_entity]
|
331
|
+
|
332
332
|
"#{USER_URL_BASE}#{@self_service_data_config[:url_entity]}&id=#{id}&action=#{USER_URL_VIEW_ACTION}"
|
333
333
|
end
|
334
334
|
|
@@ -336,6 +336,7 @@ module JSS
|
|
336
336
|
#
|
337
337
|
def self_service_execute_url
|
338
338
|
return nil unless @self_service_data_config[:url_entity]
|
339
|
+
|
339
340
|
"#{USER_URL_BASE}#{@self_service_data_config[:url_entity]}&id=#{id}&action=#{USER_URL_EXEC_ACTION}"
|
340
341
|
end
|
341
342
|
|
@@ -349,6 +350,7 @@ module JSS
|
|
349
350
|
def self_service_description=(new_val)
|
350
351
|
new_val = new_val.strip
|
351
352
|
return if @self_service_description == new_val
|
353
|
+
|
352
354
|
@self_service_description = new_val
|
353
355
|
@need_to_update = true
|
354
356
|
end
|
@@ -820,7 +822,7 @@ module JSS
|
|
820
822
|
# ssvc subset...
|
821
823
|
add_in_self_service_xml doc_root
|
822
824
|
|
823
|
-
subset_key = @self_service_data_config[:self_service_subset]
|
825
|
+
subset_key = @self_service_data_config[:self_service_subset] || :self_service
|
824
826
|
|
825
827
|
ssvc = doc_root.add_element subset_key.to_s
|
826
828
|
|
@@ -857,6 +859,7 @@ module JSS
|
|
857
859
|
# add the xml specific to profiles
|
858
860
|
def add_self_service_profile_xml(ssvc, doc_root)
|
859
861
|
return unless self_service_payload == :profile
|
862
|
+
|
860
863
|
if self_service_targets.include? :ios
|
861
864
|
sec = ssvc.add_element('security')
|
862
865
|
sec.add_element('removal_disallowed').text = PROFILE_REMOVAL_BY_USER[@self_service_user_removable]
|
@@ -871,6 +874,7 @@ module JSS
|
|
871
874
|
def add_self_service_category_xml(ssvc)
|
872
875
|
cats = ssvc.add_element('self_service_categories')
|
873
876
|
return if self_service_categories.empty?
|
877
|
+
|
874
878
|
self_service_categories.each do |cat|
|
875
879
|
catelem = cats.add_element('category')
|
876
880
|
catelem.add_element('name').text = cat[:name]
|
@@ -882,10 +886,14 @@ module JSS
|
|
882
886
|
# set macOS settings in ssvc xml
|
883
887
|
def add_self_service_macos_xml(ssvc)
|
884
888
|
return unless self_service_targets.include? :macos
|
885
|
-
|
889
|
+
|
886
890
|
ssvc.add_element('install_button_text').text = self_service_install_button_text if self_service_install_button_text
|
887
|
-
ssvc.add_element('reinstall_button_text').text = self_service_reinstall_button_text if self_service_reinstall_button_text
|
888
891
|
ssvc.add_element('force_users_to_view_description').text = self_service_force_users_to_view_description.to_s
|
892
|
+
|
893
|
+
return if self.class == JSS::MacApplication
|
894
|
+
|
895
|
+
ssvc.add_element('self_service_display_name').text = self_service_display_name if self_service_display_name
|
896
|
+
ssvc.add_element('reinstall_button_text').text = self_service_reinstall_button_text if self_service_reinstall_button_text
|
889
897
|
end
|
890
898
|
|
891
899
|
|
data/lib/jss/api_object/user.rb
CHANGED
@@ -144,7 +144,7 @@ module JSS
|
|
144
144
|
|
145
145
|
### @return [Array<Hash>]
|
146
146
|
###
|
147
|
-
### The vpp assignments associated with this user
|
147
|
+
### The user-based vpp assignments associated with this user
|
148
148
|
###
|
149
149
|
### Each Hash has then :id and :name for one assignment
|
150
150
|
###
|
@@ -249,6 +249,47 @@ module JSS
|
|
249
249
|
@need_to_update = true
|
250
250
|
end
|
251
251
|
|
252
|
+
# Workaround for the recurring Jamf Classic API Bug where
|
253
|
+
# JSON is missing data that should come in an array of hashes, but
|
254
|
+
# only comes as a hash with a single hash inside, with data for only
|
255
|
+
# the last item in the XML array.
|
256
|
+
#
|
257
|
+
# When needed, we fetch and parse the XML, which has the desired data.
|
258
|
+
# Use any truthy parameter to re-fetch the XML data, otherwise the
|
259
|
+
# data last fetched is used.
|
260
|
+
#
|
261
|
+
# In this case, the user group data is fetched as XML and returned as
|
262
|
+
# an Array of Hashes, one per group the user is a member of. Each hash
|
263
|
+
# containing three Symbol keys:
|
264
|
+
#
|
265
|
+
# id: Integer, the group id
|
266
|
+
# name: String, the group name
|
267
|
+
# is_smart: Boolean, is it a smart group or a static group?
|
268
|
+
#
|
269
|
+
# @param refresh[Boolean] Re-fetch the group data from the API
|
270
|
+
#
|
271
|
+
# @return [Array<Hash>] The groups the user is a member of.
|
272
|
+
#
|
273
|
+
def user_groups(refresh = false)
|
274
|
+
@grp_array = nil if refresh
|
275
|
+
return @grp_array if @grp_array
|
276
|
+
|
277
|
+
@grp_array = []
|
278
|
+
raw_xml = @api.get_rsrc "/users/id/#{@id}", :xml
|
279
|
+
xmlroot = REXML::Document.new(raw_xml).root
|
280
|
+
xml_grps = xmlroot.elements['user_groups']
|
281
|
+
|
282
|
+
xml_grps.each do |xml_grp|
|
283
|
+
next if xml_grp.name == 'size'
|
284
|
+
|
285
|
+
gid = xml_grp.elements['id'].text.to_i
|
286
|
+
gname = xml_grp.elements['name'].text
|
287
|
+
smart = xml_grp.elements['is_smart'].text == 'true'
|
288
|
+
@grp_array << { id: gid, name: gname, is_smart: smart }
|
289
|
+
end # groups.each
|
290
|
+
|
291
|
+
@grp_array
|
292
|
+
end # user_groups
|
252
293
|
|
253
294
|
#####################################
|
254
295
|
### Private Instance Methods
|
@@ -0,0 +1,209 @@
|
|
1
|
+
### Copyright 2020 Pixar
|
2
|
+
|
3
|
+
###
|
4
|
+
### Licensed under the Apache License, Version 2.0 (the "Apache License")
|
5
|
+
### with the following modification; you may not use this file except in
|
6
|
+
### compliance with the Apache License and the following modification to it:
|
7
|
+
### Section 6. Trademarks. is deleted and replaced with:
|
8
|
+
###
|
9
|
+
### 6. Trademarks. This License does not grant permission to use the trade
|
10
|
+
### names, trademarks, service marks, or product names of the Licensor
|
11
|
+
### and its affiliates, except as required to comply with Section 4(c) of
|
12
|
+
### the License and to reproduce the content of the NOTICE file.
|
13
|
+
###
|
14
|
+
### You may obtain a copy of the Apache License at
|
15
|
+
###
|
16
|
+
### http://www.apache.org/licenses/LICENSE-2.0
|
17
|
+
###
|
18
|
+
### Unless required by applicable law or agreed to in writing, software
|
19
|
+
### distributed under the Apache License with the above modification is
|
20
|
+
### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
21
|
+
### KIND, either express or implied. See the Apache License for the specific
|
22
|
+
### language governing permissions and limitations under the Apache License.
|
23
|
+
###
|
24
|
+
###
|
25
|
+
|
26
|
+
###
|
27
|
+
module JSS
|
28
|
+
|
29
|
+
# A VPP account defined in the JSS
|
30
|
+
#
|
31
|
+
class VPPAccount < JSS::APIObject
|
32
|
+
|
33
|
+
# Mix-Ins
|
34
|
+
#####################################
|
35
|
+
include JSS::Updatable
|
36
|
+
include JSS::Sitable
|
37
|
+
|
38
|
+
# Class Constants
|
39
|
+
#####################################
|
40
|
+
|
41
|
+
### The base for REST resources of this class
|
42
|
+
RSRC_BASE = 'vppaccounts'.freeze
|
43
|
+
|
44
|
+
### the hash key used for the JSON list output of all objects in the JSS
|
45
|
+
RSRC_LIST_KEY = :vpp_accounts
|
46
|
+
|
47
|
+
### The hash key used for the JSON object output.
|
48
|
+
### It's also used in various error messages
|
49
|
+
RSRC_OBJECT_KEY = :vpp_account
|
50
|
+
|
51
|
+
SITE_SUBSET = :top
|
52
|
+
|
53
|
+
# Attributes
|
54
|
+
#####################################
|
55
|
+
|
56
|
+
# @return [String] The full name of the local contact person for the acct
|
57
|
+
attr_reader :contact
|
58
|
+
|
59
|
+
# @return [String] The service token for connecting to the account at Apple.
|
60
|
+
# Currently not visible, appears as '***************'
|
61
|
+
attr_reader :service_token
|
62
|
+
|
63
|
+
# @return [String] The name of the company associated with the Acct/Token
|
64
|
+
attr_reader :account_name
|
65
|
+
|
66
|
+
# @return [Time] The expiration date of the Acct/Token
|
67
|
+
attr_reader :expiration_date
|
68
|
+
|
69
|
+
# @return [String] The location associated with the Acct/Token
|
70
|
+
attr_reader :location_name
|
71
|
+
alias location location_name
|
72
|
+
|
73
|
+
# @return [String] The Country Code associated with the acct
|
74
|
+
attr_reader :country
|
75
|
+
|
76
|
+
# @return [String] The AppleID associated with the acct
|
77
|
+
attr_reader :apple_id
|
78
|
+
|
79
|
+
# @return [Boolean] Automatically populate purchased content from Apple
|
80
|
+
# School Manager or Apple Business Manager in Jamf Pro
|
81
|
+
attr_reader :populate_catalog_from_vpp_content
|
82
|
+
|
83
|
+
# @return [Boolean] Display a notification to users on their mobile devices
|
84
|
+
# when a volume purchased app in a user-based volume assignment is no
|
85
|
+
# longer assigned to them
|
86
|
+
attr_reader :notify_disassociation
|
87
|
+
|
88
|
+
# @return [Boolean] Automatically register users that have Managed Apple IDs
|
89
|
+
# so they do not receive an invitation and are not prompted to register
|
90
|
+
# with volume purchasing
|
91
|
+
attr_reader :auto_register_managed_users
|
92
|
+
|
93
|
+
# Constructor
|
94
|
+
#####################################
|
95
|
+
|
96
|
+
# See JSS::APIObject#initialize
|
97
|
+
#
|
98
|
+
def initialize(args = {})
|
99
|
+
super
|
100
|
+
@contact = @init_data[:contact]
|
101
|
+
@service_token = @init_data[:service_token]
|
102
|
+
@account_name = @init_data[:account_name]
|
103
|
+
@expiration_date = @init_data[:expiration_date].to_s.empty? ? nil : Time.parse(@init_data[:expiration_date])
|
104
|
+
@location_name = @init_data[:location_name]
|
105
|
+
@country = @init_data[:country]
|
106
|
+
@apple_id = @init_data[:apple_id]
|
107
|
+
@populate_catalog_from_vpp_content = @init_data[:populate_catalog_from_vpp_content]
|
108
|
+
@notify_disassociation = @init_data[:notify_disassociation]
|
109
|
+
@auto_register_managed_users = @init_data[:auto_register_managed_users]
|
110
|
+
end
|
111
|
+
|
112
|
+
# Public Instance Methods
|
113
|
+
#####################################
|
114
|
+
|
115
|
+
# @param new_val[String] the value
|
116
|
+
#
|
117
|
+
# @return [void]
|
118
|
+
#
|
119
|
+
def contact=(new_val = @contact)
|
120
|
+
return if new_val == @contact
|
121
|
+
|
122
|
+
@contact = JSS::Validate.non_empty_string new_val, 'Contact must be a non-empty String'
|
123
|
+
@need_to_update = true
|
124
|
+
end
|
125
|
+
|
126
|
+
# @param new_val[String] the value
|
127
|
+
#
|
128
|
+
# @return [void]
|
129
|
+
#
|
130
|
+
def country=(new_val = @country)
|
131
|
+
return if new_val == @country
|
132
|
+
|
133
|
+
@country = JSS::Validate.app_store_country_code new_val
|
134
|
+
@need_to_update = true
|
135
|
+
end
|
136
|
+
|
137
|
+
# @param new_val[String] the value
|
138
|
+
#
|
139
|
+
# @return [void]
|
140
|
+
#
|
141
|
+
def apple_id=(new_val = @apple_id)
|
142
|
+
return if new_val == @apple_id
|
143
|
+
|
144
|
+
@apple_id = JSS::Validate.email_address new_val
|
145
|
+
@need_to_update = true
|
146
|
+
end
|
147
|
+
|
148
|
+
# @param new_val[String] the value
|
149
|
+
#
|
150
|
+
# @return [void]
|
151
|
+
#
|
152
|
+
def populate_catalog_from_vpp_content=(new_val = @populate_catalog_from_vpp_content)
|
153
|
+
return if new_val == @populate_catalog_from_vpp_content
|
154
|
+
|
155
|
+
@populate_catalog_from_vpp_content = JSS::Validate.boolean new_val
|
156
|
+
@need_to_update = true
|
157
|
+
end
|
158
|
+
|
159
|
+
# @param new_val[String] the value
|
160
|
+
#
|
161
|
+
# @return [void]
|
162
|
+
#
|
163
|
+
def notify_disassociation=(new_val = @notify_disassociation)
|
164
|
+
return if new_val == @notify_disassociation
|
165
|
+
|
166
|
+
@notify_disassociation = JSS::Validate.boolean new_val
|
167
|
+
@need_to_update = true
|
168
|
+
end
|
169
|
+
|
170
|
+
# @param new_val[String] the value
|
171
|
+
#
|
172
|
+
# @return [void]
|
173
|
+
#
|
174
|
+
def auto_register_managed_users=(new_val = @auto_register_managed_users)
|
175
|
+
return if new_val == @auto_register_managed_users
|
176
|
+
|
177
|
+
@auto_register_managed_users = JSS::Validate.boolean new_val
|
178
|
+
@need_to_update = true
|
179
|
+
end
|
180
|
+
|
181
|
+
# Private Instance Methods
|
182
|
+
#####################################
|
183
|
+
private
|
184
|
+
|
185
|
+
# Return a String with the XML Resource
|
186
|
+
# for submitting creation or changes to the JSS via
|
187
|
+
# the API via the Creatable or Updatable modules
|
188
|
+
#
|
189
|
+
# Most classes will redefine this method.
|
190
|
+
#
|
191
|
+
def rest_xml
|
192
|
+
doc = REXML::Document.new APIConnection::XML_HEADER
|
193
|
+
tmpl = doc.add_element self.class::RSRC_OBJECT_KEY.to_s
|
194
|
+
tmpl.add_element('name').text = @name
|
195
|
+
tmpl.add_element('contact').text = @contact
|
196
|
+
tmpl.add_element('country').text = @country
|
197
|
+
tmpl.add_element('apple_id').text = @apple_id
|
198
|
+
tmpl.add_element('populate_catalog_from_vpp_content').text = @populate_catalog_from_vpp_content.to_s
|
199
|
+
tmpl.add_element('notify_disassociation').text = @notify_disassociation.to_s
|
200
|
+
tmpl.add_element('auto_register_managed_users').text = @auto_register_managed_users.to_s
|
201
|
+
|
202
|
+
add_site_to_xml doc
|
203
|
+
|
204
|
+
doc.to_s
|
205
|
+
end
|
206
|
+
|
207
|
+
end # VPPAccount
|
208
|
+
|
209
|
+
end # JSS
|
@@ -26,9 +26,16 @@
|
|
26
26
|
###
|
27
27
|
module JSS
|
28
28
|
|
29
|
-
# A mix-in module to
|
29
|
+
# A mix-in module to handleVPP-related data in API objects that can be
|
30
30
|
# assigned via VPP.
|
31
31
|
#
|
32
|
+
# NOTE: For now we are only working with device-based VPP assignments,
|
33
|
+
# which are done via the scope of the VPPable object (macapp, mobdevapp, ebook)
|
34
|
+
#
|
35
|
+
# User-based APP assignments will require the creation of a VPPAssignment class,
|
36
|
+
# and a VPPAssignmentScope class, since those scopes are very limited compared
|
37
|
+
# to ordinary scope.
|
38
|
+
#
|
32
39
|
# To use this module, merely `include VPPable` when defining your
|
33
40
|
# subclass of JSS::APIObject
|
34
41
|
#
|
@@ -40,6 +47,50 @@ module JSS
|
|
40
47
|
#####################################
|
41
48
|
VPPABLE = true
|
42
49
|
|
50
|
+
# Mixed-in Class Methods
|
51
|
+
#
|
52
|
+
# This is a common technique to get class methods mixed in when
|
53
|
+
# you 'include' a module full of instance methods
|
54
|
+
#####################################
|
55
|
+
|
56
|
+
def self.included(klass)
|
57
|
+
klass.extend(ClassMethods)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Methods in here will become class methods of the
|
61
|
+
# classes that include VPPable
|
62
|
+
module ClassMethods
|
63
|
+
|
64
|
+
# The names and assignment data for all class members that have
|
65
|
+
# VPP licenses that can be assigned by device.
|
66
|
+
# The assignment data is a hash of three keys pointing to integers:
|
67
|
+
# {
|
68
|
+
# total: int,
|
69
|
+
# used: int,
|
70
|
+
# remaining: int
|
71
|
+
# }
|
72
|
+
#
|
73
|
+
# WARNING: This must instantiate all objects, so is slow
|
74
|
+
#
|
75
|
+
# @return [Hash{String=>Hash}] The names and assignment data
|
76
|
+
def all_vpp_device_assignable
|
77
|
+
data = {}
|
78
|
+
all_ids.each do |id|
|
79
|
+
obj = fetch id: id
|
80
|
+
next unless obj.vpp_device_based?
|
81
|
+
|
82
|
+
data[obj.name] = {
|
83
|
+
total: obj.vpp_licenses_total,
|
84
|
+
used: obj.vpp_licenses_used,
|
85
|
+
remaining: obj.vpp_licenses_remaining
|
86
|
+
}
|
87
|
+
end
|
88
|
+
data
|
89
|
+
end # all_vpp_device_assignable
|
90
|
+
|
91
|
+
end # module ClassMethods
|
92
|
+
|
93
|
+
|
43
94
|
# Mixed-in Attributes
|
44
95
|
#####################################
|
45
96
|
|
@@ -48,6 +99,7 @@ module JSS
|
|
48
99
|
|
49
100
|
# @return [Integer]
|
50
101
|
attr_reader :vpp_admin_account_id
|
102
|
+
alias vpp_account_id vpp_admin_account_id
|
51
103
|
|
52
104
|
# @return [Boolean]
|
53
105
|
attr_reader :assign_vpp_device_based_licenses
|
@@ -55,8 +107,7 @@ module JSS
|
|
55
107
|
|
56
108
|
# @return [Integer]
|
57
109
|
attr_reader :total_vpp_licenses
|
58
|
-
alias
|
59
|
-
alias vpp_license_count total_vpp_licenses
|
110
|
+
alias vpp_licenses_total total_vpp_licenses
|
60
111
|
|
61
112
|
# @return [Integer]
|
62
113
|
attr_reader :remaining_vpp_licenses
|
@@ -66,22 +117,126 @@ module JSS
|
|
66
117
|
attr_reader :used_vpp_licenses
|
67
118
|
alias vpp_licenses_used used_vpp_licenses
|
68
119
|
|
120
|
+
#### How to assign VPP content & view assignments
|
121
|
+
#
|
122
|
+
# When doing device-based assignments, they are made via the
|
123
|
+
# Scope of the VPPable Object.
|
124
|
+
#
|
125
|
+
# There is no indication in the device's API data that an app/book was
|
126
|
+
# installed/licensed via VPP, it just shows up in the
|
127
|
+
# list of installed apps like any other.
|
128
|
+
#
|
129
|
+
# When doing user-based assignments, they are made via the (limited)
|
130
|
+
# scope of a 'Volume Assignment' object in Users -> Volume Assignement
|
131
|
+
# These objects are sort of like policies or config profiles in that they have
|
132
|
+
# payloads, and can assign multiple things at once (iosapps, macapps, ebooks)
|
133
|
+
# These are available as vppassignment objects in the API.
|
134
|
+
#
|
135
|
+
# User-based assignments show up in the User's Jamf record
|
136
|
+
# Users -> username -> (vpp acct name)
|
137
|
+
# There you'll see the names of objects assigned to the user, and the
|
138
|
+
# devices on which they've accepted the VPP invitation. In the User's
|
139
|
+
# API data, there isaa 'vpp_assignments' arry of hash's like this:
|
140
|
+
# [{:id=>13733, :uid=>"258_13733"}]
|
141
|
+
# However, that 'id' is not the id of any known vppassignment object, and
|
142
|
+
# the uid is... ?? The object model at Developer.jamf.com says those
|
143
|
+
# values should be an id and a name, probably pointing to a vppassignment
|
144
|
+
# object, but that isn't the case.
|
145
|
+
#
|
146
|
+
#
|
147
|
+
#### Figuring out how many, and where VPP lic. are used....
|
148
|
+
#
|
149
|
+
# IF dev. based assignement is turned on, then
|
150
|
+
# the VPPable object (app, ebook) in the API will show the total numbers
|
151
|
+
# of both user and device based assignments:
|
152
|
+
#
|
153
|
+
# "vpp": {
|
154
|
+
# "assign_vpp_device_based_licenses": true,
|
155
|
+
# "vpp_admin_account_id": 1,
|
156
|
+
# "total_vpp_licenses": 2,
|
157
|
+
# "remaining_vpp_licenses": 0,
|
158
|
+
# "used_vpp_licenses": 2
|
159
|
+
# }
|
160
|
+
#
|
161
|
+
# However, if assign_vpp_device_based_licenses is false, meaning
|
162
|
+
# all assignments are user-based, then no other info is shown in the API.
|
163
|
+
#
|
164
|
+
# In that case, in the UI, you can see the total assignments in a table
|
165
|
+
# in Settings -> Global Mgmt -> Volume Purch -> Content -> (ios/mac)
|
166
|
+
# The numbers shown there indicate all assignments, whether user- or
|
167
|
+
# deviced-based, just like the numbers in the API data for the VPPable
|
168
|
+
# object, if they are there.
|
169
|
+
# But there's no equivalent for that table data directly in the API when
|
170
|
+
# device-based is false.
|
171
|
+
#
|
172
|
+
# Also in the UI you can see the intividual computers, mobiledevs, and users
|
173
|
+
# to whom an object is assigned, no matter how it was assigned. Go to
|
174
|
+
# Users -> Volume Assignments -> [any assigment object] -> Apps/Books -> ios/mac
|
175
|
+
# and click on the number in the rightmost 'in use' column, and you'll
|
176
|
+
# see a page with 3 tabs, showing the individual computers, mobdevs, or users
|
177
|
+
# with the app/ebook assigned. EXCEPT this doesn't seem to expand
|
178
|
+
# scoped groups - when I added a static computer group with one computer to
|
179
|
+
# the scope of a MacApp, the total in-use count went up from 6 to 7, but the
|
180
|
+
# list of computers two which it was assigned still showed only 6. :-(
|
181
|
+
#
|
182
|
+
# You can also get to the same page via: Users->SeachVolumeContent
|
183
|
+
# then perform a simple search, and in the results page, click on the in-use
|
184
|
+
# number. If you click on the VolumeAssignments number you'll see a
|
185
|
+
# breakdown of the device assignments (from the app itself) and user assignments
|
186
|
+
# and their scopes, but the scopes will not expand any groups, just list them.
|
187
|
+
#
|
188
|
+
# So 2 questions:
|
189
|
+
# 1) How to see the total/used/remaining licenses for a VPPable object in the
|
190
|
+
# API, regardless of how it's deployed
|
191
|
+
#
|
192
|
+
# - first look at the VPPable object, and if the data is there, yer done.
|
193
|
+
# - If not, then the object is only assigned to users, so we can loop thru
|
194
|
+
# the vppassignment objects and count things up.
|
195
|
+
#
|
196
|
+
# 2) How to learn where the VPPable object is actually assigned - i.e.
|
197
|
+
# a list of users and/or devices. Note: this isn't a list of where it's
|
198
|
+
# installed, but to whom/where it is assigned.
|
199
|
+
#
|
200
|
+
# - TLDR: no scopable object in Jamf gives you such a list, so we probably
|
201
|
+
# don't need it.
|
202
|
+
#
|
203
|
+
# In the UI, the page you get when clicking the 'in use' column of various
|
204
|
+
# 'volume content' lists (see above) gets you the individually assigned
|
205
|
+
# hardware or users, but doesn't show those via groups.
|
206
|
+
# In the API - there doesn't seem to be any access at all, other than the
|
207
|
+
# scopes of the VPPable Object itself, and any vppassignments that contain it.
|
208
|
+
# Scanning through them is probably the only option, but could be slow once
|
209
|
+
# there are many - and expanding those scopes into an actual list of users
|
210
|
+
# and devices would be a pain to write
|
211
|
+
#
|
212
|
+
|
213
|
+
# Mixed-in Instance Methods
|
214
|
+
#####################################
|
69
215
|
|
70
216
|
# Set whether or not the VPP licenses should be assigned
|
71
|
-
# by device
|
217
|
+
# by device as well as (or.. instead of?) by user
|
72
218
|
#
|
73
219
|
# @param new_val[Boolean] The new value
|
74
220
|
#
|
75
221
|
# @return [void]
|
76
222
|
#
|
77
223
|
def assign_vpp_device_based_licenses=(new_val)
|
78
|
-
return
|
79
|
-
|
80
|
-
@assign_vpp_device_based_licenses = new_val
|
224
|
+
return if new_val == @assign_vpp_device_based_licenses
|
225
|
+
|
226
|
+
@assign_vpp_device_based_licenses = JSS::Validate.boolean new_val
|
81
227
|
@need_to_update = true
|
82
228
|
end
|
83
229
|
alias vpp_device_based= assign_vpp_device_based_licenses=
|
84
230
|
|
231
|
+
# @return [String] The name of the vpp admin acct for this object
|
232
|
+
#
|
233
|
+
def vpp_admin_account_name
|
234
|
+
return unless @vpp_admin_account_id.is_a? Integer
|
235
|
+
|
236
|
+
JSS::VPPAccount.map_all_ids_to(:name)[@vpp_admin_account_id]
|
237
|
+
end
|
238
|
+
alias vpp_account_name vpp_admin_account_name
|
239
|
+
|
85
240
|
# Mixed-in Private Instance Methods
|
86
241
|
#####################################
|
87
242
|
private
|
@@ -92,11 +247,12 @@ module JSS
|
|
92
247
|
#
|
93
248
|
def parse_vpp
|
94
249
|
@vpp_codes = @init_data[:vpp_codes]
|
95
|
-
|
96
|
-
@
|
97
|
-
@
|
98
|
-
@
|
99
|
-
@
|
250
|
+
vpp_data = @init_data[:vpp]
|
251
|
+
@vpp_admin_account_id = vpp_data[:vpp_admin_account_id]
|
252
|
+
@assign_vpp_device_based_licenses = vpp_data[:assign_vpp_device_based_licenses]
|
253
|
+
@total_vpp_licenses = vpp_data[:total_vpp_licenses]
|
254
|
+
@remaining_vpp_licenses = vpp_data[:remaining_vpp_licenses]
|
255
|
+
@used_vpp_licenses = vpp_data[:used_vpp_licenses]
|
100
256
|
end
|
101
257
|
|
102
258
|
# Insert an appropriate vpp element into the XML for sending changes
|
@@ -109,7 +265,7 @@ module JSS
|
|
109
265
|
def add_vpp_xml(xdoc)
|
110
266
|
doc_root = xdoc.root
|
111
267
|
vpp = doc_root.add_element 'vpp'
|
112
|
-
vpp.add_element('assign_vpp_device_based_licenses').text = @assign_vpp_device_based_licenses
|
268
|
+
vpp.add_element('assign_vpp_device_based_licenses').text = @assign_vpp_device_based_licenses.to_s
|
113
269
|
end
|
114
270
|
|
115
271
|
end # VPPable
|