ruby-jss 0.7.0 → 0.8.1

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.

Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +29 -22
  3. data/README.md +66 -86
  4. data/bin/jamfHelperBackgrounder +148 -0
  5. data/bin/netseg-update +0 -1
  6. data/lib/jss.rb +20 -9
  7. data/lib/jss/api_connection.rb +369 -295
  8. data/lib/jss/api_object.rb +651 -418
  9. data/lib/jss/api_object/account.rb +69 -77
  10. data/lib/jss/api_object/advanced_search.rb +201 -236
  11. data/lib/jss/api_object/advanced_search/advanced_computer_search.rb +42 -42
  12. data/lib/jss/api_object/advanced_search/advanced_mobile_device_search.rb +33 -43
  13. data/lib/jss/api_object/advanced_search/advanced_user_search.rb +33 -43
  14. data/lib/jss/api_object/building.rb +39 -52
  15. data/lib/jss/api_object/categorizable.rb +221 -0
  16. data/lib/jss/api_object/category.rb +81 -89
  17. data/lib/jss/api_object/computer.rb +486 -525
  18. data/lib/jss/api_object/computer_invitation.rb +73 -86
  19. data/lib/jss/api_object/criteriable.rb +6 -7
  20. data/lib/jss/api_object/ebook.rb +21 -0
  21. data/lib/jss/api_object/extendable.rb +6 -8
  22. data/lib/jss/api_object/group.rb +0 -3
  23. data/lib/jss/api_object/locatable.rb +19 -20
  24. data/lib/jss/api_object/mac_application.rb +21 -0
  25. data/lib/jss/api_object/mobile_device.rb +30 -21
  26. data/lib/jss/api_object/mobile_device_application.rb +447 -0
  27. data/lib/jss/api_object/mobile_device_configuration_profile.rb +21 -0
  28. data/lib/jss/api_object/osx_configuration_profile.rb +0 -3
  29. data/lib/jss/api_object/package.rb +21 -34
  30. data/lib/jss/api_object/peripheral.rb +16 -18
  31. data/lib/jss/api_object/policy.rb +5 -83
  32. data/lib/jss/api_object/purchasable.rb +11 -13
  33. data/lib/jss/api_object/scopable.rb +11 -12
  34. data/lib/jss/api_object/script.rb +3 -17
  35. data/lib/jss/api_object/self_servable.rb +419 -205
  36. data/lib/jss/api_object/self_servable/icon.rb +179 -0
  37. data/lib/jss/api_object/updatable.rb +35 -34
  38. data/lib/jss/api_object/uploadable.rb +72 -70
  39. data/lib/jss/api_object/user.rb +6 -7
  40. data/lib/jss/api_object/vppable.rb +117 -0
  41. data/lib/jss/client.rb +264 -225
  42. data/lib/jss/db_connection.rb +7 -5
  43. data/lib/jss/exceptions.rb +50 -42
  44. data/lib/jss/ruby_extensions.rb +8 -7
  45. data/lib/jss/ruby_extensions/object.rb +19 -0
  46. data/lib/jss/utility.rb +82 -40
  47. data/lib/jss/version.rb +1 -1
  48. metadata +37 -68
  49. data/bin/jss-webhook-server +0 -3
  50. data/lib/jss/webhooks.rb +0 -53
  51. data/lib/jss/webhooks/README.md +0 -269
  52. data/lib/jss/webhooks/configuration.rb +0 -213
  53. data/lib/jss/webhooks/data/sample_handlers/RestAPIOperation-executable +0 -91
  54. data/lib/jss/webhooks/data/sample_handlers/RestAPIOperation.rb +0 -45
  55. data/lib/jss/webhooks/data/sample_jsons/ComputerAdded.json +0 -27
  56. data/lib/jss/webhooks/data/sample_jsons/ComputerCheckIn.json +0 -27
  57. data/lib/jss/webhooks/data/sample_jsons/ComputerInventoryCompleted.json +0 -27
  58. data/lib/jss/webhooks/data/sample_jsons/ComputerPolicyFinished.json +0 -27
  59. data/lib/jss/webhooks/data/sample_jsons/ComputerPushCapabilityChanged.json +0 -27
  60. data/lib/jss/webhooks/data/sample_jsons/JSSShutdown.json +0 -14
  61. data/lib/jss/webhooks/data/sample_jsons/JSSStartup.json +0 -14
  62. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceCheckIn.json +0 -26
  63. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceCommandCompleted.json +0 -26
  64. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceEnrolled.json +0 -26
  65. data/lib/jss/webhooks/data/sample_jsons/MobileDevicePushSent.json +0 -26
  66. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceUnEnrolled.json +0 -26
  67. data/lib/jss/webhooks/data/sample_jsons/PatchSoftwareTitleUpdated.json +0 -14
  68. data/lib/jss/webhooks/data/sample_jsons/PushSent.json +0 -11
  69. data/lib/jss/webhooks/data/sample_jsons/RestAPIOperation.json +0 -15
  70. data/lib/jss/webhooks/data/sample_jsons/SCEPChallenge.json +0 -10
  71. data/lib/jss/webhooks/data/sample_jsons/SmartGroupComputerMembershipChange.json +0 -13
  72. data/lib/jss/webhooks/data/sample_jsons/SmartGroupMobileDeviceMembershipChange.json +0 -13
  73. data/lib/jss/webhooks/event.rb +0 -139
  74. data/lib/jss/webhooks/event/computer_added.rb +0 -38
  75. data/lib/jss/webhooks/event/computer_check_in.rb +0 -38
  76. data/lib/jss/webhooks/event/computer_inventory_completed.rb +0 -38
  77. data/lib/jss/webhooks/event/computer_policy_finished.rb +0 -38
  78. data/lib/jss/webhooks/event/computer_push_capability_changed.rb +0 -38
  79. data/lib/jss/webhooks/event/handlers.rb +0 -192
  80. data/lib/jss/webhooks/event/jss_shutdown.rb +0 -38
  81. data/lib/jss/webhooks/event/jss_startup.rb +0 -38
  82. data/lib/jss/webhooks/event/mobile_device_check_in.rb +0 -38
  83. data/lib/jss/webhooks/event/mobile_device_command_completed.rb +0 -38
  84. data/lib/jss/webhooks/event/mobile_device_enrolled.rb +0 -38
  85. data/lib/jss/webhooks/event/mobile_device_push_sent.rb +0 -38
  86. data/lib/jss/webhooks/event/mobile_device_unenrolled.rb +0 -38
  87. data/lib/jss/webhooks/event/patch_software_title_updated.rb +0 -38
  88. data/lib/jss/webhooks/event/push_sent.rb +0 -38
  89. data/lib/jss/webhooks/event/rest_api_operation.rb +0 -38
  90. data/lib/jss/webhooks/event/scep_challenge.rb +0 -38
  91. data/lib/jss/webhooks/event/smart_group_computer_membership_change.rb +0 -38
  92. data/lib/jss/webhooks/event/smart_group_mobile_device_membership_change.rb +0 -38
  93. data/lib/jss/webhooks/event/webhook.rb +0 -40
  94. data/lib/jss/webhooks/event_objects.rb +0 -112
  95. data/lib/jss/webhooks/event_objects/computer.rb +0 -49
  96. data/lib/jss/webhooks/event_objects/jss.rb +0 -36
  97. data/lib/jss/webhooks/event_objects/mobile_device.rb +0 -48
  98. data/lib/jss/webhooks/event_objects/patch_software_title_update.rb +0 -38
  99. data/lib/jss/webhooks/event_objects/push.rb +0 -33
  100. data/lib/jss/webhooks/event_objects/rest_api_operation.rb +0 -37
  101. data/lib/jss/webhooks/event_objects/scep_challenge.rb +0 -32
  102. data/lib/jss/webhooks/event_objects/smart_group.rb +0 -35
  103. data/lib/jss/webhooks/server_app.rb +0 -37
  104. data/lib/jss/webhooks/server_app/routes.rb +0 -27
  105. data/lib/jss/webhooks/server_app/routes/handle_webhook_event.rb +0 -39
  106. data/lib/jss/webhooks/server_app/routes/home.rb +0 -37
  107. data/lib/jss/webhooks/server_app/self_signed_cert.rb +0 -65
  108. data/lib/jss/webhooks/server_app/server.rb +0 -60
  109. data/lib/jss/webhooks/version.rb +0 -32
