passbook-ruby 0.0.5

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.
@@ -0,0 +1,24 @@
1
+
2
+ # Copyright 2012 Xtreme Labs
3
+
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+
18
+
19
+ module Passbook
20
+ class Registration < ActiveRecord::Base
21
+ set_table_name "passbook_registrations"
22
+ attr_accessible :device_id, :pass_type_id, :push_token, :serial_number, :uuid
23
+ end
24
+ end
@@ -0,0 +1,36 @@
1
+
2
+ # Copyright 2012 Xtreme Labs
3
+
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+
18
+
19
+ if Passbook::Config.instance.enable_routes
20
+ Rails.application.routes.draw do
21
+ get "/v1/passes/:pass_type_id/:serial_number" => "Passbook::passes#get_pass", :constraints => { :pass_type_id => /[^\/]+/ }
22
+
23
+ post "/v1/devices/:device_id/registrations/:pass_type_id/:serial_number" => "Passbook::registrations#create", :constraints => { :pass_type_id => /[^\/]+/ }
24
+ delete "/v1/devices/:device_id/registrations/:pass_type_id/:serial_number" => "Passbook::registrations#delete", :constraints => { :pass_type_id => /[^\/]+/ }
25
+ get "/v1/devices/:device_id/registrations/:pass_type_id" =>"Passbook::registrations#updatable", :constraints => { :pass_type_id => /[^\/]+/ }
26
+
27
+ post "/v1/log" => "Passbook::logs#log"
28
+ end
29
+ end
30
+
31
+
32
+
33
+
34
+
35
+
36
+
@@ -0,0 +1,45 @@
1
+
2
+ # Copyright 2012 Xtreme Labs
3
+
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+
18
+
19
+ require "passbook/config"
20
+ require "passbook/pkpass"
21
+ require "passbook/engine"
22
+
23
+ require "action_controller"
24
+ Mime::Type.register 'application/vnd.apple.pkpass', :pkpass
25
+
26
+ ActionController::Renderers.add :pkpass do |obj, options|
27
+ pkpass = Passbook::Pkpass.new Passbook.class_name_to_pass_type_id(obj.class.to_s), obj.serial_number
28
+ obj.update_pass pkpass
29
+ pkpass_io = pkpass.package
30
+ response.headers["last-modified"] = obj.updated_at.strftime("%Y-%m-%d %H:%M:%S")
31
+ send_data(pkpass_io.sysread, :type => 'application/vnd.apple.pkpass', :disposition=>'inline', :filename=>"#{obj.serial_number}.pkpass")
32
+ end
33
+
34
+ module Passbook
35
+ def self.pass_type_id_to_class pass_type_id
36
+ Passbook::Config.instance.pass_config[pass_type_id]['class'].constantize
37
+ end
38
+
39
+ def self.class_name_to_pass_type_id class_name
40
+ Passbook::Config.instance.pass_config.each do |pass_type_id, config|
41
+ return pass_type_id if config['class']==class_name
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,74 @@
1
+
2
+ # Copyright 2012 Xtreme Labs
3
+
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+
18
+
19
+ require 'fileutils'
20
+ require 'singleton'
21
+
22
+ module Passbook
23
+ class Config
24
+ include Singleton
25
+ attr_accessor :pass_config, :wwdr_intermediate_certificate_path, :wwdr_certificate, :preload_template, :enable_routes
26
+
27
+ def configure
28
+ self.pass_config = Hash.new unless self.pass_config
29
+ yield self
30
+ read_wwdr_certificate
31
+ end
32
+
33
+ def add_pkpass
34
+ self.pass_config = Hash.new unless self.pass_config
35
+ yield self
36
+ self.preload_template ||= ::Rails.env.production? if defined? ::Rails
37
+ read_templates if self.preload_template
38
+ read_p12_certificates
39
+ end
40
+
41
+ def read_templates
42
+ self.pass_config.each do |pass_type_id, config|
43
+ raise(ArgumentError, "Please specify a template_path in your configuration (in initializer)") unless config['template_path']
44
+ config['files']||= load_file config['template_path']
45
+ end
46
+ end
47
+
48
+ def load_files path
49
+ files = {}
50
+ Dir.glob(path+"/**/**").each do |file_path|
51
+ next if File.directory? file_path
52
+ filename = Pathname.new(file_path).relative_path_from(Pathname.new(path))
53
+ file = File.open(file_path, "rb")
54
+ files[filename.to_s] = File.read(file)
55
+ end
56
+ files
57
+ end
58
+
59
+ def read_p12_certificates
60
+ pass_config.each do |pass_type_id, config|
61
+ raise(ArgumentError, "Please specify cert_path (certificate path) in your configuration (in initializer)") unless config['cert_path'] && !config['cert_path'].blank?
62
+ raise(ArgumentError, "Please specify cert_password (certificate password) in your configuration (in initializer)") unless config['cert_password']
63
+ config['p12_certificate'] ||= OpenSSL::PKCS12::new(File.read(config['cert_path']), config['cert_password'])
64
+ end
65
+ end
66
+
67
+ def read_wwdr_certificate
68
+ raise(ArgumentError, "Please specify the WWDR Intermediate certificate in your initializer") unless self.wwdr_intermediate_certificate_path
69
+ self.wwdr_certificate||= OpenSSL::X509::Certificate.new(File.read(self.wwdr_intermediate_certificate_path))
70
+ end
71
+
72
+ end
73
+ end
74
+
@@ -0,0 +1,23 @@
1
+
2
+ # Copyright 2012 Xtreme Labs
3
+
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+
18
+
19
+ module Passbook
20
+ class Engine < Rails::Engine
21
+ end
22
+ end
23
+
@@ -0,0 +1,111 @@
1
+
2
+ # Copyright 2012 Xtreme Labs
3
+
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+
18
+
19
+ require 'digest/sha1'
20
+ require 'json'
21
+ require 'openssl'
22
+ require 'zip/zip'
23
+ require 'zip/zipfilesystem'
24
+
25
+ module Passbook
26
+ class Pkpass
27
+ attr_accessor :files, :translations, :json, :pass_type_id, :serial_number, :config
28
+
29
+ def initialize(pass_type_id, serial_number)
30
+ self.pass_type_id = pass_type_id
31
+ self.serial_number = serial_number
32
+ self.translations = Hash.new
33
+ raise(ArgumentError, "Don't forget to run the generator to create the initializer") unless Config.instance.pass_config
34
+ self.config = Config.instance.pass_config[self.pass_type_id]
35
+ raise(ArgumentError, "Could not find configuration for #{self.pass_type_id}") unless self.config
36
+
37
+ if self.config.include? :files
38
+ self.files = self.config['files'].dup
39
+ else
40
+ self.files = Config.instance.load_files self.config['template_path']
41
+ end
42
+
43
+ if self.files.include? 'pass.json'
44
+ self.json = JSON.parse(self.files['pass.json'])
45
+ else
46
+ self.json = {}
47
+ puts "Warning: your template_path does not contain pass.json"
48
+ end
49
+ end
50
+
51
+ def add_file filename, content
52
+ self.files[filename] = content
53
+ end
54
+
55
+ def add_translation_string source, destination, language
56
+ self.translations[language] = Hash.new unless self.translations.include?(language)
57
+ self.translations[language][source] = destination
58
+ end
59
+
60
+ def package
61
+ #TODO: write a library that checks that all the right files are included in the package
62
+ #those requirements are going to be different depending on pass_type_id
63
+ self.write_json
64
+ self.write_translation_strings
65
+ self.generate_json_manifest
66
+ self.sign_manifest
67
+ self.compress_pass_file
68
+ end
69
+
70
+ def write_json
71
+ self.files['pass.json'] = JSON.pretty_generate(self.json)
72
+ end
73
+
74
+ def write_translation_strings
75
+ self.translations.each do |language, trans|
76
+ self.files["#{language}.lproj/pass.strings"] ||= ""
77
+ trans.each do |key, value|
78
+ #TODO: escape key and value
79
+ self.files["#{language}.lproj/pass.strings"] << "\n\"#{key}\" = \"#{value}\";"
80
+ end
81
+ end
82
+ end
83
+
84
+ def generate_json_manifest
85
+ manifest = {}
86
+ self.files.each do |filename, content|
87
+ manifest[filename] = Digest::SHA1.hexdigest(content)
88
+ end
89
+ self.files['manifest.json'] = JSON.pretty_generate(manifest)
90
+ end
91
+
92
+ def sign_manifest
93
+ flag = OpenSSL::PKCS7::BINARY|OpenSSL::PKCS7::DETACHED
94
+ signed = OpenSSL::PKCS7::sign(config['p12_certificate'].certificate, config['p12_certificate'].key, self.files['manifest.json'], [Config.instance.wwdr_certificate], flag)
95
+ self.files['signature'] = signed.to_der.force_encoding('UTF-8')
96
+ end
97
+
98
+ def compress_pass_file
99
+ stringio = Zip::ZipOutputStream::write_buffer do |z|
100
+ self.files.each do |filename, content|
101
+ z.put_next_entry filename
102
+ z.print content
103
+ end
104
+ end
105
+ stringio.set_encoding "binary"
106
+ stringio.rewind
107
+ stringio
108
+ # stringio.sysread
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,36 @@
1
+
2
+ # Copyright 2012 Xtreme Labs
3
+
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+
18
+
19
+ require 'fileutils'
20
+
21
+ module Passbook
22
+ module Generators
23
+ class ConfigGenerator < Rails::Generators::Base
24
+ source_root File.expand_path('../templates', __FILE__)
25
+ argument :wwdr_certificate_path, type: :string, optional: true, banner: "Absolute path to your WWDR certification file"
26
+
27
+ desc 'Create Passbook initializer'
28
+ def create_initializer_file
29
+ path = "#{Rails.root}/data/certificates"
30
+ FileUtils.mkdir_p path if wwdr_certificate_path.blank? && !File.exists?(path)
31
+ template 'initializer.rb', File.join('config', 'initializers', 'passbook.rb')
32
+ template 'migration.rb', File.join('db', 'migrate', "#{Time.now.strftime("%Y%m%d%H%M%S")}_create_passbook_registrations.rb")
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,35 @@
1
+
2
+ # Copyright 2012 Xtreme Labs
3
+
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+
18
+
19
+ Passbook::Config.instance.configure do |passbook|
20
+ # Templates are preloaded in production and dynamically loaded in development by default
21
+ # You can control the behaviour by setting the 'preload_templates'
22
+ #
23
+ # passbook.preload_templates = true
24
+
25
+
26
+ # Enables the routes necessary for passes to be able to:
27
+ # 1) register with the server
28
+ # 2) log errors
29
+ # 3) list pkpasses to be updated
30
+ # 4) let passbook request for update
31
+ passbook.enable_routes = true
32
+
33
+
34
+ passbook.wwdr_intermediate_certificate_path= "<%= wwdr_certificate_path.blank? ? "#{Rails.root}/data/certificates/wwdr.pem":wwdr_certificate_path %>"
35
+ end
@@ -0,0 +1,31 @@
1
+
2
+ # Copyright 2012 Xtreme Labs
3
+
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+
18
+
19
+ class CreatePassbookRegistrations < ActiveRecord::Migration
20
+ def change
21
+ create_table :passbook_registrations do |t|
22
+ t.string :uuid
23
+ t.string :device_id
24
+ t.string :push_token
25
+ t.string :serial_number
26
+ t.string :pass_type_id
27
+
28
+ t.timestamps
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,40 @@
1
+
2
+ # Copyright 2012 Xtreme Labs
3
+
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+
18
+
19
+ module Passbook
20
+ module Generators
21
+ class PkpassGenerator < Rails::Generators::NamedBase
22
+ source_root File.expand_path('../templates', __FILE__)
23
+ argument :pass_type_id, type: :string, default: 'pass.com.acme', optional: true, banner: "The Pass Type ID you registered at iOS Provisioning Portal"
24
+ argument :team_id, type: :string, optional: true, banner: "Team ID you got at the iOS Provisioning Portal when you registered the pass type id"
25
+ argument :cert_path, type: :string, optional: true, banner: "Absolute path to your pass.com.acme.p12 file"
26
+ argument :cert_password, type: :string, default: 'password', optional: true, banner: "Password for your P12 certificate"
27
+
28
+ desc 'Create and configure a model for a particular pkpass (ie. ticket)'
29
+ def create_initializer_file
30
+ template 'initializer.rb', File.join('config', 'initializers', "passbook_#{plural_name.singularize}.rb")
31
+ template 'model.rb', File.join('app', 'models', "#{plural_name.singularize}.rb")
32
+ route "get '/v1/passes/#{plural_name.singularize}' => 'Passbook::passes#get_pkpass', :defaults => { :pass_type_id => '#{pass_type_id}' }"
33
+ template 'migration.rb', File.join('db', 'migrate', "#{Time.now.strftime("%Y%m%d%H%M%S")}_create_#{plural_name}.rb")
34
+ template 'pass.json', File.join('data', 'templates', pass_type_id, "pass.json")
35
+ template 'icon.png', File.join('data', 'templates', pass_type_id, "icon.png")
36
+ template 'icon@2x.png', File.join('data', 'templates', pass_type_id, "icon@2x.png")
37
+ end
38
+ end
39
+ end
40
+ end