ruby-jss 0.7.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +29 -22
  3. data/README.md +66 -86
  4. data/bin/jamfHelperBackgrounder +148 -0
  5. data/bin/netseg-update +0 -1
  6. data/lib/jss.rb +20 -9
  7. data/lib/jss/api_connection.rb +369 -295
  8. data/lib/jss/api_object.rb +651 -418
  9. data/lib/jss/api_object/account.rb +69 -77
  10. data/lib/jss/api_object/advanced_search.rb +201 -236
  11. data/lib/jss/api_object/advanced_search/advanced_computer_search.rb +42 -42
  12. data/lib/jss/api_object/advanced_search/advanced_mobile_device_search.rb +33 -43
  13. data/lib/jss/api_object/advanced_search/advanced_user_search.rb +33 -43
  14. data/lib/jss/api_object/building.rb +39 -52
  15. data/lib/jss/api_object/categorizable.rb +221 -0
  16. data/lib/jss/api_object/category.rb +81 -89
  17. data/lib/jss/api_object/computer.rb +486 -525
  18. data/lib/jss/api_object/computer_invitation.rb +73 -86
  19. data/lib/jss/api_object/criteriable.rb +6 -7
  20. data/lib/jss/api_object/ebook.rb +21 -0
  21. data/lib/jss/api_object/extendable.rb +6 -8
  22. data/lib/jss/api_object/group.rb +0 -3
  23. data/lib/jss/api_object/locatable.rb +19 -20
  24. data/lib/jss/api_object/mac_application.rb +21 -0
  25. data/lib/jss/api_object/mobile_device.rb +30 -21
  26. data/lib/jss/api_object/mobile_device_application.rb +447 -0
  27. data/lib/jss/api_object/mobile_device_configuration_profile.rb +21 -0
  28. data/lib/jss/api_object/osx_configuration_profile.rb +0 -3
  29. data/lib/jss/api_object/package.rb +21 -34
  30. data/lib/jss/api_object/peripheral.rb +16 -18
  31. data/lib/jss/api_object/policy.rb +5 -83
  32. data/lib/jss/api_object/purchasable.rb +11 -13
  33. data/lib/jss/api_object/scopable.rb +11 -12
  34. data/lib/jss/api_object/script.rb +3 -17
  35. data/lib/jss/api_object/self_servable.rb +419 -205
  36. data/lib/jss/api_object/self_servable/icon.rb +179 -0
  37. data/lib/jss/api_object/updatable.rb +35 -34
  38. data/lib/jss/api_object/uploadable.rb +72 -70
  39. data/lib/jss/api_object/user.rb +6 -7
  40. data/lib/jss/api_object/vppable.rb +117 -0
  41. data/lib/jss/client.rb +264 -225
  42. data/lib/jss/db_connection.rb +7 -5
  43. data/lib/jss/exceptions.rb +50 -42
  44. data/lib/jss/ruby_extensions.rb +8 -7
  45. data/lib/jss/ruby_extensions/object.rb +19 -0
  46. data/lib/jss/utility.rb +82 -40
  47. data/lib/jss/version.rb +1 -1
  48. metadata +37 -68
  49. data/bin/jss-webhook-server +0 -3
  50. data/lib/jss/webhooks.rb +0 -53
  51. data/lib/jss/webhooks/README.md +0 -269
  52. data/lib/jss/webhooks/configuration.rb +0 -213
  53. data/lib/jss/webhooks/data/sample_handlers/RestAPIOperation-executable +0 -91
  54. data/lib/jss/webhooks/data/sample_handlers/RestAPIOperation.rb +0 -45
  55. data/lib/jss/webhooks/data/sample_jsons/ComputerAdded.json +0 -27
  56. data/lib/jss/webhooks/data/sample_jsons/ComputerCheckIn.json +0 -27
  57. data/lib/jss/webhooks/data/sample_jsons/ComputerInventoryCompleted.json +0 -27
  58. data/lib/jss/webhooks/data/sample_jsons/ComputerPolicyFinished.json +0 -27
  59. data/lib/jss/webhooks/data/sample_jsons/ComputerPushCapabilityChanged.json +0 -27
  60. data/lib/jss/webhooks/data/sample_jsons/JSSShutdown.json +0 -14
  61. data/lib/jss/webhooks/data/sample_jsons/JSSStartup.json +0 -14
  62. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceCheckIn.json +0 -26
  63. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceCommandCompleted.json +0 -26
  64. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceEnrolled.json +0 -26
  65. data/lib/jss/webhooks/data/sample_jsons/MobileDevicePushSent.json +0 -26
  66. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceUnEnrolled.json +0 -26
  67. data/lib/jss/webhooks/data/sample_jsons/PatchSoftwareTitleUpdated.json +0 -14
  68. data/lib/jss/webhooks/data/sample_jsons/PushSent.json +0 -11
  69. data/lib/jss/webhooks/data/sample_jsons/RestAPIOperation.json +0 -15
  70. data/lib/jss/webhooks/data/sample_jsons/SCEPChallenge.json +0 -10
  71. data/lib/jss/webhooks/data/sample_jsons/SmartGroupComputerMembershipChange.json +0 -13
  72. data/lib/jss/webhooks/data/sample_jsons/SmartGroupMobileDeviceMembershipChange.json +0 -13
  73. data/lib/jss/webhooks/event.rb +0 -139
  74. data/lib/jss/webhooks/event/computer_added.rb +0 -38
  75. data/lib/jss/webhooks/event/computer_check_in.rb +0 -38
  76. data/lib/jss/webhooks/event/computer_inventory_completed.rb +0 -38
  77. data/lib/jss/webhooks/event/computer_policy_finished.rb +0 -38
  78. data/lib/jss/webhooks/event/computer_push_capability_changed.rb +0 -38
  79. data/lib/jss/webhooks/event/handlers.rb +0 -192
  80. data/lib/jss/webhooks/event/jss_shutdown.rb +0 -38
  81. data/lib/jss/webhooks/event/jss_startup.rb +0 -38
  82. data/lib/jss/webhooks/event/mobile_device_check_in.rb +0 -38
  83. data/lib/jss/webhooks/event/mobile_device_command_completed.rb +0 -38
  84. data/lib/jss/webhooks/event/mobile_device_enrolled.rb +0 -38
  85. data/lib/jss/webhooks/event/mobile_device_push_sent.rb +0 -38
  86. data/lib/jss/webhooks/event/mobile_device_unenrolled.rb +0 -38
  87. data/lib/jss/webhooks/event/patch_software_title_updated.rb +0 -38
  88. data/lib/jss/webhooks/event/push_sent.rb +0 -38
  89. data/lib/jss/webhooks/event/rest_api_operation.rb +0 -38
  90. data/lib/jss/webhooks/event/scep_challenge.rb +0 -38
  91. data/lib/jss/webhooks/event/smart_group_computer_membership_change.rb +0 -38
  92. data/lib/jss/webhooks/event/smart_group_mobile_device_membership_change.rb +0 -38
  93. data/lib/jss/webhooks/event/webhook.rb +0 -40
  94. data/lib/jss/webhooks/event_objects.rb +0 -112
  95. data/lib/jss/webhooks/event_objects/computer.rb +0 -49
  96. data/lib/jss/webhooks/event_objects/jss.rb +0 -36
  97. data/lib/jss/webhooks/event_objects/mobile_device.rb +0 -48
  98. data/lib/jss/webhooks/event_objects/patch_software_title_update.rb +0 -38
  99. data/lib/jss/webhooks/event_objects/push.rb +0 -33
  100. data/lib/jss/webhooks/event_objects/rest_api_operation.rb +0 -37
  101. data/lib/jss/webhooks/event_objects/scep_challenge.rb +0 -32
  102. data/lib/jss/webhooks/event_objects/smart_group.rb +0 -35
  103. data/lib/jss/webhooks/server_app.rb +0 -37
  104. data/lib/jss/webhooks/server_app/routes.rb +0 -27
  105. data/lib/jss/webhooks/server_app/routes/handle_webhook_event.rb +0 -39
  106. data/lib/jss/webhooks/server_app/routes/home.rb +0 -37
  107. data/lib/jss/webhooks/server_app/self_signed_cert.rb +0 -65
  108. data/lib/jss/webhooks/server_app/server.rb +0 -60
  109. data/lib/jss/webhooks/version.rb +0 -32