@@ -1,26 +1,26 @@
1
1
  ### Copyright 2017 Pixar
2
2
 
3
- ###
3
+ ###
4
4
  ### Licensed under the Apache License, Version 2.0 (the "Apache License")
5
5
  ### with the following modification; you may not use this file except in
6
6
  ### compliance with the Apache License and the following modification to it:
7
7
  ### Section 6. Trademarks. is deleted and replaced with:
8
- ###
8
+ ###
9
9
  ### 6. Trademarks. This License does not grant permission to use the trade
10
10
  ### names, trademarks, service marks, or product names of the Licensor
11
11
  ### and its affiliates, except as required to comply with Section 4(c) of
12
12
  ### the License and to reproduce the content of the NOTICE file.
13
- ###
13
+ ###
14
14
  ### You may obtain a copy of the Apache License at
15
- ###
15
+ ###
16
16
  ### http://www.apache.org/licenses/LICENSE-2.0
17
- ###
17
+ ###
18
18
  ### Unless required by applicable law or agreed to in writing, software
19
19
  ### distributed under the Apache License with the above modification is
20
20
  ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
21
  ### KIND, either express or implied. See the Apache License for the specific
22
22
  ### language governing permissions and limitations under the Apache License.
23
- ###
23
+ ###
24
24
  ###
25
25
 
26
26
  ###
@@ -50,7 +50,6 @@ module JSS
50
50
  ###
51
51
  ### Classes that mix in this module must:
52
52
  ### - Set a Constant SCOPE_TARGET_KEY that is either :computers or :mobile_devices
53
- ### - Call {#parse_scope} during initialization to make a {JSS::Scopable::Scope} instance for their @scope attribute.
54
53
  ### - Include the result of self.scope.scope_xml in their own rest_xml output if they are {Updatable} or {Creatable}
55
54
  ###
56
55
  module Scopable
@@ -112,25 +111,25 @@ module JSS
112
111
  @need_to_update = true if @in_jss
113
112
  end
114
113
 
115
-
114
+
116
115
  ###
117
116
  ### A wrapper around the update method, to try catching RestClient::Conflict
118
117
  ### 409 errors when we couldn't verify all ldap users/groups due to lack of ldap connections
119
- ###
118
+ ###
120
119
  def update
121
120
  begin
122
121
  super
123
-
122
+
124
123
  rescue RestClient::Conflict => conflict
125
124
  if self.scope.unable_to_verify_ldap_entries == true
126
125
  raise JSS::InvalidDataError, "Potentially non-existant LDAP user or group in new scope values."
127
126
  else
128
127
  raise conflict
129
128
  end
130
-
129
+
131
130
  end # begin
132
131
  end # update
133
-
132
+
134
133
  end # module Scopable
135
134
  end # module JSS
136
135
 
@@ -61,6 +61,7 @@ module JSS
61
61
 
62
62
  include JSS::Creatable
63
63
  include JSS::Updatable
64
+ include JSS::Categorizable
64
65
 
65
66
  ### Class Methods
66
67
  #####################################
@@ -127,7 +128,6 @@ module JSS
127
128
  def initialize(args = {})
128
129
  super
129
130
 
130
- @category = JSS::APIObject.get_name(@init_data[:category])
131
131
  @filename = @init_data[:filename] || @name
132
132
  @info = @init_data[:info]
133
133
  @notes = @init_data[:notes]
@@ -263,21 +263,6 @@ module JSS
263
263
  @need_to_update = true
264
264
  end # notes=
265
265
 
266
- ### Change the category
267
- ###
268
- ### @param new_val[String] the name of the new category, which must be in {JSS::Category.all_names}
269
- ###
270
- ### @return [void]
271
- ###
272
- def category=(new_val)
273
- return nil if new_val == @category
274
- new_val = nil if new_val == ''
275
- new_val ||= JSS::Category::DEFAULT_CATEGORY
276
- raise JSS::InvalidDataError, "Category #{new_val} is not known to the JSS" unless JSS::Category.all_names.include? new_val
277
- @need_to_update = true
278
- @category = new_val
279
- end # category=
280
-
281
266
  ### Replace all the script parameters at once.
282
267
  ###
283
268
  ### This will replace the entire set with the hash provided.
@@ -570,7 +555,7 @@ module JSS
570
555
  def rest_xml
571
556
  doc = REXML::Document.new
572
557
  scpt = doc.add_element 'script'
573
- scpt.add_element('category').text = @category
558
+
574
559
  scpt.add_element('filename').text = @filename
575
560
  scpt.add_element('id').text = @id
576
561
  scpt.add_element('info').text = @info
@@ -578,6 +563,7 @@ module JSS
578
563
  scpt.add_element('notes').text = @notes
579
564
  scpt.add_element('os_requirements').text = JSS.to_s_and_a(@os_requirements)[:stringform]
580
565
  scpt.add_element('priority').text = @priority
566
+ add_category_to_xml(doc)
581
567
 
582
568
  if @parameters.empty?
583
569
  scpt.add_element('parameters').text = nil
@@ -23,184 +23,197 @@
23
23
  ###
24
24
  ###
25
25
 
26
+ require 'jss/api_object/self_servable/icon'
27
+
26
28
  ###
27
29
  module JSS
28
30
 
29
- #####################################
30
- ### Module Variables
31
+ # Module Variables
31
32
  #####################################
32
33
 
33
- #####################################
34
- ### Module Methods
34
+ # Module Methods
35
35
  #####################################
36
36
 
37
- #####################################
38
- ### Sub-Modules
37
+ # Sub-Modules
39
38
  #####################################
40
39
 
41
- ### A mix-in module for handling Self Service data for objects in the JSS.
42
- ###
43
- ### The JSS objects that have Self Service data return it in a :self_service subset,
44
- ### which have somewhat similar data, i.e. a hash with at least these keys:
45
- ### - :self_service_description
46
- ### - :self_service_icon
47
- ### - :feature_on_main_page
48
- ### - :self_service_categories
49
- ###
50
- ### Config Profiles in self service have this key:
51
- ### - :security
52
- ###
53
- ### Additionally, items that apper in macOS Slf Svc have these keys:
54
- ### - :install_button_text
55
- ### - :force_users_to_view_description
56
- ###
57
- ### See the attribute definitions for details of these values and structures.
58
- ###
59
- ### Including this module in an {APIObject} subclass and calling {#parse_self_service} in the
60
- ### subclass's constructor will give it matching attributes with 'self_service_'
61
- ### appended if needed, e.g. {#self_service_feature_on_main_page}
62
- ###
63
- ###
64
- ### Classes including this module *must*:
65
- ###
66
- ### - Define the constant SELF_SERVICE_TARGET which contains either :macos or :ios
67
- ### - Define the constant SELF_SERVICE_PAYLOAD which contains one of :policy, :profile,
68
- ### :app, or :ebook
69
- ### - Call {#parse_self_service} in the subclass's constructor, after calling super
70
- ### - Define the method #in_self_service? which returns a Boolean indicating that the item is
71
- ### available in self service. Different API objects indicate this in different ways (and
72
- ### macOS app store apps don't provide it at all via the API,
73
- ### so self service can't be implemented fully)
74
- ### - Define the methods #add_to_self_service and #remove_from_self_service, which is handled
75
- ### differently for policies, apps/ebooks, and profiles
76
- ### - Define the method #user_removable? which returns Boolean indicating that the item
77
- ### can be removed by the user in SSvc. macOS profiles store this in the :user_removable
78
- ### key of the :general subset as a boolean, whereas iOS profiles store it in
79
- ### the :security hash of the :self_service data as one of 3 strings
80
- ### - Define the method #user_removable= which sets the appropriate values depending
81
- ### on the class. (see above)
82
- ### - Define a #set_icon_to method which takes the id of a previously uploaded
83
- ### icon, or a local path (String or Pathname) to an image file to use.
84
- ### - Include the result of {#self_service_xml} in their #rest_xml output
85
- ###
86
- ###
87
- ### Generating XML for PUT or POST:
88
- ###
89
- ### If the class including this module is creatable or updatable, calling {#self_service_xml}
90
- ### returns a REXML element representing the Self Service subset, to be included with the
91
- ### #rest_xml output of the subclass.
92
- ###
93
- ### Because some self-service-related data is located outside of the Self Service
94
- ### data subset (e.g. the deployment method for profiles is in the general subset),
95
- ### classes including this module *must* also account for that and add an appropriate
96
- ### XML element as needed.
97
- ###
40
+ # A mix-in module for handling Self Service data for objects in the JSS.
41
+ #
42
+ # The JSS objects that have Self Service data return it in a :self_service subset,
43
+ # which have somewhat similar data, i.e. a hash with at least these keys:
44
+ # - :self_service_description
45
+ # - :self_service_icon
46
+ # - :feature_on_main_page
47
+ # - :self_service_categories
48
+ #
49
+ # Config Profiles in self service have this key:
50
+ # - :security
51
+ #
52
+ # Additionally, items that apper in macOS Slf Svc have these keys:
53
+ # - :install_button_text
54
+ # - :force_users_to_view_description
55
+ #
56
+ # See the attribute definitions for details of these values and structures.
57
+ #
58
+ # Including this module in an {APIObject} subclass will give it matching
59
+ # attributes with 'self_service_' appended if needed,
60
+ # e.g. {#self_service_feature_on_main_page}
61
+ #
62
+ #
63
+ # Classes including this module *MUST*:
64
+ # - call {#add_self_service_xml()} in their #rest_xml method
65
+ #
66
+ # IMPORTANT: Since SelfServable also includes #{JSS::Updatable}, for uploading icons,
67
+ # see that module for its requirements.
68
+ #
69
+ #
98
70
  module SelfServable
