ostinelli / apnotic

A Ruby APNs HTTP/2 gem able to provide instant feedback.
MIT License
477 stars 95 forks source link

Allow cert to be the actual certificate string #120

Closed natematykiewicz closed 2 years ago

natematykiewicz commented 2 years ago

We use Rails credentials. I'd like to store my certificate in the credentials store like:

apns:
  team_id: 1234
  key_id: 5678
  certificate: |-
    -----BEGIN PRIVATE KEY-----
    Key content goes here
    -----END PRIVATE KEY-----

And then configure Apnotic like:

Apnotic::ConnectionPool.new({
  auth_method: :token,
  cert: Rails.application.credentials.dig(:apns, :certificate),
  team_id: Rails.application.credentials.dig(:apns, :team_id),
  key_id: Rails.application.credentials.dig(:apns, :key_id),
}, size: 5) do |connection|
  connection.on(:error) { |exception| puts "Exception has been raised: #{exception}" }
end

This would mean that our certificate is never written to disk in plain text at all. But also, it means the certificate can be included in the repo with the rest of our credentials, because they're encrypted.

Would you be open to a feature that added cert as an option? I think it's as easy as allowing cert to be specified in the options hash, and then setting @certificate = options[:cert] in the initializer. Then, the actual certificate method won't reassign it, because it does ||=.

natematykiewicz commented 2 years ago

Okay, so you also need to adjust the check on this line to not raise if a cert has been passed in.

Here's an initializer hack I put together that makes this work. The StringIO is just something that's truthy and responds to :read so that it doesn't raise "Cert file not found".

Apnotic::Connection.prepend(Module.new do
  def initialize(options = {})
    @certificate = options[:cert]
    options = options.merge(cert_path: StringIO.new) if @certificate

    super(options)
  end
end)
benubois commented 2 years ago

Hi @natematykiewicz,

I think what you're describing should already be possible. From the readme:

Note that since :cert_path can be any object that responds to :read, it is possible to pass in a certificate string directly by wrapping it up in a StringIO object

With rails credentials this would look like:

cert = StringIO.new(Rails.application.credentials.dig(:apns, :certificate))
Apnotic::Connection.new(cert_path: cert)
natematykiewicz commented 2 years ago

I don't know how I overlooked this. I literally mentioned StringIO and everything. 🤦🏻‍♂️ Thanks for the explanation!