@@ -43,7 +43,6 @@
43
43
  require 'ruby-jss'
44
44
  require 'getoptlong'
45
45
  require 'english'
46
- load '/Users/chrisl/git/gemdev/ruby-jss/lib/jss/api_object/network_segment.rb'
47
46
 
48
47
  # The app object
49
48
  ##############################
data/lib/jss.rb CHANGED
@@ -44,7 +44,7 @@ module JSS
44
44
  require 'singleton'
45
45
  require 'pathname'
46
46
  require 'fileutils'
47
- require 'uri'
47
+ require 'open-uri'
48
48
  require 'ipaddr'
49
49
  require 'rexml/document'
50
50
  require 'base64'
@@ -52,6 +52,7 @@ module JSS
52
52
  require 'digest'
53
53
  require 'yaml'
54
54
  require 'open3'
55
+ require 'english'
55
56
 
56
57
  ###################
57
58
  ### Gems
@@ -95,15 +96,7 @@ module JSS
95
96
 
96
97
  module Composer; end
97
98
 
98
- ### Mix-in Sub Modules
99
99
 
100
- module Creatable; end
101
- module FileUpload; end
102
- module Locatable; end
103
- module Matchable; end
104
- module Purchasable; end
105
- module Updatable; end
106
- module Extendable; end
107
100
 
108
101
  ### Mix-in Sub Modules with Classes
109
102
 
@@ -123,6 +116,7 @@ module JSS
123
116
  class Client; end
124
117
  class DBConnection; end
125
118
  class Server; end
119
+ class Icon; end
126
120
  class Preferences; end
127
121
 
128
122
  ### SubClasses
@@ -152,9 +146,13 @@ module JSS
152
146
  class Category < JSS::APIObject; end
153
147
  class Computer < JSS::APIObject; end
154
148
  class Department < JSS::APIObject; end
149
+ class EBook < JSS::APIObject; end
155
150
  class DistributionPoint < JSS::APIObject; end
156
151
  class LDAPServer < JSS::APIObject; end
152
+ class MacApplication < JSS::APIObject; end
157
153
  class MobileDevice < JSS::APIObject; end
154
+ class MobileDeviceConfigurationProfile < JSS::APIObject; end
155
+ class MobileDeviceApplication < JSS::APIObject; end
158
156
  class NetBootServer < JSS::APIObject; end
159
157
  class NetworkSegment < JSS::APIObject; end
160
158
  class OSXConfigurationProfile < JSS::APIObject; end
@@ -170,6 +168,19 @@ module JSS
170
168
  class User < JSS::APIObject; end
171
169
  class WebHook < JSS::APIObject; end
172
170
 
171
+ ### Mix-in Sub Modules
172
+
173
+ module Creatable; end
174
+ module FileUpload; end
175
+ module Locatable; end
176
+ module Matchable; end
177
+ module Purchasable; end
178
+ module Updatable; end
179
+ module Extendable; end
180
+ module SelfServable; end
181
+ module Categorizable; end
182
+ module VPPable; end
183
+
173
184
  end # module JSS
174
185
 
175
186
  ### Load the rest of the module
@@ -1,5 +1,4 @@
1
1
  ### Copyright 2017 Pixar
2
-
3
2
  ###
4
3
  ### Licensed under the Apache License, Version 2.0 (the "Apache License")
5
4
  ### with the following modification; you may not use this file except in
@@ -26,251 +25,192 @@
26
25
  ###
27
26
  module JSS
28
27
 
29
- #####################################
30
- ### Constants
28
+ # Constants
31
29
  #####################################
32
30
 
33
- #####################################
34
- ### Module Variables
31
+ # Module Variables
35
32
  #####################################
36
33
 
37
- #####################################
38
- ### Module Methods
34
+ # Module Methods
39
35
  #####################################
40
36
 
41
- #####################################
42
- ### Module Classes
37
+ # Classes
43
38
  #####################################
44
39
 
45
- ###
46
- ### An API connection to the JSS.
47
- ###
48
- ### This is a singleton class, only one can exist at a time.
49
- ### Its one instance is created automatically when the module loads, but it
50
- ### isn't connected to anything at that time.
51
- ###
52
- ### Use it via the {JSS::API} constant to call the #connect
53
- ### method, and the {#get_rsrc}, {#put_rsrc}, {#post_rsrc}, & {#delete_rsrc}
54
- ### methods, q.v. below.
55
- ###
56
- ### To access the underlying RestClient::Resource instance,
57
- ### use JSS::API.cnx
58
- ###
40
+ # An API connection to the JSS.
41
+ #
42
+ # This is a singleton class, only one can exist at a time.
43
+ # Its one instance is created automatically when the module loads, but it
44
+ # isn't connected to anything at that time.
45
+ #
46
+ # Use it via the {JSS::API} constant to call the #connect
47
+ # method, and the {#get_rsrc}, {#put_rsrc}, {#post_rsrc}, & {#delete_rsrc}
48
+ # methods, q.v. below.
49
+ #
50
+ # To access the underlying RestClient::Resource instance,
51
+ # use JSS::API.cnx
52
+ #
59
53
  class APIConnection
60
- include Singleton
61
54
 
62
- #####################################
63
- ### Class Constants
55
+ # Class Constants
64
56
  #####################################
65
57
 
66
- ### The base API path in the jss URL
67
- RSRC_BASE = "JSSResource"
58
+ # The base API path in the jss URL
59
+ RSRC_BASE = 'JSSResource'.freeze
68
60
 
69
- ### A url path to load to see if there's an API available at a host.
70
- ### This just loads the API resource docs page
71
- TEST_PATH = "#{RSRC_BASE}/accounts"
61
+ # A url path to load to see if there's an API available at a host.
62
+ # This just loads the API resource docs page
63
+ TEST_PATH = "#{RSRC_BASE}/accounts".freeze
72
64
 
73
- ### If the test path loads correctly from a casper server, it'll contain
74
- ### this text (this is what we get when we make an unauthenticated
75
- ### API call.)
76
- TEST_CONTENT = "<p>The request requires user authentication</p>"
65
+ # If the test path loads correctly from a casper server, it'll contain
66
+ # this text (this is what we get when we make an unauthenticated
67
+ # API call.)
68
+ TEST_CONTENT = '<p>The request requires user authentication</p>'.freeze
77
69
 
78
- ### The Default port
70
+ # The Default port
79
71
  HTTP_PORT = 9006
80
72
 
81
- ### The SSL port
73
+ # The SSL port
82
74
  SSL_PORT = 8443
83
75
 
84
- ### The top line of an XML doc for submitting data via API
85
- XML_HEADER = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
76
+ # The top line of an XML doc for submitting data via API
77
+ XML_HEADER = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'.freeze
86
78
 
87
- ### Default timeouts in seconds
79
+ # Default timeouts in seconds
88
80
  DFT_OPEN_TIMEOUT = 60
89
81
  DFT_TIMEOUT = 60
90
82
 
91
- ### The Default SSL Version
92
- DFT_SSL_VERSION = 'TLSv1'
83
+ # The Default SSL Version
84
+ # As of Casper 9.61 we can't use SSL, must use TLS, since SSLv3 was susceptible to poodles.
85
+ # NOTE - this requires rest-client v 1.7.0 or higher
86
+ # which requires mime-types 2.0 or higher, which requires ruby 1.9.2 or higher!
87
+ # That means that support for ruby 1.8.7 stops with Casper 9.6
88
+ DFT_SSL_VERSION = 'TLSv1'.freeze
93
89
 
94
- #####################################
95
- ### Attributes
90
+ # Attributes
96
91
  #####################################
97
92
 
98
- ### @return [String] the username who's connected to the JSS API
93
+ # @return [String] the username who's connected to the JSS API
99
94
  attr_reader :jss_user
100
95
 
101
- ### @return [RestClient::Resource] the underlying connection resource
96
+ # @return [RestClient::Resource] the underlying connection resource
102
97
  attr_reader :cnx