99
71
 
100
- ### Constants
72
+ include Uploadable
73
+
74
+ # Constants
101
75
  #####################################
102
76
 
103
77
  SELF_SERVABLE = true
104
78
 
105
- IOS_PROFILE_REMOVAL_OPTIONS = ['Always', 'With Authorization', 'Never'].freeze
106
-
107
- ### Variables
79
+ PROFILE_REMOVAL_BY_USER = {
80
+ always: 'Always',
81
+ never: 'Never',
82
+ with_auth: 'With Authorization'
83
+ }.freeze
84
+
85
+ MAKE_AVAILABLE = 'Make Available in Self Service'.freeze
86
+ AUTO_INSTALL = 'Install Automatically'.freeze
87
+ AUTO_INSTALL_OR_PROMPT = 'Install Automatically/Prompt Users to Install'.freeze
88
+
89
+ DEFAULT_INSTALL_BUTTON_TEXT = 'Install'.freeze
90
+
91
+ SELF_SERVICE_CLASSES = {
92
+ JSS::Policy => {
93
+ in_self_service_data_path: [:self_service, :use_for_self_service],
94
+ in_self_service: true,
95
+ not_in_self_service: false,
96
+ targets: [:macos],
97
+ payload: :policy,
98
+ can_display_in_categories: true,
99
+ can_feature_in_categories: true
100
+ },
101
+ JSS::MacApplication => { # TODO: add the correct values when Jamf fixes this bug
102
+ in_self_service_data_path: nil, # [:general, :distribution_method],
103
+ in_self_service: nil, # MAKE_AVAILABLE,
104
+ not_in_self_service: nil, # AUTO_INSTALL_OR_PROMPT,
105
+ targets: [:macos],
106
+ payload: :app,
107
+ can_display_in_categories: true,
108
+ can_feature_in_categories: true
109
+ },
110
+ JSS::OSXConfigurationProfile => {
111
+ in_self_service_data_path: [:general, :distribution_method],
112
+ in_self_service: MAKE_AVAILABLE,
113
+ not_in_self_service: AUTO_INSTALL,
114
+ targets: [:macos],
115
+ payload: :profile,
116
+ can_display_in_categories: true,
117
+ can_feature_in_categories: true
118
+ },
119
+ JSS::EBook => {
120
+ in_self_service_data_path: [:general, :deployment_type],
121
+ in_self_service: MAKE_AVAILABLE,
122
+ not_in_self_service: AUTO_INSTALL_OR_PROMPT,
123
+ targets: [:macos, :ios],
124
+ payload: :app, # ebooks are handled the same way as apps, it seems,
125
+ can_display_in_categories: true,
126
+ can_feature_in_categories: true
127
+ },
128
+ JSS::MobileDeviceApplication => {
129
+ in_self_service_data_path: [:general, :deployment_type],
130
+ in_self_service: MAKE_AVAILABLE,
131
+ not_in_self_service: AUTO_INSTALL_OR_PROMPT,
132
+ targets: [:ios],
133
+ payload: :app,
134
+ can_display_in_categories: true,
135
+ can_feature_in_categories: false
136
+ },
137
+ JSS::MobileDeviceConfigurationProfile => {
138
+ in_self_service_data_path: [:general, :deployment_method],
139
+ in_self_service: MAKE_AVAILABLE,
140
+ not_in_self_service: AUTO_INSTALL,
141
+ targets: [:ios],
142
+ payload: :profile,
143
+ can_display_in_categories: false,
144
+ can_feature_in_categories: false
145
+ }
146
+ }.freeze
147
+
148
+ # Variables
108
149
  #####################################
109
150
 
110
- ### Attribtues
151
+ # Mixed-in Attributes
111
152
  #####################################
112
153
 
113
- ### @return [String] The verbage that appears in SelfSvc for this item
154
+ # @return [Boolean] Is this thing available in Self Service?
155
+ attr_reader :in_self_service
156
+ alias in_self_service? in_self_service
157
+
158
+ # @return [JSS::Icon, nil] The icon used in self-service
159
+ attr_reader :icon
160
+ alias self_service_icon icon
161
+
162
+ # @return [String] The verbage that appears in SelfSvc for this item
114
163
  attr_reader :self_service_description
115
164
 
116
- ### @return [Boolean] Should this item feature on the main page of SSvc?
165
+ # @return [Boolean] Should this item feature on the main page of SSvc?
166
+ # Only applicable to macOS targets
117
167
  attr_reader :self_service_feature_on_main_page
118
168
 
