ruby-jss 0.7.0 → 0.8.1

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.
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