103
98
 
104
- ### @return [Boolean] are we connected right now?
99
+ # @return [Boolean] are we connected right now?
105
100
  attr_reader :connected
106
101
 
107
- ### @return [JSS::Server] the details of the JSS to which we're connected.
102
+ # @return [JSS::Server] the details of the JSS to which we're connected.
108
103
  attr_reader :server
109
104
 
110
- ### @return [String] the hostname of the JSS to which we're connected.
105
+ # @return [String] the hostname of the JSS to which we're connected.
111
106
  attr_reader :server_host
112
107
 
113
- #####################################
114
- ### Constructor
115
- #####################################
116
-
117
- ###
118
- ### To connect, use JSS::APIConnection.instance.connect
119
- ### or a shortcut, JSS::API.connect
120
- ###
121
- def initialize ()
122
- @connected = false
123
- end # init
124
-
125
- #####################################
126
- ### Class Methods
127
- #####################################
128
-
129
- ###
130
- ### Connect to the JSS API.
131
- ###
132
- ### @param args[Hash] the keyed arguments for connection.
133
- ###
134
- ### @option args :server[String] the hostname of the JSS API server, required if not defined in JSS::CONFIG
135
- ###
136
- ### @option args :port[Integer] the port number to connect with, defaults to 8443
137
- ###
138
- ### @option args :use_ssl[Boolean] should the connection be made over SSL? Defaults to true.
139
- ###
140
- ### @option args :verify_cert[Boolean] should HTTPS SSL certificates be verified. Defaults to true.
141
- ### If your connection raises RestClient::SSLCertificateNotVerified, and you don't care about the
142
- ### validity of the SSL cert. just set this explicitly to false.
143
- ###
144
- ### @option args :user[String] a JSS user who has API privs, required if not defined in JSS::CONFIG
145
- ###
146
- ### @option args :pw[String,Symbol] Required, the password for that user, or :prompt, or :stdin
147
- ### If :prompt, the user is promted on the commandline to enter the password for the :user.
148
- ### If :stdin#, the password is read from a line of std in represented by the digit at #,
149
- ### so :stdin3 reads the passwd from the third line of standard input. defaults to line 1,
150
- ### if no digit is supplied. see {JSS.stdin}
151
- ###
152
- ### @option args :open_timeout[Integer] the number of seconds to wait for an initial response, defaults to 60
153
- ###
154
- ### @option args :timeout[Integer] the number of seconds before an API call times out, defaults to 60
155
- ###
156
- ### @return [true]
157
- ###
158
- def connect (args = {})
159
-
160
- # the server, if not specified, might come from a couple places.
161
- # see #hostname
162
- args[:server] ||= hostname
163
-
164
- # settings from config if they aren't in the args
165
- args[:server] ||= JSS::CONFIG.api_server_name
166
- args[:port] ||= JSS::CONFIG.api_server_port
167
- args[:user] ||= JSS::CONFIG.api_username
168
- args[:timeout] ||= JSS::CONFIG.api_timeout
169
- args[:open_timeout] ||= JSS::CONFIG.api_timeout_open
170
- args[:ssl_version] ||= JSS::CONFIG.api_ssl_version
171
-
172
- # if verify cert given was NOT in the args....
173
- if args[:verify_cert].nil?
174
- # set it from the prefs
175
- args[:verify_cert] = JSS::CONFIG.api_verify_cert
176
- end
108
+ # @return [Integer] the port used for the connection
109
+ attr_reader :port
177
110
 
178
- # settings from client jamf plist if needed
179
- args[:port] ||= JSS::Client.jss_port
111
+ # @return [String] the protocol being used: http or https
112
+ attr_reader :protocol
180
113
 
181
- # default settings if needed
182
- args[:port] ||= SSL_PORT
183
- args[:timeout] ||= DFT_TIMEOUT
184
- args[:open_timeout] ||= DFT_OPEN_TIMEOUT
114
+ # @return [RestClient::Response] The response from the most recent API call
115
+ attr_reader :last_http_response
185
116
 
186
- # As of Casper 9.61 we can't use SSL, must use TLS, since SSLv3 was susceptible to poodles.
187
- # NOTE - this requires rest-client v 1.7.0 or higher
188
- # which requires mime-types 2.0 or higher, which requires ruby 1.9.2 or higher!
189
- # That means that support for ruby 1.8.7 stops with Casper 9.6
190
- args[:ssl_version] ||= DFT_SSL_VERSION
117
+ # @return [String] The base URL to to the current REST API
118
+ attr_reader :rest_url
191
119
 
120
+ # Constructor
121
+ #####################################
192
122
 
193
- # must have server, user, and pw
194
- raise JSS::MissingDataError, "No JSS :server specified, or in configuration." unless args[:server]
195
- raise JSS::MissingDataError, "No JSS :user specified, or in configuration." unless args[:user]
196
- raise JSS::MissingDataError, "Missing :pw for user '#{args[:user]}'" unless args[:pw]
123
+ # To connect, use JSS::API.connect
124
+ #
125
+ def initialize
126
+ @connected = false
127
+ end # init
197
128
 
198
- # we're using ssl if 1) args[:use_ssl] is anything but false
199
- # or 2) the port is the default casper ssl port.
200
- args[:use_ssl] = (not args[:use_ssl] == false) or (args[:port] == SSL_PORT)
129
+ # Instance Methods
130
+ #####################################
201
131
 
