ruby-jss 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ruby-jss might be problematic. Click here for more details.

Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +7 -0
  3. data/CHANGES.md +112 -0
  4. data/LICENSE.txt +174 -0
  5. data/README.md +426 -0
  6. data/THANKS.md +6 -0
  7. data/bin/cgrouper +485 -0
  8. data/bin/subnet-update +400 -0
  9. data/lib/jss-api.rb +2 -0
  10. data/lib/jss.rb +190 -0
  11. data/lib/jss/api_connection.rb +410 -0
  12. data/lib/jss/api_object.rb +616 -0
  13. data/lib/jss/api_object/advanced_search.rb +389 -0
  14. data/lib/jss/api_object/advanced_search/advanced_computer_search.rb +95 -0
  15. data/lib/jss/api_object/advanced_search/advanced_mobile_device_search.rb +96 -0
  16. data/lib/jss/api_object/advanced_search/advanced_user_search.rb +95 -0
  17. data/lib/jss/api_object/building.rb +92 -0
  18. data/lib/jss/api_object/category.rb +147 -0
  19. data/lib/jss/api_object/computer.rb +852 -0
  20. data/lib/jss/api_object/creatable.rb +98 -0
  21. data/lib/jss/api_object/criteriable.rb +189 -0
  22. data/lib/jss/api_object/criteriable/criteria.rb +231 -0
  23. data/lib/jss/api_object/criteriable/criterion.rb +228 -0
  24. data/lib/jss/api_object/department.rb +93 -0
  25. data/lib/jss/api_object/distribution_point.rb +560 -0
  26. data/lib/jss/api_object/extendable.rb +221 -0
  27. data/lib/jss/api_object/extension_attribute.rb +466 -0
  28. data/lib/jss/api_object/extension_attribute/computer_extension_attribute.rb +362 -0
  29. data/lib/jss/api_object/extension_attribute/mobile_device_extension_attribute.rb +189 -0
  30. data/lib/jss/api_object/extension_attribute/user_extension_attribute.rb +117 -0
  31. data/lib/jss/api_object/group.rb +380 -0
  32. data/lib/jss/api_object/group/computer_group.rb +124 -0
  33. data/lib/jss/api_object/group/mobile_device_group.rb +139 -0
  34. data/lib/jss/api_object/group/user_group.rb +139 -0
  35. data/lib/jss/api_object/ldap_server.rb +535 -0
  36. data/lib/jss/api_object/locatable.rb +286 -0
  37. data/lib/jss/api_object/matchable.rb +97 -0
  38. data/lib/jss/api_object/mobile_device.rb +556 -0
  39. data/lib/jss/api_object/netboot_server.rb +148 -0
  40. data/lib/jss/api_object/network_segment.rb +414 -0
  41. data/lib/jss/api_object/osx_configuration_profile.rb +262 -0
  42. data/lib/jss/api_object/package.rb +839 -0
  43. data/lib/jss/api_object/peripheral.rb +335 -0
  44. data/lib/jss/api_object/peripheral_type.rb +295 -0
  45. data/lib/jss/api_object/policy.rb +898 -0
  46. data/lib/jss/api_object/purchasable.rb +316 -0
  47. data/lib/jss/api_object/removable_macaddr.rb +98 -0
  48. data/lib/jss/api_object/scopable.rb +136 -0
  49. data/lib/jss/api_object/scopable/scope.rb +621 -0
  50. data/lib/jss/api_object/script.rb +631 -0
  51. data/lib/jss/api_object/self_servable.rb +356 -0
  52. data/lib/jss/api_object/site.rb +93 -0
  53. data/lib/jss/api_object/software_update_server.rb +109 -0
  54. data/lib/jss/api_object/updatable.rb +117 -0
  55. data/lib/jss/api_object/uploadable.rb +138 -0
  56. data/lib/jss/api_object/user.rb +272 -0
  57. data/lib/jss/client.rb +504 -0
  58. data/lib/jss/compatibility.rb +66 -0
  59. data/lib/jss/composer.rb +185 -0
  60. data/lib/jss/configuration.rb +306 -0
  61. data/lib/jss/db_connection.rb +298 -0
  62. data/lib/jss/exceptions.rb +95 -0
  63. data/lib/jss/ruby_extensions.rb +35 -0
  64. data/lib/jss/ruby_extensions/filetest.rb +43 -0
  65. data/lib/jss/ruby_extensions/hash.rb +79 -0
  66. data/lib/jss/ruby_extensions/ipaddr.rb +91 -0
  67. data/lib/jss/ruby_extensions/pathname.rb +77 -0
  68. data/lib/jss/ruby_extensions/string.rb +59 -0
  69. data/lib/jss/ruby_extensions/time.rb +63 -0
  70. data/lib/jss/server.rb +108 -0
  71. data/lib/jss/utility.rb +478 -0
  72. data/lib/jss/version.rb +31 -0
  73. metadata +187 -0
