chef-licensing 0.7.5 → 1.0.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1bc6490da547bbded96f9eae129f488d1be7178feae66dc02dad8acba71fff0b
4
- data.tar.gz: 16f191bd6102f08be159bb73c88f1e992657e829248b54ecdd0c5743982f65d2
3
+ metadata.gz: 5e5b5b44e654ed1893f1301c716881eb494bcfe486284269523e45e648f6efd9
4
+ data.tar.gz: bce944cdc127668e1b89ab4dde0aa1d11574a37c7958d5b3af6a33101b806ec1
5
5
  SHA512:
6
- metadata.gz: 873ad985ccfc6cf42478d5e0deceb001d85f6ed74d6b62b0bb9355db66da9ad7f707f5a3d1f2aa2cece612f688321757d8c406c331c8e8ab543d1c12fbe4b794
7
- data.tar.gz: 155632f6ced920a84a6700c58bad75534e34c85fbf10b4f262b5cea030fecb291de9e429e41b4108b17e97e6f3ba744bc8cefbb1b15729a43bf89e75a0e8656f
6
+ metadata.gz: 777458fc88ca4e0349697ca50c6da364dd966d15e72c1e5122b504ff8446437827665bd7e0390801e39b6b0d0e826a0c385d434597805459caf26df2f833eba2
7
+ data.tar.gz: cc705ff5a9d5a8f626e74169bb683fd16db9928782226be465669521d2d0a688d644947625cd6ad931ab1fc53a386799aa4e0033296beefa53832361a2956443
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.summary = %q{Chef License storage, generation, and entitlement}
13
13
  spec.description = %q{Ruby library to support CLI tools that use Progress Chef license storage, generation, and entitlement.}
14
14
  spec.homepage = "https://github.com/chef/chef-licensing"
15
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
15
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.1.0")
16
16
 
17
17
  spec.metadata["homepage_uri"] = spec.homepage
18
18
  spec.metadata["source_code_uri"] = "https://github.com/chef/chef-licensing"
@@ -13,7 +13,7 @@ require_relative "licensing_service/local"
13
13
  module ChefLicensing
14
14
  class Config
15
15
  class << self
16
- attr_writer :license_server_url, :logger, :output, :license_server_url_check_in_file
16
+ attr_writer :license_server_url, :logger, :output, :license_server_url_check_in_file, :license_add_command, :license_list_command
17
17
 
18
18
  # is_local_license_service is used by context class
19
19
  attr_accessor :is_local_license_service, :chef_entitlement_id, :chef_product_name, :chef_executable_name
@@ -39,6 +39,14 @@ module ChefLicensing
39
39
  def output
40
40
  @output ||= STDOUT
41
41
  end
42
+
43
+ def license_add_command
44
+ @license_add_command ||= "license add"
45
+ end
46
+
47
+ def license_list_command
48
+ @license_list_command ||= "license list"
49
+ end
42
50
  end
43
51
  end
44
52
  end
@@ -25,9 +25,20 @@ module ChefLicensing
25
25
  current_context(options).license_keys
26
26
  end
27
27
 
28
+ # Return license context object via current context
29
+ def license(options = {})
30
+ current_context(options).license
31
+ end
32
+
33
+ # Set license context object via current context
34
+ # Example: ChefLicensing::Context.license = license_context_object
35
+ def license=(license)
36
+ current_context.license = license
37
+ end
38
+
28
39
  private
29
40
 
30
- def current_context(options)
41
+ def current_context(options = {})
31
42
  return @current_context if @current_context
32
43
 
33
44
  @current_context = context_based_on_state(options)
@@ -63,6 +74,16 @@ module ChefLicensing
63
74
  @state.license_keys
64
75
  end
65
76
 
77
+ # Get license context from local or global state
78
+ def license
79
+ @state.license
80
+ end
81
+
82
+ # Set license context from local or global state
83
+ def license=(license)
84
+ @state.license = license
85
+ end
86
+
66
87
  class State
67
88
  attr_accessor :context, :options
68
89
 
@@ -70,6 +91,20 @@ module ChefLicensing
70
91
  def license_keys
71
92
  raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
72
93
  end