202
- # and here's the URL
203
- ssl = args[:use_ssl] ? "s" : ''
204
- @rest_url = URI::encode "http#{ssl}://#{args[:server]}:#{args[:port]}/#{RSRC_BASE}"
132
+ # Connect to the JSS API.
133
+ #
134
+ # @param args[Hash] the keyed arguments for connection.
135
+ #
136
+ # @option args :server[String] the hostname of the JSS API server, required if not defined in JSS::CONFIG
137
+ #
138
+ # @option args :port[Integer] the port number to connect with, defaults to 8443
139
+ #
140
+ # @option args :use_ssl[Boolean] should the connection be made over SSL? Defaults to true.
141
+ #
142
+ # @option args :verify_cert[Boolean] should HTTPS SSL certificates be verified. Defaults to true.
143
+ # If your connection raises RestClient::SSLCertificateNotVerified, and you don't care about the
144
+ # validity of the SSL cert. just set this explicitly to false.
145
+ #
146
+ # @option args :user[String] a JSS user who has API privs, required if not defined in JSS::CONFIG
147
+ #
148
+ # @option args :pw[String,Symbol] Required, the password for that user, or :prompt, or :stdin
149
+ # If :prompt, the user is promted on the commandline to enter the password for the :user.
150
+ # If :stdin#, the password is read from a line of std in represented by the digit at #,
151
+ # so :stdin3 reads the passwd from the third line of standard input. defaults to line 1,
152
+ # if no digit is supplied. see {JSS.stdin}
153
+ #
154
+ # @option args :open_timeout[Integer] the number of seconds to wait for an initial response, defaults to 60
155
+ #
156
+ # @option args :timeout[Integer] the number of seconds before an API call times out, defaults to 60
157
+ #
158
+ # @return [true]
159
+ #
160
+ def connect(args = {})
161
+ args = apply_connection_defaults args
162
+ verify_basic_args args
163
+ @jss_user = args[:user]
205
164
 
165
+ @rest_url = build_rest_url args
206
166
 
207
- # prep the args for passing to RestClient::Resource
208
- # if verify_cert is anything but false, we will verify
209
- args[:verify_ssl] = (args[:verify_cert] == false) ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
167
+ # figure out :verify_ssl from :verify_cert
168
+ args[:verify_ssl] = verify_ssl args
210
169
 
211
- args[:password] = if args[:pw] == :prompt
212
- JSS.prompt_for_password "Enter the password for JSS user #{args[:user]}@#{args[:server]}:"
213
- elsif args[:pw].is_a?(Symbol) and args[:pw].to_s.start_with?('stdin')
214
- args[:pw].to_s =~ /^stdin(\d+)$/
215
- line = $1
216
- line ||= 1
217
- JSS.stdin line
218
- else
219
- args[:pw]
220
- end
170
+ # figure out :password from :pw
171
+ args[:password] = acquire_password args
221
172
 
222
173
  # heres our connection
223
- @cnx = RestClient::Resource.new("#{@rest_url}", args)
174
+ @cnx = RestClient::Resource.new(@rest_url.to_s, args)
224
175
 
225
- @jss_user = args[:user]
226
- @server_host = args[:server]
227
- @connected = true
228
- @server = JSS::Server.new
176
+ verify_server_version
229
177
 
230
- if @server.version < JSS.parse_jss_version(JSS::MINIMUM_SERVER_VERSION)[:version]
231
- raise JSS::UnsupportedError, "Your JSS Server version, #{@server.raw_version}, is to low. Must be #{JSS::MINIMUM_SERVER_VERSION} or higher."
232
- end
233
-
234
- return @connected ? @server_host : nil
178
+ @connected ? hostname : nil
235
179
  end # connect
236
180
 
237
- ### A useful string about this connection
238
- ###
239
- ### @return [String]
240
- ###
181
+ # A useful string about this connection
182
+ #
183
+ # @return [String]
184
+ #
241
185
  def to_s
242
- @connected ? "Using #{@rest_url} as user #{@jss_user}" : "not connected"
186
+ @connected ? "Using #{@rest_url} as user #{@jss_user}" : 'not connected'
243
187
  end
244
188
 
245
- ###
246
- ### Reset the response timeout for the rest connection
247
- ###
248
- ### @param timeout[Integer] the new timeout in seconds
249
- ###
250
- ### @return [void]
251
- ###
252
- def timeout= (timeout)
189
+ # Reset the response timeout for the rest connection
190
+ #
191
+ # @param timeout[Integer] the new timeout in seconds
192
+ #
193
+ # @return [void]
194
+ #
195
+ def timeout=(timeout)
253
196
  @cnx.options[:timeout] = timeout
254
197
  end
255
198
 
256
- ###
257
- ### Reset the open-connection timeout for the rest connection
258
- ###
259
- ### @param timeout[Integer] the new timeout in seconds
260
- ###
261
- ### @return [void]
262
- ###
263
- def open_timeout= (timeout)
199
+ # Reset the open-connection timeout for the rest connection
200
+ #
201
+ # @param timeout[Integer] the new timeout in seconds
202
+ #
203
+ # @return [void]
204
+ #
205
+ def open_timeout=(timeout)
264
206
  @cnx.options[:open_timeout] = timeout
265
207
  end
266
208
 
