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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES.md +9 -0
  3. data/LICENSE.txt +177 -0
  4. data/README.md +222 -0
  5. data/lib/windoo/base_classes/array_manager.rb +335 -0
  6. data/lib/windoo/base_classes/criteria_manager.rb +327 -0
  7. data/lib/windoo/base_classes/criterion.rb +226 -0
  8. data/lib/windoo/base_classes/json_object.rb +472 -0
  9. data/lib/windoo/configuration.rb +221 -0
  10. data/lib/windoo/connection/actions.rb +152 -0
  11. data/lib/windoo/connection/attributes.rb +156 -0
  12. data/lib/windoo/connection/connect.rb +402 -0
  13. data/lib/windoo/connection/constants.rb +55 -0
  14. data/lib/windoo/connection/token.rb +489 -0
  15. data/lib/windoo/connection.rb +92 -0
  16. data/lib/windoo/converters.rb +31 -0
  17. data/lib/windoo/exceptions.rb +34 -0
  18. data/lib/windoo/mixins/api_collection.rb +408 -0
  19. data/lib/windoo/mixins/constants.rb +43 -0
  20. data/lib/windoo/mixins/default_connection.rb +75 -0
  21. data/lib/windoo/mixins/immutable.rb +34 -0
  22. data/lib/windoo/mixins/loading.rb +38 -0
  23. data/lib/windoo/mixins/patch/component.rb +102 -0
  24. data/lib/windoo/mixins/software_title/extension_attribute.rb +106 -0
  25. data/lib/windoo/mixins/utility.rb +23 -0
  26. data/lib/windoo/objects/capability.rb +82 -0
  27. data/lib/windoo/objects/capability_manager.rb +52 -0
  28. data/lib/windoo/objects/component.rb +99 -0
  29. data/lib/windoo/objects/component_criteria_manager.rb +26 -0
  30. data/lib/windoo/objects/component_criterion.rb +66 -0
  31. data/lib/windoo/objects/extension_attribute.rb +149 -0
  32. data/lib/windoo/objects/kill_app.rb +92 -0
  33. data/lib/windoo/objects/kill_app_manager.rb +89 -0
  34. data/lib/windoo/objects/patch.rb +235 -0
  35. data/lib/windoo/objects/patch_manager.rb +240 -0
  36. data/lib/windoo/objects/requirement.rb +85 -0
  37. data/lib/windoo/objects/requirement_manager.rb +52 -0
  38. data/lib/windoo/objects/software_title.rb +407 -0
  39. data/lib/windoo/validate.rb +548 -0
  40. data/lib/windoo/version.rb +15 -0
  41. data/lib/windoo/zeitwerk_config.rb +158 -0
  42. data/lib/windoo.rb +56 -0
  43. 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