diff --git a/README.md b/README.md index da56f66..c5ce00d 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ If you followed the installation steps, you already saw that Passkit provides you the tables and ActiveRecord models, and also an engine with the necessary APIs already implemented. Now is your turn. Before proceeding, you need to set these ENV variables: + * `PASSKIT_WEB_SERVICE_HOST` * `PASSKIT_CERTIFICATE_KEY` * `PASSKIT_PRIVATE_P12_CERTIFICATE` @@ -68,6 +69,26 @@ Now is your turn. Before proceeding, you need to set these ENV variables: We have a [specific guide on how to get all these](docs/passkit_environment_variables.md), please follow it. You cannot start using this library without these variables set, and we cannot do the work for you. +Alternatively, you can configure passkit with an initializer, where you can use environment variables, Rails secrets, +or any other source for the required credentials: + +```ruby +Passkit.configure do |config| + # Required, no defaults + config.apple_team_identifier = "dummy ID" + config.certificate_key = "dummy key" + config.private_p12_certificate = "path/to/file" + config.apple_intermediate_certificate = "path/to/file" + config.pass_type_identifier = "pass.com.some.id" + + # Optional, defaults shown + config.dashboard_username = nil + config.dashboard_password = nil + config.skip_verification = false # Unless true, throws exceptions on startup when a required configuration is missing + config.web_service_host = "https://localhost:3000" + config.available_passes = { "Passkit::ExampleStoreCard" => -> {} } +end + ## Usage If you followed the installation steps and you have the ENV variables set, we can start looking at what is provided for you. diff --git a/lib/passkit.rb b/lib/passkit.rb index 0358c44..e2639e7 100644 --- a/lib/passkit.rb +++ b/lib/passkit.rb @@ -18,6 +18,11 @@ class << self def self.configure self.configuration ||= Configuration.new yield(configuration) if block_given? + configuration.verify! + end + + def self.configured? + self.configuration&.configured? end class Configuration @@ -27,11 +32,24 @@ class Configuration :private_p12_certificate, :apple_intermediate_certificate, :apple_team_identifier, - :pass_type_identifier + :pass_type_identifier, + :dashboard_username, + :dashboard_password, + :format_version, + :skip_verification + + REQUIRED_ATTRIBUTES = %i[ + web_service_host + certificate_key + private_p12_certificate + apple_intermediate_certificate + apple_team_identifier + pass_type_identifier + ] DEFAULT_AUTHENTICATION = proc do authenticate_or_request_with_http_basic("Passkit Dashboard. Login required") do |username, password| - username == ENV["PASSKIT_DASHBOARD_USERNAME"] && password == ENV["PASSKIT_DASHBOARD_PASSWORD"] + username == Passkit.configuration.dashboard_username && password == Passkit.configuration.dashboard_password end end def authenticate_dashboard_with(&block) @@ -40,14 +58,34 @@ def authenticate_dashboard_with(&block) end def initialize - @available_passes = {"Passkit::ExampleStoreCard" => -> {}} - @web_service_host = ENV["PASSKIT_WEB_SERVICE_HOST"] || (raise "Please set PASSKIT_WEB_SERVICE_HOST") - raise("PASSKIT_WEB_SERVICE_HOST must start with https://") unless @web_service_host.start_with?("https://") - @certificate_key = ENV["PASSKIT_CERTIFICATE_KEY"] || (raise "Please set PASSKIT_CERTIFICATE_KEY") - @private_p12_certificate = ENV["PASSKIT_PRIVATE_P12_CERTIFICATE"] || (raise "Please set PASSKIT_PRIVATE_P12_CERTIFICATE") - @apple_intermediate_certificate = ENV["PASSKIT_APPLE_INTERMEDIATE_CERTIFICATE"] || (raise "Please set PASSKIT_APPLE_INTERMEDIATE_CERTIFICATE") - @apple_team_identifier = ENV["PASSKIT_APPLE_TEAM_IDENTIFIER"] || (raise "Please set PASSKIT_APPLE_TEAM_IDENTIFIER") - @pass_type_identifier = ENV["PASSKIT_PASS_TYPE_IDENTIFIER"] || (raise "Please set PASSKIT_PASS_TYPE_IDENTIFIER") + # Required + @certificate_key = ENV["PASSKIT_CERTIFICATE_KEY"] + @private_p12_certificate = ENV["PASSKIT_PRIVATE_P12_CERTIFICATE"] + @apple_intermediate_certificate = ENV["PASSKIT_APPLE_INTERMEDIATE_CERTIFICATE"] + @apple_team_identifier = ENV["PASSKIT_APPLE_TEAM_IDENTIFIER"] + @pass_type_identifier = ENV["PASSKIT_PASS_TYPE_IDENTIFIER"] + + # Optional + @skip_verification = false + @web_service_host = ENV["PASSKIT_WEB_SERVICE_HOST"] || "https://localhost:3000" + @available_passes = { "Passkit::ExampleStoreCard" => -> {} } + @format_version = ENV["PASSKIT_FORMAT_VERSION"] || 1 + @dashboard_username = ENV["PASSKIT_DASHBOARD_USERNAME"] + @dashboard_password = ENV["PASSKIT_DASHBOARD_PASSWORD"] + end + + def configured? + REQUIRED_ATTRIBUTES.all? { |attr| send(attr).present? } + end + + def verify! + return if skip_verification + + REQUIRED_ATTRIBUTES.each do |attr| + raise Error, "Please set #{attr.upcase}" unless send(attr).present? + end + + raise Error, "PASSKIT_WEB_SERVICE_HOST must start with https://" unless web_service_host.start_with?("https://") end end end diff --git a/lib/passkit/base_pass.rb b/lib/passkit/base_pass.rb index 0d2c834..9ae6be9 100644 --- a/lib/passkit/base_pass.rb +++ b/lib/passkit/base_pass.rb @@ -5,15 +5,15 @@ def initialize(generator = nil) end def format_version - ENV["PASSKIT_FORMAT_VERSION"] || 1 + Passkit.configuration.format_version end def apple_team_identifier - ENV["PASSKIT_APPLE_TEAM_IDENTIFIER"] || raise(Error.new("Missing environment variable: PASSKIT_APPLE_TEAM_IDENTIFIER")) + Passkit.configuration.apple_team_identifier end def pass_type_identifier - ENV["PASSKIT_PASS_TYPE_IDENTIFIER"] || raise(Error.new("Missing environment variable: PASSKIT_PASS_TYPE_IDENTIFIER")) + Passkit.configuration.pass_type_identifier end def language @@ -43,8 +43,7 @@ def pass_type end def web_service_url - raise Error.new("Missing environment variable: PASSKIT_WEB_SERVICE_HOST") unless ENV["PASSKIT_WEB_SERVICE_HOST"] - "#{ENV["PASSKIT_WEB_SERVICE_HOST"]}/passkit/api" + "#{Passkit.configuration.web_service_host}/passkit/api" end # The foreground color, used for the values of fields shown on the front of the pass. diff --git a/lib/passkit/generator.rb b/lib/passkit/generator.rb index fa812a1..2853e93 100644 --- a/lib/passkit/generator.rb +++ b/lib/passkit/generator.rb @@ -111,8 +111,6 @@ def generate_json_pass File.write(@temporary_path.join("pass.json"), pass.to_json) end - # rubocop:enable Metrics/AbcSize - def generate_json_manifest manifest = {} Dir.glob(@temporary_path.join("**")).each do |file| @@ -123,14 +121,18 @@ def generate_json_manifest File.write(@manifest_url, manifest.to_json) end - CERTIFICATE = Rails.root.join(ENV["PASSKIT_PRIVATE_P12_CERTIFICATE"]) - INTERMEDIATE_CERTIFICATE = Rails.root.join(ENV["PASSKIT_APPLE_INTERMEDIATE_CERTIFICATE"]) - CERTIFICATE_PASSWORD = ENV["PASSKIT_CERTIFICATE_KEY"] + def certificate_path + Rails.root.join(Passkit.configuration.private_p12_certificate) + end + + def intermediate_certificate_path + Rails.root.join(Passkit.configuration.apple_intermediate_certificate) + end # :nocov: def sign_manifest - p12_certificate = OpenSSL::PKCS12.new(File.read(CERTIFICATE), CERTIFICATE_PASSWORD) - intermediate_certificate = OpenSSL::X509::Certificate.new(File.read(INTERMEDIATE_CERTIFICATE)) + p12_certificate = OpenSSL::PKCS12.new(File.read(certificate_path), Passkit.configuration.certificate_key) + intermediate_certificate = OpenSSL::X509::Certificate.new(File.read(intermediate_certificate_path)) flag = OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY signed = OpenSSL::PKCS7.sign(p12_certificate.certificate, diff --git a/lib/passkit/url_generator.rb b/lib/passkit/url_generator.rb index 9a970af..9240f40 100644 --- a/lib/passkit/url_generator.rb +++ b/lib/passkit/url_generator.rb @@ -3,7 +3,7 @@ class UrlGenerator include Passkit::Engine.routes.url_helpers def initialize(pass_class, generator = nil, collection_name = nil) - @url = passes_api_url(host: ENV["PASSKIT_WEB_SERVICE_HOST"], + @url = passes_api_url(host: Passkit.configuration.web_service_host, payload: PayloadGenerator.encrypted(pass_class, generator, collection_name)) end