passkit 0.8.3 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ba766112a286c8570851338476fee913b5eff4575cfc2fc6eeb91fb2617e5b0
4
- data.tar.gz: 7b28c2c662b071529705a5fa409da39d42442eb702547a25ad960369d0c8500d
3
+ metadata.gz: b483356d4bcfb7898d15381b4ac43899fbc8b147965e6579110c562a19bf0a21
4
+ data.tar.gz: d529619e1ebdd0ae04943b0fe07f1bc0c43df6f976218ed8c472ad50da1dcf99
5
5
  SHA512:
6
- metadata.gz: 53782d8dd386090a3fda13fa70ebf39a82d06afe3f00fd83cc54845cdeb59b5f0d0a414fe4e151e773451b4d18a3331720acfbd0ebd2e7eb6238effb1b8ab719
7
- data.tar.gz: f37f52f4b3f105e1a5dde4c2613ffa35372d2c5734b8da28e3551f84bf978742c4d1a257ee380c46fa950fbba04e9f22fc37c67dfa05de7149c0c902735d91f7
6
+ metadata.gz: bee3e475d2beaed1f9305613893c9aee7cf9cbfee86e9ae95f46348f0d0e0a13f82b9428147f5132f5793d07c551b892104f3f9fac7478d50924183e03c344cb
7
+ data.tar.gz: 8e614ada638617a10ac3ff807f842e5ceef98d17048a283571809562872cd256ac2de7a05ae9f154eb6e8baf4874c50d03692f73d0719dc41d5d6aedd2932ca0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## [0.9.0]
2
+ - [#50](https://github.com/coorasse/passkit/pull/50): **Breaking:** require rubyzip 3.4 or later, which fixes CVE-2026-85396, and Ruby 3.2 or later.
3
+ - [#47](https://github.com/coorasse/passkit/pull/47): Remove the generator's working directory once the `.pkpass` is built, and delete generated archives after sending them, so `tmp/passkit` no longer grows with every download.
4
+ - [#48](https://github.com/coorasse/passkit/pull/48): Answer `304 Not Modified` before generating and signing the pass when `If-Modified-Since` shows the device already has the current version.
5
+
1
6
  ## [0.8.3]
2
7
  - Exclude development and test files from the packaged gem.
3
8
 
data/README.md CHANGED
@@ -173,6 +173,11 @@ Passkit::UrlGenerator.new(Passkit::UserTicket, User.find(1), :tickets)
173
173
  and then use `.android` or `.ios` to get the URL to serve the Wallet Pass.
174
174
  Again, check the example mailer included in the gem to see how to use it.
175
175
 
176
+ If you generate passes yourself with `Passkit::Factory.create_pass` or `Passkit::Generator#generate_and_sign`
177
+ instead of going through the bundled controllers, note that the returned `.pkpass` lives in `tmp/passkit`.
178
+ The bundled controllers delete it once it has been sent; if you serve it yourself, do the same, otherwise the
179
+ folder grows by one archive per download.
180
+
176
181
  ## Debug issues
177
182
 
178
183
  * On Mac, you can open the *.pkpass files with "Pass Viewer". Open the `Console.app` to log possible error messages and filter by "Pass Viewer" process.
@@ -0,0 +1,15 @@
1
+ module Passkit
2
+ # Passes are generated into tmp/passkit. Send the archive's contents and
3
+ # delete the file, so the folder does not grow by one archive per download.
4
+ module PassDelivery
5
+ extend ActiveSupport::Concern
6
+
7
+ private
8
+
9
+ def send_pass_file(path, type:, filename: File.basename(path))
10
+ send_data File.binread(path), type: type, disposition: "attachment", filename: filename
11
+ ensure
12
+ FileUtils.rm_f(path)
13
+ end
14
+ end
15
+ end
@@ -2,6 +2,8 @@ module Passkit
2
2
  module Api
3
3
  module V1
4
4
  class PassesController < ActionController::API
5
+ include Passkit::PassDelivery
6
+
5
7
  before_action :decrypt_payload, only: :create
6
8
 
7
9
  def create
@@ -11,11 +13,15 @@ module Passkit
11
13
  files = @generator.public_send(@payload[:collection_name]).collect do |collection_item|
12
14
  Passkit::Factory.create_pass(@payload[:pass_class], collection_item)
13
15
  end
14
- file = Passkit::Generator.compress_passes_files(files)
15
- send_file(file, type: "application/vnd.apple.pkpasses", disposition: "attachment")
16
+ begin
17
+ bundle = Passkit::Generator.compress_passes_files(files)
18
+ ensure
19
+ files.each { |file| FileUtils.rm_f(file) }
20
+ end
21
+ send_pass_file(bundle, type: "application/vnd.apple.pkpasses")
16
22
  else
17
23
  file = Passkit::Factory.create_pass(@payload[:pass_class], @generator)
18
- send_file(file, type: "application/vnd.apple.pkpass", disposition: "attachment")
24
+ send_pass_file(file, type: "application/vnd.apple.pkpass")
19
25
  end
20
26
  end
21
27
 
@@ -35,19 +41,24 @@ module Passkit
35
41
  return
36
42
  end
37
43
 
38
- pass_output_path = Passkit::Generator.new(pass).generate_and_sign
39
-
40
44
  response.headers["last-modified"] = pass.last_update.httpdate
41
- if request.headers["If-Modified-Since"].nil? ||
42
- (pass.last_update.to_i > Time.zone.parse(request.headers["If-Modified-Since"]).to_i)
43
- send_file(pass_output_path, type: "application/vnd.apple.pkpass", disposition: "attachment")
44
- else
45
- head :not_modified
46
- end
45
+ return head :not_modified unless pass_modified_since?(pass, request.headers["If-Modified-Since"])
46
+
47
+ pass_output_path = Passkit::Generator.new(pass).generate_and_sign
48
+ send_pass_file(pass_output_path, type: "application/vnd.apple.pkpass")
47
49
  end
48
50
 
49
51
  private
50
52
 
53
+ # Wallet sends back the Last-Modified it received with the previous
54
+ # download. When the pass has not changed since, there is nothing to
55
+ # generate or sign.
56
+ def pass_modified_since?(pass, if_modified_since)
57
+ return true if if_modified_since.nil?
58
+
59
+ pass.last_update.to_i > Time.zone.parse(if_modified_since).to_i
60
+ end
61
+
51
62
  def decrypt_payload
52
63
  @payload = Passkit::UrlEncrypt.decrypt(params[:payload])
53
64
  if DateTime.parse(@payload[:valid_until]).past?
@@ -1,6 +1,8 @@
1
1
  module Passkit
2
2
  module Dashboard
3
3
  class PreviewsController < ApplicationController
4
+ include Passkit::PassDelivery
5
+
4
6
  def index
5
7
  end
6
8
 
@@ -8,7 +10,7 @@ module Passkit
8
10
  builder = Passkit.configuration.available_passes[params[:class_name]]
9
11
  path = Factory.create_pass(params[:class_name].constantize, builder.call)
10
12
 
11
- send_file path, type: "application/vnd.apple.pkpass", disposition: "attachment", filename: "pass.pkpass"
13
+ send_pass_file path, type: "application/vnd.apple.pkpass", filename: "pass.pkpass"
12
14
  end
13
15
  end
14
16
  end
@@ -21,17 +21,15 @@ module Passkit
21
21
  generate_json_manifest
22
22
  sign_manifest
23
23
  compress_pass_file
24
+ ensure
25
+ remove_temporary_directory
24
26
  end
25
27
 
26
28
  def self.compress_passes_files(files)
27
29
  zip_path = TMP_FOLDER.join("#{SecureRandom.uuid}.pkpasses")
28
- zipped_file = File.open(zip_path, "w")
29
30
 
30
- Zip::OutputStream.open(zipped_file.path) do |z|
31
- files.each do |file|
32
- z.put_next_entry(File.basename(file))
33
- z.print File.read(file)
34
- end
31
+ Zip::File.open(zip_path, create: true) do |zip|
32
+ files.each { |file| zip.add(File.basename(file), file) }
35
33
  end
36
34
  zip_path
37
35
  end
@@ -49,6 +47,12 @@ module Passkit
49
47
  FileUtils.rm_rf(@temporary_path) if File.directory?(@temporary_path)
50
48
  end
51
49
 
50
+ # Only the .pkpass is returned to the caller; the unpacked files it was
51
+ # built from have no further use once it exists.
52
+ def remove_temporary_directory
53
+ FileUtils.rm_rf(@temporary_path) if @temporary_path
54
+ end
55
+
52
56
  def copy_pass_to_tmp_location
53
57
  FileUtils.cp_r(@pass.pass_path, @temporary_path)
54
58
  end
@@ -148,13 +152,12 @@ module Passkit
148
152
 
149
153
  def compress_pass_file
150
154
  zip_path = TMP_FOLDER.join("#{@pass.file_name}.pkpass")
151
- zipped_file = File.open(zip_path, "w")
155
+ FileUtils.rm_f(zip_path)
152
156
 
153
- Zip::OutputStream.open(zipped_file.path) do |z|
154
- Dir.glob(@temporary_path.join("**")).each do |file|
155
- z.put_next_entry(File.basename(file))
156
- z.print File.read(file)
157
- end
157
+ # Entries added from files have a known size, so rubyzip writes a plain zip without the Zip64 extra fields
158
+ # it adds to streamed entries since 3.0.
159
+ Zip::File.open(zip_path, create: true) do |zip|
160
+ Dir.glob(@temporary_path.join("**")).each { |file| zip.add(File.basename(file), file) }
158
161
  end
159
162
  zip_path
160
163
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Passkit
4
- VERSION = "0.8.3"
4
+ VERSION = "0.9.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: passkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.3
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alessandro Rodi
@@ -29,14 +29,14 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '2.0'
32
+ version: '3.4'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '2.0'
39
+ version: '3.4'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: sqlite3
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -156,6 +156,7 @@ files:
156
156
  - app/assets/images/passkit/add_to_walletpass_en.png
157
157
  - app/assets/images/passkit/add_to_walletpass_fr.png
158
158
  - app/assets/images/passkit/add_to_walletpass_it.png
159
+ - app/controllers/concerns/passkit/pass_delivery.rb
159
160
  - app/controllers/passkit/api/v1/logs_controller.rb
160
161
  - app/controllers/passkit/api/v1/passes_controller.rb
161
162
  - app/controllers/passkit/api/v1/registrations_controller.rb
@@ -214,14 +215,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
214
215
  requirements:
215
216
  - - ">="
216
217
  - !ruby/object:Gem::Version
217
- version: 2.6.0
218
+ version: 3.2.0
218
219
  required_rubygems_version: !ruby/object:Gem::Requirement
219
220
  requirements:
220
221
  - - ">="
221
222
  - !ruby/object:Gem::Version
222
223
  version: '0'
223
224
  requirements: []
224
- rubygems_version: 4.0.9
225
+ rubygems_version: 4.0.17
225
226
  specification_version: 4
226
227
  summary: Serve Wallet Passes for iOS and Android directly from your Rails app
227
228
  test_files: []