ruby-jss 1.2.3 → 1.2.4a1

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jamf.rb +169 -0
  3. data/lib/jamf/api/abstract_classes/collection_resource.rb +422 -0
  4. data/lib/jamf/api/abstract_classes/generic_reference.rb +145 -0
  5. data/lib/jamf/api/abstract_classes/json_object.rb +1074 -0
  6. data/lib/jamf/api/abstract_classes/prestage.rb +219 -0
  7. data/lib/jamf/api/abstract_classes/prestage_skip_setup_items.rb +126 -0
  8. data/lib/jamf/api/abstract_classes/resource.rb +250 -0
  9. data/lib/jamf/api/abstract_classes/singleton_resource.rb +87 -0
  10. data/lib/jamf/api/attribute_classes/ip_address.rb +66 -0
  11. data/lib/jamf/api/attribute_classes/timestamp.rb +144 -0
  12. data/lib/jamf/api/connection.rb +734 -0
  13. data/lib/jamf/api/connection/api_error.rb +111 -0
  14. data/lib/jamf/api/connection/api_error_styleguide.rb +96 -0
  15. data/lib/jamf/api/connection/token.rb +220 -0
  16. data/lib/jamf/api/json_objects/account_prefs.rb +79 -0
  17. data/lib/jamf/api/json_objects/android_details.rb +139 -0
  18. data/lib/jamf/api/json_objects/appletv_details.rb +110 -0
  19. data/lib/jamf/api/json_objects/attachment.rb +68 -0
  20. data/lib/jamf/api/json_objects/cellular_network.rb +151 -0
  21. data/lib/jamf/api/json_objects/change_log_entry.rb +77 -0
  22. data/lib/jamf/api/json_objects/computer_prestage_skip_setup_items.rb +67 -0
  23. data/lib/jamf/api/json_objects/country.rb +51 -0
  24. data/lib/jamf/api/json_objects/extension_attribute_value.rb +128 -0
  25. data/lib/jamf/api/json_objects/installed_application.rb +59 -0
  26. data/lib/jamf/api/json_objects/installed_certificate.rb +53 -0
  27. data/lib/jamf/api/json_objects/installed_configuration_profile.rb +67 -0
  28. data/lib/jamf/api/json_objects/installed_ebook.rb +58 -0
  29. data/lib/jamf/api/json_objects/installed_provisioning_profile.rb +59 -0
  30. data/lib/jamf/api/json_objects/inventory_preload_extension_attribute.rb +52 -0
  31. data/lib/jamf/api/json_objects/ios_details.rb +244 -0
  32. data/lib/jamf/api/json_objects/location.rb +95 -0
  33. data/lib/jamf/api/json_objects/md_prestage_name.rb +57 -0
  34. data/lib/jamf/api/json_objects/md_prestage_names.rb +82 -0
  35. data/lib/jamf/api/json_objects/md_prestage_skip_setup_items.rb +165 -0
  36. data/lib/jamf/api/json_objects/mobile_device_details.rb +219 -0
  37. data/lib/jamf/api/json_objects/mobile_device_security.rb +101 -0
  38. data/lib/jamf/api/json_objects/prestage_assignment.rb +61 -0
  39. data/lib/jamf/api/json_objects/prestage_location.rb +104 -0
  40. data/lib/jamf/api/json_objects/prestage_purchasing_data.rb +132 -0
  41. data/lib/jamf/api/json_objects/prestage_scope.rb +54 -0
  42. data/lib/jamf/api/json_objects/prestage_sync_status.rb +63 -0
  43. data/lib/jamf/api/json_objects/purchasing_data.rb +125 -0
  44. data/lib/jamf/api/mixins/abstract.rb +58 -0
  45. data/lib/jamf/api/mixins/bulk_deletable.rb +39 -0
  46. data/lib/jamf/api/mixins/change_log.rb +136 -0
  47. data/lib/jamf/api/mixins/extendable.rb +75 -0
  48. data/lib/jamf/api/mixins/immutable.rb +39 -0
  49. data/lib/jamf/api/mixins/locatable.rb +124 -0
  50. data/lib/jamf/api/mixins/lockable.rb +48 -0
  51. data/lib/jamf/api/mixins/referable.rb +92 -0
  52. data/lib/jamf/api/mixins/searchable.rb +202 -0
  53. data/lib/jamf/api/mixins/uncreatable.rb +40 -0
  54. data/lib/jamf/api/mixins/undeletable.rb +40 -0
  55. data/lib/jamf/api/resources/collection_resources/account.rb +163 -0
  56. data/lib/jamf/api/resources/collection_resources/building.rb +114 -0
  57. data/lib/jamf/api/resources/collection_resources/category.rb +82 -0
  58. data/lib/jamf/api/resources/collection_resources/computer.rb +49 -0
  59. data/lib/jamf/api/resources/collection_resources/computer_prestage.rb +80 -0
  60. data/lib/jamf/api/resources/collection_resources/department.rb +79 -0
  61. data/lib/jamf/api/resources/collection_resources/extension_attribute.rb +45 -0
  62. data/lib/jamf/api/resources/collection_resources/inventory_preload_record.rb +274 -0
  63. data/lib/jamf/api/resources/collection_resources/md_prestage.rb +139 -0
  64. data/lib/jamf/api/resources/collection_resources/mobile_device.rb +315 -0
  65. data/lib/jamf/api/resources/collection_resources/script.rb +190 -0
  66. data/lib/jamf/api/resources/collection_resources/site.rb +77 -0
  67. data/lib/jamf/api/resources/singleton_resources/app_store_country_codes.rb +131 -0
  68. data/lib/jamf/api/resources/singleton_resources/authorization.rb +88 -0
  69. data/lib/jamf/api/resources/singleton_resources/client_checkin_settings.rb +139 -0
  70. data/lib/jamf/api/resources/singleton_resources/reenrollment_settings.rb +95 -0
  71. data/lib/jamf/client.rb +301 -0
  72. data/lib/jamf/client/jamf_binary.rb +132 -0
  73. data/lib/jamf/client/jamf_helper.rb +298 -0
  74. data/lib/jamf/client/management_action.rb +114 -0
  75. data/lib/jamf/compatibility.rb +88 -0
  76. data/lib/jamf/composer.rb +190 -0
  77. data/lib/jamf/configuration.rb +281 -0
  78. data/lib/jamf/exceptions.rb +107 -0
  79. data/lib/jamf/ruby_extensions.rb +36 -0
  80. data/lib/jamf/ruby_extensions/array.rb +35 -0
  81. data/lib/jamf/ruby_extensions/array/predicates.rb +46 -0
  82. data/lib/jamf/ruby_extensions/array/utils.rb +47 -0
  83. data/lib/jamf/ruby_extensions/filetest.rb +32 -0
  84. data/lib/jamf/ruby_extensions/filetest/predicates.rb +46 -0
  85. data/lib/jamf/ruby_extensions/hash.rb +33 -0
  86. data/lib/jamf/ruby_extensions/hash/backports.rb +92 -0
  87. data/lib/jamf/ruby_extensions/ipaddr.rb +37 -0
  88. data/lib/jamf/ruby_extensions/ipaddr/utils.rb +95 -0
  89. data/lib/jamf/ruby_extensions/object.rb +30 -0
  90. data/lib/jamf/ruby_extensions/object/predicates.rb +51 -0
  91. data/lib/jamf/ruby_extensions/pathname.rb +39 -0
  92. data/lib/jamf/ruby_extensions/pathname/predicates.rb +50 -0
  93. data/lib/jamf/ruby_extensions/pathname/utils.rb +75 -0
  94. data/lib/jamf/ruby_extensions/string.rb +35 -0
  95. data/lib/jamf/ruby_extensions/string/backports.rb +66 -0
  96. data/lib/jamf/ruby_extensions/string/conversions.rb +65 -0
  97. data/lib/jamf/ruby_extensions/string/predicates.rb +47 -0
  98. data/lib/jamf/utility.rb +423 -0
  99. data/lib/jamf/validate.rb +224 -0
  100. data/lib/jamf/version.rb +32 -0
  101. data/lib/jpapi.rb +26 -0
  102. data/lib/jss/version.rb +1 -1
  103. metadata +104 -4
