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

[PR-23132] Add the ability to store certificates in the database #1

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

pofystyy
Copy link

Specs will be added and modified after the review of the main logic.

@zhuravel zhuravel self-assigned this Nov 12, 2024
@@ -2,8 +2,11 @@ module Passkit
class Factory
class << self
# generator is an optional ActiveRecord object, the application data for the pass
def create_pass(pass_class, generator = nil)
pass = Passkit::Pass.create!(klass: pass_class, generator: generator)
def create_pass(pass_class, generator: nil, site_id: nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO everything site-related is part of Talkable-specific custom logic and should be in Talkable, not in gem.
Changes here should be more universal so that we could open source our changes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, but

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point is you should not hardcode site_id, not that you should not allow providing extra metadata. That can be some “options” hash that would not be tied to Talkable and would work for much more cases than this one and allow using our gem in other companies potentially.

class Passkit::CertificateSources::Factory
def self.find_source(site_id)
if Passkit.configuration.use_database_for_certificates
Passkit::CertificateSources::Database.new(site_id)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can built this source in Talkable and pass source (instead of site_id) as an argument.

Comment on lines +51 to 55
render json: {}, status: :ok unless @device

registrations = @pass.registrations.where(passkit_device_id: @device.id)
registrations.destroy_all
render json: {}, status: :ok
Copy link
Member

@zhuravel zhuravel Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double render happens here, you need to return after the first render, or better rewrite this to only have a single render.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initial changes

good point
will be fixed

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my bad.

@@ -9,12 +9,12 @@ def create

if @generator && @payload[:collection_name].present?
files = @generator.public_send(@payload[:collection_name]).collect do |collection_item|
Passkit::Factory.create_pass(@payload[:pass_class], collection_item)
Passkit::Factory.create_pass(@payload[:pass_class], generator: collection_item, site_id: params[:site_id])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we have some method that can be redefined in child controller, e.g. pass_attributes instead of using hardcoded site_id in gem? Don’t want gem to know anything about sites.

and update the configuration as follows:
```ruby
Passkit.configure do |config|
config.use_database_for_certificates = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This configuration is not needed if we make the gem accept certificate_source object and call #certificate and #password methods on it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

without this configuration, ENV variables will be checked each time

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why check ENV variables when certificate_source is passed?

@@ -0,0 +1,22 @@
class Passkit::CertificateSources::Database

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe worth of adding db migration as well, but optional.

private

def certificate_record
@certificate_record ||= Passkit::Certificate.find_by!(site_id: @site_id)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

definitely we shouldn't search here by site_id.

@@ -155,5 +158,9 @@ def compress_pass_file
end
zip_path
end

def certificate_source
@certificate_source ||= Passkit::CertificateSources::Factory.find_source(@pass.site_id)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here too, pass doesn't have site_id.

Comment on lines +51 to 55
render json: {}, status: :ok unless @device

registrations = @pass.registrations.where(passkit_device_id: @device.id)
registrations.destroy_all
render json: {}, status: :ok

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my bad.

@max-si-m
Copy link

max-si-m commented Nov 21, 2024

as an idea, we can pass intermediate and p12_certificate into as it looks like this is only the one place where certificate actually used

def sign_manifest
p12_certificate = OpenSSL::PKCS12.new(File.read(CERTIFICATE), CERTIFICATE_PASSWORD)
intermediate_certificate = OpenSSL::X509::Certificate.new(File.read(INTERMEDIATE_CERTIFICATE))
flag = OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY
signed = OpenSSL::PKCS7.sign(p12_certificate.certificate,
p12_certificate.key, File.read(@manifest_url),
[intermediate_certificate], flag)
@signature_url = @temporary_path.join("signature")
File.open(@signature_url, "w") { |f| f.syswrite signed.to_der }
end

how this can be used:

module Passkit
  class Factory
    class << self
      # generator is an optional ActiveRecord object, the application data for the pass
      def create_pass(pass_class, generator = nil, options = {})
        pass = Passkit::Pass.find_or_create_by!(klass: pass_class, generator: generator, **options)
        p12_certificate = OpenSSL::PKCS12.new(db.certificate, db.password)
        intermediate_certificate = OpenSSL::X509::Certificate.new(File.read(INTERMEDIATE_CERTIFICATE))

        Passkit::Generator.new(pass).generate_and_sign(p12_certificate, intermediate_certificate)
      end
    end
  end
end

feel free to reach out and set up a quick call to with me and @zhuravel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants