Skip to content

Commit 5454e26

Browse files
authored
Merge branch 'master' into test_yabeda
2 parents d364f1e + 88c81c2 commit 5454e26

File tree

10 files changed

+126
-74
lines changed

10 files changed

+126
-74
lines changed

Gemfile.lock

+6-8
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ GEM
3030
minitest (>= 5.1)
3131
tzinfo (~> 2.0)
3232
zeitwerk (~> 2.3)
33-
addressable (2.8.6)
34-
public_suffix (>= 2.0.2, < 6.0)
33+
addressable (2.8.7)
34+
public_suffix (>= 2.0.2, < 7.0)
3535
anyway_config (2.6.4)
3636
ruby-next-core (~> 1.0)
3737
ast (2.4.2)
@@ -138,13 +138,13 @@ GEM
138138
guard (~> 2.1)
139139
guard-compat (~> 1.1)
140140
rspec (>= 2.99.0, < 4.0)
141-
hashdiff (1.1.0)
141+
hashdiff (1.1.1)
142142
hpricot (0.8.6)
143143
i18n (1.14.5)
144144
concurrent-ruby (~> 1.0)
145145
json (2.3.1)
146146
jsonapi-renderer (0.2.2)
147-
jwt (2.9.0)
147+
jwt (2.9.3)
148148
base64
149149
listen (3.6.0)
150150
rb-fsevent (~> 0.10, >= 0.10.3)
@@ -213,8 +213,7 @@ GEM
213213
responders (3.1.1)
214214
actionpack (>= 5.2)
215215
railties (>= 5.2)
216-
rexml (3.3.6)
217-
strscan
216+
rexml (3.3.9)
218217
ronn (0.7.3)
219218
hpricot (>= 0.8.2)
220219
mustache (>= 0.7.0)
@@ -301,7 +300,6 @@ GEM
301300
sqlite3 (1.4.4)
302301
strong_migrations (0.7.9)
303302
activerecord (>= 5)
304-
strscan (3.1.0)
305303
sync (0.5.0)
306304
term-ansicolor (1.7.1)
307305
tins (~> 1.0)
@@ -322,7 +320,7 @@ GEM
322320
activesupport (>= 3)
323321
railties (>= 3)
324322
yard (~> 0.9.20)
325-
webmock (3.23.1)
323+
webmock (3.24.0)
326324
addressable (>= 2.8.0)
327325
crack (>= 0.3.2)
328326
hashdiff (>= 0.4.0, < 2.0.0)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class ChangeLocalPathType < ActiveRecord::Migration[6.1]
2+
def up
3+
safety_assured do
4+
change_column :repositories, :local_path, :string, limit: 512
5+
change_column :downloaded_files, :local_path, :string, limit: 512
6+
end
7+
end
8+
9+
def down
10+
change_column :repositories, :local_path, :string
11+
change_column :downloaded_files, :local_path, :string
12+
end
13+
end

db/schema.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema.define(version: 2024_07_29_103525) do
13+
ActiveRecord::Schema.define(version: 2024_08_21_114908) do
1414

1515
create_table "activations", charset: "utf8", force: :cascade do |t|
1616
t.bigint "service_id", null: false
@@ -34,7 +34,7 @@
3434
create_table "downloaded_files", charset: "utf8", force: :cascade do |t|
3535
t.string "checksum_type"
3636
t.string "checksum"
37-
t.string "local_path"
37+
t.string "local_path", limit: 512
3838
t.bigint "file_size", unsigned: true
3939
t.index ["checksum_type", "checksum"], name: "index_downloaded_files_on_checksum_type_and_checksum"
4040
t.index ["local_path"], name: "index_downloaded_files_on_local_path", unique: true
@@ -104,7 +104,7 @@
104104
t.string "auth_token"
105105
t.boolean "installer_updates", default: false, null: false
106106
t.boolean "mirroring_enabled", default: false, null: false
107-
t.string "local_path", null: false
107+
t.string "local_path", limit: 512, null: false
108108
t.datetime "last_mirrored_at"
109109
t.string "friendly_id"
110110
t.index ["external_url"], name: "index_repositories_on_external_url", unique: true