267
-
268
- ###
269
- ### With a REST connection, there isn't any real "connection" to disconnect from
270
- ### So to disconnect, we just unset all our credentials.
271
- ###
272
- ### @return [void]
273
- ###
209
+ # With a REST connection, there isn't any real "connection" to disconnect from
210
+ # So to disconnect, we just unset all our credentials.
211
+ #
212
+ # @return [void]
213
+ #
274
214
  def disconnect
275
215
  @jss_user = nil
276
216
  @rest_url = nil
@@ -279,141 +219,275 @@ module JSS
279
219
  @connected = false
280
220
  end # disconnect
281
221
 
282
- ###
283
- ### Get an arbitrary JSS resource
284
- ###
285
- ### The first argument is the resource to get (the part of the API url
286
- ### after the 'JSSResource/' )
287
- ###
288
- ### By default we get the data in JSON, and parse it
289
- ### into a ruby data structure (arrays, hashes, strings, etc)
290
- ### with symbolized Hash keys.
291
- ###
292
- ### @param rsrc[String] the resource to get
293
- ### (the part of the API url after the 'JSSResource/' )
294
- ###
295
- ### @param format[Symbol] either ;json or :xml
296
- ### If the second argument is :xml, the XML data is returned as a String.
297
- ###
298
- ### @return [Hash,String] the result of the get
299
- ###
300
- def get_rsrc (rsrc, format = :json)
301
- raise JSS::InvalidConnectionError, "Not Connected. Use JSS::API.connect first." unless @connected
302
- rsrc = URI::encode rsrc
303
- data = @cnx[rsrc].get(:accept => format)
304
- return JSON.parse(data, :symbolize_names => true) if format == :json
305
- data
222
+ # Get an arbitrary JSS resource
223
+ #
224
+ # The first argument is the resource to get (the part of the API url
225
+ # after the 'JSSResource/' )
226
+ #
227
+ # By default we get the data in JSON, and parse it
228
+ # into a ruby data structure (arrays, hashes, strings, etc)
229
+ # with symbolized Hash keys.
230
+ #
231
+ # @param rsrc[String] the resource to get
232
+ # (the part of the API url after the 'JSSResource/' )
233
+ #
234
+ # @param format[Symbol] either ;json or :xml
235
+ # If the second argument is :xml, the XML data is returned as a String.
236
+ #
237
+ # @return [Hash,String] the result of the get
238
+ #
239
+ def get_rsrc(rsrc, format = :json)
240
+ raise JSS::InvalidConnectionError, 'Not Connected. Use JSS::API.connect first.' unless @connected
241
+ rsrc = URI.encode rsrc
242
+ @last_http_response = @cnx[rsrc].get(accept: format)
243
+ return JSON.parse(@last_http_response, symbolize_names: true) if format == :json
306
244
  end
307
245
 
308
- ###
309
- ### Change an existing JSS resource
310
- ###
311
- ### @param rsrc[String] the API resource being changed, the URL part after 'JSSResource/'
312
- ###
313
- ### @param xml[String] the xml specifying the changes.
314
- ###
315
- ### @return [String] the xml response from the server.
316
- ###
317
- def put_rsrc(rsrc,xml)
318
- raise JSS::InvalidConnectionError, "Not Connected. Use JSS::API.connect first." unless @connected
319
-
320
- ### convert CRs & to &#13;
246
+ # Change an existing JSS resource
247
+ #
248
+ # @param rsrc[String] the API resource being changed, the URL part after 'JSSResource/'
249
+ #
250
+ # @param xml[String] the xml specifying the changes.
251
+ #
252
+ # @return [String] the xml response from the server.
253
+ #
254
+ def put_rsrc(rsrc, xml)
255
+ raise JSS::InvalidConnectionError, 'Not Connected. Use JSS::API.connect first.' unless @connected
256
+
257
+ # convert CRs & to &#13;
321
258
  xml.gsub!(/\r/, '&#13;')
322
259
 
323
- ### send the data
324
- @cnx[rsrc].put(xml, :content_type => 'text/xml')
260
+ # send the data
261
+ @last_http_response = @cnx[rsrc].put(xml, content_type: 'text/xml')
262
+ rescue RestClient::Conflict => exception
263
+ raise_conflict_error(exception)
325
264
  end
326
265
 
327
- ###
328
- ### Create a new JSS resource
329
- ###
330
- ### @param rsrc[String] the API resource being created, the URL part after 'JSSResource/'
331
- ###
332
- ### @param xml[String] the xml specifying the new object.
333
- ###
334
- ### @return [String] the xml response from the server.
335
- ###
336
- def post_rsrc(rsrc,xml)
337
- raise JSS::InvalidConnectionError, "Not Connected. Use JSS::API.connect first." unless @connected
338
-
339
- ### convert CRs & to &#13;
266
+ # Create a new JSS resource
267
+ #
268
+ # @param rsrc[String] the API resource being created, the URL part after 'JSSResource/'
269
+ #
270
+ # @param xml[String] the xml specifying the new object.
271
+ #
272
+ # @return [String] the xml response from the server.
273
+ #
274
+ def post_rsrc(rsrc, xml)
275
+ raise JSS::InvalidConnectionError, 'Not Connected. Use JSS::API.connect first.' unless @connected
276
+
277
+ # convert CRs & to &#13;
340
278
  xml.gsub!(/\r/, '&#13;')
341
279
 
