lib/jss/api_object/policy.rb in ruby-jss-0.6.5 vs lib/jss/api_object/policy.rb in ruby-jss-0.6.6
- old
+ new
@@ -1,109 +1,113 @@
### Copyright 2016 Pixar
-###
+###
### Licensed under the Apache License, Version 2.0 (the "Apache License")
### with the following modification; you may not use this file except in
### compliance with the Apache License and the following modification to it:
### Section 6. Trademarks. is deleted and replaced with:
-###
+###
### 6. Trademarks. This License does not grant permission to use the trade
### names, trademarks, service marks, or product names of the Licensor
### and its affiliates, except as required to comply with Section 4(c) of
### the License and to reproduce the content of the NOTICE file.
-###
+###
### You may obtain a copy of the Apache License at
-###
+###
### http://www.apache.org/licenses/LICENSE-2.0
-###
+###
### Unless required by applicable law or agreed to in writing, software
### distributed under the Apache License with the above modification is
### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
### KIND, either express or implied. See the Apache License for the specific
### language governing permissions and limitations under the Apache License.
-###
###
+###
###
module JSS
- ######################
### Module Constants
######################
-
- ######################
### Module Variables
######################
- ######################
### Module Methods
######################
-
- #####################################
### Classes
#####################################
- ###
### A class implementing a JSS Policy.
###
### Like many API objects, the data comes from the API in sections, and
### the items in the :general section are mapped to direct attributes
### of this Class.
###
###
### Policy instances are partially read-only:
- ### - New policies cannot be created via this class, please use the WebApp.
- ### - Only a few attributes can be changed and updated via the Policy class:
+ ### - Due to limitations in the API implementation of policies, as well as the complexity
+ ### of policy objects, only these attributes can be set and updated via the Policy class:
### - - name
### - - frequency
### - - target_drive
### - - offline
### - - enabled
### - - category
### - - triggers
### - - scope, see {JSS::Scopable} and {JSS::Scopable::Scope}
### - - files and processes
+ ### - - packages, see {#add_package} and {#remove_package}
+ ### - - scripts see {#add_script} and {#remove_script}
+ ### - - self service, see {JSS::SelfServable}
+ ###
### All other values and sections must be edited via the Web App.
###
### Policies may be deleted via this class
###
- ### @see JSS::APIObject
- ###
class Policy < JSS::APIObject
#####################################
### Mix-Ins
#####################################
+ include JSS::Creatable
include JSS::Updatable
include JSS::Scopable
include JSS::Uploadable
+ include JSS::SelfServable
#####################################
### Class Methods
#####################################
#####################################
### Class Constants
#####################################
-
### The base for REST resources of this class
- RSRC_BASE = "policies"
+ RSRC_BASE = 'policies'.freeze
### the hash key used for the JSON list output of all objects in the JSS
RSRC_LIST_KEY = :policies
### The hash key used for the JSON object output.
### It's also used in various error messages
RSRC_OBJECT_KEY = :policy
### these keys, as well as :id and :name, are present in valid API JSON data for this class
- VALID_DATA_KEYS = [:scope, :user_interaction, :files_processes ]
+ VALID_DATA_KEYS = [:scope, :user_interaction, :files_processes].freeze
+ ### policies can take uploaded icons
+ UPLOAD_TYPES = { icon: :policies }.freeze
+ ### policies are available in macOS self Serviec
+ SELF_SERVICE_TARGET = :macos
+
+ ### policies via self services are still polcies
+ SELF_SERVICE_PAYLOAD = :policy
+
SECTIONS = [
:general,
:maintenance,
:account_maintenance,
:scripts,
@@ -114,77 +118,94 @@
:reboot,
:files_processes,
:dock_items,
:disk_encryption,
:printers
- ]
+ ].freeze
FREQUENCIES = {
- :ongoing => "Ongoing",
- :once_per_computer => "Once per computer",
- :once_per_user =>"Once per user",
- :daily => "Once every day",
- :weekly => "Once every week",
- :monthly => "Once every month"
- }
+ ongoing: 'Ongoing',
+ once_per_computer: 'Once per computer',
+ once_per_user: 'Once per user',
+ daily: 'Once every day',
+ weekly: 'Once every week',
+ monthly: 'Once every month'
+ }.freeze
RESTART_WHEN = {
- :if_pkg_requires => "Restart if a package or update requires it",
- :now => "Restart immediately",
- :delayed => "Restart",
- :dont => "Do not restart"
- }
+ if_pkg_requires: 'Restart if a package or update requires it',
+ now: 'Restart immediately',
+ delayed: 'Restart',
+ dont: 'Do not restart'
+ }.freeze
RESTART_DISKS = {
- :current => "Current Startup Disk",
- :selected => "Currently Selected Startup Disk (No Bless)",
- :netboot => "NetBoot",
- :os_installer => "inPlaceOSUpgradeDirectory"
- } # Note: any other value in :specify_startup is a path to some other drive to boot from, e.g. /Volumes/Foo
+ current: 'Current Startup Disk',
+ selected: 'Currently Selected Startup Disk (No Bless)',
+ netboot: 'NetBoot',
+ os_installer: 'inPlaceOSUpgradeDirectory'
+ }.freeze # Note: any other value in :specify_startup is a path to some other drive to boot from, e.g. /Volumes/Foo
ACCOUNT_ACTIONS = {
- :create => "Create",
- :change_pw => "Reset",
- :delete => "Delete",
- :disable_fv2 => "DisableFileVault"
- }
+ create: 'Create',
+ change_pw: 'Reset',
+ delete: 'Delete',
+ disable_fv2: 'DisableFileVault'
+ }.freeze
MGMT_ACCOUNT_ACTIONS = {
- :no_change => "doNotChange",
- :change_pw => "specified",
- :generate_pw => "random",
- :enable_fv2 => "fileVaultEnable",
- :disable_fv2 => "fileVaultDisable"
- }
+ no_change: 'doNotChange',
+ change_pw: 'specified',
+ generate_pw: 'random',
+ enable_fv2: 'fileVaultEnable',
+ disable_fv2: 'fileVaultDisable'
+ }.freeze
PACKAGE_ACTIONS = {
- :install => "Install",
- :remove => "Uninstall",
- :cache =>"Cache",
- :install_cache => "Install Cached"
- }
+ install: 'Install',
+ remove: 'Uninstall',
+ cache: 'Cache',
+ install_cache: 'Install Cached'
+ }.freeze
- SCRIPT_PRIORITIES = {:pre => "Before", :post => "After"}
+ SCRIPT_PRIORITIES = {
+ pre: 'Before',
+ before: 'Before',
+ post: 'After',
+ after: 'After'
+ }.freeze
- PRINTER_ACTIIONS = {:map => "install", :unmap => "uninstall"}
+ PRINTER_ACTIIONS = {
+ map: 'install',
+ unmap: 'uninstall'
+ }.freeze
- DOCK_ITEM_ACTIONS = {:add_start => "Add To Beginning", :add_end => "Add To End", :remove => "Remove"}
+ DOCK_ITEM_ACTIONS = {
+ add_start: 'Add To Beginning',
+ add_end: 'Add To End', remove: 'Remove'
+ }.freeze
- NETWORK_REQUIREMENTS = {:any => "Any", :ethernet => "EtherNet"}
+ NETWORK_REQUIREMENTS = {
+ any: 'Any',
+ ethernet: 'EtherNet'
+ }.freeze
+ TRIGGER_TYPES = {
+ event: 'EVENT',
+ user: 'USER_INITIATED'
+ }.freeze
+
TRIGGER_EVENTS = {
- :startup => :trigger_startup,
- :login => :trigger_login,
- :logout => :trigger_logout,
- :checkin => :trigger_checkin,
- :network_state => :trigger_network_state_changed ,
- :enrollment => :trigger_enrollment_complete ,
- :custom => :trigger_other
- }
+ startup: :trigger_startup,
+ login: :trigger_login,
+ logout: :trigger_logout,
+ checkin: :trigger_checkin,
+ network_state: :trigger_network_state_changed,
+ enrollment: :trigger_enrollment_complete,
+ custom: :trigger_other
+ }.freeze
- TRIGGER_TYPES = {:event => "EVENT", :user => "USER_INITIATED"}
-
SCOPE_TARGET_KEY = :computers
######################
### Attributes
######################
@@ -211,16 +232,31 @@
attr_reader :enabled
### @return [String] a string with the site name
attr_reader :site
-
### @return [Hash]
###
### Overrides for various defaults
###
- ### The hash looks like: !{ :distribution_point => "", :force_afp_smb => false, :netboot_server => "current", :target_drive => "default", :sus => "default"}
+ ### NOTE: There's an API bug in both XML and JSON with the
+ ### :distribution_point and :target_drive values.
+ ### First off, it's not clear what the :target_drive value here
+ ### is overriding, since there's a :target_drive value in the
+ ### main General hash.
+ ### Second off - when you set a non-default dist.point in the
+ ### packages section of the UI, that value shows up in both
+ ### this :target_drive and the general one, but the :distribution_point
+ ### value here stays empty.
+ ###
+ ### The hash looks like:
+ ### :distribution_point => "",
+ ### :force_afp_smb => false,
+ ### :netboot_server => "current",
+ ### :target_drive => "default",
+ ### :sus => "default"
+ ###
attr_reader :override_default_settings
### The API has a :network_requirements key in the general section, but
### in the UI its in a subsection called Client Side Limitiations.
### so we'll store it in a hash called client_side_limitations,
@@ -302,14 +338,15 @@
### set_trigger_event(type, new_val)
### where type is one of the keys in TRIGGER_EVENTS and new val is the new value (usually boolean)
###
attr_reader :trigger_events
- ##### client machine maintenence
+ ###### client machine maintenence
### These are the computer maint. tasks
### that might be performed by this policy
### All are boolean
+ ### TODO: make individial getters/setters as for @files_processes
### @return [Boolean] client maintenance task
attr_reader :verify_startup_disk
### @return [Boolean] client maintenance task
@@ -331,24 +368,18 @@
attr_reader :install_cached_pkgs
### @return [Boolean] client maintenance task
attr_reader :flush_user_cache
- ### attr_reader :heal # deprecated
- ### attr_reader :prebinding # deprecated
-
- ##### client account maint
- ### acct related maintenence performed by this policy
-
### @return [Array<Hash>]
###
### The directory bindings applied
###
### each hash is like: !{:name => "LDAP", :id => 4}
+ ### TODO: handle as for packages & scripts
attr_reader :directory_bindings
-
### @return [Hash] the open firmware mode and password
attr_reader :open_firmware_efi_password
### @return [Hash]
###
@@ -359,10 +390,11 @@
### - :managed_password
### - :managed_password_md5
### - :managed_password_sha256
### - :managed_password_length # for random generating pws
###
+ ### TODO: make individial getters/setters as for @files_processes
attr_reader :management_account
### @return [Array<Hash>]
###
### Local accts acted-upon by this policy
@@ -375,13 +407,14 @@
### - :home => "/Users/chrisltest",
### - :realname => "ChrisTest Lasell",
### - :filevault_enabled => true,
### - :username => "chrisltest",
### - :password_md5 => "3858f62230ac3c915f300c664312c63f",
- ### - : password => "foobar",
+ ### - :password => "foobar",
### - :password_sha256=> "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"
###
+ ### TODO: make individial getters/setters as for @files_processes
attr_reader :accounts
### @return [Array<Hash>]
###
### The pkgs handled by this policy
@@ -412,34 +445,19 @@
### - :id => 1428,
### - :priority => "After"
###
attr_reader :scripts
-
- ### @return [Hash]
- ###
- ### Self-service-related data for this policy
- ###
- ### The hash keys are:
- ### - :self_service_icon => !{:uri => String,:id => Integer, :filename => String}
- ### - :use_for_self_service => true,
- ### - :install_button_text => "Install",
- ### - :self_service_description => "Descriptive text",
- ### - :force_users_to_view_description => false
- ###
- ### Note: we'll add a ruby-std convenience method below "self_service?"
- ### which returns the :use_for_self_service key.
- attr_reader :self_service
-
- #### user interaction
+ ###### user interaction
### These are extracted from the :user_interaction hash
### in the JSON output, which looks like this:
### :message_start => "",
### :allow_users_to_defer => false,
### :allow_deferral_until_utc => "",
### :message_finish => ""
###
+ ### TODO: make individial getters/setters as for @files_processes
### @return [Boolean] can the user defer the policy?
attr_reader :user_may_defer
### @return [Time] when is the user no longer allowed to defer?
@@ -456,15 +474,17 @@
### Reboot options for the policy
###
### The hash keys are:
### - :user_logged_in => "Do not restart",
### - :minutes_until_reboot => 5,
- ### - :message=> "This computer will restart in 5 minutes. Please save anything you are working on and log out by choosing Log Out from the bottom of the Apple menu.",
+ ### - :message=> "This computer will restart in 5 minutes. yaddayadda.",
### - :startup_disk => "Current Startup Disk",
### - :specify_startup => "",
### - :no_user_logged_in => "Do not restart"
+ ### - :file_vault_2_reboot => false
###
+ ### TODO: make individial getters/setters as for @files_processes
attr_reader :reboot_options
##### files & processes
### a hash like this:
### {:spotlight_search => "Spotlight This",
@@ -478,11 +498,10 @@
###
### NOTE, since these items are editable, they have custom getters/setters
### so that the hash isn't directly changable without @need_to_update.
### attr_reader :files_processes
-
### @return [Array<Hash>]
###
### The dock items handled by this policy
###
### each item hash looks like: !{:name => "Mail", :id => 14, :action => "Add To Beginning"}
@@ -504,232 +523,266 @@
#####################################
### Public Instance Methods
#####################################
- ###
### @see APIObject#initialize
###
def initialize(args = {})
-
super
+ if @in_jss
+ gen = @init_data[:general]
+ @category = JSS::APIObject.get_name(gen[:category])
+ @frequency = gen[:frequency]
+ @target_drive = gen[:target_drive]
+ @offline = gen[:offline]
+ @enabled = gen[:enabled]
+ @site = JSS::APIObject.get_name(gen[:site][:name])
+ @override_default_settings = gen[:override_default_settings]
+ @trigger = gen[:trigger]
+ @trigger_events = {
+ trigger_startup: gen[:trigger_startup],
+ trigger_login: gen[:trigger_login],
+ trigger_logout: gen[:trigger_logout],
+ trigger_checkin: gen[:trigger_checkin],
+ trigger_network_state_changed: gen[:trigger_network_state_changed],
+ trigger_enrollment_complete: gen[:trigger_enrollment_complete],
+ trigger_other: gen[:trigger_other]
+ }
- gen = @init_data[:general]
- @category = JSS::APIObject.get_name(gen[:category])
- @frequency = gen[:frequency]
- @target_drive = gen[:target_drive]
- @offline = gen[:offline]
- @enabled = gen[:enabled]
- @site = JSS::APIObject.get_name(gen[:site][:name])
- @override_default_settings = gen[:override_default_settings]
- @trigger = gen[:trigger ]
- @trigger_events = {
- :trigger_startup => gen[:trigger_startup ],
- :trigger_login => gen[:trigger_login ],
- :trigger_logout => gen[:trigger_logout ],
- :trigger_checkin => gen[:trigger_checkin ],
- :trigger_network_state_changed => gen[:trigger_network_state_changed ],
- :trigger_enrollment_complete => gen[:trigger_enrollment_complete ],
- :trigger_other => gen[:trigger_other ]
- }
+ dtl = gen[:date_time_limitations]
- dtl = gen[:date_time_limitations]
+ @server_side_limitations = {
+ activation: JSS.epoch_to_time(dtl[:activation_date_epoch]),
+ expiration: JSS.epoch_to_time(dtl[:expiration_date_epoch])
+ }
- @server_side_limitations = {
- :activation => JSS.epoch_to_time(dtl[:activation_date_epoch]),
- :expiration => JSS.epoch_to_time(dtl[:expiration_date_epoch])
- }
+ @client_side_limitations = {
+ no_execute_on: dtl[:no_execute_on], # NOTE- there's a bug in the JSON output, it's been reported to JAMF.
+ no_execute_start: dtl[:no_execute_start], # String like "1:01 AM"
+ no_execute_end: dtl[:no_execute_end], # String like "2:02 PM"
+ network_requirements: gen[:network_requirements]
+ }
- @client_side_limitations = {
- :no_execute_on => dtl[:no_execute_on], # NOTE- there's a bug in the JSON output, it's been reported to JAMF.
- :no_execute_start => dtl[:no_execute_start], # String like "1:01 AM"
- :no_execute_end => dtl[:no_execute_end], # String like "2:02 PM"
- :network_requirements => gen[:network_requirements]
- }
+ maint = @init_data[:maintenance]
+ @verify_startup_disk = maint[:verify]
+ @permissions_repair = maint[:permissions]
+ @recon = maint[:recon]
+ @fix_byhost = maint[:byhost]
+ @reset_name = maint[:reset_name]
+ @flush_system_cache = maint[:system_cache]
+ @install_cached_pkgs = maint[:install_all_cached_packages]
+ @flush_user_cache = maint[:user_cache]
- maint = @init_data[:maintenance]
- @verify_startup_disk = maint[:verify]
- @permissions_repair = maint[:permissions]
- @recon = maint[:recon]
- @fix_byhost = maint[:byhost]
- @reset_name = maint[:reset_name]
- @flush_system_cache = maint[:system_cache]
- @install_cached_pkgs = maint[:install_all_cached_packages]
- @flush_user_cache = maint[:user_cache]
+ amaint = @init_data[:account_maintenance]
+ @directory_bindings = amaint[:directory_bindings]
+ @open_firmware_efi_password = amaint[:open_firmware_efi_password]
+ @management_account = amaint[:management_account]
+ @accounts = amaint[:accounts]
- amaint = @init_data[:account_maintenance]
- @directory_bindings = amaint[:directory_bindings]
- @open_firmware_efi_password = amaint[:open_firmware_efi_password]
- @management_account = amaint[:management_account]
- @accounts = amaint[:accounts]
+ @packages = @init_data[:package_configuration][:packages] ? @init_data[:package_configuration][:packages] : []
- @packages = @init_data[:package_configuration][:packages] ? @init_data[:package_configuration][:packages] : []
+ @scripts = @init_data[:scripts]
- @scripts = @init_data[:scripts]
+ uint = @init_data[:user_interaction]
+ @user_may_defer = uint[:allow_users_to_defer]
+ @user_may_defer_until = JSS.parse_datetime uint[:allow_deferral_until_utc]
+ @user_message_start = uint[:message_start]
+ @user_message_finish = uint[:message_finish]
- @self_service = @init_data[:self_service]
+ @reboot_options = @init_data[:reboot]
- uint = @init_data[:user_interaction]
- @user_may_defer = uint[:allow_users_to_defer]
- @user_may_defer_until = JSS.parse_datetime uint[:allow_deferral_until_utc]
- @user_message_start = uint[:message_start]
- @user_message_finish = uint[:message_finish]
+ @files_processes = @init_data[:files_processes]
- @reboot_options = @init_data[:reboot]
+ @dock_items = @init_data[:dock_items]
- @files_processes = @init_data[:files_processes]
+ @disk_encryption = @init_data[:disk_encryption]
- @dock_items = @init_data[:dock_items]
+ @printers = @init_data[:printers]
- @disk_encryption = @init_data[:disk_encryption]
+ parse_scope
+ parse_self_service
+ @in_self_service = @init_data[:self_service][:use_for_self_service]
- @printers = @init_data[:printers]
+ ### Not in jss yet
+ end
- parse_scope
+ # set non-nil defaults
+ @enabled ||= false
+ @frequency ||= 'Once per computer'
+ @target_drive ||= '/'
+ @offline ||= false
+ @override_default_settings ||= {}
+ @scripts ||= []
+ @server_side_limitations ||= {}
+ @client_side_limitiations ||= {}
+ @trigger_events ||= {}
+ @directory_bindings ||= []
+ @open_firmware_efi_password ||= {}
+ @management_account ||= {}
+ @accounts ||= []
+ @packages ||= []
+ @scripts ||= []
+ @self_service ||= {}
+ @dock_items ||= []
+ @disk_encryption ||= {}
+ @printers ||= []
+ @files_processes ||= {}
+ unless @reboot_options
+ @reboot_options = {}
+ @reboot_options[:user_logged_in] = 'Do not restart'
+ @reboot_options[:no_user_logged_in] = 'Do not restart'
+ end
- end # init
+ @scope ||= JSS::Scopable::Scope.new(:computers, all_computers: false)
+ end # init
- ###
+ ###### General
+
### Change the enabled state of this item
###
### @param new_val[Boolean] the new state.
###
### @return [void]
###
- def enabled= (new_val)
+ def enabled=(new_val)
return nil if @enabled == new_val
- raise JSS::InvalidDataError, "New value must be true or false" unless JSS::TRUE_FALSE.include? new_val
+ raise JSS::InvalidDataError, 'New value must be true or false' unless JSS::TRUE_FALSE.include? new_val
@enabled = new_val
@need_to_update = true
end
- ###
+ ### Shortcut for enabled = true
+ def enable
+ self.enabled = true
+ end
+
+ ### Shortcut for endabled = false
+ def disable
+ self.enabled = false
+ end
+
### Set a new frequency for this policy.
###
### @param freq[Symbol] the desired frequency, must be one of the keys of {FREQUENCIES}
###
### @return [void]
###
- def frequency= (freq)
- raise JSS::InvalidDataError, "New frequency must be one of :#{FREQUENCIES.keys.join ", :"}" unless FREQUENCIES.keys.include? freq
+ def frequency=(freq)
+ raise JSS::InvalidDataError, "New frequency must be one of :#{FREQUENCIES.keys.join ', :'}" unless FREQUENCIES.keys.include? freq
@frequency = FREQUENCIES[freq]
@need_to_update = true
end
- ###
### Set a new target drive for this policy.
###
### @param path_to_drive[String,Pathname] the full path to the target drive, must start with a '/'
###
### @return [void]
###
- def target_drive= (path_to_drive)
- raise JSS::InvalidDataError, "Path to target drive must be absolute" unless path_to_drive.to_s.start_with? '/'
+ def target_drive=(path_to_drive)
+ raise JSS::InvalidDataError, 'Path to target drive must be absolute' unless path_to_drive.to_s.start_with? '/'
@target_drive = path_to_drive.to_s
@need_to_update = true
end
- ###
### Set whether this policy is available offline.
###
### @param new_val[Boolean]
###
### @return [void]
###
- def offline= (new_val)
- raise JSS::InvalidDataError, "New value must be boolean true or false" unless JSS::TRUE_FALSE.include? new_val
+ def offline=(new_val)
+ raise JSS::InvalidDataError, 'New value must be boolean true or false' unless JSS::TRUE_FALSE.include? new_val
@offline = new_val
@need_to_update = true
end
+ ### Change the category of this policy, arg is a category name
###
- ### Change the category of this item, arg is a category name
- ###
### @param new_val[String] the name of the new category
###
### @return [void]
###
- def category= (new_val = JSS::Category::DEFAULT_CATEGORY)
+ def category=(new_val = JSS::Category::DEFAULT_CATEGORY)
return nil if @category == new_val
new_val = nil if new_val == ''
new_val ||= JSS::Category::DEFAULT_CATEGORY
raise JSS::NoSuchItemError, "No category '#{new_val}' in the JSS" unless JSS::Category.all_names(:refresh).include? new_val
@category = new_val
@need_to_update = true
end
- ###
### Change a trigger event
###
### @param type[Symbol] the type of trigger, one of the keys of {TRIGGER_EVENTS}
###
### @param new_val[Boolean] whether the type of trigger is active or not.
###
### @return [void]
###
- def set_trigger_event (type, new_val)
+ def set_trigger_event(type, new_val)
raise JSS::InvalidDataError, "Trigger type must be one of #{TRIGGER_EVENTS.keys.join(', ')}" unless TRIGGER_EVENTS.keys.include? type
if type == :custom
- raise JSS::InvalidDataError, "Custom triggers must be Strings" unless new_val.kind_of? String
+ raise JSS::InvalidDataError, 'Custom triggers must be Strings' unless new_val.is_a? String
else
- raise JSS::InvalidDataError, "Non-custom triggers must be true or false" unless JSS::TRUE_FALSE.include? new_val
+ raise JSS::InvalidDataError, 'Non-custom triggers must be true or false' unless JSS::TRUE_FALSE.include? new_val
end
@trigger_events[TRIGGER_EVENTS[type]] = new_val
@need_to_update = true
end
- ###
+ ###### Files & Processes
+
### @return [String] The unix shell command to run on ths client.
###
- def run_command ; @files_processes[:run_command] ; end
+ def run_command
+ @files_processes[:run_command]
+ end
- ###
### Set the unix shell command to be run on the client
###
### @param command[String] the unix shell command to be run on the client
###
### @return [void]
###
- def run_command= (command)
- raise JSS::InvalidDataError, "Command to run must be a String" unless command.is_a? String
+ def run_command=(command)
+ raise JSS::InvalidDataError, 'Command to run must be a String' unless command.is_a? String
@files_processes[:run_command] = command
@need_to_update = true
end
- ###
### @return [Boolean] Should we update the database used by the locate command?
###
- def update_locate_database? ; @files_processes[:update_locate_database] ; end
+ def update_locate_database?
+ @files_processes[:update_locate_database]
+ end
- ###
### Set whether or not to update the database used by the locate command.
###
### @param tf[Boolean] whether or not to update the database used by the locate command.
###
### @return [void]
###
- def update_locate_database= (tf)
+ def update_locate_database=(tf)
@files_processes[:update_locate_database] = tf ? true : false
@need_to_update = true
end
- ###
### @return [String] The process name to search for on the client
###
def search_for_process
@files_processes[:search_for_process]
end
- ###
### @return [Boolean] Should the searched-for process be killed if found.
###
def kill_process?
@files_processes[:kill_process]
end
- ###
### Set the process name to search for, and if it should be killed if found.
###
### Setter methods (which end with =) can't easily take
### multiple arguments, so we instead name them "set_blah_blah"
### rather than "blah_blah="
@@ -738,27 +791,28 @@
###
### @param kill[Boolean] should be process be killed if found
###
### @return [void]
###
- def set_search_for_process (process, kill = false)
+ def set_search_for_process(process, kill = false)
@files_processes[:search_for_process] = process.to_s
@files_processes[:kill_process] = kill ? true : false
@need_to_update = true
end
- ###
### @return [Pathname] The path to search for
###
- def search_by_path ; Pathname.new @files_processes[:search_by_path] ; end
+ def search_by_path
+ Pathname.new @files_processes[:search_by_path]
+ end
- ###
### @return [Boolean] Should the searched-for path be deleted if found?
###
- def delete_file? ; @files_processes[:delete_file] ; end
+ def delete_file?
+ @files_processes[:delete_file]
+ end
- ###
### Set the path to search for, a String or Pathname, and whether or not to delete it if found.
###
### Setter methods (which end with =) can't easily take
### multiple arguments, so we instead name them "set_blah_blah"
### rather than "blah_blah="
@@ -767,132 +821,417 @@
###
### @param delete[Boolean] should the path be deleted if found
###
### @return [void]
###
- def set_search_by_path (path, delete = false)
- raise JSS::InvalidDataError, "Path to search for must be a String or a Pathname" unless path.is_a? String or path.is_a? Pathname
+ def set_search_by_path(path, delete = false)
+ raise JSS::InvalidDataError, 'Path to search for must be a String or a Pathname' unless path.is_a?(String) || path.is_a?(Pathname)
@files_processes[:search_by_path] = path.to_s
@files_processes[:delete_file] = delete ? true : false
@need_to_update = true
end
- ###
### @return [String] The term to search for using spotlight
###
- def spotlight_search ; @files_processes[:spotlight_search] ; end
+ def spotlight_search
+ @files_processes[:spotlight_search]
+ end
### Set the term to seach for using spotlight
###
### @param term[String] the term to seach for using spotlight
###
### @return [void]
###
- def spotlight_search= (term)
- raise JSS::InvalidDataError, "Spotlight search term must be a String" unless term.is_a? String
+ def spotlight_search=(term)
+ raise JSS::InvalidDataError, 'Spotlight search term must be a String' unless term.is_a? String
@files_processes[:spotlight_search] = term
@need_to_update = true
end
- ###
### @return [String] The term to seach for using the locate command
###
- def locate_file ; @files_processes[:locate_file] ; end
+ def locate_file
+ @files_processes[:locate_file]
+ end
### Set the term to seach for using the locate command
###
### @param term[String] the term to seach for using the locate command
###
### @return [void]
###
- def locate_file= (term)
- raise JSS::InvalidDataError, "Term to locate must be a String" unless term.is_a? String
+ def locate_file=(term)
+ raise JSS::InvalidDataError, 'Term to locate must be a String' unless term.is_a? String
@files_processes[:locate_file] = term
@need_to_update = true
end
+ ###### Client maintenance
+
+ ### Set the
+
+ ###### Packages
+
### @return [Array] the id's of the packages handled by the policy
- def package_ids; @packages.map{|p| p[:id]} ; end
+ def package_ids
+ @packages.map { |p| p[:id] }
+ end
### @return [Array] the names of the packages handled by the policy
- def package_names; @packages.map{|p| p[:name]} ; end
+ def package_names
+ @packages.map { |p| p[:name] }
+ end
+ ### Add a package to the list of pkgs handled by this policy.
+ ### If the pkg already exists in the policy, nil is returned and
+ ### no changes are made.
+ ###
+ ### @param [String,Integer] identifier the name or id of the package to add to this policy
+ ###
+ ### @param [Hash] opts the options for this pkg
+ ###
+ ### @option [Symbol, Integer] position: :end where to add this pkg among the list of
+ ### pkgs. Zero-based, :start and 0 are the same, as are :end and -1. Defaults to :end
+ ###
+ ### @option [String] action: 'Install' One of the values of PACKAGE_ACTIONS
+ ###
+ ### @option [Boolean] feu: false Overrides the setting for the pkg itself
+ ###
+ ### @option [Boolean] fut: false Overrides the setting for the pkg itself
+ ###
+ ### @option [Boolean] update_autorun: false
+ ###
+ ### @return [Array, nil] the new @packages array, nil if pkg was already in the policy
+ ###
+ def add_package(identifier, opts = {})
+ opts[:position] ||= -1
+ opts[:action] ||= :install
+ opts[:feu] = false if opts[:feu].nil?
+ opts[:fut] = false if opts[:fut].nil?
+ opts[:update_autorun] = false if opts[:update_autorun].nil?
+
+ id = JSS::Package.valid_id identifier
+ raise JSS::NoSuchItemError, "No package matches '#{identifier}'" unless id
+
+ return nil if @packages.map { |p| p[:id] }.include? id
+
+ name = JSS::Package.map_all_ids_to(:name)[id]
+
+ position = case opts[:position]
+ when :start then 0
+ when :end then -1
+ else opts[:position]
+ end
+
+ raise JSS::InvalidDataError, "action must be one of: :#{PACKAGE_ACTIONS.keys.join ', :'}" unless \
+ PACKAGE_ACTIONS.include? opts[:action]
+ raise JSS::InvalidDataError, 'feu must be true or false' unless \
+ JSS::TRUE_FALSE.include? opts[:feu]
+ raise JSS::InvalidDataError, 'fut must be true or false' unless \
+ JSS::TRUE_FALSE.include? opts[:fut]
+ raise JSS::InvalidDataError, 'update_autorun must be true or false' unless \
+ JSS::TRUE_FALSE.include? opts[:update_autorun]
+
+ pkg_data = {
+ id: id,
+ name: name,
+ action: PACKAGE_ACTIONS[opts[:action]],
+ feu: opts[:feu],
+ fut: opts[:feu],
+ update_autorun: opts[:update_autorun]
+ }
+
+ @packages.insert position, pkg_data
+
+ ### if the user gave a large number for position, it created nil entries in the array, they need
+ ### to be removed.
+ @packages.compact!
+
+ @need_to_update = true
+ @packages
+ end
+
+ ### Remove a package from this policy by name or id
+ ###
+ ### @param identfier [String,Integer] the name or id of the package to remove
+ ###
+ ### @return [Array, nil] the new packages array or nil if no change
+ ###
+ def remove_package(identifier)
+ removed = @packages.delete_if { |p| p[:id] == identifier || p[:name] == identifier }
+ @need_to_update = true if removed
+ removed
+ end
+
+ ###### Scripts
+
### @return [Array] the id's of the scripts handled by the policy
- def script_ids; @scripts.map{|p| p[:id]} ; end
+ def script_ids
+ @scripts.map { |p| p[:id] }
+ end
### @return [Array] the names of the scripts handled by the policy
- def script_names; @scripts.map{|p| p[:name]} ; end
+ def script_names
+ @scripts.map { |p| p[:name] }
+ end
+ ### Add a script to the list of SCRIPT_PRIORITIESipts run by this policy.
+ ### If the script already exists in the policy, nil is returned and
+ ### no changes are made.
+ ###
+ ### @param [String,Integer] identifier the name or id of the script to add to this policy
+ ###
+ ### @param [Hash] opts the options for this script
+ ###
+ ### @option [Symbol, Integer] position: where to add this script among the list of
+ ### scripts. Zero-based, :start and 0 are the same, as are :end and -1. Defaults to :end
+ ###
+ ### @option [Symbol] priority: either :before or :after
+ ###
+ ### @option [String] parameter4: the value of the 4th parameter passed to the script. this
+ ### overrides the same parameter in the script object itself.
+ ###
+ ### @option [String] parameter5: the value of the 5th parameter passed to the script. this
+ ### overrides the same parameter in the script object itself.
+ ###
+ ### @option [String] parameter6: the value of the 6th parameter passed to the script. this
+ ### overrides the same parameter in the script object itself.
+ ###
+ ### @option [String] parameter7: the value of the 7th parameter passed to the script. this
+ ### overrides the same parameter in the script object itself.
+ ###
+ ### @option [String] parameter8: the value of the 8th parameter passed to the script. this
+ ### overrides the same parameter in the script object itself.
+ ###
+ ### @option [String] parameter9: the value of the 9th parameter passed to the script. this
+ ### overrides the same parameter in the script object itself.
+ ###
+ ### @option [String] parameter10: the value of the 10th parameter passed to the script. this
+ ### overrides the same parameter in the script object itself.
+ ###
+ ### @option [String] parameter11: the value of the 11th parameter passed to the script. this
+ ### overrides the same parameter in the script object itself.
+ ###
+ ### @return [Array, nil] the new @scripts array, nil if script was already in the policy
+ ###
+ def add_script(identifier, opts = {})
+ opts[:position] ||= -1
+ opts[:priority] ||= :after
+
+ raise JSS::NoSuchItemError, "No script matches '#{identifier}'" unless (id = JSS::Script.valid_id(identifier))
+
+ return nil if @scripts.map { |s| s[:id] }.include? id
+
+ name = JSS::Script.map_all_ids_to(:name)[id]
+
+ position = case opts[:position]
+ when :start then 0
+ when :end then -1
+ else opts[:position]
+ end
+
+ raise JSS::InvalidDataError, "priority must be one of: :#{SCRIPT_PRIORITIES.keys.join ', :'}" unless \
+ SCRIPT_PRIORITIES.include? opts[:priority]
+
+ script_data = {
+ id: id,
+ name: name,
+ priority: SCRIPT_PRIORITIES[opts[:priority]],
+ parameter4: opts[:parameter4],
+ parameter5: opts[:parameter5],
+ parameter6: opts[:parameter6],
+ parameter7: opts[:parameter7],
+ parameter8: opts[:parameter8],
+ parameter9: opts[:parameter9],
+ parameter10: opts[:parameter10],
+ parameter11: opts[:parameter11]
+ }
+
+ @scripts.insert position, script_data
+
+ ### if the user gave a large number for position, it created nil entries in the array, they need
+ ### to be removed.
+ @scripts.compact!
+
+ @need_to_update = true
+ @scripts
+ end
+
+ ### Remove a script from this policy by name or id
+ ###
+ ### @param identfier [String,Integer] the name or id of the script to remove
+ ###
+ ### @return [Array, nil] the new scripts array or nil if no change
+ ###
+ def remove_script(identifier)
+ removed = @scripts.delete_if { |s| s[:id] == identifier || s[:name] == identifier }
+ @need_to_update = true if removed
+ removed
+ end
+
+ ###### Directory Bindings
+
### @return [Array] the id's of the directory_bindings handled by the policy
- def directory_binding_ids; @directory_bindings.map{|p| p[:id]} ; end
+ def directory_binding_ids
+ @directory_bindings.map { |p| p[:id] }
+ end
### @return [Array] the names of the directory_bindings handled by the policy
- def directory_binding_names; @directory_bindings.map{|p| p[:name]} ; end
+ def directory_binding_names
+ @directory_bindings.map { |p| p[:name] }
+ end
+ ###### Dock items
+
### @return [Array] the id's of the dock_items handled by the policy
- def dock_item_ids; @dock_items.map{|p| p[:id]} ; end
+ def dock_item_ids
+ @dock_items.map { |p| p[:id] }
+ end
### @return [Array] the names of the dock_items handled by the policy
- def dock_item_names; @dock_items.map{|p| p[:name]} ; end
+ def dock_item_names
+ @dock_items.map { |p| p[:name] }
+ end
+ ###### Printers
+
### @return [Array] the id's of the printers handled by the policy
- def printer_ids; @printers.map{|p| p[:id]} ; end
+ def printer_ids
+ @printers.map { |p| p[:id] }
+ end
### @return [Array] the names of the printers handled by the policy
- def printer_names; @printers.map{|p| p[:name]} ; end
+ def printer_names
+ @printers.map { |p| p[:name] }
+ end
- ### @return [Boolean] is this policy available in SelfService?
- def self_service?; @self_service[:use_for_self_service] ; end
-
+ ###### SelfService
+ ### Is this policy in self service?
+ ###
+ ### @return [Boolean] Is this policy in self service?
+ ###
+ def in_self_service?
+ @in_self_service
+ end
+
+ ### policies in self service aren't 'removable'
+ ###
+ ### @return [FalseClass]
+ ###
+ def user_removable?
+ false
+ end
+ alias user_removable= user_removable?
+
+ ### Add this policy to Self Service
+ ###
+ ### @return [void]
+ def add_to_self_service
+ @need_to_update = (@in_self_service == false)
+ @in_self_service = true
+ end
+
+ ### Remove this policy from Self Service
+ ###
+ ### @return [void]
+ def remove_from_self_service
+ @need_to_update = (@in_self_service == true)
+ @in_self_service = false
+ end
+
+ ### Select or upload an image file to be the self service icon.
+ ### The policy must already exist in the JSS to upload an image.
+ ###
+ ### NOTE: There is no way to verify the validity of an icon id, as they are not
+ ### available via the API. Caveat Emptor.
+ ###
+ ### NOTE: When setting by id, {#update} or #{save} must be used to save the change.
+ ###
+ ### @param icon [String, Pathname, Integer] the path to the local image file, to upload
+ ### or the id of a previously uploaded one.
+ ###
+ ### @return [void]
+ ###
+ def assign_icon(icon)
+ if icon.is_a? Integer
+ @self_service_icon = { id: icon }
+ @need_to_update = true
+ return
+ end
+ upload(:icon, icon)
+ end
+
+ ###### Actions
+
### Try to execute this policy on this machine.
###
- ### @param show_output[Boolean] should the stdout and stderr of the
+ ### @param show_output[Boolean] should the stdout and stderr of the
### 'jamf policy' command be sent to stdout in realtime?
###
### @return [Boolean, nil] The success of the 'jamf policy' command, or nil
### if the policy couldn't be executed (out of scope, policy disabled, etc)
###
- def run (show_output = false)
+ def run(show_output = false)
return nil unless enabled?
- output = JSS::Client.run_jamf("policy", "-id #{id}", show_output)
+ output = JSS::Client.run_jamf('policy', "-id #{id}", show_output)
return nil if output.include? 'No policies were found for the ID'
- return $?.exitstatus == 0 ? true : false
+ $CHILD_STATUS.exitstatus.zero? ? true : false
end
-
- ### Aliases
+
+ ###### Aliases
+
alias enabled? enabled
alias pkgs packages
alias command_to_run run_command
alias delete_path? delete_file?
alias execute run
-
- #####################################
+ alias self_service? in_self_service?
+
### Private Instance Methods
#####################################
private
def rest_xml
doc = REXML::Document.new APIConnection::XML_HEADER
obj = doc.add_element RSRC_OBJECT_KEY.to_s
- general = obj.add_element "general"
+ general = obj.add_element 'general'
general.add_element('name').text = @name
general.add_element('enabled').text = @enabled
general.add_element('frequency').text = @frequency
general.add_element('target_drive').text = @target_drive
general.add_element('offline').text = @offline
- general.add_element('category').add_element('name').text = @category
+ general.add_element('category').add_element('name').text = @category if @category
- JSS.hash_to_rexml_array(@trigger_events).each{|t| general << t}
+ JSS.hash_to_rexml_array(@trigger_events).each { |t| general << t }
obj << @scope.scope_xml
- files_processes = obj.add_element "files_processes"
- JSS.hash_to_rexml_array(@files_processes).each{|f| files_processes << f}
+ files_processes = obj.add_element 'files_processes'
+ JSS.hash_to_rexml_array(@files_processes).each { |f| files_processes << f }
- return doc.to_s
+ pkg_conf = obj.add_element 'package_configuration'
+ pkgs = pkg_conf.add_element 'packages'
+ @packages.each do |p|
+ pkg = pkgs.add_element 'package'
+ pdeets = JSS.hash_to_rexml_array p
+ pdeets.each { |d| pkg << d }
+ end
+
+ scripts = obj.add_element 'scripts'
+ @scripts.each do |s|
+ script = scripts.add_element 'script'
+ sdeets = JSS.hash_to_rexml_array s
+ sdeets.each { |d| script << d }
+ end
+
+ self_svc = self_service_xml
+ self_svc.add_element('use_for_self_service').text = @in_self_service
+ obj << self_svc
+
+ doc.to_s
end
end # class policy
end # module
-