engines/registry/app/models/access_scope.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ def allowed_paths(system = nil)
9494
}
9595
allowed_non_free_product_classes.each do |non_free_prod_class|
9696
activation_state = SccProxy.scc_check_subscription_expiration(
97-
auth_header, system.login, system.system_token, Rails.logger, system.proxy_byos_mode, non_free_prod_class
98-
)
97+
auth_header, system, non_free_prod_class
98+
)
9999
unless activation_state[:is_active]
100100
Rails.logger.info(
101101
"Access to #{non_free_prod_class} from system #{system.login} denied: #{activation_state[:message]}"

engines/registry/spec/app/models/access_scope_spec.rb

+2-10
Original file line numberDiff line numberDiff line change
@@ -215,22 +215,15 @@
215215
allow(SccProxy).to receive(:scc_check_subscription_expiration)
216216
.with(
217217
header_expected,
218-
system.login,
219-
system.system_token,
220-
Rails.logger,
221-
system.proxy_byos_mode,
218+
system,
222219
'SLES15-SP4-LTSS-X86'
223220
).and_return(scc_response)
224221
end
225222

226-
# rubocop:disable RSpec/ExampleLength
227223
it 'returns no actions allowed' do
228224
expect(SccProxy).to receive(:scc_check_subscription_expiration).with(
229225
header_expected,
230-
system.login,
231-
system.system_token,
232-
Rails.logger,
233-
system.proxy_byos_mode,
226+
system,
234227
'SLES15-SP4-LTSS-X86'
235228
)
236229
yaml_string = access_policy_content
@@ -250,7 +243,6 @@
250243
}
251244
)
252245
end
253-
# rubocop:enable RSpec/ExampleLength
254246
end
255247
end
256248

engines/registry/spec/requests/api/connect/v3/systems/activations_controller_spec.rb

+64-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
include_context 'version header', 3
44

55
describe '#activations' do
6-
let(:system) { FactoryBot.create(:system, :with_activated_product) }
6+
let(:system) { FactoryBot.create(:system, :payg, :with_activated_product) }
77
let(:headers) { auth_header.merge(version_header) }
88

99
context 'without valid repository cache' do
@@ -57,5 +57,68 @@
5757
expect(data[0]['service']['url']).to match(%r{^plugin:/susecloud})
5858
end
5959
end
60+
61+
context 'system is hybrid' do
62+
let(:system) { FactoryBot.create(:system, :hybrid, :with_activated_product) }
63+
let(:plugin_double) { instance_double('InstanceVerification::Providers::Example') }
64+
let(:cache_name) { "repo/cache/127.0.0.1-#{system.login}-#{system.products.first.id}" }
65+
let(:scc_systems_activations_url) { 'https://scc.suse.com/connect/systems/activations' }
66+
let(:body_active) do
67+
{
68+
id: 1,
69+
regcode: '631dc51f',
70+
name: 'Subscription 1',
71+
type: 'FULL',
72+
status: 'ACTIVE',
73+
starts_at: 'null',
74+
expires_at: '2014-03-14T13:10:21.164Z',
75+
system_limit: 6,
76+
systems_count: 1,
77+
service: {
78+
product: {
79+
id: system.activations.first.product.id
80+
}
81+
}
82+
}
83+
end
84+
85+
before do
86+
allow(InstanceVerification::Providers::Example).to receive(:new).and_return(plugin_double)
87+
88+
allow(plugin_double).to(
89+
receive(:instance_valid?).and_return(true)
90+
)
91+
allow(File).to receive(:join).and_call_original
92+
allow(InstanceVerification).to receive(:update_cache)
93+
allow(ZypperAuth).to receive(:verify_instance).and_call_original
94+
stub_request(:get, scc_systems_activations_url).to_return(status: 200, body: [body_active].to_json, headers: {})
95+
headers['X-Instance-Data'] = 'IMDS'
96+
end
97+
98+
context 'no registry' do
99+
it 'refreshes registry cache key only' do
100+
FileUtils.mkdir_p('repo/cache')
101+
expect(InstanceVerification).to receive(:update_cache).with('127.0.0.1', system.login, system.activations.first.product.id)
102+
get '/connect/systems/activations', headers: headers
103+
FileUtils.rm_rf('repo/cache')
104+
data = JSON.parse(response.body)
105+
expect(SccProxy).not_to receive(:product_path_access)
106+
expect(data[0]['service']['url']).to match(%r{^plugin:/susecloud})
107+
end
108+
end
109+
110+
context 'registry' do
111+
it 'refreshes registry cache key only' do
112+
FileUtils.mkdir_p('repo/cache')
113+
FileUtils.touch(cache_name)
114+
expect(InstanceVerification).to receive(:update_cache).with('127.0.0.1', system.login, nil, registry: true)
115+
get '/connect/systems/activations', headers: headers
116+
FileUtils.rm_rf('repo/cache')
117+
data = JSON.parse(response.body)
118+
expect(SccProxy).not_to receive(:product_path_access)
119+
expect(data[0]['service']['url']).to match(%r{^plugin:/susecloud})
120+
end
121+
end
122+
end
60123
end
61124
end

engines/scc_proxy/lib/scc_proxy/engine.rb

+23-26
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,19 @@ def parse_error(error_message, token = nil, email = nil)
201201
error_message
202202
end
203203

204-
def get_scc_activations(headers, system_token, mode)
204+
def get_scc_activations(headers, system)
205205
auth = headers['HTTP_AUTHORIZATION'] if headers && headers.include?('HTTP_AUTHORIZATION')
206206
uri = URI.parse(SYSTEMS_ACTIVATIONS_URL)
207207
http = Net::HTTP.new(uri.host, uri.port)
208208
http.use_ssl = true
209-
uri.query = URI.encode_www_form({ byos_mode: mode })
210-
scc_request = Net::HTTP::Get.new(uri.path, headers(auth, system_token))
211-
http.request(scc_request)
209+
uri.query = URI.encode_www_form({ byos_mode: system.proxy_byos_mode })
210+
scc_request = Net::HTTP::Get.new(uri.path, headers(auth, system.system_token))
211+
response = http.request(scc_request)
212+
unless response.code_type == Net::HTTPOK
213+
Rails.logger.info "Could not get the system (#{system.login}) activations, error: #{response.message} #{response.code}"
214+
raise ActionController::TranslatedError.new(response.body)
215+
end
216+
JSON.parse(response.body)
212217
end
213218

214219
def product_path_access(x_original_uri, products_ids)
@@ -236,11 +241,17 @@ def product_class_access(scc_systems_activations, product)
236241
end
237242
end
238243

244+
# rubocop:disable Metrics/PerceivedComplexity
239245
def activations_fail_state(scc_systems_activations, headers, product = nil)
240246
return SccProxy.product_class_access(scc_systems_activations, product) unless product.nil?
241247

242248
active_products_ids = scc_systems_activations.map { |act| act['service']['product']['id'] if act['status'].casecmp('active').zero? }.flatten
243249
x_original_uri = headers.fetch('X-Original-URI', '')
250+
# if there is no product info to compare the activations with
251+
# probably means the query is to refresh credentials
252+
# in any case, verification is true if ALL activations are ACTIVE
253+
return { is_active: (scc_systems_activations.length == active_products_ids.length) } if x_original_uri.empty?
254+
244255
if SccProxy.product_path_access(x_original_uri, active_products_ids)
245256
{ is_active: true }
246257
else
@@ -265,15 +276,10 @@ def activations_fail_state(scc_systems_activations, headers, product = nil)
265276
end
266277
end
267278
end
279+
# rubocop:enable Metrics/PerceivedComplexity
268280

269-
def scc_check_subscription_expiration(headers, login, system_token, logger, mode, product = nil) # rubocop:disable Metrics/ParameterLists
270-
response = SccProxy.get_scc_activations(headers, system_token, mode)
271-
unless response.code_type == Net::HTTPOK
272-
logger.info "Could not get the system (#{login}) activations, error: #{response.message} #{response.code}"
273-
response.message = SccProxy.parse_error(response.message) if response.message.include? 'json'
274-
return { is_active: false, message: response.message }
275-
end
276-
scc_systems_activations = JSON.parse(response.body)
281+
def scc_check_subscription_expiration(headers, system, product = nil)
282+
scc_systems_activations = SccProxy.get_scc_activations(headers, system)
277283
return { is_active: false, message: 'No activations.' } if scc_systems_activations.empty?
278284

279285
no_status_products_ids = scc_systems_activations.map do |act|
@@ -282,6 +288,8 @@ def scc_check_subscription_expiration(headers, login, system_token, logger, mode
282288
return { is_active: true } unless no_status_products_ids.all?(&:nil?)
283289

284290
SccProxy.activations_fail_state(scc_systems_activations, headers, product)
291+
rescue StandardError
292+
{ is_active: false, message: 'Could not check the activations from SCC' }
285293
end
286294

287295
def scc_upgrade(auth, product, system_login, mode, logger)
@@ -299,7 +307,6 @@ def scc_upgrade(auth, product, system_login, mode, logger)
299307
end
300308
end
301309

302-
# rubocop:disable Metrics/ClassLength
303310
class Engine < ::Rails::Engine
304311
isolate_namespace SccProxy
305312
config.generators.api_only = true
@@ -440,26 +447,17 @@ def scc_deactivate_product
440447
elsif @system.hybrid? && @product.extension?
441448
# check if product is on SCC and
442449
# if it is -> de-activate it
443-
scc_systems_activations = find_hybrid_activations_on_scc(request.headers)
444-
if scc_systems_activations.map { |act| act['service']['product']['id'] == @product.id }.present?
450+
scc_hybrid_system_activations = SccProxy.get_scc_activations(headers, @system)
451+
if scc_hybrid_system_activations.map { |act| act['service']['product']['id'] == @product.id }.present?
445452
# if product is found on SCC, regardless of the state
446453
# it is OK to remove it from SCC
447454
SccProxy.deactivate_product_scc(auth, @product, @system.system_token, logger)
448-
make_system_payg(auth) if scc_systems_activations.reject { |act| act['service']['product']['id'] == @product.id }.blank?
455+
make_system_payg(auth) if scc_hybrid_system_activations.reject { |act| act['service']['product']['id'] == @product.id }.blank?
449456
end
450457
end
451458
logger.info "Product '#{@product.friendly_name}' successfully deactivated from SCC"
452459
end
453460

454-
def find_hybrid_activations_on_scc(headers)
455-
response = SccProxy.get_scc_activations(headers, @system.system_token, @system.proxy_byos_mode)
456-
unless response.code_type == Net::HTTPOK
457-
logger.info "Could not get the system (#{@system.login}) activations, error: #{response.message} #{response.code}"
458-
raise ActionController::TranslatedError.new(response.body)
459-
end
460-
JSON.parse(response.body)
461-
end
462-
463461
def make_system_payg(auth)
464462
# if the system does not have more products activated on SCC
465463
# switch it back to payg
@@ -542,6 +540,5 @@ def get_system(systems)
542540
end
543541
end
544542
end
545-
# rubocop:enable Metrics/ClassLength
546543
end
547544
# rubocop:enable Metrics/ModuleLength

engines/zypper_auth/lib/zypper_auth/engine.rb

+8-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ def auth_logger
66
Thread.current[:logger]
77
end
88

9-
def verify_instance(request, logger, system)
9+
def verify_instance(request, logger, system, params_product_id = nil)
1010
return false unless request.headers['X-Instance-Data']
1111

1212
instance_data = Base64.decode64(request.headers['X-Instance-Data'].to_s)
@@ -31,12 +31,13 @@ def verify_instance(request, logger, system)
3131
)
3232

3333
is_valid = verification_provider.instance_valid?
34-
return false if is_valid && system.hybrid? && !handle_scc_subscription(request, system, verification_provider, logger)
34+
return false if is_valid && system.hybrid? && !handle_scc_subscription(request, system, verification_provider, params_product_id)
35+
3536
# update repository and registry cache
3637
InstanceVerification.update_cache(request.remote_ip, system.login, base_product.id)
3738
is_valid
3839
rescue InstanceVerification::Exception => e
39-
return handle_scc_subscription(request, system, verification_provider, logger) if system.byos?
40+
return handle_scc_subscription(request, system, verification_provider) if system.byos?
4041

4142
ZypperAuth.zypper_auth_message(request, system, verification_provider, e.message)
4243
false
@@ -49,8 +50,9 @@ def verify_instance(request, logger, system)
4950
false
5051
end
5152

52-
def handle_scc_subscription(request, system, verification_provider, logger)
53-
result = SccProxy.scc_check_subscription_expiration(request.headers, system.login, system.system_token, logger, system.proxy_byos_mode)
53+
def handle_scc_subscription(request, system, verification_provider, params_product_id = nil)
54+
product_class = Product.find_by(id: params_product_id).product_class if params_product_id.present?
55+
result = SccProxy.scc_check_subscription_expiration(request.headers, system, product_class)
5456
return true if result[:is_active]
5557

5658
ZypperAuth.zypper_auth_message(request, system, verification_provider, result[:message])
@@ -128,7 +130,7 @@ def make_repo_url(base_url, repo_local_path, service_name = nil)
128130
# additional validation for zypper service XML controller
129131
before_action :verify_instance
130132
def verify_instance
131-
unless ZypperAuth.verify_instance(request, logger, @system)
133+
unless ZypperAuth.verify_instance(request, logger, @system, params.fetch('id', nil))
132134
render(xml: { error: 'Instance verification failed' }, status: 403)
133135
end
134136
end

0 commit comments

Comments
 (0)