119
- ### @return [Array<Hash>] The categories in which this item should appear in SSvc
120
- ###
121
- ### Each Hash has these keys about the category
122
- ### - :id => [Integer] the JSS id of the category
123
- ### - :name => [String] the name of the category
124
- ###
125
- ### Most objects also include one or both of these keys:
126
- ### - :display_in => [Boolean] should the item be displayed in this category in SSvc? (OSX SSvc only)
127
- ### - :feature_in => [Boolean] should the item be featured in this category in SSVC? (OSX SSvc only)
128
- ###
129
- ### NOTE: as of Casper 9.61 there's a bug in the JSON output from the API, and only the last
130
- ### category is returned, if more than one are set.
131
- ###
169
+ # @return [Array<Hash>] The categories in which this item should appear in SSvc
170
+ #
171
+ # Each Hash has these keys about the category
172
+ # - :id => [Integer] the JSS id of the category
173
+ # - :name => [String] the name of the category
174
+ #
175
+ # Most objects also include one or both of these keys:
176
+ # - :display_in => [Boolean] should the item be displayed in this category in SSvc? (not MobDevConfProfiles)
177
+ # - :feature_in => [Boolean] should the item be featured in this category in SSVC? (macOS targets only)
178
+ #
132
179
  attr_reader :self_service_categories
133
180
 
134
- ### @return [Hash] The icon that appears in SelfSvc for this item
135
- ###
136
- ### The Hash contains these keys with info about the icon:
137
- ### - :uri => [String] the URI for retriving the icon
138
- ### - :id => [Integer] the JSS id number for the icon (not all SSvc items have this)
139
- ### - :data => [String] the icon image encoded as Base64 (not all SSvc items have this)
140
- ### - :filename => [String] The name of the image file uploaded to the JSS, if applicable
141
- ###
142
- attr_reader :self_service_icon
143
-
144
- ### @return [Hash] The security settings for profiles in SSvc
145
- ###
146
- ### The keys are
147
- ### - :removal_disallowed => [String] one of the items in PROFILE_REMOVAL_OPTIONS
148
- ### - :password => [String] if :removal_disallowed is "With Authorization", this contains the passwd (in plaintext)
149
- ### needed to remove the profile.
150
- ###
151
- ### NOTE that the key should be called :removal_allowed, since 'Never' means it can't be removed.
152
- ###
181
+ # Profiles in Self Service have an option to allow the user to remove them
182
+ # and for iOS profiles, if authentication is required to do so, and if so,
183
+ # the password needed for removal.
184
+ #
185
+ # This data is held in the :security Hash of the selfsvc data.
186
+ # The keys are:
187
+ # - :removal_disallowed, which should be "removal allowed"
188
+ # - :password => [String] if :removal_disallowed is "With Authorization",
189
+ # this contains the passwd (in plaintext) needed to remove the profile.
190
+ #
191
+ # NOTE that the key should be called :removal_allowed, since 'Never' means it can't be removed.
192
+ #
193
+ # These values are stored in the next two attributes.
194
+
195
+ # @return [Symbol] one of the keys in PROFILE_REMOVAL_BY_USER
153
196
  attr_reader :self_service_user_removable
154
197
 
155
- ### @return [String] The text label on the install button in SSvc (OSX SSvc only)
198
+ # @return [String] The password needed for removal, in plain text.
199
+ attr_reader :self_service_removal_password
200
+
201
+ # @return [String] The text label on the install button in SSvc (OSX SSvc only)
156
202
  attr_reader :self_service_install_button_text
157
203
 
158
- ### @return [Boolean] Should an extra window appear before the user can install the item? (OSX SSvc only)
204
+ # @return [Boolean] Should an extra window appear before the user can install the item? (OSX SSvc only)
159
205
  attr_reader :self_service_force_users_to_view_description
160
206
 
207
+ # Mixed-in Public Instance Methods
161
208
  #####################################
162
- ### Mixed-in Instance Methods
163
- #####################################
164
-
165
- ### Call this during initialization of
166
- ### objects that have a self_service subset
167
- ### and the self_service attributes will be populated
168
- ### (as primary attributes) from @init_data
169
- ###
170
- ### @return [void]
171
- ###
172
- def parse_self_service
173
- @init_data[:self_service] ||= {}
174
- @ss_data = @init_data[:self_service]
175
-
176
- @self_service_description = @ss_data[:self_service_description]
177
- @self_service_icon = @ss_data[:self_service_icon]
178
- @self_service_icon ||= {}
179
-
180
- @self_service_feature_on_main_page = @ss_data[:feature_on_main_page]
181
- @self_service_feature_on_main_page ||= false
182
-
183
- @self_service_categories = @ss_data[:self_service_categories]
184
- @self_service_categories ||= []
185
-
186
- # make this an empty hash if needed
187
- @self_service_security = @ss_data[:security]
188
- @self_service_security ||= {}
189
209
 
190
- @self_service_install_button_text = @ss_data[:install_button_text]
191
- @self_service_install_button_text ||= 'Install'
210
+ # Setters
211
+ #
192
212
 
193
- @self_service_force_users_to_view_description = @ss_data[:force_users_to_view_description]
194
- @self_service_force_users_to_view_description ||= false
195
- end # parse
196
-
197
- ### Setters
198
- ###
199
-
200
- ### @param new_val[String] the new discription
201
- ###
202
- ### @return [void]
203
- ###
213
+ # @param new_val[String] the new discription
214
+ #
215
+ # @return [void]
216
+ #
204
217
  def self_service_description=(new_val)
205
218
  new_val.strip!
206
219
  return if @self_service_description == new_val
@@ -208,57 +221,68 @@ module JSS
208
221
  @need_to_update = true
209
222
  end
210
223
 