94
+
95
+ # Get license context in local or global state
96
+ def license
97
+ if license_keys.empty?
98
+ @license ||= nil
99
+ else
100
+ @license ||= ChefLicensing.client(license_keys: license_keys)
101
+ end
102
+ end
103
+
104
+ # Set license context in local or global state
105
+ def license=(license)
106
+ @license ||= license
107
+ end
73
108
  end
74
109
 
75
110
  # Implement various behaviors, associated with a state of the Context.
@@ -0,0 +1,9 @@
1
+ require_relative "error"
2
+
3
+ module ChefLicensing
4
+ class UnsupportedContentType < Error
5
+ def message
6
+ super || "Unsupported content type"
7
+ end
8
+ end
9
+ end
@@ -133,6 +133,10 @@ module ChefLicensing
133
133
  status.eql?("Expired")
134
134
  end
135
135
 
136
+ def exhausted?
137
+ status.eql?("Exhausted")
138
+ end
139
+
136
140
  def active?
137
141
  status.eql?("Active")
138
142
  end
@@ -148,4 +152,4 @@ module ChefLicensing
148
152
  status.eql?("Active") && expiration_status.eql?("Expired") && ((1..7).include? number_of_days_in_expiration)
149
153
  end
150
154
  end
151
- end
155
+ end
@@ -69,20 +69,20 @@ interactions:
69
69
 
70
70
  trial_restriction_message:
71
71
  prompt_type: "say"
72
- messages:
73
- Please generate a Free Tier or Commercial License by running <%= input[:pastel].bold("#{ChefLicensing::Config.chef_executable_name} license add")%>.
72
+ messages: |
73
+ Please generate a Free Tier or Commercial License by running <%= input[:pastel].bold("#{ChefLicensing::Config.chef_executable_name} #{ChefLicensing::Config.license_add_command}")%>.
74
74
  paths: [exit_with_message]
75
75
 
76
76
  free_restriction_message:
77
77
  prompt_type: "say"
78
- messages:
79
- Please generate a Trial or Commercial License by running <%= input[:pastel].bold("#{ChefLicensing::Config.chef_executable_name} license add")%>.
78
+ messages: |
79
+ Please generate a Trial or Commercial License by running <%= input[:pastel].bold("#{ChefLicensing::Config.chef_executable_name} #{ChefLicensing::Config.license_add_command}")%>.
80
80
  paths: [exit_with_message]
81
81
 
82
82
  only_commercial_allowed_message:
83
83
  prompt_type: "say"
84
- messages:
85
- Please generate a Commercial License by running <%= input[:pastel].bold("#{ChefLicensing::Config.chef_executable_name} license add")%>.
84
+ messages: |
85
+ Please generate a Commercial License by running <%= input[:pastel].bold("#{ChefLicensing::Config.chef_executable_name} #{ChefLicensing::Config.license_add_command}")%>.
86
86
  paths: [exit_with_message]
87
87
 
88
88
  prompt_license_expired:
@@ -90,11 +90,13 @@ interactions:
90
90
  ------------------------------------------------------------
91
91
  <%= input[:pastel].yellow("! [WARNING]")%> <%= input[:license_type] %> License Expired
92
92
 
93
- Get a Commercial License to receive bug fixes, updates
94
- and new features.
95
- Get a Free Tier License to scan limited <%= input[:unit_measure] %>.
93
+ We hope you've been enjoying Chef <%= input[:chef_product_name] %>!
94
+ However, it seems like your license has expired.
96
95
 
97
- To get a new license, run <%= input[:pastel].bold("#{ChefLicensing::Config.chef_executable_name} license add")%>
96
+ Reach out to our sales team at <%= input[:pastel].underline.green("chef-sales@progress.com")%>
97
+ to move to commercial tier.
98
+
99
+ To get a new license, run <%= input[:pastel].bold("#{ChefLicensing::Config.chef_executable_name} #{ChefLicensing::Config.license_add_command}") %>
98
100
  and select a license type.
99
101
  ------------------------------------------------------------
100
102
  prompt_type: "say"
@@ -105,9 +107,11 @@ interactions:
105
107
  ------------------------------------------------------------
106
108
  <%= input[:pastel].yellow("! [WARNING]")%> <%= input[:license_type] %> License Expired
107
109
 
