ruby-jss 3.1.0 → 3.2.0b3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +32 -0
- data/lib/jamf/api/classic/api_objects/patch_policy.rb +13 -2
- data/lib/jamf/api/classic/api_objects/policy.rb +1 -1
- data/lib/jamf/api/classic/api_objects/scopable/scope.rb +406 -183
- data/lib/jamf/api/classic/api_objects/scopable.rb +16 -11
- data/lib/jamf/api/jamf_pro/api_objects/device_enrollment.rb +1 -2
- data/lib/jamf/version.rb +1 -1
- data/test/tests/policy.rb +0 -14
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f0e6a46c0c834feb898c00c2df2f509756a7f84cd346510c6f2502be433258a
|
4
|
+
data.tar.gz: f765f17913f8bf75f8ea9d066845b451150ce893139d089e797fed061a84929d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb2c176af326d5d03e7d2b650c5aa8d2bafd3de420e0a3514af9532a9a68619ec5bdc9888e718ebeec1f355061b5c931ae4663dd43310d7d8c3e8edf99fa121c
|
7
|
+
data.tar.gz: 12c3b89f22ed8e9a9384e8928d7bd4c5f09122276f793cf49e418ded3293e4a741bdfa2fad9ca96d47c6016d97fed63db5aeb447cf46cb4cf59e9b3582641700
|
data/CHANGES.md
CHANGED
@@ -15,6 +15,38 @@ __Please update all installations of ruby-jss to at least v1.6.0.__
|
|
15
15
|
Many many thanks to actae0n of Blacksun Hackers Club for reporting this issue and providing examples of how it could be exploited.
|
16
16
|
|
17
17
|
--------
|
18
|
+
## \[UNRELEASED]
|
19
|
+
|
20
|
+
### Changed
|
21
|
+
- Improved handling of known API bug in Jamf::Scopable::Scope.
|
22
|
+
|
23
|
+
There is a long-standing bug in working with 'scope' via the Classic API, which can cause data-loss when you have 'Users' and 'User Groups' (known in the api data as `jss_users` and `jss_user_groups`) defined in the targets or exclusions. Thanks to @yanniks on GitHub, I recently learned that the bug only affects a few API objects, namely Policies and PatchPolicies.
|
24
|
+
|
25
|
+
This release of ruby-jss will properly handle jss_users and jss_user_groups in all scopes where the API can handle them. In Policy and PatchPolicy objects, those values aren't allowed, since the API can't deal with them. If you edit any other aspect of the scope of one of those obects in ruby-jss, you'll get a warning that saving your changes may cause data-loss - deleting any defined Users and User Groups in the targets or exclusions. If the scope really doesn't use any Users or User Groups, you should be OK saving your changes. To prevent the warnings, call `Jamf::Scopable::Scope.do_not_warn_about_policy_scope_bugs` before changing any scopes.
|
26
|
+
|
27
|
+
For more details, see the discussion in the comments/docs for the Jamf::Scopeable::Scope class in lib/jamf/api/classic/api_objects/scopable/scope.rb or in the [rubydocs page for the Scope class](https://www.rubydoc.info/gems/ruby-jss/Jamf/Scopable/Scope).
|
28
|
+
|
29
|
+
Many thanks to @yanniks for bringing to my attention that the bug doesn't occur in all scopes.
|
30
|
+
|
31
|
+
- Warn of API bug when using jss_user_groups as scope targets of OSXConfigurationProfiles
|
32
|
+
|
33
|
+
We discovered a new (to us) isolated occurrance of the long-standing XML Array => JSON Hash bug
|
34
|
+
(which can cause data loss). If you have more that one jss_user_groups defined as scope targets
|
35
|
+
of a OSXConfigurationProfile, the API will only return the last of those groups in the JSON data,
|
36
|
+
and saving changes to the profile via ruby-jss will remove the other groups from the Profile in
|
37
|
+
Jamf.
|
38
|
+
|
39
|
+
This seems to only affect scope targets of OSXConfigurationProfiles - groups used in exclusions
|
40
|
+
seem to be fine, as do other scopable objects that uses jss_user_groups anywhere in their scope.
|
41
|
+
|
42
|
+
When you edit the scope of a scopable object and ruby-jss notices this API bug applies, you'll see a warning that saving changes to the scope may cause data loss. To disable these warnings, call `Jamf::Scopable::Scope.do_not_warn_about_array_hash_scope_bugs` before changing any scopes.
|
43
|
+
|
44
|
+
For more details, see the discussion in the comments/docs for the Jamf::Scopeable::Scope class in lib/jamf/api/classic/api_objects/scopable/scope.rb or in the [rubydocs page for the Scope class](https://www.rubydoc.info/gems/ruby-jss/Jamf/Scopable/Scope).
|
45
|
+
|
46
|
+
|
47
|
+
### Fixed
|
48
|
+
- Jamf::DeviceEnrollment.device no longer uses String#upcase!, which fails on frozen strings. Instead just use String#casecmp?
|
49
|
+
|
18
50
|
## \[3.1.0] 2023-06-06
|
19
51
|
|
20
52
|
### Added
|
@@ -408,7 +408,6 @@ module Jamf
|
|
408
408
|
@grace_period_message = grace[:message]
|
409
409
|
@grace_period_message = DFT_GRACE_PERIOD_MESSAGE if @grace_period_message.to_s.empty?
|
410
410
|
|
411
|
-
|
412
411
|
# read-only values, they come from the version.
|
413
412
|
@release_date = JSS.epoch_to_time gen[:release_date]
|
414
413
|
@incremental_update = gen[:incremental_update]
|
@@ -432,6 +431,7 @@ module Jamf
|
|
432
431
|
#
|
433
432
|
def patch_title_name
|
434
433
|
return @patch_title.name if @patch_title
|
434
|
+
|
435
435
|
Jamf::PatchTitle.map_all_ids_to(:name)[software_title_configuration_id]
|
436
436
|
end
|
437
437
|
|
@@ -439,6 +439,7 @@ module Jamf
|
|
439
439
|
#
|
440
440
|
def target_version=(new_tgt_vers)
|
441
441
|
return if new_tgt_vers == target_version
|
442
|
+
|
442
443
|
@target_version = validate_target_version new_tgt_vers
|
443
444
|
@need_to_update = true
|
444
445
|
@refetch_for_new_version = true
|
@@ -450,6 +451,7 @@ module Jamf
|
|
450
451
|
#
|
451
452
|
def enable
|
452
453
|
return if enabled
|
454
|
+
|
453
455
|
@enabled = true
|
454
456
|
@need_to_update = true
|
455
457
|
end
|
@@ -460,6 +462,7 @@ module Jamf
|
|
460
462
|
#
|
461
463
|
def disable
|
462
464
|
return unless enabled
|
465
|
+
|
463
466
|
@enabled = false
|
464
467
|
@need_to_update = true
|
465
468
|
end
|
@@ -468,6 +471,7 @@ module Jamf
|
|
468
471
|
#
|
469
472
|
def allow_downgrade=(new_val)
|
470
473
|
return if new_val == allow_downgrade
|
474
|
+
|
471
475
|
@allow_downgrade = Jamf::Validate.boolean new_val
|
472
476
|
@need_to_update = true
|
473
477
|
end
|
@@ -476,6 +480,7 @@ module Jamf
|
|
476
480
|
#
|
477
481
|
def patch_unknown=(new_val)
|
478
482
|
return if new_val == patch_unknown
|
483
|
+
|
479
484
|
@patch_unknown = Jamf::Validate.boolean new_val
|
480
485
|
@need_to_update = true
|
481
486
|
end
|
@@ -488,6 +493,7 @@ module Jamf
|
|
488
493
|
days = NO_DEADLINE unless days.positive?
|
489
494
|
end
|
490
495
|
return if days == deadline
|
496
|
+
|
491
497
|
@deadline = days
|
492
498
|
@need_to_update = true
|
493
499
|
end
|
@@ -498,6 +504,7 @@ module Jamf
|
|
498
504
|
mins = Jamf::Validate.integer(mins)
|
499
505
|
mins = 0 if mins.negative?
|
500
506
|
return if mins == grace_period
|
507
|
+
|
501
508
|
@grace_period = mins
|
502
509
|
@need_to_update = true
|
503
510
|
end
|
@@ -506,6 +513,7 @@ module Jamf
|
|
506
513
|
#
|
507
514
|
def grace_period_subject=(subj)
|
508
515
|
return if grace_period_subject == subj.to_s
|
516
|
+
|
509
517
|
@grace_period_subject = subj.to_s
|
510
518
|
@need_to_update = true
|
511
519
|
end
|
@@ -514,6 +522,7 @@ module Jamf
|
|
514
522
|
#
|
515
523
|
def grace_period_message=(msg)
|
516
524
|
return if grace_period_message == msg
|
525
|
+
|
517
526
|
@grace_period_message = msg
|
518
527
|
@need_to_update = true
|
519
528
|
end
|
@@ -563,8 +572,10 @@ module Jamf
|
|
563
572
|
return a_title.id
|
564
573
|
end
|
565
574
|
raise Jamf::MissingDataError, ':patch_title is required' unless a_title
|
575
|
+
|
566
576
|
title_id = Jamf::PatchTitle.valid_id a_title
|
567
577
|
return title_id if title_id
|
578
|
+
|
568
579
|
raise Jamf::NoSuchItemError, "No Patch Title matches '#{a_title}'"
|
569
580
|
end
|
570
581
|
|
@@ -613,7 +624,7 @@ module Jamf
|
|
613
624
|
general.add_element('allow_downgrade').text = allow_downgrade
|
614
625
|
general.add_element('patch_unknown').text = patch_unknown
|
615
626
|
|
616
|
-
obj << scope.scope_xml
|
627
|
+
obj << scope.scope_xml if scope.should_update?
|
617
628
|
|
618
629
|
add_self_service_xml doc
|
619
630
|
|
@@ -2111,7 +2111,7 @@ module Jamf
|
|
2111
2111
|
activation = @server_side_limitations[:activation]
|
2112
2112
|
date_time_limitations.add_element('activation_date_epoch').text = activation.to_jss_epoch if activation
|
2113
2113
|
|
2114
|
-
obj << @scope.scope_xml
|
2114
|
+
obj << @scope.scope_xml if @scope.should_update?
|
2115
2115
|
|
2116
2116
|
reboot = obj.add_element 'reboot'
|
2117
2117
|
JSS.hash_to_rexml_array(@reboot_options).each { |elem| reboot << elem }
|
@@ -35,7 +35,7 @@ module Jamf
|
|
35
35
|
#
|
36
36
|
# Scope data comes from the API as a hash within the overall object data.
|
37
37
|
# The main keys of the hash define the included targets of the scope. A
|
38
|
-
# sub-hash defines limitations on those
|
38
|
+
# sub-hash defines limitations on those targets, and another sub-hash
|
39
39
|
# defines explicit exclusions.
|
40
40
|
#
|
41
41
|
# This class provides methods for adding, removing, or fully replacing the
|
@@ -44,85 +44,143 @@ module Jamf
|
|
44
44
|
# This class also provides a way to see if a machine will be included in
|
45
45
|
# this scope.
|
46
46
|
#
|
47
|
-
#
|
47
|
+
# = Discussion: Users & User Groups in Scopes:
|
48
|
+
#######################################
|
49
|
+
# The Classic API has bugs, as well as non-obvious/historical oddness,
|
50
|
+
# regarding the use of Users, UserGroups, Directory Service/Local Users,
|
51
|
+
# and Directory Service User Groups in scopes.
|
52
|
+
# Here's a discussion of those issues, and how ruby-jss handles them.
|
48
53
|
#
|
49
|
-
#
|
50
|
-
# LDAP/Local Users, & LDAP User Groups in scopes. Here's a discussion
|
51
|
-
# of those bugs and how ruby-jss handles them.
|
54
|
+
# == Historical Oddness
|
52
55
|
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
# - You must use the Web UI to work with them in a Scope.
|
58
|
-
# - 'User Groups' in the Scope UI can only be Jamf::UserGroups - No LDAP
|
59
|
-
# - BUG: They do not appear in API data (XML or JSON) and are
|
60
|
-
# NOT SUPPORTED in ruby-jss.
|
61
|
-
# - You must use the Web UI to work with them in a Scope.
|
56
|
+
# Because the concept of 'scope' existed before Jamf Pro had 'Users'
|
57
|
+
# and 'User Groups' (Jamf::User and Jamf::UserGroup classes in ruby-jss)
|
58
|
+
# there is non-obvious inconsistency between the labels for API data, and
|
59
|
+
# the labels for that data in the web UI:
|
62
60
|
#
|
63
|
-
#
|
64
|
-
# - 'LDAP/Local Users' can be any string
|
65
|
-
# - The Web UI accepts any string, even if no matching Local or LDAP user.
|
66
|
-
# - The data shows up in API data in scope=>limitations=>users
|
67
|
-
# by name only (the string provided), no IDs
|
68
|
-
# - 'LDAP User Groups' can only be LDAP groups that actually exist
|
69
|
-
# - The Web UI won't let you add a group that doesn't exist in ldap
|
70
|
-
# - The data shows up in API data in scope=>limitations=>user_groups
|
71
|
-
# by name and LDAP ID (which may be empty)
|
72
|
-
# - The data ALSO shows up in API data in scope=>limit_to_users=>user_groups
|
73
|
-
# by name only, no LDAP IDs. ruby-jss ignores this and looks at
|
74
|
-
# scope=>limitations=>user_groups
|
61
|
+
# === Users
|
75
62
|
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
# - BUG: They do not appear in API data (XML or JSON) and are
|
79
|
-
# NOT SUPPORTED in ruby-jss.
|
80
|
-
# - You must use the Web UI to work with them in a Scope.
|
81
|
-
# - 'User Groups' in the Scope UI can only be Jamf::UserGroups - No LDAP
|
82
|
-
# - BUG: They do not appear in API data (XML or JSON) and are
|
83
|
-
# NOT SUPPORTED in ruby-jss.
|
84
|
-
# - You must use the Web UI to work with them in a Scope.
|
85
|
-
# - 'LDAP/Local Users' can be any string
|
86
|
-
# - The Web UI accepts any string, even if no matching Local or LDAP user.
|
87
|
-
# - The data shows up in API data in scope=>exclusions=>users
|
88
|
-
# by name only (the string provided), no IDs
|
89
|
-
# - 'LDAP User Groups' can only be LDAP groups that actually exist
|
90
|
-
# - The Web UI won't let you add a group that doesn't exist in ldap
|
91
|
-
# - The data shows up in API data in scope=>exclusions=>user_groups
|
92
|
-
# by name and LDAP ID (which may be empty)
|
63
|
+
# What appears in the UI as 'Users' are User objects in Jamf pro, which in ruby-jss
|
64
|
+
# are Jamf::User instances.
|
93
65
|
#
|
66
|
+
# These will appear in the API data as \<jss_users> element with \<user> sub-elements (XML)
|
67
|
+
# or the 'jss_users' array (JSON). These are available as Targets or Exclusions.
|
94
68
|
#
|
95
|
-
#
|
69
|
+
# In this class, they are also referred to as 'jss_users'
|
96
70
|
#
|
97
|
-
#
|
98
|
-
# :user, :users, :user_group, :user_groups.
|
71
|
+
# === Directory Service/Local Users
|
99
72
|
#
|
100
|
-
#
|
73
|
+
# When editing a scope in the UI, in Limitations and Exclusions, you can
|
74
|
+
# add arbitrary strings that will be matched to the users assigned to machines,
|
75
|
+
# or that appear in any of the defined LDAP servers.
|
76
|
+
# These scope items are called 'Directory Service/Local Users' but
|
77
|
+
# used to be called 'LDAP/Local Users'
|
101
78
|
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
# string(s) must exist as either a Jamf::User or an LDAP user
|
106
|
-
# - :user_group or :ldap_user_group (and their plurals) for working with
|
107
|
-
# 'LDAP User Groups'. When setting or adding, the provided string
|
108
|
-
# must exist as a group in LDAP.
|
79
|
+
# In the API data for scopes, these items appear in the \<users> element
|
80
|
+
# with \<user> sub-elements (XML) or 'users' array (JSON) of the limitations
|
81
|
+
# and exclusions data
|
109
82
|
#
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
# - :user_group or :ldap_user_group (and their plurals) for working with
|
115
|
-
# 'LDAP User Groups''. When setting or adding, the provided string
|
116
|
-
# must exist as a group in LDAP.
|
83
|
+
# In this class, these items ultimately use the same names they have in the API data:
|
84
|
+
# 'users' but when specifying that you are setting that value, you can use any of
|
85
|
+
# these synonyms, plural or singular:
|
86
|
+
# ldap_users, jamf_ldap_users, directory_service_local_users
|
117
87
|
#
|
118
|
-
#
|
88
|
+
# === User Groups
|
119
89
|
#
|
120
|
-
#
|
121
|
-
#
|
90
|
+
# What appears in the UI as 'User Groups' are User Group objects in Jamf Pro, both
|
91
|
+
# static and smart. In ruby-jss, these are Jamf::UserGroup instances.
|
122
92
|
#
|
123
|
-
#
|
124
|
-
#
|
93
|
+
# They will appear in the API data as \<jss_user_groups> element with \<user_group>
|
94
|
+
# sub-elements (XML) or the 'jss_user_groups' array (JSON). These are available as
|
95
|
+
# Targets or Exclusions.
|
125
96
|
#
|
97
|
+
# In this class they are also referred to as 'jss_user_groups'
|
98
|
+
#
|
99
|
+
# === Directory Service User Groups
|
100
|
+
#
|
101
|
+
# When editing a scope in the UI, in Limitations and Exclusions, you can
|
102
|
+
# look up and add groups from any of the defined LDAP servers.
|
103
|
+
# These scope items are called 'Directory Service User Groups' but
|
104
|
+
# used to be called 'LDAP User Groups'
|
105
|
+
#
|
106
|
+
# In the API data for scopes, these items appear in the \<user_groups> element
|
107
|
+
# with \<user_group> sub-elements (XML) or 'user_groups' array (JSON) of the limitations
|
108
|
+
# and exclusions data
|
109
|
+
#
|
110
|
+
# In this class, these items ultimately use the same names they have in the API data:
|
111
|
+
# 'user_groups' but when specifying that you are setting that value, you can use any of
|
112
|
+
# these synonyms, singular or plural:
|
113
|
+
# ldap_user_groups, directory_service_user_groups
|
114
|
+
#
|
115
|
+
###################################
|
116
|
+
# = IMPORTANT: API BUG IN POLICY AND PATCH POLICY SCOPES - CAN CAUSE DATA LOSS
|
117
|
+
###################################
|
118
|
+
#
|
119
|
+
# When you GET the data for policies and patch policies from the Classic API
|
120
|
+
# the scope data returned will NOT include the 'jss_users' and 'jss_user_groups'
|
121
|
+
# data in the targets or the exclusions, even if they are defined in the web UI.
|
122
|
+
#
|
123
|
+
# More importantly, if you try to include those in the XML when you PUT a policy
|
124
|
+
# back to make a change via the API, you'll get an error because the API endpoint
|
125
|
+
# doesn't know what <jss_users> or <jss_user_groups> elements are.
|
126
|
+
#
|
127
|
+
# Even more importanly, since you cannot include those elements in your PUT body,
|
128
|
+
# if they actually exist in the scope, THEY WILL BE ERASED from the actual scope,
|
129
|
+
# because they weren't in the PUT data.
|
130
|
+
# This will always happen if you include the <scope> element in your
|
131
|
+
# PUT data, even if you didn't change the scope.
|
132
|
+
#
|
133
|
+
# - How ruby-jss handles this bug:
|
134
|
+
#
|
135
|
+
# Fortunately the Classic API, or at least this part of it, doesn't fully adhere
|
136
|
+
# to the REST standards for PUT, and if you don't include the <scope> element in
|
137
|
+
# the XML, the server will just ignore the scope entirely, and nothing will change.
|
138
|
+
#
|
139
|
+
# We make use of that here to allow for editing Policies without fear of erasing
|
140
|
+
# those parts of the scope. As long as you don't change anything about the scope,
|
141
|
+
# there will be no <scope> element in the XML sent with a PUT, and the scope is
|
142
|
+
# safe from harm.
|
143
|
+
#
|
144
|
+
# If you DO change the scope of a policy, this bug cannot be avoided, and you'll
|
145
|
+
# delete any "User"/jss_user and "User Groups/jss_user_groups" defined in the
|
146
|
+
# targets or exclusions.
|
147
|
+
#
|
148
|
+
# By default, if you try to change the scope of a Policy of PatchPolicy, you'll
|
149
|
+
# get a warning about the possibility of losing data when you save.
|
150
|
+
#
|
151
|
+
# You can supress those warnings either by supressing all ruby warnings, or
|
152
|
+
# by calling Jamf::Scopable::Scope.do_not_warn_about_policy_scope_bugs
|
153
|
+
#
|
154
|
+
###################################
|
155
|
+
# = IMPORTANT: API BUG IN OSX CONFIG PROFILE SCOPES - CAN CAUSE DATA LOSS
|
156
|
+
###################################
|
157
|
+
#
|
158
|
+
# When fetching the data for OSX Configuration Profiles using JSON (which ruby-jss does)
|
159
|
+
# and the scope of the profile contains more than one `jss_user_groups` as a target, then
|
160
|
+
# only the last one will be returned. If you have more than one such group as a target, and
|
161
|
+
# use ruby-jss to make changes to the scope, all but the last jss_user_groups used as targets will
|
162
|
+
# be removed.
|
163
|
+
#
|
164
|
+
# This only appears to affect scope targets, not exclusions, and only for
|
165
|
+
# OSX Config Profiles. Other scopable objects that use jss_user_groups in their API data
|
166
|
+
# seem to be OK.
|
167
|
+
#
|
168
|
+
# This is due to a long-standing API bug regarding how Arrays in XML are incorrectly
|
169
|
+
# translated into Hashes of a single Hash when returning the data as JSON - they shoud be
|
170
|
+
# Arrays of Hashes in JSON - one hash for each item.
|
171
|
+
#
|
172
|
+
# Even though this bug was first reported to jamf in 2009, it still appears in many places throughout
|
173
|
+
# the Classic API. ruby-jss works around some of the worst instances of the bug, but such workarounds
|
174
|
+
# are complex requiring re-fetching the data in XML and parsing it manually. At the moment there are no
|
175
|
+
# plans to do that for this specific scope bug.
|
176
|
+
#
|
177
|
+
# By default, if you try to change the scope of an object affected by this bug, you'll
|
178
|
+
# get a warning about the possibility of losing data when you save.
|
179
|
+
#
|
180
|
+
# You can supress those warnings either by supressing all ruby warnings, or
|
181
|
+
# by calling Jamf::Scopable::Scope.do_not_warn_about_array_hash_scope_bugs
|
182
|
+
#
|
183
|
+
########################
|
126
184
|
#
|
127
185
|
# @see Jamf::Scopable
|
128
186
|
#
|
@@ -133,38 +191,93 @@ module Jamf
|
|
133
191
|
|
134
192
|
# These are the classes that Scopes can use for defining a scope,
|
135
193
|
# keyed by appropriate symbols.
|
136
|
-
#
|
137
|
-
#
|
194
|
+
#
|
195
|
+
# synonyms, including singular/plural forms, are used to allow for
|
196
|
+
# more natural language when specifying these scope entities. The
|
197
|
+
# key used in the actual API data is usually the plural.
|
198
|
+
#
|
199
|
+
# NOTE: user[s] and user_group[s] in Scope data refer to
|
200
|
+
# 'Directory Service/Local User' and 'Directory Service User Group'
|
201
|
+
# as labeled in the web-ui. These were formerly labeled
|
202
|
+
# as 'LDAP/Local User' and 'LDAP User Group'.
|
203
|
+
#
|
138
204
|
SCOPING_CLASSES = {
|
139
205
|
computers: Jamf::Computer,
|
140
206
|
computer: Jamf::Computer,
|
207
|
+
|
141
208
|
computer_groups: Jamf::ComputerGroup,
|
142
209
|
computer_group: Jamf::ComputerGroup,
|
210
|
+
|
143
211
|
mobile_devices: Jamf::MobileDevice,
|
144
212
|
mobile_device: Jamf::MobileDevice,
|
213
|
+
|
145
214
|
mobile_device_groups: Jamf::MobileDeviceGroup,
|
146
215
|
mobile_device_group: Jamf::MobileDeviceGroup,
|
216
|
+
|
147
217
|
buildings: Jamf::Building,
|
148
218
|
building: Jamf::Building,
|
219
|
+
|
149
220
|
departments: Jamf::Department,
|
150
221
|
department: Jamf::Department,
|
222
|
+
|
151
223
|
network_segments: Jamf::NetworkSegment,
|
152
224
|
network_segment: Jamf::NetworkSegment,
|
153
|
-
|
225
|
+
|
154
226
|
ibeacons: Jamf::IBeacon,
|
155
|
-
|
227
|
+
ibeacon: Jamf::IBeacon,
|
228
|
+
|
229
|
+
jss_users: Jamf::User,
|
230
|
+
jss_user: Jamf::User,
|
231
|
+
|
232
|
+
jss_user_groups: Jamf::UserGroup,
|
233
|
+
jss_user_group: Jamf::UserGroup,
|
234
|
+
|
156
235
|
users: nil,
|
157
|
-
|
236
|
+
user: nil,
|
158
237
|
ldap_users: nil,
|
159
|
-
|
238
|
+
ldap_user: nil,
|
160
239
|
jamf_ldap_users: nil,
|
161
|
-
|
240
|
+
jamf_ldap_user: nil,
|
241
|
+
directory_service_local_users: nil,
|
242
|
+
directory_service_local_user: nil,
|
243
|
+
|
162
244
|
user_groups: nil,
|
245
|
+
user_group: nil,
|
246
|
+
ldap_user_groups: nil,
|
163
247
|
ldap_user_group: nil,
|
164
|
-
|
248
|
+
directory_service_user_groups: nil,
|
249
|
+
directory_service_user_group: nil
|
165
250
|
}.freeze
|
166
251
|
|
167
|
-
# These
|
252
|
+
# These classes are affected by the jss_users/jss_user_groups bug.
|
253
|
+
#
|
254
|
+
# They do not accept jss_users or jss_user_groups in their targets or
|
255
|
+
# exclusions, and editing their scope via the API will always delete those
|
256
|
+
# items from the scope if they exist.
|
257
|
+
#
|
258
|
+
# See discussion in the Scope class comments.
|
259
|
+
JAMF_DATA_LOSS_BUG_CLASSES = [
|
260
|
+
Jamf::Policy,
|
261
|
+
Jamf::PatchPolicy
|
262
|
+
].freeze
|
263
|
+
|
264
|
+
# The classes affected by the jss_users/jss_user_groups bug do not
|
265
|
+
# include these items in their Target or Exclusion API data, even if
|
266
|
+
# the scope has such items defined in the JSS
|
267
|
+
#
|
268
|
+
# See discussion in the Scope class comments.
|
269
|
+
JAMF_DATA_LOSS_BUG_KEYS = %i[jss_users jss_user_groups].freeze
|
270
|
+
|
271
|
+
# In the API data for limitations and exclusions
|
272
|
+
# 'users' is what appears as Directory Service/Local Users in the web UI
|
273
|
+
# and 'user_groups' appears as 'Directory Service User Groups'.
|
274
|
+
#
|
275
|
+
# Contrasted with 'jss_users' and 'jss_user_groups' in the API data for
|
276
|
+
# targets and exlcusions, which are Jamf::User and Jamf::UserGroup objects.
|
277
|
+
#
|
278
|
+
LDAP_BASED_KEYS = %i[users user_groups].freeze
|
279
|
+
|
280
|
+
# These keys always mean :users
|
168
281
|
LDAP_JAMF_USER_KEYS = %i[
|
169
282
|
user
|
170
283
|
users
|
@@ -172,14 +285,18 @@ module Jamf
|
|
172
285
|
ldap_users
|
173
286
|
jamf_ldap_user
|
174
287
|
jamf_ldap_users
|
288
|
+
directory_service_local_user
|
289
|
+
directory_service_local_users
|
175
290
|
].freeze
|
176
291
|
|
177
|
-
# These keys always mean :
|
292
|
+
# These keys always mean :user_groups
|
178
293
|
LDAP_GROUP_KEYS = %i[
|
179
294
|
user_group
|
180
295
|
user_groups
|
181
296
|
ldap_user_group
|
182
297
|
ldap_user_groups
|
298
|
+
directory_service_user_group
|
299
|
+
directory_service_user_groups
|
183
300
|
].freeze
|
184
301
|
|
185
302
|
# This hash maps the availble Scope Target keys from SCOPING_CLASSES to
|
@@ -189,9 +306,12 @@ module Jamf
|
|
189
306
|
# added to the ends of singular key names if needed, e.g. computer_group => computer_groups
|
190
307
|
ESS = 's'.freeze
|
191
308
|
|
192
|
-
# These can be part of the base
|
309
|
+
# These can be part of the base target list of the scope,
|
193
310
|
# along with the appropriate target and target group keys
|
194
|
-
|
311
|
+
TARGETS = %i[buildings departments jss_users jss_user_groups].freeze
|
312
|
+
|
313
|
+
# Backward Compatibility
|
314
|
+
INCLUSIONS = TARGETS
|
195
315
|
|
196
316
|
# These can limit the inclusion list
|
197
317
|
# These are the keys that come from the API
|
@@ -201,21 +321,47 @@ module Jamf
|
|
201
321
|
LIMITATIONS = %i[
|
202
322
|
ibeacons
|
203
323
|
network_segments
|
204
|
-
|
205
|
-
|
324
|
+
users
|
325
|
+
user_groups
|
206
326
|
].freeze
|
207
327
|
|
208
328
|
# any of them can be excluded
|
209
|
-
EXCLUSIONS =
|
329
|
+
EXCLUSIONS = TARGETS + LIMITATIONS
|
210
330
|
|
211
331
|
# Here's a default scope as it might come from the API.
|
212
332
|
DEFAULT_SCOPE = {
|
213
|
-
all_computers:
|
214
|
-
all_mobile_devices:
|
333
|
+
all_computers: false,
|
334
|
+
all_mobile_devices: false,
|
215
335
|
limitations: {},
|
216
336
|
exclusions: {}
|
217
337
|
}.freeze
|
218
338
|
|
339
|
+
# Class Methods
|
340
|
+
######################
|
341
|
+
|
342
|
+
# call this to suppress warnings about data loss bug in
|
343
|
+
# Policy and Patch Policy scopes
|
344
|
+
def self.do_not_warn_about_policy_scope_bugs
|
345
|
+
@do_not_warn_about_policy_scope_bugs = true
|
346
|
+
end
|
347
|
+
|
348
|
+
# Has do_not_warn_about_policy_scope_bugs been set?
|
349
|
+
def self.do_not_warn_about_policy_scope_bugs?
|
350
|
+
@do_not_warn_about_policy_scope_bugs
|
351
|
+
end
|
352
|
+
|
353
|
+
# call this to suppress warnings about data loss bug in
|
354
|
+
# OSXConfigurationProfile scopes when there are jss_user_groups
|
355
|
+
# used as targets
|
356
|
+
def self.do_not_warn_about_array_hash_scope_bugs
|
357
|
+
@do_not_warn_about_array_hash_scope_bugs = true
|
358
|
+
end
|
359
|
+
|
360
|
+
# Has do_not_warn_about_policy_scope_bugs been set?
|
361
|
+
def self.do_not_warn_about_array_hash_scope_bugs?
|
362
|
+
@do_not_warn_about_array_hash_scope_bugs
|
363
|
+
end
|
364
|
+
|
219
365
|
# Attributes
|
220
366
|
######################
|
221
367
|
|
@@ -257,6 +403,9 @@ module Jamf
|
|
257
403
|
# - :group_targets - a synonym for :computer_groups or :mobile_device_groups
|
258
404
|
# - :departments
|
259
405
|
# - :buildings
|
406
|
+
# - :jss_users
|
407
|
+
# - :jss_user_groups
|
408
|
+
#
|
260
409
|
# and the values are Arrays of names of those things.
|
261
410
|
#
|
262
411
|
# @return [Hash{Symbol: Array<Integer>}]
|
@@ -268,8 +417,9 @@ module Jamf
|
|
268
417
|
#
|
269
418
|
# The arrays of ids are:
|
270
419
|
# - :network_segments
|
271
|
-
# - :
|
420
|
+
# - :users
|
272
421
|
# - :user_groups
|
422
|
+
# - :ibeacons
|
273
423
|
#
|
274
424
|
# @return [Hash{Symbol: Array<Integer, String>}]
|
275
425
|
attr_reader :limitations
|
@@ -284,37 +434,64 @@ module Jamf
|
|
284
434
|
# - :departments
|
285
435
|
# - :buildings
|
286
436
|
# - :network_segments
|
437
|
+
# - :jss_users
|
438
|
+
# - :jss_user_groups
|
287
439
|
# - :users
|
288
|
-
# - :user_groups
|
289
|
-
#
|
440
|
+
# - :user_groups #
|
290
441
|
# @return [Hash{Symbol: Array<Integer, String>}]
|
291
442
|
attr_reader :exclusions
|
292
443
|
|
444
|
+
# @return [Boolean] Have changes been made to the scope, that need
|
445
|
+
# to be sent to the server?
|
446
|
+
attr_accessor :should_update
|
447
|
+
alias should_update? should_update
|
448
|
+
|
293
449
|
# Public Instance Methods
|
294
450
|
#####################################
|
295
451
|
|
296
|
-
# If raw_scope is empty, a default scope, scoped to
|
452
|
+
# If raw_scope is empty, a default scope, scoped to no targets, is created, and can be modified
|
297
453
|
# as needed.
|
298
454
|
#
|
299
|
-
# @param target_key[Symbol] the kind of thing we're scoping,
|
455
|
+
# @param target_key[Symbol] the kind of thing we're scoping, a key from {TARGETS_AND_GROUPS}
|
300
456
|
#
|
301
457
|
# @param raw_scope[Hash] the JSON :scope data from an API query that is scopable, e.g. a Policy.
|
302
458
|
#
|
303
|
-
|
459
|
+
# @param container[Jamf::APIObject] The scopable object to which this scope belongs, e,g, an
|
460
|
+
# instance of Jamf::Policy, Jamf::MobileDeviceApplication, etc.. If not provided, will be
|
461
|
+
# set automatically after initialization
|
462
|
+
#
|
463
|
+
###########################
|
464
|
+
def initialize(target_key, raw_scope = nil, container: nil)
|
304
465
|
raw_scope ||= DEFAULT_SCOPE.dup
|
305
466
|
unless TARGETS_AND_GROUPS.key?(target_key)
|
306
467
|
raise Jamf::InvalidDataError, "The target class of a Scope must be one of the symbols :#{TARGETS_AND_GROUPS.keys.join(', :')}"
|
307
468
|
end
|
308
469
|
|
470
|
+
@should_update = false
|
471
|
+
@container = container
|
472
|
+
|
309
473
|
@target_key = target_key
|
310
474
|
@target_class = SCOPING_CLASSES[@target_key]
|
311
475
|
@group_key = TARGETS_AND_GROUPS[@target_key]
|
312
476
|
@group_class = SCOPING_CLASSES[@group_key]
|
313
477
|
|
314
|
-
@target_keys = [@target_key, @group_key] +
|
478
|
+
@target_keys = [@target_key, @group_key] + TARGETS
|
315
479
|
@exclusion_keys = [@target_key, @group_key] + EXCLUSIONS
|
316
480
|
|
317
|
-
|
481
|
+
if JAMF_DATA_LOSS_BUG_CLASSES.include?(@container.class)
|
482
|
+
@target_keys -= JAMF_DATA_LOSS_BUG_KEYS
|
483
|
+
@exclusion_keys -= JAMF_DATA_LOSS_BUG_KEYS
|
484
|
+
end
|
485
|
+
|
486
|
+
parse_targets(raw_scope)
|
487
|
+
parse_limitations(raw_scope)
|
488
|
+
parse_exclusions(raw_scope)
|
489
|
+
end # init
|
490
|
+
|
491
|
+
# parse the targets from the init data
|
492
|
+
###########################
|
493
|
+
def parse_targets(raw_scope)
|
494
|
+
@all_key = "all_#{@target_key}".to_sym
|
318
495
|
@all_targets = raw_scope[@all_key]
|
319
496
|
|
320
497
|
# Everything gets mapped from an Array of Hashes to
|
@@ -322,73 +499,64 @@ module Jamf
|
|
322
499
|
@targets = {}
|
323
500
|
@target_keys.each do |k|
|
324
501
|
raw_scope[k] ||= []
|
325
|
-
@targets[k] =
|
502
|
+
@targets[k] =
|
503
|
+
if raw_scope[k].is_a? Array
|
504
|
+
# the data should be an array of hashes with :id and :name
|
505
|
+
raw_scope[k].compact.map { |n| n[:id].to_i }
|
506
|
+
|
507
|
+
elsif raw_scope[k].is_a? Hash
|
508
|
+
# its a hash of hashes, it suffers the 2009 XML->JSON Array bug and
|
509
|
+
# there will be data loss of any more than one item.
|
510
|
+
# We know this to be the case for OSXConfigProfiles using
|
511
|
+
# jss_user_groups as targets. When used as exclusions, they are
|
512
|
+
# the correct array of hashes
|
513
|
+
@array_hash_bug_target_key = k unless raw_scope[k].empty?
|
514
|
+
raw_scope[k].values.compact.map { |n| n[:id].to_i }
|
515
|
+
else
|
516
|
+
[]
|
517
|
+
end
|
326
518
|
@targets[:direct_targets] = @targets[k] if k == @target_key
|
327
519
|
@targets[:group_targets] = @targets[k] if k == @group_key
|
328
520
|
end # @target_keys.each do |k|
|
521
|
+
end
|
522
|
+
private :parse_targets
|
329
523
|
|
330
|
-
|
331
|
-
|
332
|
-
|
524
|
+
# parse the limitations from the init data
|
525
|
+
###########################
|
526
|
+
def parse_limitations(raw_scope)
|
333
527
|
@limitations = {}
|
334
|
-
|
335
|
-
|
336
|
-
LIMITATIONS.each do |k|
|
337
|
-
# :jamf_ldap_users comes from :users in the API data
|
338
|
-
if k == :jamf_ldap_users
|
339
|
-
api_data = raw_scope[:limitations][:users]
|
340
|
-
api_data ||= []
|
341
|
-
@limitations[k] = api_data.compact.map { |n| n[:name].to_s }
|
342
|
-
|
343
|
-
# :ldap_user_groups comes from :user_groups in the API data
|
344
|
-
elsif k == :ldap_user_groups
|
345
|
-
api_data = raw_scope[:limitations][:user_groups]
|
346
|
-
api_data ||= []
|
347
|
-
@limitations[k] = api_data.compact.map { |n| n[:name].to_s }
|
348
|
-
|
349
|
-
# others handled normally.
|
350
|
-
else
|
351
|
-
api_data = raw_scope[:limitations][k]
|
352
|
-
api_data ||= []
|
353
|
-
@limitations[k] = api_data.compact.map { |n| n[:id].to_i }
|
354
|
-
end
|
355
|
-
end # LIMITATIONS.each do |k|
|
356
|
-
end # if raw_scope[:limitations]
|
528
|
+
return unless raw_scope[:limitations]
|
357
529
|
|
358
|
-
|
359
|
-
|
360
|
-
|
530
|
+
LIMITATIONS.each do |k|
|
531
|
+
api_data = raw_scope[:limitations][k]
|
532
|
+
api_data ||= []
|
533
|
+
@limitations[k] = api_data.compact.map do |n|
|
534
|
+
LDAP_BASED_KEYS.include?(k) ? n[:name].to_s : n[:id].to_i
|
535
|
+
end
|
536
|
+
end # LIMITATIONS.each do |k|
|
537
|
+
end
|
538
|
+
private :parse_limitations
|
539
|
+
|
540
|
+
# parse the limitations from the init data
|
541
|
+
###########################
|
542
|
+
def parse_exclusions(raw_scope)
|
361
543
|
@exclusions = {}
|
362
|
-
|
363
|
-
|
364
|
-
@exclusion_keys.each do |k|
|
365
|
-
# :jamf_ldap_users comes from :users in the API data
|
366
|
-
if k == :jamf_ldap_users
|
367
|
-
api_data = raw_scope[:exclusions][:users]
|
368
|
-
api_data ||= []
|
369
|
-
@exclusions[k] = api_data.compact.map { |n| n[:name].to_s }
|
370
|
-
|
371
|
-
# :ldap_user_groups comes from :user_groups in the API data
|
372
|
-
elsif k == :ldap_user_groups
|
373
|
-
api_data = raw_scope[:exclusions][:user_groups]
|
374
|
-
api_data ||= []
|
375
|
-
@exclusions[k] = api_data.compact.map { |n| n[:name].to_s }
|
376
|
-
|
377
|
-
# others handled normally.
|
378
|
-
else
|
379
|
-
api_data = raw_scope[:exclusions][k]
|
380
|
-
api_data ||= []
|
381
|
-
@exclusions[k] = api_data.compact.map { |n| n[:id].to_i }
|
382
|
-
@exclusions[:direct_exclusions] = @exclusions[k] if k == @target_key
|
383
|
-
@exclusions[:group_exclusions] = @exclusions[k] if k == @group_key
|
384
|
-
end # if ...elsif... else
|
385
|
-
end # @exclusion_keys.each
|
386
|
-
end # if raw_scope[:exclusions]
|
387
|
-
|
388
|
-
@container = nil
|
389
|
-
end # init
|
544
|
+
return unless raw_scope[:exclusions]
|
390
545
|
|
391
|
-
|
546
|
+
@exclusion_keys.each do |k|
|
547
|
+
api_data = raw_scope[:exclusions][k]
|
548
|
+
api_data ||= []
|
549
|
+
@exclusions[k] = api_data.compact.map do |n|
|
550
|
+
LDAP_BASED_KEYS.include?(k) ? n[:name].to_s : n[:id].to_i
|
551
|
+
end
|
552
|
+
|
553
|
+
@exclusions[:direct_exclusions] = @exclusions[k] if k == @target_key
|
554
|
+
@exclusions[:group_exclusions] = @exclusions[k] if k == @group_key
|
555
|
+
end # @exclusion_keys.each
|
556
|
+
end
|
557
|
+
private :parse_exclusions
|
558
|
+
|
559
|
+
# Set the scope's targets to all.
|
392
560
|
#
|
393
561
|
# By default, the limitations and exclusions remain.
|
394
562
|
# If a non-false parameter is provided, they will be removed also.
|
@@ -408,7 +576,7 @@ module Jamf
|
|
408
576
|
@exclusions = {}
|
409
577
|
@exclusion_keys.each { |k| @exclusions[k] = [] }
|
410
578
|
end
|
411
|
-
|
579
|
+
note_pending_changes
|
412
580
|
end
|
413
581
|
alias include_all set_all_targets
|
414
582
|
|
@@ -455,7 +623,7 @@ module Jamf
|
|
455
623
|
|
456
624
|
@targets[key] = list
|
457
625
|
@all_targets = false
|
458
|
-
|
626
|
+
note_pending_changes
|
459
627
|
end # sinclude_in_scope
|
460
628
|
alias set_target set_targets
|
461
629
|
alias set_inclusion set_targets
|
@@ -472,7 +640,7 @@ module Jamf
|
|
472
640
|
set_all_targets
|
473
641
|
else
|
474
642
|
@all_targets = false
|
475
|
-
|
643
|
+
note_pending_changes
|
476
644
|
end
|
477
645
|
end
|
478
646
|
|
@@ -511,7 +679,7 @@ module Jamf
|
|
511
679
|
|
512
680
|
@targets[key] << item_id
|
513
681
|
@all_targets = false
|
514
|
-
|
682
|
+
note_pending_changes
|
515
683
|
end
|
516
684
|
alias add_inclusion add_target
|
517
685
|
|
@@ -533,7 +701,7 @@ module Jamf
|
|
533
701
|
return unless @targets[key]&.include?(item_id)
|
534
702
|
|
535
703
|
@targets[key].delete item_id
|
536
|
-
|
704
|
+
note_pending_changes
|
537
705
|
end
|
538
706
|
alias remove_inclusion remove_target
|
539
707
|
|
@@ -553,7 +721,7 @@ module Jamf
|
|
553
721
|
#
|
554
722
|
# @todo handle ldap user group lookups
|
555
723
|
#
|
556
|
-
def
|
724
|
+
def set_limitations(key, list)
|
557
725
|
key = pluralize_key(key)
|
558
726
|
raise Jamf::InvalidDataError, "List must be an Array of #{key} identifiers, it may be empty." unless list.is_a? Array
|
559
727
|
|
@@ -570,9 +738,9 @@ module Jamf
|
|
570
738
|
return nil if list.sort == @limitations[key].sort
|
571
739
|
|
572
740
|
@limitations[key] = list
|
573
|
-
|
741
|
+
note_pending_changes
|
574
742
|
end # set_limitation
|
575
|
-
alias set_limitations
|
743
|
+
alias set_limitation set_limitations
|
576
744
|
|
577
745
|
# Add a single item for limiting this scope.
|
578
746
|
#
|
@@ -599,7 +767,7 @@ module Jamf
|
|
599
767
|
end
|
600
768
|
|
601
769
|
@limitations[key] << item_id
|
602
|
-
|
770
|
+
note_pending_changes
|
603
771
|
end
|
604
772
|
|
605
773
|
# Remove a single item for limiting this scope.
|
@@ -622,7 +790,7 @@ module Jamf
|
|
622
790
|
return unless @limitations[key]&.include?(item_id)
|
623
791
|
|
624
792
|
@limitations[key].delete item_id
|
625
|
-
|
793
|
+
note_pending_changes
|
626
794
|
end ###
|
627
795
|
|
628
796
|
# Replace an exclusion list for this scope
|
@@ -639,7 +807,7 @@ module Jamf
|
|
639
807
|
#
|
640
808
|
# @return [void]
|
641
809
|
#
|
642
|
-
def
|
810
|
+
def set_exclusions(key, list)
|
643
811
|
key = pluralize_key(key)
|
644
812
|
raise Jamf::InvalidDataError, "List must be an Array of #{key} identifiers, it may be empty." unless list.is_a? Array
|
645
813
|
|
@@ -662,8 +830,9 @@ module Jamf
|
|
662
830
|
return nil if list.sort == @exclusions[key].sort
|
663
831
|
|
664
832
|
@exclusions[key] = list
|
665
|
-
|
666
|
-
end #
|
833
|
+
note_pending_changes
|
834
|
+
end # set_exclusion
|
835
|
+
alias set_exclusion set_exclusions
|
667
836
|
|
668
837
|
# Add a single item for exclusions of this scope.
|
669
838
|
#
|
@@ -688,7 +857,7 @@ module Jamf
|
|
688
857
|
raise Jamf::AlreadyExistsError, "Can't exclude #{key} '#{item}' because it's already an explicit limitation." if @limitations[key]&.include?(item)
|
689
858
|
|
690
859
|
@exclusions[key] << item_id
|
691
|
-
|
860
|
+
note_pending_changes
|
692
861
|
end
|
693
862
|
|
694
863
|
# Remove a single item for exclusions of this scope
|
@@ -708,7 +877,7 @@ module Jamf
|
|
708
877
|
return unless @exclusions[key]&.include?(item_id)
|
709
878
|
|
710
879
|
@exclusions[key].delete item_id
|
711
|
-
|
880
|
+
note_pending_changes
|
712
881
|
end
|
713
882
|
|
714
883
|
# @api private
|
@@ -726,20 +895,24 @@ module Jamf
|
|
726
895
|
list.compact!
|
727
896
|
list.delete 0
|
728
897
|
list_as_hashes = list.map { |i| { id: i } }
|
729
|
-
|
898
|
+
|
899
|
+
xml_list = SCOPING_CLASSES[klass].xml_list(list_as_hashes, :id)
|
900
|
+
xml_list.name = 'jss_users' if SCOPING_CLASSES[klass] == Jamf::User
|
901
|
+
xml_list.name = 'jss_user_groups' if SCOPING_CLASSES[klass] == Jamf::UserGroup
|
902
|
+
scope << xml_list
|
730
903
|
end
|
731
904
|
|
732
905
|
limitations = scope.add_element('limitations')
|
733
906
|
@limitations.each do |klass, list|
|
734
907
|
list.compact!
|
735
908
|
list.delete 0
|
736
|
-
if klass == :
|
909
|
+
if klass == :users
|
737
910
|
users_xml = limitations.add_element 'users'
|
738
911
|
list.each do |name|
|
739
912
|
user_xml = users_xml.add_element 'user'
|
740
913
|
user_xml.add_element('name').text = name
|
741
914
|
end
|
742
|
-
elsif klass == :
|
915
|
+
elsif klass == :user_groups
|
743
916
|
user_groups_xml = limitations.add_element 'user_groups'
|
744
917
|
list.each do |name|
|
745
918
|
user_group_xml = user_groups_xml.add_element 'user_group'
|
@@ -756,13 +929,13 @@ module Jamf
|
|
756
929
|
list = @exclusions[klass]
|
757
930
|
list.compact!
|
758
931
|
list.delete 0
|
759
|
-
if klass == :
|
932
|
+
if klass == :users
|
760
933
|
users_xml = exclusions.add_element 'users'
|
761
934
|
list.each do |name|
|
762
935
|
user_xml = users_xml.add_element 'user'
|
763
936
|
user_xml.add_element('name').text = name
|
764
937
|
end
|
765
|
-
elsif klass == :
|
938
|
+
elsif klass == :user_groups
|
766
939
|
user_groups_xml = exclusions.add_element 'user_groups'
|
767
940
|
list.each do |name|
|
768
941
|
user_group_xml = user_groups_xml.add_element 'user_group'
|
@@ -770,13 +943,18 @@ module Jamf
|
|
770
943
|
end
|
771
944
|
else
|
772
945
|
list_as_hashes = list.map { |i| { id: i } }
|
773
|
-
|
946
|
+
|
947
|
+
xml_list = SCOPING_CLASSES[klass].xml_list(list_as_hashes, :id)
|
948
|
+
xml_list.name = 'jss_users' if SCOPING_CLASSES[klass] == Jamf::User
|
949
|
+
xml_list.name = 'jss_user_groups' if SCOPING_CLASSES[klass] == Jamf::UserGroup
|
950
|
+
exclusions << xml_list
|
951
|
+
|
774
952
|
end
|
775
953
|
end
|
776
954
|
scope
|
777
955
|
end # scope_xml
|
778
956
|
|
779
|
-
# Remove
|
957
|
+
# Remove large or redundant data structures from
|
780
958
|
# the instance_variables used to create
|
781
959
|
# pretty-print (pp) output.
|
782
960
|
#
|
@@ -869,28 +1047,41 @@ module Jamf
|
|
869
1047
|
#
|
870
1048
|
def validate_item(realm, key, ident, error_if_not_found: true)
|
871
1049
|
# which keys allowed depends on how the item is used...
|
1050
|
+
# Classes susceptible to the Data Loss Bug have JAMF_DATA_LOSS_BUG_KEYS
|
1051
|
+
# removed from the possible keys.
|
872
1052
|
possible_keys =
|
873
1053
|
case realm
|
874
|
-
when :target
|
875
|
-
|
876
|
-
when :
|
1054
|
+
when :target
|
1055
|
+
JAMF_DATA_LOSS_BUG_CLASSES.include?(@container.class) ? (@target_keys - JAMF_DATA_LOSS_BUG_KEYS) : @target_keys
|
1056
|
+
when :limitation
|
1057
|
+
LIMITATIONS
|
1058
|
+
when :exclusion
|
1059
|
+
JAMF_DATA_LOSS_BUG_CLASSES.include?(@container.class) ? (@exclusion_keys - JAMF_DATA_LOSS_BUG_KEYS) : @exclusion_keys
|
877
1060
|
else
|
878
1061
|
raise ArgumentError, 'Unknown realm, must be :target, :limitation, or :exclusion'
|
879
1062
|
end
|
880
1063
|
|
881
1064
|
key = pluralize_key(key)
|
882
1065
|
|
883
|
-
|
884
|
-
|
1066
|
+
unless possible_keys.include? key
|
1067
|
+
msg = "#{realm} key must be one of :#{possible_keys.join(', :')}."
|
1068
|
+
if JAMF_DATA_LOSS_BUG_CLASSES.include?(@container.class) && JAMF_DATA_LOSS_BUG_KEYS.include?(key)
|
1069
|
+
msg = "#{msg}\nJAMF BUG WARNING: The API cannot handle :jss_users or :jss_user_groups in scope targets or exclusions of Policies or Patch Policies. If any exist in the scope they will be deleted when you save any changes to the policy via the API."
|
1070
|
+
end
|
1071
|
+
raise Jamf::InvalidDataError, msg
|
1072
|
+
end
|
885
1073
|
|
886
1074
|
id = nil
|
887
1075
|
|
888
1076
|
# id will be a string
|
889
|
-
if key == :
|
890
|
-
id = ident
|
1077
|
+
if key == :users
|
1078
|
+
id = ident
|
1079
|
+
# the web UI doesn't validate this data, it accepts any string, so we do too
|
1080
|
+
# id = ident if Jamf::User.all_names(:refresh, cnx: container.cnx).include?(ident) || Jamf::LdapServer.user_in_ldap?(ident)
|
891
1081
|
|
892
1082
|
# id will be a string
|
893
|
-
elsif key == :
|
1083
|
+
elsif key == :user_groups
|
1084
|
+
# The web UI does validate that the group exists in LDAP
|
894
1085
|
id = ident if Jamf::LdapServer.group_in_ldap? ident, cnx: container.cnx
|
895
1086
|
|
896
1087
|
# id will be an integer
|
@@ -905,11 +1096,12 @@ module Jamf
|
|
905
1096
|
|
906
1097
|
# the symbols used in the API data are plural, e.g. 'network_segments'
|
907
1098
|
# this will pluralize them, allowing us to use singulars as well.
|
1099
|
+
# This also handles the synonyms for users and user_groups
|
908
1100
|
def pluralize_key(key)
|
909
1101
|
if LDAP_JAMF_USER_KEYS.include? key
|
910
|
-
:
|
1102
|
+
:users
|
911
1103
|
elsif LDAP_GROUP_KEYS.include? key
|
912
|
-
:
|
1104
|
+
:user_groups
|
913
1105
|
else
|
914
1106
|
key.to_s.end_with?(ESS) ? key : "#{key}s".to_sym
|
915
1107
|
end
|
@@ -1161,8 +1353,39 @@ module Jamf
|
|
1161
1353
|
false
|
1162
1354
|
end
|
1163
1355
|
|
1356
|
+
# make a note both in this instance and in our container
|
1357
|
+
# that a change has been made and an update is needed
|
1358
|
+
def note_pending_changes
|
1359
|
+
warn_about_data_loss_bug
|
1360
|
+
warn_about_array_hash_bug
|
1361
|
+
@should_update = true
|
1362
|
+
@container&.should_update
|
1363
|
+
end
|
1364
|
+
|
1365
|
+
# display data loss warning.
|
1366
|
+
def warn_about_data_loss_bug
|
1367
|
+
return unless JAMF_DATA_LOSS_BUG_CLASSES.include?(@container.class)
|
1368
|
+
return if Jamf::Scopable::Scope.do_not_warn_about_policy_scope_bugs?
|
1369
|
+
return if @warn_about_data_loss_bug_has_run
|
1370
|
+
|
1371
|
+
warn "WARNING: Saving changes to this scope may cause data loss!\nDue to a bug in the Classic API, if the scope uses Jamf Users or User Groups in the Targets or Exclusions, they will be deleted from the scope when you save!"
|
1372
|
+
|
1373
|
+
@warn_about_data_loss_bug_has_run = true
|
1374
|
+
end
|
1375
|
+
|
1376
|
+
# display warning about array-hash bug data loss
|
1377
|
+
def warn_about_array_hash_bug
|
1378
|
+
return unless @array_hash_bug_target_key
|
1379
|
+
return if Jamf::Scopable::Scope.do_not_warn_about_array_hash_scope_bugs?
|
1380
|
+
return if @warn_about_array_hash_bug_has_run
|
1381
|
+
|
1382
|
+
warn "WARNING: At least one #{@array_hash_bug_target_key} is used as a scope target.\nDue to a bug in the Classic API, if you save changes to this scope, all but the last #{@array_hash_bug_target_key} will be deleted from the scope targets when you save!"
|
1383
|
+
|
1384
|
+
@warn_about_array_hash_bug_has_run = true
|
1385
|
+
end
|
1386
|
+
|
1164
1387
|
end # class Scope
|
1165
1388
|
|
1166
1389
|
end # module Scopable
|
1167
1390
|
|
1168
|
-
end #
|
1391
|
+
end # moduleß
|
@@ -75,8 +75,8 @@ module Jamf
|
|
75
75
|
### @return [void]
|
76
76
|
###
|
77
77
|
def parse_scope
|
78
|
-
@scope = Jamf::Scopable::Scope.new self.class::SCOPE_TARGET_KEY, @init_data[:scope]
|
79
|
-
@scope.container
|
78
|
+
@scope = Jamf::Scopable::Scope.new self.class::SCOPE_TARGET_KEY, @init_data[:scope], container: self
|
79
|
+
@scope.container ||= self
|
80
80
|
end
|
81
81
|
|
82
82
|
### Change the scope
|
@@ -85,9 +85,14 @@ module Jamf
|
|
85
85
|
###
|
86
86
|
### @return [void]
|
87
87
|
###
|
88
|
-
def scope=
|
89
|
-
raise Jamf::InvalidDataError,
|
90
|
-
|
88
|
+
def scope=(new_scope)
|
89
|
+
raise Jamf::InvalidDataError, 'Jamf::Scopable::Scope instance required' unless new_criteria.is_a?(Jamf::Scopable::Scope)
|
90
|
+
|
91
|
+
unless self.class::SCOPE_TARGET_KEY == new_scope.target_key
|
92
|
+
raise Jamf::InvalidDataError,
|
93
|
+
"Scope object must have target_key of :#{self.class::SCOPE_TARGET_KEY}"
|
94
|
+
end
|
95
|
+
|
91
96
|
@scope = new_scope
|
92
97
|
@need_to_update = true
|
93
98
|
end
|
@@ -105,13 +110,13 @@ module Jamf
|
|
105
110
|
#
|
106
111
|
def update
|
107
112
|
super
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
end
|
113
|
+
@scope.should_update = false
|
114
|
+
rescue Jamf::ConflictError => e
|
115
|
+
raise Jamf::InvalidDataError, 'Potentially non-existant LDAP user or group in new scope values.' if scope.unable_to_verify_ldap_entries == true
|
116
|
+
|
117
|
+
raise e
|
114
118
|
end # update
|
115
119
|
|
116
120
|
end # module Scopable
|
121
|
+
|
117
122
|
end # module Jamf
|
@@ -222,9 +222,8 @@ module Jamf
|
|
222
222
|
# @return [Jamf::DeviceEnrollmentDevice, nil] the device as known to DEP
|
223
223
|
#
|
224
224
|
def self.device(sn, instance = nil, refresh: false, cnx: Jamf.cnx)
|
225
|
-
sn.upcase! # SNs from apple are always uppercase
|
226
225
|
devs = devices(instance, refresh: refresh, cnx: cnx)
|
227
|
-
devs.select { |d| d.serialNumber
|
226
|
+
devs.select { |d| d.serialNumber.casecmp? sn }.first
|
228
227
|
end
|
229
228
|
|
230
229
|
# The history of sync operations between Apple and a given DeviceEnrollment
|
data/lib/jamf/version.rb
CHANGED
data/test/tests/policy.rb
CHANGED
@@ -38,20 +38,6 @@ module JamfTest
|
|
38
38
|
def run_class_tests
|
39
39
|
# policies are special so we define all the object tests here
|
40
40
|
run_collection_tests do_object_tests: false
|
41
|
-
|
42
|
-
create_new
|
43
|
-
|
44
|
-
add_data_to_new
|
45
|
-
|
46
|
-
# save_new
|
47
|
-
# fetch_new
|
48
|
-
# validate_fetched
|
49
|
-
# modify_fetched
|
50
|
-
# re_save_fetched
|
51
|
-
# re_fetch
|
52
|
-
# validate_changes
|
53
|
-
# delete
|
54
|
-
# confirm_deleted
|
55
41
|
end
|
56
42
|
|
57
43
|
#################
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-jss
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0b3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Lasell
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2023-
|
13
|
+
date: 2023-08-08 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: CFPropertyList
|
@@ -882,9 +882,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
882
882
|
version: 2.6.3
|
883
883
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
884
884
|
requirements:
|
885
|
-
- - "
|
885
|
+
- - ">"
|
886
886
|
- !ruby/object:Gem::Version
|
887
|
-
version:
|
887
|
+
version: 1.3.1
|
888
888
|
requirements: []
|
889
889
|
rubygems_version: 3.0.3.1
|
890
890
|
signing_key:
|