211
- ### @param new_val[String] the new install button text
212
- ###
213
- ### @return [void]
214
- ###
224
+ # @param new_val[String] the new install button text
225
+ #
226
+ # @return [void]
227
+ #
215
228
  def self_service_install_button_text=(new_val)
216
229
  return nil if @self_service_install_button_text == new_val
217
- raise JSS::InvalidDataError, 'Only OS X Self Service Items can have custom button text' unless self.class::SELF_SERVICE_TARGET == :macos
230
+ raise JSS::InvalidDataError, 'Only OS X Self Service Items can have custom button text' unless self_service_targets.include? :macos
218
231
  @self_service_install_button_text = new_val.strip
219
232
  @need_to_update = true
220
233
  end
221
234
 
222
- ### @param new_val[Boolean] should this appear on the main SelfSvc page?
223
- ###
224
- ### @return [void]
225
- ###
235
+ # @param new_val[Boolean] should this appear on the main SelfSvc page?
236
+ #
237
+ # @return [void]
238
+ #
226
239
  def self_service_feature_on_main_page=(new_val)
227
240
  return nil if @self_service_feature_on_main_page == new_val
228
- raise JSS::InvalidDataError, 'New value must be true or false' unless JSS::TRUE_FALSE.include? new_val
241
+ return nil unless @self_service_data_config[:can_feature_in_categories]
242
+ raise JSS::InvalidDataError, 'New value must be true or false' unless new_val.jss_boolean?
229
243
  @self_service_feature_on_main_page = new_val
230
244
  @need_to_update = true
231
245
  end
232
246
 
233
- ### @param new_val[Boolean] should this appear on the main SelfSvc page?
234
- ###
235
- ### @return [void]
236
- ###
247
+ # @param new_val[Boolean] Should the description be shown to users in a new
248
+ # window before executing the payload?
249
+ #
250
+ # @return [void]
251
+ #
237
252
  def self_service_force_users_to_view_description=(new_val)
238
253
  return nil if @self_service_force_users_to_view_description == new_val
239
- raise JSS::InvalidDataError, 'Only OS X Self Service Items can force users to view description' unless self.class::SELF_SERVICE_TARGET == :macos
240
- raise JSS::InvalidDataError, 'New value must be true or false' unless JSS::TRUE_FALSE.include? new_val
254
+ raise JSS::InvalidDataError, 'Only OS X Self Service Items can force users to view description' unless self_service_targets.include? :macos
255
+ raise JSS::InvalidDataError, 'New value must be true or false' unless new_val.jss_boolean?
241
256
  @self_service_force_users_to_view_description = new_val
242
257
  @need_to_update = true
243
258
  end
244
259
 
245
- ### Add or change one of the categories for this item in SSvc.
246
- ###
247
- ### @param new_cat[String] the name of a category for this item in SelfSvc
248
- ###
249
- ### @param display_in[Boolean] should this item appear in the SelfSvc page for the new category?
250
- ###
251
- ### @param feature_in[Boolean] should this item be featured in the SelfSvc page for the new category?
252
- ###
253
- ### @return [void]
254
- ###
260
+ # Add or change one of the categories for this item in self service
261
+ #
262
+ # @param new_cat[String, Integer] the name or id of a category where this
263
+ # object should appear in SelfSvc
264
+ #
265
+ # @param display_in[Boolean] should this item appear in the SelfSvc page for
266
+ # the category? Only meaningful in applicable classes
267
+ #
268
+ # @param feature_in[Boolean] should this item be featured in the SelfSvc page
269
+ # for the category? Only meaningful in applicable classes.
270
+ # NOTE: this will always be false if display_in is false.
271
+ #
272
+ # @return [void]
273
+ #
255
274
  def add_self_service_category(new_cat, display_in: true, feature_in: false)
256
- new_cat.strip!
275
+ new_cat = JSS::Category.map_all_ids_to(:name)[new_cat] if new_cat.is_a? Integer
276
+ feature_in = false if display_in == false
257
277
  raise JSS::NoSuchItemError, "No category '#{new_cat}' in the JSS" unless JSS::Category.all_names(:refresh).include? new_cat
258
- raise JSS::InvalidDataError, 'display_in must be true or false' unless JSS::TRUE_FALSE.include? display_in
259
- raise JSS::InvalidDataError, 'feature_in must be true or false' unless JSS::TRUE_FALSE.include? feature_in
260
278
 
261
- new_data = { name: new_cat, display_in: display_in, feature_in: feature_in }
279
+ raise JSS::InvalidDataError, 'display_in must be true or false' unless display_in.jss_boolean?
280
+
281
+ raise JSS::InvalidDataError, 'feature_in must be true or false' unless feature_in.jss_boolean?
282
+
283
+ new_data = { name: new_cat }
284
+ new_data[:display_in] = display_in if @self_service_data_config[:can_display_in_categories]
285
+ new_data[:feature_in] = feature_in if @self_service_data_config[:can_feature_in_categories]
262
286
 
263
287
  # see if this category is already among our categories.
264
288
  idx = @self_service_categories.index { |c| c[:name] == new_cat }
@@ -271,57 +295,247 @@ module JSS
271
295
 
272
296
  @need_to_update = true
273
297
  end
274
-
275
- ### Remove a category from those for this item in SSvc
276
- ###
277
- ### @param cat [String] the name of the category to remove
278
- ###
279
- ### @return [void]
280
- ###
298
+ alias set_self_service_category add_self_service_category
299
+
300
+ # Remove a category from those for this item in SSvc
301
+ #
302
+ # @param cat [String, Integer] the name or id of the category to remove
303
+ #
304
+ # @return [void]
305
+ #
281
306
  def remove_self_service_category(cat)
