windoo 1.0.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.
- checksums.yaml +7 -0
- data/CHANGES.md +9 -0
- data/LICENSE.txt +177 -0
- data/README.md +222 -0
- data/lib/windoo/base_classes/array_manager.rb +335 -0
- data/lib/windoo/base_classes/criteria_manager.rb +327 -0
- data/lib/windoo/base_classes/criterion.rb +226 -0
- data/lib/windoo/base_classes/json_object.rb +472 -0
- data/lib/windoo/configuration.rb +221 -0
- data/lib/windoo/connection/actions.rb +152 -0
- data/lib/windoo/connection/attributes.rb +156 -0
- data/lib/windoo/connection/connect.rb +402 -0
- data/lib/windoo/connection/constants.rb +55 -0
- data/lib/windoo/connection/token.rb +489 -0
- data/lib/windoo/connection.rb +92 -0
- data/lib/windoo/converters.rb +31 -0
- data/lib/windoo/exceptions.rb +34 -0
- data/lib/windoo/mixins/api_collection.rb +408 -0
- data/lib/windoo/mixins/constants.rb +43 -0
- data/lib/windoo/mixins/default_connection.rb +75 -0
- data/lib/windoo/mixins/immutable.rb +34 -0
- data/lib/windoo/mixins/loading.rb +38 -0
- data/lib/windoo/mixins/patch/component.rb +102 -0
- data/lib/windoo/mixins/software_title/extension_attribute.rb +106 -0
- data/lib/windoo/mixins/utility.rb +23 -0
- data/lib/windoo/objects/capability.rb +82 -0
- data/lib/windoo/objects/capability_manager.rb +52 -0
- data/lib/windoo/objects/component.rb +99 -0
- data/lib/windoo/objects/component_criteria_manager.rb +26 -0
- data/lib/windoo/objects/component_criterion.rb +66 -0
- data/lib/windoo/objects/extension_attribute.rb +149 -0
- data/lib/windoo/objects/kill_app.rb +92 -0
- data/lib/windoo/objects/kill_app_manager.rb +89 -0
- data/lib/windoo/objects/patch.rb +235 -0
- data/lib/windoo/objects/patch_manager.rb +240 -0
- data/lib/windoo/objects/requirement.rb +85 -0
- data/lib/windoo/objects/requirement_manager.rb +52 -0
- data/lib/windoo/objects/software_title.rb +407 -0
- data/lib/windoo/validate.rb +548 -0
- data/lib/windoo/version.rb +15 -0
- data/lib/windoo/zeitwerk_config.rb +158 -0
- data/lib/windoo.rb +56 -0
- metadata +141 -0
@@ -0,0 +1,402 @@
|
|
1
|
+
# Copyright 2025 Pixar
|
2
|
+
#
|
3
|
+
# Licensed under the terms set forth in the LICENSE.txt file available at
|
4
|
+
# at the root of this project.
|
5
|
+
#
|
6
|
+
#
|
7
|
+
|
8
|
+
# frozen_string_literal: true
|
9
|
+
|
10
|
+
module Windoo
|
11
|
+
|
12
|
+
class Connection
|
13
|
+
|
14
|
+
# This module defines constants and methods used for processing the connection
|
15
|
+
# parameters, acquiring passwords and tokens, and creating the underlying Faraday
|
16
|
+
# connection objects to the Title Editor API. It also defines the disconnection
|
17
|
+
# methods
|
18
|
+
#############################################
|
19
|
+
module Connect
|
20
|
+
|
21
|
+
def self.included(includer)
|
22
|
+
Windoo.verbose_include(includer, self)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Connect to the both the Classic and Jamf Pro APIs
|
26
|
+
#
|
27
|
+
# IMPORTANT: http (non-SSL, unencrypted) connections are not allowed.
|
28
|
+
#
|
29
|
+
# The first parameter may be a URL (must be https) from which
|
30
|
+
# the host & port will be used, and if present, the user and password
|
31
|
+
# E.g.
|
32
|
+
# connect 'https://myuser:pass@host.domain.edu:8443'
|
33
|
+
#
|
34
|
+
# which is the same as:
|
35
|
+
# connect host: 'host.domain.edu', port: 8443, user: 'myuser', pw: 'pass'
|
36
|
+
#
|
37
|
+
# When using a URL, other parameters below may be specified, however
|
38
|
+
# host: and port: parameters will be ignored, since they came from the URL,
|
39
|
+
# as will user: and :pw, if they are present in the URL. If the URL doesn't
|
40
|
+
# contain user and pw, they can be provided via the parameters, or left
|
41
|
+
# to default values.
|
42
|
+
#
|
43
|
+
# ### Passwords
|
44
|
+
#
|
45
|
+
# The pw: parameter also accepts the symbols :prompt, and :stdin[X]
|
46
|
+
#
|
47
|
+
# If :prompt, the user is promted on the commandline to enter the password
|
48
|
+
# for the :user.
|
49
|
+
#
|
50
|
+
# If pw: is omitted, and running from an interactive terminal, the user is
|
51
|
+
# prompted as with :prompt
|
52
|
+
#
|
53
|
+
# ### Tokens
|
54
|
+
# Instead of a user and password, you may specify a valid 'token:', which is
|
55
|
+
# either:
|
56
|
+
#
|
57
|
+
# A Windoo::Connection::Token object, which can be extracted from an active
|
58
|
+
# Windoo::Connection via its #token method
|
59
|
+
#
|
60
|
+
# or
|
61
|
+
#
|
62
|
+
# A valid token string e.g. "eyJhdXR...6EKoo" from any source can also be used.
|
63
|
+
#
|
64
|
+
# When using an existing token or token string, the username used to create
|
65
|
+
# the token will be read from the server. However, if you don't also provide
|
66
|
+
# the users password using the pw: parameter, then the pw_fallback option
|
67
|
+
# will always be false.
|
68
|
+
#
|
69
|
+
# ### Default values
|
70
|
+
#
|
71
|
+
# Any values available via Windoo.config will be used if they are not provided
|
72
|
+
# in the parameters. See Windoo::Configuration. If there are no config values
|
73
|
+
# then a built-in default is used if available.
|
74
|
+
#
|
75
|
+
# @param url[String] The URL to use for the connection. Must be 'https'.
|
76
|
+
# The host, port, and (if provided), user & password will be extracted.
|
77
|
+
# Any of those params explicitly provided will be ignored if present in
|
78
|
+
# the url
|
79
|
+
#
|
80
|
+
# @param params[Hash] the keyed parameters for connection.
|
81
|
+
#
|
82
|
+
# @option params :host[String] the hostname of the Title Editor server, required
|
83
|
+
# if not defined in Windoo.config
|
84
|
+
#
|
85
|
+
# @option params :user[String] a JSS user who has API privs, required if not
|
86
|
+
# defined in Jamf::CONFIG
|
87
|
+
#
|
88
|
+
# @option params :pw[String, Symbol] The user's password, or :prompt
|
89
|
+
# If :prompt, the user is promted on the commandline to enter the password
|
90
|
+
#
|
91
|
+
# @option params :port[Integer] the port number to connect with, defaults
|
92
|
+
# to 443
|
93
|
+
#
|
94
|
+
# @option params :ssl_version[String, Symbol] The SSL version to use. Default
|
95
|
+
# is TLSv1_2
|
96
|
+
#
|
97
|
+
# @option params :verify_cert[Boolean] should SSL certificates be verified.
|
98
|
+
# Defaults to true.
|
99
|
+
#
|
100
|
+
# @option params :open_timeout[Integer] the number of seconds to wait for an
|
101
|
+
# initial response, defaults to 60
|
102
|
+
#
|
103
|
+
# @option params :timeout[Integer] the number of seconds before an API call
|
104
|
+
# times out, defaults to 60
|
105
|
+
#
|
106
|
+
# @option params :keep_alive[Boolean] Should the token for the connection
|
107
|
+
# for be automatically refreshed before it expires? Default is true
|
108
|
+
#
|
109
|
+
# @option params :pw_fallback [Boolean] If keep_alive, should the passwd be
|
110
|
+
# cached in memory and used to create a new token, if there are problems
|
111
|
+
# with the normal token refresh process?
|
112
|
+
#
|
113
|
+
# @option params :token [String] A valid token string. If provided, no need
|
114
|
+
# to provide user:. pw: must only be provided if pw_fallback is set to true,
|
115
|
+
# and must be the correct pw for the user who generated the token string.
|
116
|
+
#
|
117
|
+
# @return [String] connection description, the output of #to_s
|
118
|
+
#
|
119
|
+
#######################################################
|
120
|
+
def connect(url = nil, **params)
|
121
|
+
raise ArgumentError, 'No url or connection parameters provided' if url.nil? && params.empty?
|
122
|
+
|
123
|
+
# reset all values, flush caches
|
124
|
+
disconnect
|
125
|
+
|
126
|
+
# Get host, port, user and pw from a URL, or
|
127
|
+
# build a base_url from those provided in the params
|
128
|
+
# when finished, params will include
|
129
|
+
# base_url, user, host, port, and possibly pw
|
130
|
+
parse_url url, params
|
131
|
+
|
132
|
+
# Default to prompting for the pw
|
133
|
+
params[:pw] ||= :prompt
|
134
|
+
|
135
|
+
prompt_for_password(params) if params[:pw] == :prompt
|
136
|
+
|
137
|
+
# apply defaults from config, client, and then windoo itself.
|
138
|
+
apply_default_params params
|
139
|
+
|
140
|
+
# Once we're here, all params have been parsed & defaulted into the
|
141
|
+
# params hash, so make sure we have the minimum needed params for a connection
|
142
|
+
verify_basic_params params
|
143
|
+
|
144
|
+
# Now we can build out base url
|
145
|
+
build_base_url params
|
146
|
+
|
147
|
+
# it there's no @token yet, get one from a token string or a password
|
148
|
+
create_token params
|
149
|
+
|
150
|
+
# Now set our attribs
|
151
|
+
|
152
|
+
@timeout = params[:timeout]
|
153
|
+
@open_timeout = params[:open_timeout]
|
154
|
+
|
155
|
+
@name ||= "#{params[:user]}@#{params[:host]}:#{params[:port]}"
|
156
|
+
|
157
|
+
# the faraday connection object
|
158
|
+
@cnx = create_connection
|
159
|
+
|
160
|
+
@connect_time = Time.now
|
161
|
+
@connected = true
|
162
|
+
|
163
|
+
to_s
|
164
|
+
end # connect
|
165
|
+
alias login connect
|
166
|
+
|
167
|
+
# raise exception if not connected, and make sure we're using
|
168
|
+
# the current token
|
169
|
+
def validate_connected
|
170
|
+
using_dft = 'Windoo.cnx' if self == Windoo.cnx
|
171
|
+
return if connected?
|
172
|
+
|
173
|
+
raise Windoo::NotConnectedError,
|
174
|
+
"Connection '#{name}' Not Connected. Use #{using_dft}.connect first."
|
175
|
+
end
|
176
|
+
|
177
|
+
# With a REST connection, there isn't any real "connection" to disconnect from
|
178
|
+
# So to disconnect, we just unset all our credentials.
|
179
|
+
#
|
180
|
+
# @return [void]
|
181
|
+
#
|
182
|
+
#######################################################
|
183
|
+
def disconnect
|
184
|
+
@token&.disconnect
|
185
|
+
@token = nil
|
186
|
+
@cnx = nil
|
187
|
+
|
188
|
+
@connected = false
|
189
|
+
:disconnected
|
190
|
+
end # disconnect
|
191
|
+
alias logout disconnect
|
192
|
+
|
193
|
+
##### Parsing Params & creating connections
|
194
|
+
######################################################
|
195
|
+
private
|
196
|
+
|
197
|
+
# Validate a token string, generating a fresh token and setting
|
198
|
+
# @user.
|
199
|
+
# ######################################################ß˙
|
200
|
+
def parse_token(params)
|
201
|
+
return unless params[:token]
|
202
|
+
|
203
|
+
@token = Windoo::Connection::Token.new(
|
204
|
+
token_string: params[:token]
|
205
|
+
)
|
206
|
+
end
|
207
|
+
|
208
|
+
# Get host, port, user and pw from a URL, or
|
209
|
+
# build a base url from those provided in the params
|
210
|
+
# when finished, params will include
|
211
|
+
# base_url, user, host, port, and possibly pw
|
212
|
+
#
|
213
|
+
# @return [String, nil] the pw if present
|
214
|
+
#
|
215
|
+
#######################################################
|
216
|
+
def parse_url(url, params)
|
217
|
+
return unless url
|
218
|
+
|
219
|
+
url = URI.parse url.to_s
|
220
|
+
raise ArgumentError, 'Invalid url, scheme must be https' unless url.scheme == HTTPS_SCHEME
|
221
|
+
|
222
|
+
params[:host] ||= url.host
|
223
|
+
params[:port] ||= url.port unless url.port == SSL_PORT
|
224
|
+
params[:user] ||= url.user if url.user
|
225
|
+
params[:pw] ||= url.password if url.password
|
226
|
+
end
|
227
|
+
|
228
|
+
# Apply defaults to the unset params for the #connect method
|
229
|
+
# First apply them from from the Jamf.config,
|
230
|
+
# then from the Jamf::Client (read from the jamf binary config),
|
231
|
+
# then from the Jamf module defaults
|
232
|
+
#
|
233
|
+
# @param params[Hash] The params for #connect
|
234
|
+
#
|
235
|
+
# @return [Hash] The params with defaults applied
|
236
|
+
#
|
237
|
+
#######################################################
|
238
|
+
def apply_default_params(params)
|
239
|
+
apply_defaults_from_config(params)
|
240
|
+
apply_module_defaults(params)
|
241
|
+
end
|
242
|
+
|
243
|
+
# Apply defaults from the Jamf.config
|
244
|
+
# to the params for the #connect method
|
245
|
+
#
|
246
|
+
# @param params[Hash] The params for #connect
|
247
|
+
#
|
248
|
+
# @return [Hash] The params with defaults applied
|
249
|
+
#
|
250
|
+
#######################################################
|
251
|
+
def apply_defaults_from_config(params)
|
252
|
+
# settings from config if they aren't in the params
|
253
|
+
params[:host] ||= Windoo.config.title_editor_server_name
|
254
|
+
params[:port] ||= Windoo.config.title_editor_server_port
|
255
|
+
params[:user] ||= Windoo.config.title_editor_username
|
256
|
+
params[:timeout] ||= Windoo.config.title_editor_timeout
|
257
|
+
params[:open_timeout] ||= Windoo.config.title_editor_open_timeout
|
258
|
+
params[:ssl_version] ||= Windoo.config.title_editor_ssl_version
|
259
|
+
|
260
|
+
# if verify cert was not in the params, get it from the prefs.
|
261
|
+
# We can't use ||= because the desired value might be 'false'
|
262
|
+
params[:verify_cert] = Windoo.config.title_editor_verify_cert if params[:verify_cert].nil?
|
263
|
+
end # apply_defaults_from_config
|
264
|
+
|
265
|
+
# Apply the module defaults to the params for the #connect method
|
266
|
+
#
|
267
|
+
# @param params[Hash] The params for #connect
|
268
|
+
#
|
269
|
+
# @return [Hash] The params with defaults applied
|
270
|
+
#
|
271
|
+
#######################################################
|
272
|
+
def apply_module_defaults(params)
|
273
|
+
# if we have no port set by this point, assume on-prem.
|
274
|
+
params[:port] ||= SSL_PORT
|
275
|
+
params[:timeout] ||= DFT_TIMEOUT
|
276
|
+
params[:open_timeout] ||= DFT_OPEN_TIMEOUT
|
277
|
+
params[:ssl_version] ||= DFT_SSL_VERSION
|
278
|
+
# if we have a TTY, pw defaults to :prompt
|
279
|
+
params[:pw] ||= :prompt if $stdin.tty?
|
280
|
+
|
281
|
+
params[:keep_alive] = true unless params[:keep_alive] == false
|
282
|
+
params[:pw_fallback] = true unless params[:pw_fallback] == false
|
283
|
+
params[:verify_cert] = true unless params[:verify_cert] == false
|
284
|
+
end
|
285
|
+
|
286
|
+
# Raise execeptions if we don't have essential data for a new connection
|
287
|
+
# namely a host, user, and pw
|
288
|
+
#
|
289
|
+
# @param params[Hash] The params for #connect
|
290
|
+
#
|
291
|
+
# @return [void]
|
292
|
+
#
|
293
|
+
#######################################################
|
294
|
+
def verify_basic_params(params)
|
295
|
+
# must have a host, it could have come from a url, or a param
|
296
|
+
raise Windoo::MissingDataError, 'No :host specified in params or configuration.' unless params[:host]
|
297
|
+
|
298
|
+
# no need for user or pass if using a token string
|
299
|
+
# (tho a pw might be given)
|
300
|
+
return if params[:token].is_a? String
|
301
|
+
|
302
|
+
# must have user or token
|
303
|
+
unless params[:user]
|
304
|
+
raise Windoo::MissingDataError,
|
305
|
+
'No :user or :token specified in params or configuration.'
|
306
|
+
end
|
307
|
+
# but here, theres no token so must have a pw
|
308
|
+
raise Windoo::MissingDataError, "No :pw specified for user '#{params[:user]}'" unless params[:pw]
|
309
|
+
end
|
310
|
+
|
311
|
+
# create a token from a token string or a password
|
312
|
+
#######################################################
|
313
|
+
def create_token(params)
|
314
|
+
token_src = params[:token] ? :token_string : :pw
|
315
|
+
@token = token_from token_src, params
|
316
|
+
end
|
317
|
+
|
318
|
+
# given a token string or a password, get a valid token
|
319
|
+
# Token.new will raise an exception if the token string or
|
320
|
+
# credentials are invalid
|
321
|
+
#######################################################
|
322
|
+
def token_from(type, params)
|
323
|
+
token_params = {
|
324
|
+
base_url: params[:base_url],
|
325
|
+
user: params[:user],
|
326
|
+
timeout: params[:timeout],
|
327
|
+
keep_alive: params[:keep_alive],
|
328
|
+
pw_fallback: params[:pw_fallback],
|
329
|
+
ssl_version: params[:ssl_version],
|
330
|
+
verify_cert: params[:verify_cert],
|
331
|
+
pw: params[:pw]
|
332
|
+
}
|
333
|
+
token_params[:token_string] = params[:token] if type == :token_string
|
334
|
+
|
335
|
+
self.class::Token.new(**token_params)
|
336
|
+
end
|
337
|
+
|
338
|
+
# Build the base URL for the API connection
|
339
|
+
#
|
340
|
+
# @param args[Hash] The args for #connect
|
341
|
+
#
|
342
|
+
# @return [String] The URI encoded URL
|
343
|
+
#
|
344
|
+
#######################################################
|
345
|
+
def build_base_url(params)
|
346
|
+
params[:base_url] = +"#{HTTPS_SCHEME}://#{params[:host]}"
|
347
|
+
params[:base_url] << ":#{params[:port]}" if params[:port]
|
348
|
+
params[:base_url] << "/#{RSRC_VERSION}"
|
349
|
+
end
|
350
|
+
|
351
|
+
# From whatever was given in args[:pw], figure out the real password
|
352
|
+
#
|
353
|
+
# @param args[Hash] The args for #connect
|
354
|
+
#
|
355
|
+
# @return [String] The password for the connection
|
356
|
+
#
|
357
|
+
#######################################################
|
358
|
+
def prompt_for_password(params)
|
359
|
+
return unless params[:pw] == :prompt
|
360
|
+
|
361
|
+
user_display =
|
362
|
+
if params[:token]
|
363
|
+
'the user who generated the given token'
|
364
|
+
else
|
365
|
+
"TitleEditor user #{params[:user]}@#{params[:host]}"
|
366
|
+
end
|
367
|
+
|
368
|
+
message = "Enter the password for #{user_display}:"
|
369
|
+
begin
|
370
|
+
$stdin.reopen '/dev/tty' unless $stdin.tty?
|
371
|
+
$stderr.print "#{message} "
|
372
|
+
system '/bin/stty -echo'
|
373
|
+
pw = $stdin.gets.chomp("\n")
|
374
|
+
puts
|
375
|
+
ensure
|
376
|
+
system '/bin/stty echo'
|
377
|
+
end # begin
|
378
|
+
|
379
|
+
params[:pw] = pw
|
380
|
+
end
|
381
|
+
|
382
|
+
# @return [Faraday::Connection]
|
383
|
+
#######################################################
|
384
|
+
def create_connection
|
385
|
+
Faraday.new(@token.base_url, ssl: @token.ssl_options) do |cnx|
|
386
|
+
cnx.request :authorization, 'Bearer', -> { @token.token }
|
387
|
+
|
388
|
+
cnx.options[:timeout] = @timeout
|
389
|
+
cnx.options[:open_timeout] = @open_timeout
|
390
|
+
|
391
|
+
cnx.request :json
|
392
|
+
cnx.response :json, parser_options: { symbolize_names: true }
|
393
|
+
|
394
|
+
cnx.adapter :net_http
|
395
|
+
end # Faraday.new
|
396
|
+
end
|
397
|
+
|
398
|
+
end # module Connect
|
399
|
+
|
400
|
+
end # class Connection
|
401
|
+
|
402
|
+
end # module Windoo
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Copyright 2025 Pixar
|
2
|
+
#
|
3
|
+
# Licensed under the terms set forth in the LICENSE.txt file available at
|
4
|
+
# at the root of this project.
|
5
|
+
#
|
6
|
+
#
|
7
|
+
|
8
|
+
# frozen_string_literal: true
|
9
|
+
|
10
|
+
module Windoo
|
11
|
+
|
12
|
+
class Connection
|
13
|
+
|
14
|
+
# When using included modules to define constants,
|
15
|
+
# the constants have to be defined at the level where they will be
|
16
|
+
# referenced, or else they
|
17
|
+
# aren't available to other broken-out-and-included sub modules
|
18
|
+
#
|
19
|
+
# See https://cultivatehq.com/posts/ruby-constant-resolution/ for
|
20
|
+
# an explanation
|
21
|
+
|
22
|
+
HTTPS_SCHEME = 'https'
|
23
|
+
SSL_PORT = 443
|
24
|
+
DFT_SSL_VERSION = 'TLSv1_2'
|
25
|
+
|
26
|
+
DFT_OPEN_TIMEOUT = 60
|
27
|
+
DFT_TIMEOUT = 60
|
28
|
+
|
29
|
+
# the entire API is at this path
|
30
|
+
RSRC_VERSION = 'v2'
|
31
|
+
|
32
|
+
# Only these variables are displayed with PrettyPrint
|
33
|
+
# This avoids displaying lots of extraneous data
|
34
|
+
PP_VARS = %i[
|
35
|
+
@name
|
36
|
+
@connected
|
37
|
+
@open_timeout
|
38
|
+
@timeout
|
39
|
+
@connect_time
|
40
|
+
].freeze
|
41
|
+
|
42
|
+
# This module defines constants related to API connctions, used throughout
|
43
|
+
# the connection class and elsewhere.
|
44
|
+
##########################################
|
45
|
+
module Constants
|
46
|
+
|
47
|
+
def self.included(includer)
|
48
|
+
Windoo.verbose_include(includer, self)
|
49
|
+
end
|
50
|
+
|
51
|
+
end # module Constants
|
52
|
+
|
53
|
+
end # class Connection
|
54
|
+
|
55
|
+
end # module Windoo
|