@@ -0,0 +1,228 @@
1
+ ### Copyright 2016 Pixar
2
+ ###
3
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
4
+ ### with the following modification; you may not use this file except in
5
+ ### compliance with the Apache License and the following modification to it:
6
+ ### Section 6. Trademarks. is deleted and replaced with:
7
+ ###
8
+ ### 6. Trademarks. This License does not grant permission to use the trade
9
+ ### names, trademarks, service marks, or product names of the Licensor
10
+ ### and its affiliates, except as required to comply with Section 4(c) of
11
+ ### the License and to reproduce the content of the NOTICE file.
12
+ ###
13
+ ### You may obtain a copy of the Apache License at
14
+ ###
15
+ ### http://www.apache.org/licenses/LICENSE-2.0
16
+ ###
17
+ ### Unless required by applicable law or agreed to in writing, software
18
+ ### distributed under the Apache License with the above modification is
19
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20
+ ### KIND, either express or implied. See the Apache License for the specific
21
+ ### language governing permissions and limitations under the Apache License.
22
+ ###
23
+ ###
24
+
25
+ ###
26
+ module JSS
27
+
28
+ module Criteriable
29
+
30
+ #####################################
31
+ ### Module Variables
32
+ #####################################
33
+
34
+ #####################################
35
+ ### Module Methods
36
+ #####################################
37
+
38
+ #####################################
39
+ ### Classes
40
+ #####################################
41
+
42
+ ### This class defines a single criterion used in advanced searches and
43
+ ### smart groups throughout the JSS module.
44
+ ###
45
+ ### They are used within {JSS::Criteriable::Criteria} instances which store an
46
+ ### array of these objects and provides methods for working with them as a group.
47
+ ###
48
+ ### The classes that mix-in {JSS::Criteriable} each have a :criteria attribute which
49
+ ### holds one {JSS::Criteriable::Criteria}
50
+ ###
51
+ ### See {JSS::Criteriable} for examples
52
+ ###
53
+ class Criterion
54
+
55
+ #####################################
56
+ ### Mix Ins
57
+ #####################################
58
+
59
+ include Comparable # this allows us compare instances using <=>
60
+
61
+ #####################################
62
+ ### Class Constants
63
+ #####################################
64
+
65
+ ### These are the available search-types for building criteria
66
+ SEARCH_TYPES = [
67
+ "is",
68
+ "is not",
69
+ "like",
70
+ "not like",
71
+ "has",
72
+ "does not have",
73
+ "more than",
74
+ "less than",
75
+ "before (yyyy-mm-dd)",
76
+ "after (yyyy-mm-dd)",
77
+ "more than x days ago",
78
+ "less than x days ago",
79
+ "in more than x days",
80
+ "in less than x days",
81
+ "member of",
82
+ "not member of",
83
+ "current",
84
+ "not current"
85
+ ]
86
+
87
+ ### the acceptable symboles for and/or
88
+ AND_OR = [:and, :or]
89
+
90
+ #####################################
91
+ ### Attributes
92
+ #####################################
93
+
94
+ ### @return [Integer] zero-based index of this criterion within an array of criteria
95
+ ### used for an advanced search or smart group.
96
+ ### This is maintained automaticaly by the enclosing Criteria object
97
+ attr_accessor :priority
98
+
99
+ ### @return [Symbol] :and or :or - the and_or value for associating this criterion with the previous one
100
+ attr_reader :and_or
101
+
102
+ ### @return [String] the name of the field being searched
103
+ attr_accessor :name
104
+
105
+ ### @return [String] the comparator between the field and the value, must be one of SEARCH_TYPES
106
+ ### @see #criteria=
107
+ attr_reader :search_type
108
+
109
+ ### @return [String] the value being searched for in the field named by :name
110
+ attr_reader :value
111
+
112
+ ###
113
+ ### @param args[Hash] a hash of settings for the new criterion
114
+ ### @option args :and_or [String, Symbol] :and, or :or. How should this criterion be join with its predecessor?
115
+ ### @option args :name [String] the name of a Criterion as is visible in the JSS webapp.
116
+ ### @option args :search_type [String] one of SEARCH_TYPES, the comparison between the stored value and :value
117
+ ### @option args :value [String] the value to compare with that stored for :name
118
+ ###
119
+ ### @note :priority is maintained by the JSS::Criteriable::Criteria object holding this instance
120
+ ###
121
+ def initialize(args = {})
122
+
123
+ @priority = args[:priority]
124
+
125
+ if args[:and_or]
126
+ @and_or = args[:and_or].to_sym
127
+ raise JSS::InvalidDataError, ":and_or must be 'and' or 'or'." unless AND_OR.include? @and_or
128
+ end
129
+
130
+ @name = args[:name]
131
+
132
+ if args[:search_type]
133
+ raise JSS::InvalidDataError, "Invalid :search_type" unless SEARCH_TYPES.include? args[:search_type]
134
+ @search_type = args[:search_type]
135
+ end
136
+
137
+ @value = args[:value]
138
+ end # init
139
+
140
+ ###
141
+ ### Set a new and_or for the criteron
142
+ ###
143
+ ### @param new_val[Symbol] the new and_or
144
+ ###
145
+ ### @return [void]
146
+ ###
147
+ def and_or= (new_val)
148
+ @and_or = new_val.to_sym
149
+ raise JSS::InvalidDataError, ":and_or must be 'and' or 'or'." unless AND_OR.include? @and_or.to_sym
150
+ end
151
+
152
+ ###
153
+ ### Set a new search type for the criteron
154
+ ###
155
+ ### @param new_val[String] the new search type
156
+ ###
157
+ ### @return [void]
158
+ ###
159
+ def search_type= (new_val)
160
+ raise JSS::InvalidDataError, "Invalid :search_type" unless SEARCH_TYPES.include? new_val
161
+ @search_type = new_val
162
+ end
163
+
164
+ ###
165
+ ### Set a new value for the criteron
166
+ ###
167
+ ### @param new_val[Integer,String] the new value
168
+ ###
169
+ ### @return [void]
170
+ ###
171
+ def value=(new_val)
172
+ case @search_type
173
+
174
+ when *["more than", "less than", "more than x days ago", "less than x days ago"]
175
+ raise JSS::InvalidDataError, "Value must be an integer for search type '#{new_val}'" unless new_val =~ /^\d+$/
176
+
177
+ when *["before (yyyy-mm-dd)", "after (yyyy-mm-dd)"]
178
+ raise JSS::InvalidDataError, "Value must be a a date in the format yyyy-mm-dd for search type '#{new_val}'" unless new_val =~ /^\d\d\d\d-\d\d-\d\d$/
179
+
180
+ end # case
181
+
182
+ @value = new_val
183
+
184
+ end
185
+
186
+ ###
187
+ ### @return [String] All our values except priority joined together
188
+ ### for comparing this Criterion to another for equality and order
189
+ ###
190
+ ### @see #<=>
191
+ ###
192
+ def signature
193
+ [@and_or, @name, @search_type, @value].join ","
194
+ end
195
+
196
+
197
+ ###
198
+ ### Comparison - allows the Comparable module to do its work
199
+ ###
200
+ ### @return [Integer] -1, 0, or 1
201
+ ###
202
+ ### @see Comparable
203
+ ###
204
+ def <=>(other)
205
+ self.signature <=> other.signature
206
+ end
207
+
208
+ ###
209
+ ### @api private
210
+ ###
211
+ ### @return [REXML::Element] The xml element for the criterion, to be embeded in that of
212
+ ### a Criteria instance
213
+ ###
214
+ ### @note For this class, rest_xml can't be a private method.
215
+ ###
216
+ def rest_xml
217
+ crn = REXML::Element.new 'criterion'
218
+ crn.add_element('priority').text = @priority
219
+ crn.add_element('and_or').text = @and_or
220
+ crn.add_element('name').text = @name
221
+ crn.add_element('search_type').text = @search_type
222
+ crn.add_element('value').text = @value
223
+ return crn
224
+ end
225
+
226
+ end # class criterion
227
+ end # module Criteriable
228
+ end # module
@@ -0,0 +1,93 @@
1
+ ### Copyright 2016 Pixar
2
+ ###
3
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
4
+ ### with the following modification; you may not use this file except in
5
+ ### compliance with the Apache License and the following modification to it:
6
+ ### Section 6. Trademarks. is deleted and replaced with:
7
+ ###
8
+ ### 6. Trademarks. This License does not grant permission to use the trade
9
+ ### names, trademarks, service marks, or product names of the Licensor
10
+ ### and its affiliates, except as required to comply with Section 4(c) of
11
+ ### the License and to reproduce the content of the NOTICE file.
12
+ ###
13
+ ### You may obtain a copy of the Apache License at
14
+ ###
15
+ ### http://www.apache.org/licenses/LICENSE-2.0
16
+ ###
17
+ ### Unless required by applicable law or agreed to in writing, software
18
+ ### distributed under the Apache License with the above modification is
19
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20
+ ### KIND, either express or implied. See the Apache License for the specific
21
+ ### language governing permissions and limitations under the Apache License.
22
+ ###
23
+ ###
24
+
25
+ ###
26
+ module JSS
27
+
28
+ #####################################
29
+ ### Module Variables
30
+ #####################################
31
+
32
+ #####################################
33
+ ### Module Methods
34
+ #####################################
35
+
36
+ #####################################
37
+ ### Classes
38
+ #####################################
39
+
40
+ ###
41
+ ### A department in the JSS.
42
+ ### These are simple, in that they only have an ID and a name
43
+ ###
44
+ ### @see JSS::APIObject
45
+ ###
46
+ class Department < JSS::APIObject
47
+
48
+ #####################################
49
+ ### Mix-Ins
50
+ #####################################
51
+ include JSS::Creatable
52
+ include JSS::Updatable
53
+
54
+ #####################################
55
+ ### Class Methods
56
+ #####################################
57
+
58
+ #####################################
59
+ ### Class Constants
60
+ #####################################
61
+
62
+ ### The base for REST resources of this class
63
+ RSRC_BASE = "departments"
64
+
65
+ ### the hash key used for the JSON list output of all objects in the JSS
66
+ RSRC_LIST_KEY = :departments
67
+
68
+ ### The hash key used for the JSON object output.
69
+ ### It's also used in various error messages
70
+ RSRC_OBJECT_KEY = :department
71
+
72
+ ### these keys, as well as :id and :name, are present in valid API JSON data for this class
73
+ VALID_DATA_KEYS = []
74
+
75
+ #####################################
76
+ ### Attributes
77
+ #####################################
78
+
79
+ #####################################
80
+ ### Constructor
81
+ #####################################
82
+
83
+ ###
84
+ ### See JSS::APIObject#initialize
85
+ ###
86
+
87
+ #####################################
88
+ ### Public Instance Methods
89
+ #####################################
90
+
91
+ end # class department
92
+
93
+ end # module
@@ -0,0 +1,560 @@
1
+ ### Copyright 2016 Pixar
2
+ ###
3
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
4
+ ### with the following modification; you may not use this file except in
5
+ ### compliance with the Apache License and the following modification to it:
6
+ ### Section 6. Trademarks. is deleted and replaced with:
7
+ ###
8
+ ### 6. Trademarks. This License does not grant permission to use the trade
9
+ ### names, trademarks, service marks, or product names of the Licensor
10
+ ### and its affiliates, except as required to comply with Section 4(c) of
11
+ ### the License and to reproduce the content of the NOTICE file.
12
+ ###
13
+ ### You may obtain a copy of the Apache License at
14
+ ###
15
+ ### http://www.apache.org/licenses/LICENSE-2.0
16
+ ###
17
+ ### Unless required by applicable law or agreed to in writing, software
18
+ ### distributed under the Apache License with the above modification is
19
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20
+ ### KIND, either express or implied. See the Apache License for the specific
21
+ ### language governing permissions and limitations under the Apache License.
22
+ ###
23
+ ###
24
+
25
+ ###
26
+ module JSS
27
+
28
+ #####################################
29
+ ### Module Variables
30
+ #####################################
31
+
32
+ ### the master dist. point, see JSS.master_distribution_point
33
+ @@master_distribution_point = nil
34
+
35
+ ### the dist point for this machine right now
36
+ @@my_distribution_point =nil
37
+
38
+ #####################################
39
+ ### Module Methods
40
+ #####################################
41
+
42
+
43
+
44
+ ###
45
+ ### A Distribution Point in the JSS
46
+ ###
47
+ ### As well as the normal Class and Instance methods for {APIObject} subclasses, the
48
+ ### DistributionPoint class provides more interaction with other parts of the API.
49
+ ###
50
+ ### Beyond the standard listing methods DistributionPoint.all, .all_ids, etc, every JSS
51
+ ### has a single "master" distribution point. The Class method {DistributionPoint.master_distribution_point} will
52
+ ### return the JSS::DistributionPoint object for that master.
53
+ ###
54
+ ### Also, some network segments have specific DistributionPoints assigned to them. Calling the Class method
55
+ ### {DistributionPoint.my_distribution_point} will return a JSS::DistributionPoint object for your local IP address.
56
+ ###
57
+ ### Once you have an instance of JSS::DistributionPoint, you can mount it (on a Mac) by calling its {#mount} method
58
+ ### and unmount it with {#unmount}. The {JSS::Package} and possibly {JSS::Script} classes use this to upload
59
+ ### items to the master.
60
+ ###
61
+ ### @see JSS::APIObject
62
+ ###
63
+ class DistributionPoint < JSS::APIObject
64
+
65
+ #####################################
66
+ ### Mix-Ins
67
+ #####################################
68
+
69
+ #####################################
70
+ ### Class Constants
71
+ #####################################
72
+
73
+ ### The base for REST resources of this class
74
+ RSRC_BASE = "distributionpoints"
75
+
76
+ ### the hash key used for the JSON list output of all objects in the JSS
77
+ ### its also used in various error messages
78
+ RSRC_LIST_KEY = :distribution_points
79
+
80
+ ### The hash key used for the JSON object output.
81
+ ### It's also used in various error messages
82
+ RSRC_OBJECT_KEY = :distribution_point
83
+
84
+ ### these keys, as well as :id and :name, are present in valid API JSON data for this class
85
+ VALID_DATA_KEYS = [:read_only_username, :ssh_username, :is_master ]
86
+
87
+ ### what are the mount options? these are comma-separated, and are passed with -o
88
+ MOUNT_OPTIONS = 'nobrowse'
89
+
90
+ ### An empty SHA256 digest
91
+ EMPTY_PW_256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
92
+
93
+
94
+
95
+ #####################################
96
+ ### Class Variables
97
+ #####################################
98
+
99
+ @@master_distribution_point = nil
100
+
101
+ @@my_distribution_point = nil
102
+
103
+ #####################################
104
+ ### Class Methods
105
+ #####################################
106
+
107
+ ### Get the DistributionPoint instance for the master
108
+ ### distribution point in the JSS. If there's only one
109
+ ### in the JSS, return it even if not marked as master.
110
+ ###
111
+ ### @return [JSS::DistributionPoint]
112
+ ###
113
+ def self.master_distribution_point(refresh = false)
114
+ @@master_distribution_point = nil if refresh
115
+ return @@master_distribution_point if @@master_distribution_point
116
+
117
+ case self.all.count
118
+ when 0
119
+ raise JSS::NoSuchItemError, "No distribution points defined"
120
+ when 1
121
+ self.new :id => self.all_ids[0]
122
+ else
123
+ self.new :id => :master
124
+ end
125
+ end
126
+
127
+ ### Get the DistributionPoint instance for the machine running
128
+ ### this code, based on its IP address. If none is defined for this IP address,
129
+ ### use the result of master_distribution_point
130
+ ###
131
+ ### @param refresh[Boolean] should the distribution point be re-queried?
132
+ ###
133
+ ### @return [JSS::DistributionPoint]
134
+ ###
135
+ def self.my_distribution_point(refresh = false)
136
+ @@my_distribution_point = nil if refresh
137
+ return @@my_distribution_point if @@my_distribution_point
138
+
139
+ my_net_seg = JSS::NetworkSegment.my_network_segment[0]
140
+ specific = if my_net_seg
141
+ JSS::NetworkSegment.new(:id => my_net_seg).distribution_point
142
+ else
143
+ nil
144
+ end
145
+ return specific ? self.new(:name => specific) : self.master_distribution_point
146
+ end
147
+
148
+ #####################################
149
+ ### Class Attributes
150
+ #####################################
151
+
152
+ ### @return [String] the hostname of this DP
153
+ attr_reader :ip_address
154
+
155
+ ### @return [String] the local path on the server to the distribution point directory
156
+ attr_reader :local_path
157
+
158
+ ### @return [String] load balanacing enabled?
159
+ attr_reader :enable_load_balancing
160
+
161
+ ### @return [Integer] the id of the DP to use for failover
162
+ attr_reader :failover_point
163
+
164
+ ### @return [Boolean] is this the master DP?
165
+ attr_reader :is_master
166
+
167
+ ### FileService Access
168
+
169
+ ### @return [String] Protocol for fileservice access (e.g. AFP, SMB)
170
+ attr_reader :connection_type
171
+
172
+ ### @return [Integer] the port for fileservice access
173
+ attr_reader :share_port
174
+
175
+ ### @return [String] the name of the fileservice sharepoint
176
+ attr_reader :share_name
177
+
178
+ ### @return [String] the read-write username for fileservice access
179
+ attr_reader :read_write_username
180
+
181
+ ### @return [String] the read-write password as a SHA256 digest
182
+ attr_reader :read_write_password_sha256
183
+
184
+ ### @return [String] read-only username for fileservice
185
+ attr_reader :read_only_username
186
+
187
+ ### @return [String] read-only password as a SHA256 digest
188
+ attr_reader :read_only_password_sha256
189
+
190
+ ### @return [String] work group or domain for SMB
191
+ attr_reader :workgroup_or_domain
192
+
193
+ ### http(s) access
194
+
195
+ ### @return [Boolean] are http downloads available from this DP?
196
+ attr_reader :http_downloads_enabled
197
+
198
+ ### @return [String] the protocol to use for http downloads (http/https)
199
+ attr_reader :protocol
200
+
201
+ ### @return [Integer] the port for http access
202
+ attr_reader :port
203
+
204
+ ### @return [String] the "context" for http downloads (what goes after the hostname part of the URL)
205
+ attr_reader :context
206
+
207
+ ### @return [Boolean] do http downloads work without auth?
208
+ attr_reader :no_authentication_required
209
+
210
+ ### @return [Boolean] do http downloads use cert. authentication?
211
+ attr_reader :certificate_required
212
+
213
+ ### @return [Boolean] do http downloads use user/pw auth?
214
+ attr_reader :username_password_required
215
+
216
+ ### @return [String] the username to use for http downloads if needed for user/pw auth
217
+ attr_reader :http_username
218
+
219
+ ### @return [String] the password for http downloads, if needed, as a SHA256 digest
220
+ attr_reader :http_password_sha256
221
+
222
+ ### @return [String] the name of the cert. used for http cert. auth.
223
+ attr_reader :certificate
224
+
225
+ ### @return [String] the URL for http downloads
226
+ attr_reader :http_url
227
+
228
+ ### @return [String] the URL to use if this one doesn't work
229
+ attr_reader :failover_point_url
230
+
231
+ ### ssh (scp, rsync, sftp) access
232
+
233
+ ### @return [String] ssh username
234
+ attr_reader :ssh_username
235
+
236
+ ### @return [String] the ssh password as a SHA256 digest
237
+ attr_reader :ssh_password_sha256
238
+
239
+ ###
240
+ ### As well as the standard :id, :name, and :data, you can
241
+ ### instantiate this class with :id => :master, in which case you'll
242
+ ### get the Master Distribution Point as defined in the JSS.
243
+ ### An error will be raised if one hasn't been defined.
244
+ ###
245
+ ### You can also do this more easily by calling JSS.master_distribution_point
246
+ ###
247
+ def initialize(args = {})
248
+
249
+ @init_data = nil
250
+
251
+ ### looking for master?
252
+ if args[:id] == :master
253
+
254
+ self.class.all_ids.each do |id|
255
+ @init_data = JSS::API.get_rsrc("#{RSRC_BASE}/id/#{id}")[RSRC_OBJECT_KEY]
256
+ if @init_data[:is_master]
257
+ @id = @init_data[:id]
258
+ @name = @init_data[:name]
259
+ break
260
+ end # if data is master
261
+ @init_data = nil
262
+ end # each id
263
+ end # if args is master
264
+
265
+ if @init_data.nil?
266
+ super(args)
267
+ end
268
+
269
+ @ip_address = @init_data[:ip_address]
270
+ @local_path = @init_data[:local_path]
271
+ @enable_load_balancing = @init_data[:enable_load_balancing]
272
+ @failover_point = @init_data[:failover_point]
273
+ @is_master = @init_data[:is_master]
274
+
275
+ @connection_type = @init_data[:connection_type]
276
+ @share_port = @init_data[:share_port]
277
+ @share_name = @init_data[:share_name]
278
+ @workgroup_or_domain = @init_data[:workgroup_or_domain]
279
+
280
+ @read_write_username = @init_data[:read_write_username]
281
+ @read_write_password_sha256 = @init_data[:read_write_password_sha256]
282
+ @read_only_username = @init_data[:read_only_username]
283
+ @read_only_password_sha256 = @init_data[:read_only_password_sha256]
284
+ @ssh_username = @init_data[:ssh_username]
285
+ @ssh_password_sha256 = @init_data[:ssh_password_sha256]
286
+ @http_username = @init_data[:http_username]
287
+ @http_password_sha256 = @init_data[:http_password_sha256]
288
+
289
+
290
+ @http_downloads_enabled = @init_data[:http_downloads_enabled]
291
+ @protocol = @init_data[:protocol]
292
+ @port = @init_data[:port]
293
+ @context = @init_data[:context]
294
+ @no_authentication_required = @init_data[:no_authentication_required]
295
+ @certificate_required = @init_data[:certificate_required]
296
+ @username_password_required = @init_data[:username_password_required]
297
+ @certificate = @init_data[:certificate]
298
+ @http_url = @init_data[:http_url]
299
+ @failover_point_url = @init_data[:failover_point_url]
300
+
301
+
302
+ @port = @init_data[:ssh_password]
303
+
304
+ ### Note, as of Casper 9.3:
305
+ ### :management_password_md5=>"xxxxx"
306
+ ### and
307
+ ### :management_password_sha256=> "xxxxxxxxxx"
308
+ ### Are the read/write password
309
+ ###
310
+ ### An empty passwd is
311
+ ### MD5 = d41d8cd98f00b204e9800998ecf8427e
312
+ ### SHA256 = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
313
+ ###
314
+ ### Seemms the read-only pw isn't available in the API
315
+
316
+
317
+ ### if we mount for fileservice, where's the mountpoint?
318
+ @mountpoint = Pathname.new "/Volumes/CasperDistribution-id-#{@id}"
319
+
320
+ end #init
321
+
322
+ ###
323
+ ### Check the validity of a password.
324
+ ###
325
+ ### @param user[Symbol] one of :ro, :rw, :ssh, :http
326
+ ###
327
+ ### @param pw[String] the password to check for the given user
328
+ ###
329
+ ### @return [Boolean,Nil] was the password correct?
330
+ ### nil is returned if there is no password set in the JSS.
331
+ ###
332
+ def check_pw(user, pw)
333
+ raise JSS::InvalidDataError, "The first parameter must be one of :ro, :rw, :ssh, :http" unless [:ro, :rw, :ssh, :http].include? user
334
+ sha256 = case user
335
+ when :rw then @read_write_password_sha256
336
+ when :ro then @read_only_password_sha256
337
+ when :http then @http_password_sha256
338
+ when :ssh then @ssh_password_sha256
339
+ end # case
340
+
341
+ return nil if sha256 == EMPTY_PW_256
342
+
343
+ sha256 == Digest::SHA2.new(256).update(pw).to_s
344
+ end
345
+
346
+ ### Check to see if this dist point is reachable for downloads (read-only)
347
+ ### via either http, if available, or filesharing.
348
+ ###
349
+ ### @param pw[String] the read-only password to use for checking the connection
350
+ ### If http downloads are enabled, and no http password is required
351
+ ### this can be omitted.
352
+ ###
353
+ ### @param check_http[Boolean] should we try the http download first, if enabled?
354
+ ### If you're intentionally using the ro password for filesharing, and want to check
355
+ ### only filesharing, then set this to false.
356
+ ###
357
+ ### @return [FalseClass, Symbol] false if not reachable, otherwise :http or :mountable
358
+ ###
359
+ def reachable_for_download? (pw = '', check_http = true)
360
+ pw ||= ''
361
+ http_checked = ""
362
+ if check_http && http_downloads_enabled
363
+ if @username_password_required
364
+ # we don't check the pw here, because if the connection fails, we'll
365
+ # drop down below to try the password for mounting.
366
+ # we'll escape all the chars that aren't unreserved
367
+ reserved_chars = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}]")
368
+ user_pass = "#{URI.escape @http_username,reserved_chars}:#{URI.escape ro_pw, reserved_chars}@"
369
+ url = @http_url.sub "://#{@ip_address}", "://#{user_pass}#{@ip_address}"
370
+ else
371
+ url = @http_url
372
+ end
373
+
374
+ begin
375
+ open(url).read
376
+ return :http
377
+ rescue
378
+ http_checked = "http and "
379
+ end
380
+ end # if check_http && http_downloads_enabled
381
+
382
+ return :mountable if mounted?
383
+
384
+ return false unless check_pw :ro , pw
385
+
386
+ begin
387
+ mount pw, :ro
388
+ return :mountable
389
+ rescue
390
+ return false
391
+ ensure
392
+ unmount
393
+ end
394
+ end
395
+
396
+ ### Check to see if this dist point is reachable for uploads (read-write)
397
+ ### via filesharing.
398
+ ###
399
+ ### @param pw[String] the read-write password to use for checking the connection
400
+ ###
401
+ ### @return [FalseClass, Symbol] false if not reachable, otherwise :mountable
402
+ ###
403
+ def reachable_for_upload? (pw)
404
+ return :mountable if mounted?
405
+ return false unless check_pw :rw , pw
406
+ begin
407
+ mount pw, :rw
408
+ return :mountable
409
+ rescue
410
+ return false
411
+ ensure
412
+ unmount
413
+ end
414
+ end
415
+
416
+
417
+ ###
418
+ ### Mount this distribution point locally.
419
+ ###
420
+ ### @param pw[String,Symbol] the read-only or read-write password for this DistributionPoint
421
+ ### If :prompt, the user is promted on the commandline to enter the password for the :user.
422
+ ### If :stdin#, the password is read from a line of std in represented by the digits at #,
423
+ ### so :stdin3 reads the passwd from the third line of standard input. defaults to line 2,
424
+ ### if no digit is supplied. see {JSS.stdin}
425
+ ###
426
+ ### @param access[Symbol] how to mount the DistributionPoint, and which password to expect.
427
+ ### :ro (or anything else) = read-only, :rw = read-write
428
+ ###
429
+ ### @return [Pathname] the mountpoint.
430
+ ###
431
+ def mount(pw = nil, access = :ro)
432
+ return @mountpoint if mounted?
433
+ access = :ro unless access == :rw
434
+
435
+ password = if pw == :prompt
436
+ JSS.prompt_for_password "Enter the password for the #{access} user '#{access == :ro ? @read_only_username : @read_write_username }':"
437
+ elsif pw.is_a?(Symbol) and pw.to_s.start_with?('stdin')
438
+ pw.to_s =~ /^stdin(\d+)$/
439
+ line = $1
440
+ line ||= 2
441
+ JSS.stdin line
442
+ else
443
+ pw
444
+ end
445
+
446
+ pwok = check_pw(access, password)
447
+ unless pwok
448
+ msg = pwok.nil? ? "No #{access} password set in the JSS" : "Incorrect password for #{access} account"
449
+ raise JSS::InvalidDataError, msg
450
+ end
451
+
452
+ username = access == :ro ? @read_only_username : @read_write_username
453
+
454
+ safe_pw = URI.escape password, /[^a-zA-Z\d]/
455
+
456
+ @mount_url = "#{@connection_type.downcase}://#{username}:#{safe_pw}@#{@ip_address}/#{@share_name}"
457
+ @mnt_cmd = case @connection_type.downcase
458
+ when 'smb' then '/sbin/mount_smbfs'
459
+ when 'afp' then '/sbin/mount_afp'
460
+ else raise "Can't mount distribution point #{@name}: no known connection type."
461
+ end
462
+
463
+ @mountpoint.mkpath
464
+
465
+ mount_out = `#{@mnt_cmd} -o '#{MOUNT_OPTIONS}' '#{@mount_url}' '#{@mountpoint}' 2>&1`
466
+ if $?.exitstatus == 0 and @mountpoint.mountpoint?
467
+ #if system @mnt_cmd.to_s, *['-o', MOUNT_OPTIONS, @mount_url, @mountpoint.to_s]
468
+ @mounted = access
469
+ else
470
+ @mountpoint.rmdir if @mountpoint.directory?
471
+ @mounted = nil
472
+ raise JSS::FileServiceError, "Can't mount #{@ip_address}: #{mount_out}"
473
+ end
474
+ return @mountpoint
475
+ end # mount
476
+
477
+ ###
478
+ ### Unmount the distribution point.
479
+ ###
480
+ ### Does nothing if it wasn't mounted with #mount.
481
+ ###
482
+ ### @return [void]
483
+ ###
484
+ def unmount
485
+ return nil unless mounted?
486
+ if system "umount '#{@mountpoint}'"
487
+ @mountpoint.rmdir if @mountpoint.directory? and (not @mountpoint.mountpoint?)
488
+ @mounted = false
489
+ else
490
+ raise JSS::FileServiceError ,"There was a problem unmounting #{@mountpoint}"
491
+ end
492
+ nil
493
+ end # unmount
494
+
495
+
496
+ ###
497
+ ### Is this thing mounted right now?
498
+ ###
499
+ ### @return [Boolean]
500
+ ###
501
+ def mounted?
502
+ @mountpoint.directory? and @mountpoint.mountpoint?
503
+ end
504
+
505
+
506
+ #### aliases
507
+ alias hostname ip_address
508
+ alias umount unmount
509
+
510
+ ######################################
511
+ ### Private Instance Methods
512
+ ######################################
513
+ private
514
+
515
+ ###
516
+ ### Unused - until I get around to making DP's updatable
517
+ ###
518
+ ### the XML representation of the current state of this object,
519
+ ### for POSTing or PUTting back to the JSS via the API
520
+ ### Will be supported for Dist Points some day, I'm sure.
521
+ ###
522
+ def rest_xml
523
+ doc = REXML::Document.new
524
+ dp = doc.add_element "distribution_point"
525
+ dp.add_element(:name.to_s).text = @name
526
+ dp.add_element(:ip_address.to_s).text = @ip_address
527
+ dp.add_element(:local_path.to_s).text = @local_path
528
+ dp.add_element(:enable_load_balancing.to_s).text = @enable_load_balancing
529
+ dp.add_element(:failover_point.to_s).text = @failover_point
530
+ dp.add_element(:is_master.to_s).text = @is_master
531
+
532
+ dp.add_element(:connection_type.to_s).text = @connection_type
533
+ dp.add_element(:share_port.to_s).text = @share_port
534
+ dp.add_element(:share_name.to_s).text = @share_name
535
+ dp.add_element(:read_write_username.to_s).text = @read_write_username
536
+ dp.add_element(:read_write_password.to_s).text = @read_write_password
537
+ dp.add_element(:read_only_username.to_s).text = @read_only_username
538
+ dp.add_element(:read_only_password.to_s).text = @read_only_password
539
+ dp.add_element(:workgroup_or_domain.to_s).text = @workgroup_or_domain
540
+
541
+ dp.add_element(:http_downloads_enabled.to_s).text = @http_downloads_enabled
542
+ dp.add_element(:protocol.to_s).text = @protocol
543
+ dp.add_element(:port.to_s).text = @port
544
+ dp.add_element(:context.to_s).text = @context
545
+ dp.add_element(:no_authentication_required.to_s).text = @no_authentication_required
546
+ dp.add_element(:certificate_required.to_s).text = @certificate_required
547
+ dp.add_element(:username_password_required.to_s).text = @username_password_required
548
+ dp.add_element(:http_username.to_s).text = @http_username
549
+ dp.add_element(:certificate.to_s).text = @certificate
550
+ dp.add_element(:http_url.to_s).text = @http_url
551
+ dp.add_element(:failover_point_url.to_s).text = @failover_point_url
552
+
553
+ dp.add_element(:ssh_username.to_s).text = @ssh_username
554
+ dp.add_element(:ssh_password.to_s).text = @ssh_password if @ssh_password
555
+
556
+ return doc.to_s
557
+ end #rest_xml
558
+
559
+ end # class
560
+ end # module