282
- @self_service_categories.reject! { |c| c[:name] == cat }
307
+ @self_service_categories.reject! { |c| c[:name] == cat || c[:id] == cat }
308
+ @need_to_update = true
309
+ end
310
+
311
+ # Set the value for user-removability of profiles, optionally
312
+ # providing a password for removal, on iOS targets.
313
+ #
314
+ # @param new_val[Symbol] One of the keys of PROFILE_REMOVAL_BY_USER,
315
+ # :always, :never, or :with_auth
316
+ #
317
+ # @param pw[String] A new password to use if removable :with_auth
318
+ #
319
+ # @return [void]
320
+ #
321
+ def self_service_user_removable=(new_val, pw = @self_service_removal_password)
322
+ return nil if new_val == @self_service_user_removable && pw == @self_service_removal_password
323
+ validate_user_removable new_val
324
+
325
+ @self_service_user_removable = new_val
326
+ @self_service_removal_password = pw
327
+ @need_to_update = true
328
+ end
329
+
330
+ # Set a new Self Service icon for this object.
331
+ #
332
+ # Since JSS::Icon objects are read-only,
333
+ # the icon can only be changed by supplying the id number
334
+ # of an icon already existing in the JSS, or a path to
335
+ # a local file, which will be uploaded to the JSS and added
336
+ # to this instance. Uploads
337
+ # take effect immediately, but if an integer is supplied, the change
338
+ # must be sent to the JSS via {#update} or {#create}
339
+ #
340
+ # @param new_icon[Integer, String, Pathname] The id or path to the new icon.
341
+ #
342
+ # @return [false, Integer, Pathname] false means no change was made.
343
+ #
344
+ def icon=(new_icon)
345
+ if new_icon.is_a? Integer
346
+ return if new_icon == @icon.id
347
+ validate_icon new_icon
348
+ @new_icon_id = new_icon
349
+ @need_to_update = true
350
+ else
351
+ unless uploadable? && self.class::UPLOAD_TYPES.keys.include?(:icon)
352
+ raise JSS::UnsupportedError, "Class #{self.class} does not support icon uploads."
353
+ end
354
+ new_icon = Pathname.new new_icon
355
+ upload(:icon, new_icon)
356
+ refresh_icon
357
+ end # new_icon.is_a? Integer
358
+ new_icon
359
+ end # icon =
360
+ alias self_service_icon= icon=
361
+ alias assign_icon icon=
362
+
363
+ # Add this object to self service if not already there.
364
+ #
365
+ # @return [void]
366
+ #
367
+ def add_to_self_service
368
+ return nil unless @self_service_data_config[:in_self_service_data_path]
369
+ return nil if in_self_service?
370
+ @in_self_service = true
283
371
  @need_to_update = true
284
372
  end
285
373
 
286
- ### @api private
287
- ###
288
- ### Return a REXML <location> element to be
289
- ### included in the rest_xml of
290
- ### objects that have a Location subset
291
- ###
292
- ### @return [REXML::Element]
293
- ###
294
- def self_service_xml
295
- ssvc = REXML::Element.new('self_service')
374
+ # Remove this object from self service if it's there.
375
+ #
376
+ # @return [void]
377
+ #
378
+ def remove_from_self_service
379
+ return nil unless @self_service_data_config[:in_self_service_data_path]
380
+ return nil unless in_self_service?
381
+ @in_self_service = false
382
+ @need_to_update = true
383
+ end
384
+
385
+ # Can this thing be removed by the user?
386
+ #
387
+ # @return [Boolean, nil] nil means 'not applicable'
388
+ #
389
+ def user_removable?
390
+ return nil unless self_service_payload == :profile
391
+ @self_service_user_removable != :never
392
+ end
393
+
394
+ # What devices types can get this thing in Self Service
395
+ #
396
+ # @return [Array<Symbol>] An array of :macos, :ios, or both.
397
+ #
398
+ def self_service_targets
399
+ @self_service_data_config[:targets]
400
+ end
401
+
402
+ # What does this object deploy to the device
403
+ # via self service?
404
+ #
405
+ # @return [Symbol] :profile, :app, or :policy
406
+ #
407
+ def self_service_payload
408
+ @self_service_data_config[:payload]
409
+ end
410
+
411
+ # Mixed-in Private Instance Methods
412
+ #####################################
413
+ private
414
+
415
+ # Call this during initialization of
416
+ # objects that have a self_service subset
417
+ # and the self_service attributes will be populated
418
+ # (as primary attributes) from @init_data
419
+ #
420
+ # @return [void]
421
+ #
422
+ def parse_self_service
423
+ ss_data = @init_data[:self_service]
424
+ ss_data ||= {}
425
+ @self_service_data_config = SELF_SERVICE_CLASSES[self.class]
426
+
427
+ @in_self_service = in_self_service_at_init?
428
+
429
+ if ss_data[:security]
430
+ removable_value = ss_data[:security][:removal_disallowed]
431
+ @self_service_user_removable = PROFILE_REMOVAL_BY_USER.invert[removable_value]
432
+ @self_service_removal_password = ss_data[:security][:password]
433
+ end
434
+
435
+ @self_service_description = ss_data[:self_service_description]
436
+
437
+ @icon = JSS::Icon.new(ss_data[:self_service_icon]) if ss_data[:self_service_icon]
438
+
439
+ @self_service_feature_on_main_page = ss_data[:feature_on_main_page]
440
+ @self_service_feature_on_main_page ||= false
441
+
442
+ @self_service_categories = ss_data[:self_service_categories]
443
+ @self_service_categories ||= []
444
+
445
+ @self_service_install_button_text = ss_data[:install_button_text]
446
+ @self_service_install_button_text ||= DEFAULT_INSTALL_BUTTON_TEXT
447
+
448
+ @self_service_force_users_to_view_description = ss_data[:force_users_to_view_description]
449
+ @self_service_force_users_to_view_description ||= false
450
+ end # parse
451
+
452
+ # Figure out if this object is in Self Service, from the API
453
+ # initialization data.
454
+ # Alas, how to do it is far from consistent
455
+ #
456
+ # @return [Boolean]
457
+ #
458
+ def in_self_service_at_init?
459
+ return nil unless @self_service_data_config[:in_self_service_data_path]
460
+ subsection, key = @self_service_data_config[:in_self_service_data_path]
461
+ @init_data[subsection][key] == @self_service_data_config[:in_self_service]
462
+ end
463
+
464
+ def validate_user_removable=(new_val)
465
+ raise JSS::UnsupportedError, 'User removal settings not applicable to this class' unless self_service_payload == :profile
466
+
467
+ raise JSS::UnsupportedError, 'Removal :with_auth not applicable to this class' if new_val == :with_auth && !self_service_targets.include?(:ios)
468
+
469
+ raise JSS::InvalidDataError, "Value must be one of: :#{PROFILE_REMOVAL_BY_USER.keys.join(', :')}" unless PROFILE_REMOVAL_BY_USER.keys.include? new_val
470
+ end
471
+
472
+ def validate_icon(id)
473
+ if JSS::DB_CNX.connected?
474
+ raise JSS::NoSuchItemError, "No icon with id #{new_icon}" unless JSS::Icon.all_ids.include? id
475
+ end
476
+ end
477
+ # Re-read the icon data for this object from the API
478
+ # Generally done after uploading a new icon via {#icon=}
479
+ #
480
+ # @return [void]
481
+ #
482
+ def refresh_icon
483
+ return nil unless @in_jss
484
+ fresh_data = JSS::API.get_rsrc(@rest_rsrc)[self.class::RSRC_OBJECT_KEY]
485
+ icon_data = fresh_data[:self_service][:self_service_icon]
486
+ @icon = JSS::Icon.new icon_data
487
+ end # refresh icon
488
+
489
+ # Add a REXML <self_service> element to the root of the provided REXML::Document
490
+ #
491
+ # @param xdoc[REXML::Document] The XML Document to which we're adding a Self
492
+ # Service subsection
493
+ #
494
+ # @return [void]
495
+ #
496
+ def add_self_service_xml(xdoc)
497
+ doc_root = xdoc.root
498
+ ssvc = doc_root.add_element 'self_service'
296
499
 