@@ -0,0 +1,88 @@
1
+ # Copyright 2019 Pixar
2
+
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "Apache License")
5
+ # with the following modification; you may not use this file except in
6
+ # compliance with the Apache License and the following modification to it:
7
+ # Section 6. Trademarks. is deleted and replaced with:
8
+ #
9
+ # 6. Trademarks. This License does not grant permission to use the trade
10
+ # names, trademarks, service marks, or product names of the Licensor
11
+ # and its affiliates, except as required to comply with Section 4(c) of
12
+ # the License and to reproduce the content of the NOTICE file.
13
+ #
14
+ # You may obtain a copy of the Apache License at
15
+ #
16
+ # http://www.apache.org/licenses/LICENSE-2.0
17
+ #
18
+ # Unless required by applicable law or agreed to in writing, software
19
+ # distributed under the Apache License with the above modification is
20
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ # KIND, either express or implied. See the Apache License for the specific
22
+ # language governing permissions and limitations under the Apache License.
23
+ #
24
+ #
25
+
26
+ # backporting of some newer ruby methods into older rubies
27
+ #
28
+
29
+ # Hash.key
30
+ #
31
+ # #key exists in ruby 1.9+, in 1.8 its called #index
32
+ # it returns the hash key for a given value, if the value exists
33
+ # in the hash
34
+ #
35
+ ###########################################
36
+ unless {}.respond_to? :key
37
+ class Hash
38
+
39
+ alias key index
40
+
41
+ end
42
+ end
43
+
44
+ # Array.sample
45
+ #
46
+ # #sample exists in ruby 1.9+, in 1.8 its called #choice
47
+ # it returns a randomly chosen element of the given array
48
+ # eg: [1, 2, 3].sample returns either 1, 2, or 3
49
+ #
50
+ ###########################################
51
+ unless [].respond_to? :sample
52
+ class Array
53
+
54
+ alias sample choice
55
+
56
+ end
57
+ end
58
+
59
+ # String.force_encoding
60
+ #
61
+ #
62
+ ###########################################
63
+ unless ''.respond_to? :force_encoding
64
+ class String
65
+
66
+ def force_encoding(_args = nil)
67
+ self
68
+ end
69
+
70
+ end
71
+ end
72
+
73
+
74
+ # String.casecmp?
75
+ #
76
+ #
77
+ ###########################################
78
+ unless ''.respond_to? :casecmp?
79
+ class String
80
+
81
+ def casecmp?(other)
82
+ return nil unless other.is_a? String
83
+
84
+ casecmp(other).zero?
85
+ end
86
+
87
+ end
88
+ end
@@ -0,0 +1,190 @@
1
+ ### Copyright 2019 Pixar
2
+
3
+ ###
4
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
5
+ ### with the following modification; you may not use this file except in
6
+ ### compliance with the Apache License and the following modification to it:
7
+ ### Section 6. Trademarks. is deleted and replaced with:
8
+ ###
9
+ ### 6. Trademarks. This License does not grant permission to use the trade
10
+ ### names, trademarks, service marks, or product names of the Licensor
11
+ ### and its affiliates, except as required to comply with Section 4(c) of
12
+ ### the License and to reproduce the content of the NOTICE file.
13
+ ###
14
+ ### You may obtain a copy of the Apache License at
15
+ ###
16
+ ### http://www.apache.org/licenses/LICENSE-2.0
17
+ ###
18
+ ### Unless required by applicable law or agreed to in writing, software
19
+ ### distributed under the Apache License with the above modification is
20
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ ### KIND, either express or implied. See the Apache License for the specific
22
+ ### language governing permissions and limitations under the Apache License.
23
+ ###
24
+ ###
25
+
26
+ ###
27
+ module Jamf
28
+
29
+ ###
30
+ ### This module provides two methods for building very simple Casper-happy .pkg and .dmg packages for deployment.
31
+ ###
32
+ ### Unlike Composer.app from JAMF, this module currently doesn't offer a way to do a before/after disk scan
33
+ ### and use the differences to build the root folder from which the package is built. Nor does the module support
34
+ ### editing the pre/post install scripts in .pkgs.
35
+ ###
36
+ ### The 'root folder', a folder representing the root filesystem of the target machine where the package will be installed,
37
+ ### must already exist and be fully populated and with correct permissions.
38
+ ###
39
+ module Composer
40
+
41
+ #####################################
42
+ ### Constants
43
+ #####################################
44
+
45
+ ### the apple pkgutil tool
46
+ PKG_UTIL = Pathname.new '/usr/sbin/pkgutil'
47
+
48
+ ### The location of the cli tool for making .pkgs
49
+ PKGBUILD = Pathname.new '/usr/bin/pkgbuild'
50
+
51
+ ### the default bundle identifier prefix for pkgs
52
+ PKG_BUNDLE_ID_PFX = 'ruby-jss-composer'.freeze
53
+
54
+ ### Apple's hdiutil for making dmgs
55
+ HDI_UTIL = '/usr/bin/hdiutil'.freeze
56
+
57
+ ### Where to save the output ?
58
+ DEFAULT_OUT_DIR = Pathname.new '/Users/Shared'
59
+
60
+ ### Make a casper-happy .pkg out of a root folder, permissions are assumed to be correct.
61
+ ###
62
+ ### @param name[String] the name of the .pkg. The .pkg suffix will be added if not present
63
+ ###
64
+ ### @param version[String] the version of the .pkg, needed for building the .pkg
65
+ ###
66
+ ### @param root[String, Pathname] the path to the 'root folder' representing
67
+ ### the root file system of the target install drive
68
+ ###
69
+ ### @param opts[Hash] the options for building the .pkg
70
+ ###
71
+ ### @options opts :pkg_id[String] the full package if for the new pkg.
72
+ ### e.g. 'com.mycompany.myapp'
73
+ ###
74
+ ### @option opts :bundle_id_prefix[String] the pkg bundle identifier prefix.
75
+ ### If no :pkg_id is provided, one is made using this prefix and
76
+ ### the name provided. e.g. 'com.mycompany'
77
+ ### Defaults to '{PKG_BUNDLE_ID_PFX}'. See 'man pkgbuild' for more info
78
+ ###
79
+ ### @option opts :out_dir[String,Pathname] he folder in which the .pkg will be
80
+ ### created. Defaults to {DEFAULT_OUT_DIR}
81
+ ###
82
+ ### @option opts :preserve_ownership[Boolean] If true, the owner/group of the
83
+ ### rootpath are preserved.
84
+ ### Default is false: they become the pkgbuild/installer 'recommended'
85
+ ### (root/wheel or root/admin)
86
+ ###
87
+ ### @option opts :signing_identity[String] the optional name of the signing identity (certificate) to
88
+ ### use for signing the pkg. See `man pkgbuild` for details
89
+ ###
90
+ ### @option opts :signing_options[String] the optional string of options to pass to pkgbuild.
91
+ ### See `man pkgbuild` for details
92
+ ###
93
+ ### @return [Pathname] the local path to the new .pkg
94
+ ###
95
+ def self.mk_pkg(name, version, root, opts = {})
96
+ raise NoSuchItemError, "Missing pkgbuild tool. Please make sure you're running 10.8 or later." unless PKGBUILD.executable?
97
+
98
+ opts[:out_dir] ||= DEFAULT_OUT_DIR
99
+ opts[:bundle_id_prefix] ||= PKG_BUNDLE_ID_PFX
100
+
101
+ pkg_filename = name.end_with?('.pkg') ? name : name + '.pkg'
102
+ pkg_id = opts[:pkg_id]
103
+ pkg_id ||= opts[:bundle_id_prefix] + '.' + name
104
+ pkg_out = "#{opts[:out_dir]}/#{pkg_filename}"
105
+ pkg_ownership = opts[:preserve_ownership] ? 'preserve' : 'recommended'
106
+
107
+ if opts[:signing_identity]
108
+ signing = "--sign '#{opts[:signing_identity]}'"
109
+ signing << " #{opts[:signing_options]}" if opts[:signing_options]
110
+ else
111
+ signing = ''
112
+ end # if opts[:signing_identity]
113
+
114
+ ### first, run 'analyze' to get a 'component plist' in which we can change some settings
115
+ ### for any bundles in the root (bundles like .apps, frameworks, plugins, etc..)
116
+ ###
117
+ ### we edit the settings thus:
118
+ ### BundleOverwriteAction = upgrade, totally replace any version current on disk
119
+ ### BundleIsVersionChecked = false, allow us to install regardless of what version is currently installed
120
+ ### BundleIsRelocatable = false, if there's a version of this in some other location, Do Not move this one there after installation
121
+ ### BundleHasStrictIdentifier = false, don't care if there's something at the install path with a different bundle id.
122
+ ###
123
+ ### In other words, just install the thing!
124
+ ### (see 'man pkgbuild' for more info)
125
+ ###
126
+ ###
127
+ comp_plist_out = Pathname.new "/tmp/#{PKG_BUNDLE_ID_PFX}-#{pkg_filename}.plist"
128
+ system "#{PKGBUILD} --analyze --root '#{root}' '#{comp_plist_out}'"
129
+ comp_plist = Plist.parse_xml comp_plist_out.read
130
+
131
+ ### if the plist is empty, there are no bundles in the pkg
132
+ if comp_plist[0].nil?
133
+ comp_plist_arg = ''
134
+ else
135
+ ### otherwise, edit the bundle dictionaries
136
+ comp_plist.each do |bndl|
137
+ bndl.delete 'ChildBundles' if bndl['ChildBundles']
138
+ bndl['BundleOverwriteAction'] = 'upgrade'
139
+ bndl['BundleIsVersionChecked'] = false
140
+ bndl['BundleIsRelocatable'] = false
141
+ bndl['BundleHasStrictIdentifier'] = false
142
+ end
143
+ ### write out the edits
144
+ comp_plist_out.open('w') { |f| f.write comp_plist.to_plist }
145
+ comp_plist_arg = "--component-plist '#{comp_plist_out}'"
146
+ end
147
+
148
+ ### now build the pkg
149
+ begin
150
+ it_built = system "#{PKGBUILD} --identifier '#{pkg_id}' --version '#{version}' --ownership #{pkg_ownership} --install-location / --root '#{root}' #{signing} #{comp_plist_arg} '#{pkg_out}' "
151
+
152
+ raise 'There was an error building the .pkg' unless it_built
153
+ ensure
154
+ comp_plist_out.delete if comp_plist_out.exist?
155
+ end
156
+
157
+ Pathname.new pkg_out
158
+ end # mk_dot_pkg
159
+
160
+ ###
161
+ ### Make a casper-happy .dmg out of a root folder, permissions are assumed to be correct.
162
+ ###
163
+ ### @param name[String] The name of the .dmg, the suffix will be added if needed
164
+ ###
165
+ ### @param root[String, Pathname] the path to the "root folder" representing the root file system of the target install drive
166
+ ###
167
+ ### @param out_dir[String, Pathname] the folder in which the .pkg will be created. Defaults to {DEFAULT_OUT_DIR}
168
+ ###
169
+ ### @return [Pathname] the local path to the new .dmg
170
+ ###
171
+ ###
172
+ def self.mk_dmg(name, root, out_dir = DEFAULT_OUT_DIR)
173
+ dmg_filename = "#{name}.dmg"
174
+ dmg_vol = name
175
+ dmg_out = Pathname.new "#{out_dir}/#{dmg_filename}"
176
+ if dmg_out.exist?
177
+ mv_to = dmg_out.dirname + "#{dmg_out.basename}.#{Time.now.strftime('%Y%m%d%H%M%S')}"
178
+ dmg_out.rename mv_to
179
+ end # if dmg out exist
180
+
181
+ ### TODO - this may need to be sudo'd to handle proper internal permissions.
182
+ system "#{HDI_UTIL} create -volname '#{dmg_vol}' -scrub -srcfolder '#{root}' '#{dmg_out}'"
183
+
184
+ raise 'There was an error building the .dmg' unless $?.exitstatus.zero?
185
+ Pathname.new dmg_out
186
+ end # mk_dmg
187
+
188
+ end # module Composer
189
+
190
+ end # module Jamf
@@ -0,0 +1,281 @@
1
+ # Copyright 2019 Pixar
2
+
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "Apache License")
5
+ # with the following modification; you may not use this file except in
6
+ # compliance with the Apache License and the following modification to it:
7
+ # Section 6. Trademarks. is deleted and replaced with:
8
+ #
9
+ # 6. Trademarks. This License does not grant permission to use the trade
10
+ # names, trademarks, service marks, or product names of the Licensor
11
+ # and its affiliates, except as required to comply with Section 4(c) of
12
+ # the License and to reproduce the content of the NOTICE file.
13
+ #
14
+ # You may obtain a copy of the Apache License at
15
+ #
16
+ # http://www.apache.org/licenses/LICENSE-2.0
17
+ #
18
+ # Unless required by applicable law or agreed to in writing, software
19
+ # distributed under the Apache License with the above modification is
20
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ # KIND, either express or implied. See the Apache License for the specific
22
+ # language governing permissions and limitations under the Apache License.
23
+ #
24
+ #
25
+
26
+ module Jamf
27
+
28
+ # Module Variables
29
+ #####################################
30
+
31
+ # Module Methods
32
+ #####################################
33
+
34
+ # Classes
35
+ #####################################
36
+
37
+
38
+ # A class for working with pre-defined settings & preferences for the JAMF Gem
39
+ #
40
+ # This is a singleton class, only one instance can exist at a time.
41
+ #
42
+ # When the JAMF module loads, that instance is created and stored in the constant {Jamf::CONFIG},
43
+ # which can then be used in applications to avoid always having to pass in server names, API user names
44
+ # and so on.
45
+ #
46
+ # When the Jamf::Configuration instance is created, the {GLOBAL_CONF} file (/etc/jss_gem.conf) is examined if it exists, and
47
+ # the items in it are loaded into the attributes.
48
+ #
49
+ # Then the user-specific {USER_CONF} file (~/.ruby-jss.conf) is examined if it exists, and any attributes defined there will
50
+ # override those values from the {GLOBAL_CONF}.
51
+ #
52
+ # The file format is one attribute per line, thus:
53
+ # attr_name: value
54
+ #
55
+ # Lines that don't start with a known attribute name followed by a colon are ignored. If an attribute is defined
56
+ # more than once, the last one wins.
57
+ #
58
+ # The known attributes are:
59
+ # - api_server_name [String] the hostname of the JAMF API server
60
+ # - api_server_port [Integer] the port number for the API connection
61
+ # - api_ssl_version [String] the SSL version (from the open_ssl module) to use for the connection.
62
+ # - api_verify_cert [Boolean] if SSL is used, should the SSL certificate be verified (usually false for a self-signed cert)
63
+ # - api_username [String] the JAMF username for connecting to the API
64
+ # - api_timeout_open [Integer] the number of seconds for the open-connection timeout
65
+ # - api_timeout [Integer] the number of seconds for the response timeout
66
+ #
67
+ # The {APIConnection#connect} method will first use any values given as method arguments. For any not given, it will
68
+ # look at the Preferences instance for any values there, and if still none, will use default values or raise an exception.
69
+ #
70
+ # At any point, the attributes can read or changed using standard Ruby getter/setter methods matching the name of the attribute,
71
+ # e.g.
72
+ # Jamf::CONFIG.api_server_name # => 'myjss.mycompany.com'
73
+ # Jamf::CONFIG.api_server_name = 'otherjss.mycompany.com' # sets the api_server_name to a new value
74
+ #
75
+ #
76
+ # The current settings may be saved to the GLOBAL_CONF file, the USER_CONF file, or an arbitrary file using {#save}.
77
+ # The argument to {#save} should be either :user, :global, or a String or Pathname file path.
78
+ # NOTE: This overwrites any existing file.
79
+ #
80
+ # To re-load the settings use {#reload}. This clears the current settings, and re-reads both the global and user files.
81
+ # If a pathname is provided, e.g.
82
+ # Jamf::CONFIG.reload '/path/to/other/file'
83
+ # the current settings are cleared and reloaded from that other file.
84
+ #
85
+ # To view the current settings, use {#print}.
86
+ #
87
+ # @note Passwords are not saved in prefs files. Your application will have to acquire them using the :prompt or :stdin options to
88
+ # {APIConnection#connect}, or by custom means.
89
+ #
90
+ class Configuration
91
+
92
+ include Singleton
93
+
94
+ # Class Constants
95
+ #####################################
96
+
97
+ # The filename for storing the config, globally or user-level.
98
+ # The first matching file is used - the array provides
99
+ # backward compatibility with earlier versions.
100
+ # Saving will always happen to the first filename
101
+ CONF_FILENAME = 'ruby-jss.conf'.freeze
102
+
103
+ # The Pathname to the machine-wide preferences
104
+ GLOBAL_CONF_FILE = Pathname.new "/etc/#{CONF_FILENAME}"
105
+
106
+ # The Pathname to the user-specific preferences plist
107
+ USER_CONF_FILE = ENV['HOME'] ? Pathname.new("~/.#{CONF_FILENAME}").expand_path : nil
108
+
109
+ # The attribute keys we maintain, and the type they should be stored as
110
+ CONF_KEYS = {
111
+ api_server_name: :to_s,
112
+ api_server_port: :to_i,
113
+ api_ssl_version: :to_s,
114
+ api_verify_cert: :j_to_bool,
115
+ api_username: :to_s,
116
+ api_timeout_open: :to_i,
117
+ api_timeout: :to_i,
118
+ db_server_name: :to_s,
119
+ db_server_port: :to_i,
120
+ db_server_socket: :to_s,
121
+ db_username: :to_s,
122
+ db_name: :to_s,
123
+ db_connect_timeout: :to_i,
124
+ db_read_timeout: :to_i,
125
+ db_write_timeout: :to_i
126
+ }.freeze
127
+
128
+ # Attributes
129
+ #####################################
130
+
131
+ # automatically create accessors for all the CONF_KEYS
132
+ CONF_KEYS.keys.each { |k| attr_accessor k }
133
+
134
+ # Constructor
135
+ #####################################
136
+
137
+
138
+ # Initialize!
139
+ #
140
+ def initialize
141
+ read_global
142
+ read_user
143
+ end
144
+
145
+ # Public Instance Methods
146
+ #####################################
147
+
148
+
149
+ # Clear all values
150
+ #
151
+ # @return [void]
152
+ #
153
+ def clear_all
154
+ CONF_KEYS.keys.each { |k| send "#{k}=".to_sym, nil }
155
+ end
156
+
157
+ # (Re)read the global prefs, if it exists.
158
+ #
159
+ # @return [void]
160
+ #
161
+ def read_global
162
+ read GLOBAL_CONF_FILE if GLOBAL_CONF_FILE.file? && GLOBAL_CONF_FILE.readable?
163
+ end
164
+
165
+ # (Re)read the user prefs, if it exists.
166
+ #
167
+ # @return [void]
168
+ #
169
+ def read_user
170
+ read USER_CONF_FILE if USER_CONF_FILE.file? && USER_CONF_FILE.readable?
171
+ end
172
+
173
+ # Clear the settings and reload the prefs files, or another file if provided
174
+ #
175
+ # @param file[String,Pathname] a non-standard prefs file to load
176
+ #
177
+ # @return [void]
178
+ #
179
+ def reload(file = nil)
180
+ clear_all
181
+ if file
182
+ read file
183
+ return true
184
+ end
185
+ read_global
186
+ read_user
187
+ true
188
+ end
189
+
190
+ # Save the prefs into a file
191
+ #
192
+ # @param file[Symbol,String,Pathname] either :user, :global, or an arbitrary file to save.
193
+ #
194
+ # @return [void]
195
+ #
196
+ def save(file)
197
+ path =
198
+ case file
199
+ when :global then GLOBAL_CONF_FILE
200
+ when :user then USER_CONF_FILE
201
+ else Pathname.new(file)
202
+ end
203
+
204
+ raise Jamf::MissingDataError, "No HOME environment variable, can't write to user conf file." if path.nil?
205
+
206
+ # file already exists? read it in and update the values.
207
+ if path.readable?
208
+ data = path.read
209
+
210
+ # go thru the known attributes/keys
211
+ CONF_KEYS.keys.sort.each do |k|
212
+ # if the key exists, update it.
213
+ if data =~ /^#{k}:/
214
+ data.sub!(/^#{k}:.*$/, "#{k}: #{send k}")
215
+
216
+ # if not, add it to the end unless it's nil
217
+ else
218
+ data += "\n#{k}: #{send k}" unless send(k).nil?
219
+ end # if data =~ /^#{k}:/
220
+ end # each do |k|
221
+
222
+ else # not readable, make a new file
223
+ data = ''
224
+ CONF_KEYS.keys.sort.each do |k|
225
+ data << "#{k}: #{send k}\n" unless send(k).nil?
226
+ end
227
+ end # if path readable
228
+
229
+ # make sure we end with a newline, the save it.
230
+ data << "\n" unless data.end_with?("\n")
231
+ path.j_save data
232
+ end # read file
233
+
234
+ # Print out the current settings to stdout
235
+ #
236
+ # @return [void]
237
+ #
238
+ def print
239
+ CONF_KEYS.keys.sort.each { |k| puts "#{k}: #{send k}" }
240
+ end
241
+
242
+ # Private Instance Methods
243
+ #####################################
244
+ private
245
+
246
+ # Read in any prefs file
247
+ #
248
+ # @param file[String,Pathname] the file to read
249
+ #
250
+ # @return [void]
251
+ #
252
+ def read(file)
253
+ Pathname.new(file).read.each_line do |line|
254
+ # skip blank lines and those starting with #
255
+ next if line =~ /^\s*(#|$)/
256
+
257
+ line.strip =~ /^(\w+?):\s*(\S.*)$/
258
+ next unless Regexp.last_match(1)
259
+
260
+ attr = Regexp.last_match(1).to_sym
261
+ setter = "#{attr}=".to_sym
262
+ value = Regexp.last_match(2).strip
263
+
264
+ next unless CONF_KEYS.key?(attr)
265
+
266
+ if value
267
+ # convert the value to the correct class
268
+ value = value.send(CONF_KEYS[attr])
269
+ end
270
+ send(setter, value)
271
+ # if
272
+ end # do line
273
+ end # read file
274
+
275
+ end # class Config
276
+
277
+ def self.config
278
+ Jamf::Configuration.instance
279
+ end
280
+
281
+ end # module