Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move touched attributes into license model #927

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions app/models/license.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ class License < ApplicationRecord
before_create :autogenerate_key, if: -> { key.nil? && policy.present? }
before_create :crypt_key, if: -> { scheme? && !legacy_encrypted? }

# Once a license has been persisted, reset the temporary changes stored in memory
# for last_validated_attributes
# @see License#set_last_validated_attributes
after_commit :reset_last_validated_attributes_updates

# Licenses automatically inherit their owner's group ID. We're using before_validation
# instead of before_create so that this can be run when the owner is changed as well,
# and so that we can keep our group limit validations in play.
Expand Down Expand Up @@ -885,10 +890,42 @@ def transfer!(new_policy)
save!
end

# Set the last validated attributes for a license. This is used to store
# temporary changes to the license that are not persisted to the database
# until the license has been validated.
# @param [Hash] attrs_hash A hash of attributes to set
def set_last_validated_attributes(**attrs_hash)
last_validated_attributes_updates.merge!(attrs_hash)
end

# Persist the last validated attributes for a license to the database. Updates are persisted
# asynchronously to prevent blocking the request thread
#
def persist_last_validated_attributes!
return if last_validated_attributes_updates.empty?
# Attempt to store touches in database
TouchLicenseWorker.perform_async(id, last_validated_attributes_updates.as_json)
# Store in-memory for returning up-to-date information in the response body
assign_attributes(**last_validated_attributes_updates)
end

# The updates to the last validated attributes intended to be persisted to the database
# @return [Hash]
# @see License#set_last_validated_attributes
def last_validated_attributes_updates
@last_validated_attributes_updates ||= reset_last_validated_attributes_updates
end

private

attr_accessor :seed_key

attr_writer :last_validated_attributes_updates

def reset_last_validated_attributes_updates
self.last_validated_attributes_updates = {}
end

def default_seed_key
case scheme
when "RSA_2048_PKCS1_ENCRYPT"
Expand Down
23 changes: 7 additions & 16 deletions app/services/license_validation_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ def initialize(license:, scope: nil, skip_touch: false)
@license = license
@scope = scope
@skip_touch = skip_touch
@touches = {
last_validated_at: Time.current,
}
self.license.set_last_validated_attributes(last_validated_at: Time.current)
end

def call
Expand All @@ -24,8 +22,7 @@ def call
attr_reader :account,
:product,
:license,
:scope,
:touches
:scope

def skip_touch? = !!@skip_touch

Expand Down Expand Up @@ -230,8 +227,8 @@ def validate!
return [false, "checksum scope is not valid (does not match any accessible artifacts)", :CHECKSUM_SCOPE_MISMATCH]
end

touches[:last_validated_checksum] = checksum
touches[:last_validated_version] = artifact.version
license.set_last_validated_attributes(last_validated_checksum: checksum,
last_validated_version: artifact.version)
else
return [false, "checksum scope is required", :CHECKSUM_SCOPE_REQUIRED] if license.policy.require_checksum_scope?
end
Expand All @@ -247,8 +244,7 @@ def validate!
if release.nil?
return [false, "version scope is not valid (does not match any accessible releases)", :VERSION_SCOPE_MISMATCH]
end

touches[:last_validated_version] = release.version
license.set_last_validated_attributes(last_validated_version: release.version)
else
return [false, "version scope is required", :VERSION_SCOPE_REQUIRED] if license.policy.require_version_scope?
end
Expand Down Expand Up @@ -418,13 +414,8 @@ def validate!
end

def touch!
return if
skip_touch? || license.nil? || touches.empty?

# Attempt to store touches in database
TouchLicenseWorker.perform_async(license.id, touches.as_json)
return if skip_touch? || license.nil?

# Store in-memory for response
license.assign_attributes(**touches)
license.persist_last_validated_attributes!
end
end
62 changes: 62 additions & 0 deletions spec/models/license_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -347,4 +347,66 @@
expect(License.with_status(:banned).count).to eq 3
end
end

describe '#set_last_validated_attributes' do
it 'sets #last_validated_attributes_updates' do
license = License.new
license.set_last_validated_attributes(foo:"bar")
expect(license.last_validated_attributes_updates).to include(foo:"bar")
end

it 'updates last_validated_attributes_updates' do
license = License.new
license.set_last_validated_attributes(key:"value")
license.set_last_validated_attributes(key:"buzz", bar:"baz")

expect(license.last_validated_attributes_updates).to eq(key:"buzz", bar:"baz")
end
end

describe '#last_validated_attributes_updates' do
it 'defaults to empty hash' do
license = License.new
expect(license.last_validated_attributes_updates).to eql({})
end

it 'resets to empty hash after persisting' do
license = create(:license, account:)
license.set_last_validated_attributes(key:"value")
license.save!

expect(license.last_validated_attributes_updates).to eql({})
end
end

describe '#persist_last_validated_attributes!' do
it 'persists the changes on the database' do
freeze_time do
timestamp = 1.day.ago
license = create(:license, account:)
license.set_last_validated_attributes(last_validated_at:timestamp)

Sidekiq::Testing.inline! do
license.persist_last_validated_attributes!

license.reload
expect(license.last_validated_at).to eql(timestamp)
end
end
end

it 'updates the attributes in memory' do
freeze_time do
timestamp = 1.day.ago
license = create(:license, account:)
license.set_last_validated_attributes(last_validated_at:timestamp)

Sidekiq::Testing.inline! do
license.persist_last_validated_attributes!

expect(license.last_validated_at).to eql(timestamp)
end
end
end
end
end