342
- ### send the data
343
- @cnx[rsrc].post xml, :content_type => 'text/xml', :accept => :json
344
- end #post_rsrc
345
-
346
- ### Delete a resource from the JSS
347
- ###
348
- ### @param rsrc[String] the resource to create, the URL part after 'JSSResource/'
349
- ###
350
- ### @return [String] the xml response from the server.
351
- ###
280
+ # send the data
281
+ @last_http_response = @cnx[rsrc].post xml, content_type: 'text/xml', accept: :json
282
+ rescue RestClient::Conflict => exception
283
+ raise_conflict_error(exception)
284
+ end # post_rsrc
285
+
286
+ # Delete a resource from the JSS
287
+ #
288
+ # @param rsrc[String] the resource to create, the URL part after 'JSSResource/'
289
+ #
290
+ # @return [String] the xml response from the server.
291
+ #
352
292
  def delete_rsrc(rsrc)
353
- raise JSS::InvalidConnectionError, "Not Connected. Use JSS::API.connect first." unless @connected
354
- raise MissingDataError, "Missing :rsrc" if rsrc.nil?
355
-
356
- ### delete the resource
357
- @cnx[rsrc].delete
358
-
359
- end #delete_rsrc
360
-
361
-
362
- ### Test that a given hostname & port is a JSS API server
363
- ###
364
- ### @param server[String] The hostname to test,
365
- ###
366
- ### @param port[Integer] The port to try connecting on
367
- ###
368
- ### @return [Boolean] does the server host a JSS API?
369
- ###
370
- def valid_server? (server, port = SSL_PORT)
293
+ raise JSS::InvalidConnectionError, 'Not Connected. Use JSS::API.connect first.' unless @connected
294
+ raise MissingDataError, 'Missing :rsrc' if rsrc.nil?
295
+
296
+ # delete the resource
297
+ @last_http_response = @cnx[rsrc].delete
298
+ end # delete_rsrc
299
+
300
+ # Test that a given hostname & port is a JSS API server
301
+ #
302
+ # @param server[String] The hostname to test,
303
+ #
304
+ # @param port[Integer] The port to try connecting on
305
+ #
306
+ # @return [Boolean] does the server host a JSS API?
307
+ #
308
+ def valid_server?(server, port = SSL_PORT)
371
309
  # cheating by shelling out to curl, because getting open-uri, or even net/http to use
372
310
  # ssl_options like :OP_NO_SSLv2 and :OP_NO_SSLv3 will take time to figure out..
373
311
  return true if `/usr/bin/curl -s 'https://#{server}:#{port}/#{TEST_PATH}'`.include? TEST_CONTENT
374
312
  return true if `/usr/bin/curl -s 'http://#{server}:#{port}/#{TEST_PATH}'`.include? TEST_CONTENT
375
- return false
376
-
377
- # try ssl first
378
- # NOTE: doesn't work if we can't disallow SSLv3 or force TLSv1
379
- # See cheat above.
380
- begin
381
- return true if open("https://#{server}:#{port}/#{TEST_PATH}", ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE).read.include? TEST_CONTENT
382
-
383
- rescue
384
- # then regular http
385
- begin
386
- return true if open("http://#{server}:#{port}/#{TEST_PATH}").read.include? TEST_CONTENT
387
- rescue
388
- # any errors = no API
389
- return false
390
- end # begin
391
- end #begin
392
- # if we're here, no API
393
- return false
313
+ false
314
+
315
+ # # try ssl first
316
+ # # NOTE: doesn't work if we can't disallow SSLv3 or force TLSv1
317
+ # # See cheat above.
318
+ # begin
319
+ # return true if open("https://#{server}:#{port}/#{TEST_PATH}", ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE).read.include? TEST_CONTENT
320
+ #
321
+ # rescue
322
+ # # then regular http
323
+ # begin
324
+ # return true if open("http://#{server}:#{port}/#{TEST_PATH}").read.include? TEST_CONTENT
325
+ # rescue
326
+ # # any errors = no API
327
+ # return false
328
+ # end # begin
329
+ # end # begin
330
+ # # if we're here, no API
331
+ # false
394
332
  end
395
333
 
396
- ### The server to which we are connected, or will
397
- ### try connecting to if none is specified with the
398
- ### call to #connect
399
- ###
400
- ### @return [String] the hostname of the server
401
- ###
334
+ # The server to which we are connected, or will
335
+ # try connecting to if none is specified with the
336
+ # call to #connect
337
+ #
338
+ # @return [String] the hostname of the server
339
+ #
402
340
  def hostname
403
341
  return @server_host if @server_host
404
342
  srvr = JSS::CONFIG.api_server_name
405
343
  srvr ||= JSS::Client.jss_server
406
- return srvr
344
+ srvr
407
345
  end
408
346
 
409
- ### aliases
347
+ # aliases
410
348
  alias connected? connected
349
+ alias host hostname
350
+
351
+ # Private Insance Methods
352
+ ####################################
353
+ private
354
+
355
+ # Apply defaults from the JSS::CONFIG, the JSS::Client
356
+ # or the module defaults to the args for the #connect method
357
+ #
358
+ # @param args[Hash] The args for #connect
359
+ #
360
+ # @return [Hash] The args with defaults applied
361
+ #
362
+ def apply_connection_defaults(args)
363
+ # settings from config if they aren't in the args
364
+ args[:server] ||= JSS::CONFIG.api_server_name
365
+ args[:port] ||= JSS::CONFIG.api_server_port
366
+ args[:user] ||= JSS::CONFIG.api_username
367
+ args[:timeout] ||= JSS::CONFIG.api_timeout
368
+ args[:open_timeout] ||= JSS::CONFIG.api_timeout_open
369
+ args[:ssl_version] ||= JSS::CONFIG.api_ssl_version
411
370
 