108
- Get a Commercial License to receive bug fixes, updates
109
- and new features.
110
- Get a Free Tier License to scan limited <%= input[:unit_measure] %>.
110
+ We hope you've been enjoying Chef <%= input[:chef_product_name] %>!
111
+ However, it seems like your license has expired.
112
+
113
+ Reach out to our sales team at <%= input[:pastel].underline.green("chef-sales@progress.com")%>
114
+ to move to commercial tier.
111
115
  ------------------------------------------------------------
112
116
  prompt_type: "say"
113
117
  paths: [fetch_license_id]
@@ -195,12 +199,13 @@ interactions:
195
199
 
196
200
  validate_license_expiration:
197
201
  action: license_expiration_status?
198
- paths: [validation_success, prompt_license_about_to_expire, prompt_license_expired, prompt_license_expired_local_mode]
202
+ paths: [validation_success, prompt_license_about_to_expire, prompt_license_expired, prompt_license_expired_local_mode, prompt_license_exhausted]
199
203
  response_path_map:
200
204
  "active": validation_success
201
205
  "about_to_expire": prompt_license_about_to_expire
202
206
  "expired": prompt_license_expired
203
207
  "expired_in_local_mode": prompt_license_expired_local_mode
208
+ "exhausted_license": prompt_license_exhausted
204
209
 
205
210
  validation_success:
206
211
  messages: "✔ [Success] License validated successfully."
@@ -332,8 +337,50 @@ interactions:
332
337
 
333
338
  fetch_license_id:
334
339
  action: fetch_license_id
340
+ paths: [is_commercial_license]
341
+
342
+ is_commercial_license:
343
+ action: is_commercial_license?
344
+ response_path_map:
345
+ "true": exit
346
+ "false": warn_non_commercial_license
347
+ paths: [warn_non_commercial_license, exit]
348
+
349
+ warn_non_commercial_license:
350
+ messages: |
351
+ ------------------------------------------------------------
352
+ <%= input[:pastel].yellow("! [WARNING]")%> Non-Commercial License
353
+
354
+ You are using a <%= input[:license_type].downcase == "free" ? "free tier" : "trial" %> version - not meant for commercial usage.
355
+
356
+ If you are using it for commercial purposes, please reach
357
+ out to our sales team at <%= input[:pastel].underline.green("chef-sales@progress.com")%> to get
358
+ commercial license to be compliant with Progress Chef MLSA.
359
+ ------------------------------------------------------------
360
+ prompt_type: "say"
335
361
  paths: [exit]
336
362
 
363
+ prompt_license_exhausted:
364
+ messages: |
365
+ ------------------------------------------------------------
366
+ <%= input[:pastel].yellow("! [WARNING]")%> <%= input[:license_type].capitalize %> License Exhausted
367
+
368
+ We hope you've been enjoying Chef <%= input[:chef_product_name] %>!
369
+ However, it seems like you have exceeded your entitled usage limit on <%= input[:unit_measure] %>.
370
+
371
+ Reach out to our sales team at <%= input[:pastel].underline.green("chef-sales@progress.com")%>
372
+ to move to commercial-tier.
373
+ ------------------------------------------------------------
374
+ prompt_type: "say"
375
+ paths: [is_run_allowed_on_license_exhausted]
376
+
377
+ is_run_allowed_on_license_exhausted:
378
+ action: is_run_allowed_on_license_exhausted?
379
+ response_path_map:
380
+ "true": fetch_license_id
381
+ "false": exit
382
+ paths: [fetch_license_id, exit]
383
+
337
384
  exit:
338
385
  messages: ""
339
386
 
@@ -67,6 +67,15 @@ module ChefLicensing
67
67
  @active_trial_status
68
68
  end
69
69
 
70
+ def user_has_active_trial_or_free_license?
71
+ read_license_key_file
72
+ return false unless contents&.key?(:licenses)
73
+
74
+ all_license_keys = contents[:licenses].collect { |license| license[:license_key] }
75
+ license_obj = ChefLicensing.client(license_keys: all_license_keys)
76
+ (%w{trial free}.include? license_obj.license_type&.downcase) && license_obj.active?
77
+ end
78
+
70
79
  def fetch_allowed_license_types_for_addition
71
80
  license_types = %i{free trial commercial}
72
81
  existing_license_types = fetch_license_types
@@ -85,9 +85,9 @@ module ChefLicensing
85
85
  end