297
500
  ssvc.add_element('self_service_description').text = @self_service_description
298
501
  ssvc.add_element('feature_on_main_page').text = @self_service_feature_on_main_page
299
502
 
503
+ if @new_icon_id
504
+ icon = ssvc.add_element('self_service_icon')
505
+ icon.add_element('id').text = @new_icon_id
506
+ end
507
+
300
508
  cats = ssvc.add_element('self_service_categories')
301
509
  @self_service_categories.each do |cat|
302
510
  catelem = cats.add_element('category')
303
511
  catelem.add_element('name').text = cat[:name]
304
- catelem.add_element('display_in').text = cat[:display_in] if cat.keys.include? :display_in
305
- catelem.add_element('feature_in').text = cat[:feature_in] if cat.keys.include? :feature_in
512
+ catelem.add_element('display_in').text = cat[:display_in] if @self_service_data_config[:can_display_in_categories]
513
+ catelem.add_element('feature_in').text = cat[:feature_in] if @self_service_data_config[:can_feature_in_categories]
306
514
  end
307
515
 
308
- icon = ssvc.add_element('self_service_icon')
309
- @self_service_icon.each { |key, val| icon.add_element(key.to_s).text = val }
516
+ if self_service_targets.include? :macos
517
+ ssvc.add_element('install_button_text').text = @self_service_install_button_text
518
+ ssvc.add_element('force_users_to_view_description').text = @self_service_force_users_to_view_description
519
+ end
310
520
 
311
- unless @self_service_security.empty?
521
+ if self_service_payload == :profile
312
522
  sec = ssvc.add_element('security')
313
- sec.add_element('removal_disallowed').text = @self_service_security[:removal_disallowed] if @self_service_security[:removal_disallowed]
314
- sec.add_element('password').text = @self_service_security[:password] if @self_service_security[:password]
523
+ sec.add_element('removal_disallowed').text = PROFILE_REMOVAL_BY_USER[@self_service_user_removable]
524
+ sec.add_element('password').text = @self_service_removal_password if @self_service_removal_password
315
525
  end
316
526
 
317
- ssvc.add_element('install_button_text').text = @self_service_install_button_text if @self_service_install_button_text
318
- ssvc.add_element('force_users_to_view_description').text = @self_service_force_users_to_view_description \
319
- unless @self_service_force_users_to_view_description.nil?
527
+ return unless @self_service_data_config[:in_self_service_data_path]
320
528
 
321
- ssvc
322
- end
529
+ in_ss_section, in_ss_elem = @self_service_data_config[:in_self_service_data_path]
530
+
531
+ in_ss_value = @in_self_service ? @self_service_data_config[:in_self_service] : @self_service_data_config[:not_in_self_service]
532
+
533
+ in_ss_section_xml = doc_root.elements[in_ss_section.to_s]
534
+ in_ss_section_xml ||= doc_root.add_element(in_ss_section.to_s)
535
+ in_ss_section_xml.add_element(in_ss_elem.to_s).text = in_ss_value.to_s
536
+ end # add_self_service_xml
323
537
 
324
- ### aliases
538
+ # aliases
325
539
  alias change_self_service_category add_self_service_category
326
540
 
327
541
  end # module SelfServable