371
+ # if verify cert was not in the args, get it from the prefs.
372
+ # We can't use ||= because the desired value might be 'false'
373
+ args[:verify_cert] = JSS::CONFIG.api_verify_cert if args[:verify_cert].nil?
412
374
 
413
- end # class JSSAPIConnection
375
+ # these settings can come from the jamf binary config, if this machine is a JSS client.
376
+ args[:server] ||= JSS::Client.jss_server
377
+ args[:port] ||= JSS::Client.jss_port
378
+ args[:use_ssl] ||= JSS::Client.jss_protocol.end_with? 's'
379
+
380
+ # defaults from the module if needed
381
+ args[:port] ||= args[:use_ssl] ? SSL_PORT : HTTP_PORT
382
+ args[:timeout] ||= DFT_TIMEOUT
383
+ args[:open_timeout] ||= DFT_OPEN_TIMEOUT
384
+ args[:ssl_version] ||= DFT_SSL_VERSION
385
+ args
386
+ end
387
+
388
+ # Raise execeptions if we don't have essential data for the connection
389
+ #
390
+ # @param args[Hash] The args for #connect
391
+ #
392
+ # @return [void]
393
+ #
394
+ def verify_basic_args(args)
395
+ # must have server, user, and pw
396
+ raise JSS::MissingDataError, 'No JSS :server specified, or in configuration.' unless args[:server]
397
+ raise JSS::MissingDataError, 'No JSS :user specified, or in configuration.' unless args[:user]
398
+ raise JSS::MissingDataError, "Missing :pw for user '#{args[:user]}'" unless args[:pw]
399
+ end
414
400
 
415
- ### The single instance of the APIConnection
416
- API = APIConnection.instance
401
+ # Verify that we can connect with the args provided, and that
402
+ # the server version is high enough for this version of ruby-jss.
403
+ #
404
+ # This makes the first API GET call and will raise an exception if things
405
+ # are wrong, like failed authentication. Will also raise an exception
406
+ # if the JSS version is too low
407
+ # (see also JSS::Server)
408
+ #
409
+ # @return [void]
410
+ #
411
+ def verify_server_version
412
+ @connected = true
413
+ @server = JSS::Server.new
414
+ min_vers = JSS.parse_jss_version(JSS::MINIMUM_SERVER_VERSION)[:version]
415
+ return unless @server.version < min_vers
416
+ err_msg = "JSS version #{@server.raw_version} to low. Must be >= #{min_vers}"
417
+ @connected = false
418
+ raise JSS::UnsupportedError, err_msg
419
+ end
420
+
421
+ # Build the base URL for the API connection
422
+ #
423
+ # @param args[Hash] The args for #connect
424
+ #
425
+ # @return [String] The URI encoded URL
426
+ #
427
+ def build_rest_url(args)
428
+ # we're using ssl if:
429
+ # 1) args[:use_ssl] is anything but false
430
+ # or
431
+ # 2) the port is the default casper ssl port.
432
+ (args[:use_ssl] = (args[:use_ssl] != false)) || (args[:port] == SSL_PORT)
433
+
434
+ # and here's the URL
435
+ @protocol = 'http'
436
+ @protocol << 's' if args[:use_ssl]
437
+ @server_host = args[:server]
438
+ @port = args[:port].to_i
439
+ URI.encode "#{@protocol}://#{@server_host}:#{@port}/#{RSRC_BASE}"
440
+ end
441
+
442
+ # From whatever was given in args[:pw], figure out the real password
443
+ #
444
+ # @param args[Hash] The args for #connect
445
+ #
446
+ # @return [String] The password for the connection
447
+ #
448
+ def acquire_password(args)
449
+ if args[:pw] == :prompt
450
+ JSS.prompt_for_password "Enter the password for JSS user #{args[:user]}@#{args[:server]}:"
451
+ elsif args[:pw].is_a?(Symbol) && args[:pw].to_s.start_with?('stdin')
452
+ args[:pw].to_s =~ /^stdin(\d+)$/
453
+ line = Regexp.last_match(1)
454
+ line ||= 1
455
+ JSS.stdin line
456
+ else
457
+ args[:pw]
458
+ end
459
+ end
460
+
461
+ # Get the appropriate OpenSSL::SSL constant for
462
+ # certificate verification.
463
+ #
464
+ # @param args[Hash] The args for #connect
465
+ #
466
+ # @return [Type] description_of_returned_object
467
+ #
468
+ def verify_ssl(args)
469
+ # if verify_cert is anything but false, we will verify
470
+ args[:verify_cert] == false ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
471
+ end
472
+
473
+ # Parses the HTTP body of a RestClient::Conflict (409 conflict)
474
+ # exception and re-raises a JSS::ConflictError with a more
475
+ # useful error message.
476
+ #
477
+ # @param exception[RestClient::Conflict] the exception to parse
478
+ #
479
+ # @return [void]
480
+ #
481
+ def raise_conflict_error(exception)
482
+ exception.http_body =~ %r{<p>Error:(.*)</p>}
483
+ conflict_reason = Regexp.last_match(1)
484
+ conflict_reason ||= exception.http_body
485
+ raise JSS::ConflictError, conflict_reason
486
+ end
487
+
488
+ end # class JSSAPIConnection
417
489
 
490
+ # The default APIConnection
491
+ API = APIConnection.new
418
492
 
419
493
  end # module