86
86
  end
87
87
 
88
- # Scenario: When a user is prompted for license expiry and license is not yet renewed
89
- if %i{prompt_license_about_to_expire prompt_license_expired_local_mode}.include?(config[:start_interaction])
90
- # Not blocking any license type in case of expiry
88
+ # Expired trial licenses and exhausted free licenses will be blocked
89
+ # Not blocking commercial licenses
90
+ if license && ((!license.expired? && !license.exhausted?) || (license.license_type.downcase == "commercial"))
91
91
  return @license_keys
92
92
  end
93
93
 
@@ -119,7 +119,7 @@ module ChefLicensing
119
119
  # Return keys if there is any error in /client API call, and do not block the flow.
120
120
  # Client API possible errors will be handled in software entitlement check call (made after this)
121
121
  # client_api_call_error is set to true when there is an error in licenses_active? call
122
- return @license_keys if (!@license_keys.empty? && licenses_active?) || client_api_call_error
122
+ return @license_keys if (!@license_keys.empty? && licenses_active? && ChefLicensing::Context.license.license_type.downcase == "commercial") || client_api_call_error
123
123
 
124
124
  # Lowest priority is to interactively prompt if we have a TTY
125
125
  if config[:output].isatty
@@ -131,13 +131,19 @@ module ChefLicensing
131
131
  # If license type is not selected using TUI, assign it using API call to fetch type.
132
132
  prompt_fetcher.license_type ||= get_license_type(new_keys.first)
133
133
  persist_and_concat(new_keys, prompt_fetcher.license_type)
134
- return license_keys
134
+ license ||= ChefLicensing::Context.license
135
+ # Expired trial licenses and exhausted free licenses will be blocked
136
+ # Not blocking commercial licenses
137
+ if (!license&.expired? && !license&.exhausted?) || (license&.license_type&.downcase == "commercial")
138
+ return license_keys
139
+ end
135
140
  end
136
141
  end
137
142
 
138
- # Scenario: When a user is prompted for license expiry and license is not yet renewed
139
- if new_keys.empty? && %i{prompt_license_about_to_expire prompt_license_expired}.include?(config[:start_interaction])
140
- # Not blocking any license type in case of expiry
143
+ # Expired trial licenses and exhausted free licenses will be blocked
144
+ # Not blocking commercial licenses
145
+ license ||= ChefLicensing::Context.license
146
+ if new_keys.empty? && license && ((!license.expired? && !license.exhausted?) || (license.license_type.downcase == "commercial"))
141
147
  return @license_keys
142
148
  end
143
149
 
@@ -198,6 +204,7 @@ module ChefLicensing
198
204
  extra_info[:license_type] = license.license_type.capitalize
199
205
  extra_info[:number_of_days_in_expiration] = license.number_of_days_in_expiration
200
206
  extra_info[:license_expiration_date] = Date.parse(license.expiration_date).strftime("%a, %d %b %Y")
207
+ extra_info[:is_commercial] = license.license_type.downcase == "commercial"
201
208
  end
202
209
 
203
210
  unless info.empty? # ability to add info hash through arguments
@@ -215,6 +222,9 @@ module ChefLicensing
215
222
  # This call returns a license based on client logic
216
223
  # This API call is only made when multiple license keys are present or if client call was never done
217
224
  self.license = ChefLicensing.client(license_keys: @license_keys) if !license || @license_keys.count > 1
225
+
226
+ # Cache license context
227
+ ChefLicensing::Context.license = license
218
228
  # Intentional lag of 2 seconds when license is expiring or expired
219
229
  sleep 2 if license.expiring_or_expired?
220
230
  spinner.success # Stop the spinner
@@ -230,7 +240,13 @@ module ChefLicensing
230
240
  config[:start_interaction] = :prompt_license_about_to_expire
231
241
  prompt_fetcher.config = config
232
242
  false
243
+ elsif license.exhausted? && (license.license_type.downcase == "commercial" || license.license_type.downcase == "free")
244
+ config[:start_interaction] = :prompt_license_exhausted
245
+ prompt_fetcher.config = config
246
+ false
233
247
  else
248
+ # If license is not expired or expiring, return true. But if the license is not commercial, warn the user.
249
+ config[:start_interaction] = :warn_non_commercial_license unless license.license_type.downcase == "commercial"
234
250
  true
