chef-licensing 0.7.5 → 1.0.2

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: cf3d6793cef07a261141a45f8c500260a8590a1d60c7fb77076c9c802d42e1e3
4
+ data.tar.gz: 482f0db3a900d2ff6871f667331f0b38bd94acde43e6e3fb1fc8de0c450900d1
5
5
  SHA512:
6
- metadata.gz: 873ad985ccfc6cf42478d5e0deceb001d85f6ed74d6b62b0bb9355db66da9ad7f707f5a3d1f2aa2cece612f688321757d8c406c331c8e8ab543d1c12fbe4b794
7
- data.tar.gz: 155632f6ced920a84a6700c58bad75534e34c85fbf10b4f262b5cea030fecb291de9e429e41b4108b17e97e6f3ba744bc8cefbb1b15729a43bf89e75a0e8656f
6
+ metadata.gz: da0e21e823c1a3683df7943eb83b0784889de7b8fa2c05d269ca42758c45536bdf3aab9aac88b872baeb13521dab568f1070aefec084fbd20489eb8d37881791
7
+ data.tar.gz: d4a1a3b6acd7553186d771238a3930bcc967a7c51a57231afe15820b73ca2788d684fca25c67ae6aaad3dba43247d28a2a463454286d7e4600396067dc8f353c
@@ -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,21 @@ 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
141
+ else
142
+ new_keys = []
136
143
  end
137
144
 
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
145
+ # Expired trial licenses and exhausted free licenses will be blocked
146
+ # Not blocking commercial licenses
147
+ license ||= ChefLicensing::Context.license
148
+ if new_keys.empty? && license && ((!license.expired? && !license.exhausted?) || (license.license_type.downcase == "commercial"))
141
149
  return @license_keys
142
150
  end
143
151
 
@@ -198,6 +206,7 @@ module ChefLicensing
198
206
  extra_info[:license_type] = license.license_type.capitalize
199
207
  extra_info[:number_of_days_in_expiration] = license.number_of_days_in_expiration
200
208
  extra_info[:license_expiration_date] = Date.parse(license.expiration_date).strftime("%a, %d %b %Y")
209
+ extra_info[:is_commercial] = license.license_type.downcase == "commercial"
201
210
  end
202
211
 
203
212
  unless info.empty? # ability to add info hash through arguments
@@ -215,6 +224,9 @@ module ChefLicensing
215
224
  # This call returns a license based on client logic
216
225
  # This API call is only made when multiple license keys are present or if client call was never done
217
226
  self.license = ChefLicensing.client(license_keys: @license_keys) if !license || @license_keys.count > 1
227
+
228
+ # Cache license context
229
+ ChefLicensing::Context.license = license
218
230
  # Intentional lag of 2 seconds when license is expiring or expired
219
231
  sleep 2 if license.expiring_or_expired?
220
232
  spinner.success # Stop the spinner
@@ -230,7 +242,13 @@ module ChefLicensing
230
242
  config[:start_interaction] = :prompt_license_about_to_expire
231
243
  prompt_fetcher.config = config
232
244
  false
245
+ elsif license.exhausted? && (license.license_type.downcase == "commercial" || license.license_type.downcase == "free")
246
+ config[:start_interaction] = :prompt_license_exhausted
247
+ prompt_fetcher.config = config
248
+ false
233
249
  else
250
+ # If license is not expired or expiring, return true. But if the license is not commercial, warn the user.
251
+ config[:start_interaction] = :warn_non_commercial_license unless license.license_type.downcase == "commercial"
234
252
  true
235
253
  end
236
254
  rescue ChefLicensing::ClientError => e
@@ -274,8 +292,8 @@ module ChefLicensing
274
292
  end
275
293
 
276
294
  def get_license_type(license_key)
277
- self.license = ChefLicensing.client(license_keys: [license_key])
278
- license.license_type.downcase.to_sym
295
+ license_obj = ChefLicensing.client(license_keys: [license_key])
296
+ license_obj.license_type.downcase.to_sym
279
297
  end
280
298
 
281
299
  def license_restricted?(license_type)
@@ -300,16 +318,22 @@ module ChefLicensing
300
318
  # However, if user is trying to add Free Tier License, and user has active trial license, we fetch the trial license key
301
319
  if license_type == :free && file_fetcher.user_has_active_trial_license?
302
320
  existing_license_keys_in_file = file_fetcher.fetch_license_keys_based_on_type(:trial)
303
- else
321
+ elsif file_fetcher.user_has_active_trial_or_free_license?
322
+ # Handling license addition restriction scenarios only if the current license is an active license
304
323
  existing_license_keys_in_file = file_fetcher.fetch_license_keys_based_on_type(license_type)
305
324
  end
325
+
306
326
  # 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
327
+ if existing_license_keys_in_file
328
+ unless existing_license_keys_in_file.last == new_keys.first
329
+ # prompt the message that this addition of license is restricted.
330
+ prompt_license_addition_restricted(license_type, existing_license_keys_in_file)
331
+ return false
332
+ end
311
333
  end
312
- true # license type is restricted but not the key since it is the same key hence returning true
334
+ # license addition should be restricted but it is not because the key is same as that of one user is trying to add
335
+ # license addition should be restricted but it is not because the license is expired and warning wont be handled by this restriction
336
+ true
313
337
  else
314
338
  persist_and_concat(new_keys, license_type)
315
339
  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.2".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.2
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-23 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