235
251
  end
236
252
  rescue ChefLicensing::ClientError => e
@@ -274,8 +290,8 @@ module ChefLicensing
274
290
  end
275
291
 
276
292
  def get_license_type(license_key)
277
- self.license = ChefLicensing.client(license_keys: [license_key])
278
- license.license_type.downcase.to_sym
293
+ license_obj = ChefLicensing.client(license_keys: [license_key])
294
+ license_obj.license_type.downcase.to_sym
279
295
  end
280
296
 
281
297
  def license_restricted?(license_type)
@@ -300,16 +316,22 @@ module ChefLicensing
300
316
  # However, if user is trying to add Free Tier License, and user has active trial license, we fetch the trial license key
301
317
  if license_type == :free && file_fetcher.user_has_active_trial_license?
302
318
  existing_license_keys_in_file = file_fetcher.fetch_license_keys_based_on_type(:trial)
303
- else
319
+ elsif file_fetcher.user_has_active_trial_or_free_license?
320
+ # Handling license addition restriction scenarios only if the current license is an active license
304
321
  existing_license_keys_in_file = file_fetcher.fetch_license_keys_based_on_type(license_type)
305
322
  end
323
+
306
324
  # Only prompt when a new trial license is added
307
- unless existing_license_keys_in_file.last == new_keys.first
308
- # prompt the message that this addition of license is restricted.
309
- prompt_license_addition_restricted(license_type, existing_license_keys_in_file)
310
- return false
325
+ if existing_license_keys_in_file
326
+ unless existing_license_keys_in_file.last == new_keys.first
327
+ # prompt the message that this addition of license is restricted.
328
+ prompt_license_addition_restricted(license_type, existing_license_keys_in_file)
329
+ return false
330
+ end
311
331
  end
312
- true # license type is restricted but not the key since it is the same key hence returning true
332
+ # license addition should be restricted but it is not because the key is same as that of one user is trying to add
333
+ # license addition should be restricted but it is not because the license is expired and warning wont be handled by this restriction
334
+ true
313
335
  else
314
336
  persist_and_concat(new_keys, license_type)
315
337
  true
@@ -7,6 +7,7 @@ require_relative "../exceptions/restful_client_connection_error"
7
7
  require_relative "../exceptions/missing_api_credentials_error"
8
8
  require_relative "../config"
9
9
  require_relative "middleware/exceptions_handler"
10
+ require_relative "middleware/content_type_validator"
10
11
 
11
12
  module ChefLicensing
12
13
  module RestfulClient
@@ -137,6 +138,7 @@ module ChefLicensing
137
138
  config.response :json, parser_options: { object_class: OpenStruct }
138
139
  config.use Faraday::HttpCache, shared_cache: false, logger: logger, store: store
139
140
  config.use Middleware::ExceptionsHandler
141
+ config.use Middleware::ContentTypeValidator
140
142
  config.adapter Faraday.default_adapter
141
143
  end
142
144
  end
@@ -0,0 +1,24 @@
1
+ require "faraday/middleware"
2
+ require_relative "../../../chef-licensing/exceptions/unsupported_content_type"
3
+
4
+ module Middleware
5
+ class ContentTypeValidator < Faraday::Middleware
6
+ def call(env)
7
+ @app.call(env).on_complete do |response_env|
8
+ content_type = response_env[:response_headers]["content-type"]
9
+ body = response_env[:body]
10
+ # trim the body to 1000 characters to avoid printing a huge string in the error message
11
+ body = body[0..1000] if body.is_a?(String)
12
+ raise ChefLicensing::UnsupportedContentType, error_message(content_type, body) unless content_type == "application/json"
13
+ end
14
+ end
15
+
16
+ def error_message(content_type, body = nil)
17
+ <<~EOM
18
+ Expected 'application/json' content-type, but received '#{content_type}' from the licensing server.
19
+ Snippet of body: `#{body}`
20
+ Possible causes: Check for firewall restrictions, ensure proper server response, or seek support assistance.
21
+ EOM
22
+ end
23
+ end
24
+ end
@@ -43,6 +43,9 @@ module ChefLicensing
43
43
  def is_license_allowed?(input)
44
44
  client_api_call(license_id)
45
45
  self.license_type = get_license_type
46
+ # When user enters the license via TUI, we need to check if the license type is commercial to display warnings.
47
+ input[:is_commercial] = license_type == :commercial ? true : false
48
+ input[:license_type] = license_type
46
49
  if license_restricted?(license_type)
47
50
  # Existing license keys needs to be fetcher to show details of existing license of license type which is restricted.
48
51
  # However, if user is trying to add Free Tier License, and user has active trial license, we fetch the trial license key
@@ -66,15 +69,26 @@ module ChefLicensing
66
69
  input[:license_expiration_date] = Date.parse(license.expiration_date).strftime("%a, %d %b %Y")
67
70
  input[:number_of_days_in_expiration] = license.number_of_days_in_expiration
68
71
  "about_to_expire"
72
+ elsif license.exhausted? && (license.license_type.downcase == "commercial" || license.license_type.downcase == "free")
73
+ input[:license_type] = license.license_type
74
+ "exhausted_license"
69
75
  else
70
76
  "active"
71
77
  end
72
78
  end
73
79
 
80
+ def is_run_allowed_on_license_exhausted?(input)
81
+ input[:license_type].downcase == "commercial"
82
+ end
83
+
74
84
  def fetch_license_id(input)
75
85
  license_id
76
86
  end
77
87
 
88
+ def is_commercial_license?(input)
89
+ input[:is_commercial]
90
+ end
91
+
78
92
  def fetch_invalid_license_msg(input)
79
93
  invalid_license_msg
80
94
  end
@@ -1,3 +1,3 @@
1
1
  module ChefLicensing
2
- VERSION = "0.7.5".freeze
2
+ VERSION = "1.0.0".freeze
3
3
  end
@@ -8,6 +8,7 @@ require "chef-licensing/exceptions/software_not_entitled"
8
8
  require "chef-licensing/exceptions/client_error"
9
9
  require "chef-licensing/api/client"
10
10
  require "chef-licensing/license_key_fetcher/prompt"
11
+ require "chef-licensing/context"
11
12
 
12
13
  module ChefLicensing
13
14
  class << self
@@ -66,5 +67,9 @@ module ChefLicensing
66
67
  def add_license
67
68
  ChefLicensing::LicenseKeyFetcher.add_license
68
69
  end
70
+
71
+ def license_context
72
+ ChefLicensing::Context.license
73
+ end
69
74
  end
70
75
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-licensing
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.5
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Inspec Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-10-10 00:00:00.000000000 Z
11
+ date: 2024-07-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef-config
@@ -141,6 +141,7 @@ files:
141
141
  - lib/chef-licensing/exceptions/restful_client_connection_error.rb
142
142
  - lib/chef-licensing/exceptions/restful_client_error.rb
143
143
  - lib/chef-licensing/exceptions/software_not_entitled.rb
144
+ - lib/chef-licensing/exceptions/unsupported_content_type.rb
144
145
  - lib/chef-licensing/license.rb
145
146
  - lib/chef-licensing/license_key_fetcher.rb
146
147
  - lib/chef-licensing/license_key_fetcher/base.rb
@@ -154,6 +155,7 @@ files:
154
155
  - lib/chef-licensing/licensing_service/local.rb
155
156
  - lib/chef-licensing/list_license_keys.rb
156
157
  - lib/chef-licensing/restful_client/base.rb
158
+ - lib/chef-licensing/restful_client/middleware/content_type_validator.rb
157
159
  - lib/chef-licensing/restful_client/middleware/exceptions_handler.rb
158
160
  - lib/chef-licensing/restful_client/v1.rb
159
161
  - lib/chef-licensing/tui_engine.rb
@@ -178,14 +180,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
178
180
  requirements:
179
181
  - - ">="
180
182
  - !ruby/object:Gem::Version
181
- version: 2.3.0
183
+ version: 3.1.0
182
184
  required_rubygems_version: !ruby/object:Gem::Requirement
183
185
  requirements:
184
186
  - - ">="
185
187
  - !ruby/object:Gem::Version
186
188
  version: '0'
187
189
  requirements: []
188
- rubygems_version: 3.1.4
190
+ rubygems_version: 3.2.3
189
191
  signing_key:
190
192
  specification_version: 4
191
193
  summary: Chef License storage, generation, and entitlement