push_package 0.0.1 → 0.0.2

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,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MTdmMDFlZmVlNWE5MDFkYzgzNGVhMjM4ZDAxMTc4YTY4N2VlMDVlNA==
4
+ YzMyYmUwZDI3OTNhMjQ5ZmMzYTljZDk0M2Y1YmU0NTUwM2M3MTA2OA==
5
5
  data.tar.gz: !binary |-
6
- OTEwZTQ0NjBkOGZlNTBmYmY5M2QyZmQ3ZTU2NTE0NGFhN2FiYzU2OQ==
6
+ YmQwOGVjMjRiMTUwZTYzMGM0MGEzM2I1MzcxODczMGRkNGQ0ZGU4Zg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZjcwNTc2YzVlZDc0MTY3NGMwZTFkNDU5OTE3NGYzN2UyMzljMzc0YTE5OTVm
10
- NTk5OGFhZDA1OWZhYjQ1MmJiMmI3NGU0Nzc4MjNlMjM5MTM0MWMwYTRjNGQz
11
- NzQzYzcxNjE5MmFiZDRiYzZjODg1MGU1Y2RmYWU1ZDA4MzQ4NTE=
9
+ MWQzY2NkYzBhZjhjMGI3MzJkMjg4YTZkYzUwYTNiMmE4NzQ5MmJiZDE4NDJi
10
+ MTdkZGQyY2I0ZDUzZjdmMzc0MjJmYzI4MTk4YjBkMjY0NjU4M2RkZmUyNzJi
11
+ NzJhNTJkOGVkN2JkODkzYWMyNDQxMDgyYmEwMGE0ZjUyZmZjMTg=
12
12
  data.tar.gz: !binary |-
13
- MjQ0NTM4NzBkNjg1Y2U4NmY3MWY2MDU2MjcwNTM3ZmE2NWY1ZmNhNzM3YjYw
14
- Njk4NjUwZTQ1ZWRhOTllMGRhZDU2ZDE2MzkyMTM4Mzc0YzMyOGRjOTIyYzY0
15
- ZWJlZGEyMzk2ZDlhNjAzMmQzNGNhNGEwMjFlZGYwNmY3YjA1Mjk=
13
+ ZmZjYjNkYWFmOWQxMmY0ZmJmMDIyNDExZDU0MmExMDliZjEzMjM4MWQzOGZl
14
+ Y2Y1N2I1MDRiNDIzNjIwYmYwMDFmMDdlOWU3MDJhYTdiMmFmMjMxZTUyMDFk
15
+ NWRkMDk2ZGE0NTVjZjYyZGI4NWNmYjAxMjdkZWVmNzk1YTRjMTY=
data/Gemfile.lock CHANGED
@@ -1,18 +1,26 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- push_package (0.0.1)
4
+ push_package (0.0.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
+ coderay (1.0.9)
10
+ method_source (0.8.2)
9
11
  minitest (4.7.0)
12
+ pry (0.9.12.2)
13
+ coderay (~> 1.0.5)
14
+ method_source (~> 0.8)
15
+ slop (~> 3.4)
10
16
  rake (10.0.3)
17
+ slop (3.4.6)
11
18
 
12
19
  PLATFORMS
13
20
  ruby
14
21
 
15
22
  DEPENDENCIES
16
23
  minitest (~> 4.7.0)
24
+ pry
17
25
  push_package!
18
26
  rake (~> 10.0.3)
data/README.md CHANGED
@@ -2,7 +2,19 @@
2
2
  Make implementing safari push notifications easier for ruby developers.
3
3
 
4
4
  ## Notes:
5
- This is not ready for use.
5
+
6
+ ```ruby
7
+ def create
8
+ package = PushPackage.new(website_params, iconset_path, certificate)
9
+ package.save('path/to/save')
10
+ send_file 'path/to/save'
11
+ end
12
+ ```
13
+
14
+ ```shell
15
+ $> push_package --website-json=./website.json --iconset-path=~/project/iconset --output-dir=./ --certificate=./Certificate.p12
16
+ wrote: ./pushPackage.zip
17
+ ```
6
18
 
7
19
  ## Contributing
8
20
 
data/lib/push_package.rb CHANGED
@@ -1,4 +1,78 @@
1
1
  require 'push_package/version'
2
+ require 'json'
3
+ require 'fileutils'
4
+ require 'tmpdir'
5
+ require 'openssl'
2
6
 
3
- module PushPackage
7
+ class PushPackage
8
+ class InvalidIconsetError < StandardError; end
9
+ class InvalidParameterError < StandardError; end
10
+
11
+ REQUIRED_WEBSITE_PARAMS = ["websiteName", "websitePushID", "allowedDomains", "urlFormatString", "authenticationToken", "webServiceURL"]
12
+ REQUIRED_ICONSET_FILES = ["icon_16x16.png", "icon_16x16@2x.png", "icon_32x32.png", "icon_32x32@2x.png", "icon_128x128.png", "icon_128x128@2x.png" ]
13
+
14
+ attr_reader :p12
15
+
16
+ def initialize(website_params, iconset_path, certificate, password = nil)
17
+ raise InvalidParameterError unless valid_website_params?(website_params)
18
+ raise InvalidIconsetError unless valid_iconset?(iconset_path)
19
+ raise ArgumentError unless certificate
20
+
21
+ @website_params = website_params
22
+ @iconset_path = iconset_path
23
+
24
+ if certificate.respond_to?(:read)
25
+ cert_data = certificate.read
26
+ certificate.rewind if certificate.respond_to?(:rewind)
27
+ else
28
+ cert_data = File.read(certificate)
29
+ end
30
+ @p12 = OpenSSL::PKCS12.new(cert_data, password)
31
+ end
32
+
33
+ def save(output_path)
34
+ Dir.mktmpdir('pushPackage') do |dir|
35
+ json = File.open(dir + '/website.json', 'w+')
36
+ json.write(JSON.dump(@website_params))
37
+ json.close
38
+
39
+ Dir.mkdir(File.join(dir,'icon.iconset'))
40
+ Dir.glob(@iconset_path + '/*.png').each do |icon|
41
+ FileUtils.cp(icon, dir + '/icon.iconset/')
42
+ end
43
+
44
+ manifest_keys = REQUIRED_ICONSET_FILES.map{|f| 'icon.iconset/' + f }
45
+ manifest_keys << 'website.json'
46
+ manifest_values = manifest_keys.map {|file| Digest::SHA1.file(File.join(dir, file)).hexdigest }
47
+ manifest_data = Hash[manifest_keys.zip(manifest_values)].to_json
48
+ File.open(dir + '/manifest.json', 'w+') do |manifest|
49
+ manifest.write(manifest_data)
50
+ end
51
+
52
+ #use the certificate to create a pkcs7 detached signature
53
+ signature = OpenSSL::PKCS7::sign(@p12.certificate, @p12.key, manifest_data, [], OpenSSL::PKCS7::BINARY | OpenSSL::PKCS7::DETACHED)
54
+
55
+ File.open(dir + '/signature', 'wb+') do |file|
56
+ file << signature.to_der
57
+ end
58
+
59
+ `pushd #{dir}; zip -r #{output_path} ./; popd`
60
+ end
61
+
62
+ File.open(output_path, 'r')
63
+ end
64
+
65
+ private
66
+
67
+ def valid_website_params?(params)
68
+ REQUIRED_WEBSITE_PARAMS.all? do |param|
69
+ params.keys.include?(param)
70
+ end
71
+ end
72
+
73
+ def valid_iconset?(iconset_path)
74
+ REQUIRED_ICONSET_FILES.all? do |file|
75
+ File.exist?(File.join(iconset_path, file))
76
+ end
77
+ end
4
78
  end
@@ -1,3 +1,3 @@
1
- module PushPackage
2
- VERSION = '0.0.1'.freeze
1
+ class PushPackage
2
+ VERSION = '0.0.2'.freeze
3
3
  end
data/push_package.gemspec CHANGED
@@ -21,4 +21,5 @@ Gem::Specification.new do |gem|
21
21
 
22
22
  gem.add_development_dependency 'minitest', '~> 4.7.0'
23
23
  gem.add_development_dependency 'rake', '~> 10.0.3'
24
+ gem.add_development_dependency 'pry'
24
25
  end
@@ -0,0 +1 @@
1
+ {"icon.iconset/icon_128x128.png":"28969578f1788252807a7d8205db269cb7699fa8","icon.iconset/icon_128x128@2x.png":"dd2bf0e3cb998467b0e5f5ae11675a454ad77601","icon.iconset/icon_16x16.png":"48e791d0c88b92fae51ffa8363821857210fca01","icon.iconset/icon_16x16@2x.png":"5a74d295cc09ca5896a4ceb7cac0d030cc85e894","icon.iconset/icon_32x32.png":"8c71bc22f4cfe12ad98aabe94da6a70fe9f15741","icon.iconset/icon_32x32@2x.png":"750e080d38efe1c227b2498f73f006007f3da24b","website.json":"3eaed6475443b895a49e3a1220e547f2be90434a"}
Binary file
Binary file
@@ -1,10 +1,131 @@
1
1
  require 'spec_helper'
2
+ require 'fileutils'
2
3
 
3
4
  describe PushPackage do
4
5
 
6
+ let(:iconset_path) { fixture_path 'iconset' }
7
+
8
+ let(:website_params) do
9
+ {
10
+ 'websiteName' => 'Push Package Test',
11
+ 'websitePushID' => 'web.com.symmetricinfinity.push_package',
12
+ 'allowedDomains' => ['http://symmetricinfinity.com/push_package/', 'http://lvh.me'],
13
+ 'urlFormatString' => 'http://symmetricinfinity.com/push_package/?%@=%@',
14
+ 'authenticationToken' => 'nr2o1spn515949r5q54so22o8rq95575',
15
+ 'webServiceURL' => 'https://api.zeropush.com/safari'
16
+ }
17
+ end
18
+
19
+ let(:expected_manifest) do
20
+ {
21
+ 'icon.iconset/icon_128x128.png' => '28969578f1788252807a7d8205db269cb7699fa8',
22
+ 'icon.iconset/icon_128x128@2x.png' => 'dd2bf0e3cb998467b0e5f5ae11675a454ad77601',
23
+ 'icon.iconset/icon_16x16.png' => '48e791d0c88b92fae51ffa8363821857210fca01',
24
+ 'icon.iconset/icon_16x16@2x.png' => '5a74d295cc09ca5896a4ceb7cac0d030cc85e894',
25
+ 'icon.iconset/icon_32x32.png' => '8c71bc22f4cfe12ad98aabe94da6a70fe9f15741',
26
+ 'icon.iconset/icon_32x32@2x.png' => '750e080d38efe1c227b2498f73f006007f3da24b',
27
+ 'website.json' => '3eaed6475443b895a49e3a1220e547f2be90434a'
28
+ }
29
+ end
30
+
31
+ let(:certificate) { File.open(fixture_path('self-signed.p12')) }
32
+
5
33
  describe 'the truth' do
6
34
  it 'should pass' do
7
35
  'test'.must_equal('test')
8
36
  end
9
37
  end
38
+
39
+ describe '.new' do
40
+ it 'should check website_params' do
41
+ lambda do
42
+ PushPackage.new({}, iconset_path, certificate)
43
+ end.must_raise(PushPackage::InvalidParameterError)
44
+ end
45
+
46
+ it 'must have a valid iconset' do
47
+ lambda do
48
+ PushPackage.new(website_params, '/tmp', certificate)
49
+ end.must_raise(PushPackage::InvalidIconsetError)
50
+ end
51
+
52
+ it 'should support a certificate path' do
53
+ lambda do
54
+ PushPackage.new(website_params, iconset_path, nil)
55
+ end.must_raise(ArgumentError)
56
+ end
57
+
58
+ it 'should support certificate path' do
59
+ lambda do
60
+ PushPackage.new(website_params, iconset_path, '/some/file.p12')
61
+ end.must_raise(Errno::ENOENT)
62
+ end
63
+ end
64
+
65
+ describe '#save' do
66
+ let(:output_path) { '/tmp/pushPackage.zip' }
67
+ let(:tmp_path) { '/tmp/pushPackage' }
68
+ let(:extracted_package) do
69
+ `unzip #{output_path} -d #{tmp_path}`
70
+ Dir.glob(tmp_path + '/**/*').map do |d|
71
+ d.gsub(tmp_path + '/', '')
72
+ end
73
+ end
74
+
75
+ let(:push_package) { PushPackage.new(website_params, iconset_path, certificate) }
76
+
77
+ before do
78
+ push_package.save(output_path)
79
+ end
80
+
81
+ after do
82
+ File.delete(output_path) if File.exist?(output_path)
83
+ FileUtils.rm_rf(tmp_path)
84
+ end
85
+
86
+ it 'should save to the file system' do
87
+ File.exist?(output_path).must_equal(true)
88
+ end
89
+
90
+ it 'should return the file handle' do
91
+ file = push_package.save(output_path)
92
+ file.must_be_instance_of File
93
+ File.exists?(file.path).must_equal true
94
+ end
95
+
96
+ it 'should be a zip file' do
97
+ extracted_package.wont_be_empty
98
+ $?.success?.must_equal true
99
+ end
100
+
101
+ it 'should have a valid manifest.json file' do
102
+ extracted_package.must_include('manifest.json')
103
+ manifest = JSON.load(File.open(tmp_path + '/manifest.json', 'r'))
104
+ manifest.sort.must_equal(expected_manifest.sort)
105
+ end
106
+
107
+ it 'should have the iconset in icon.iconset subdirectory' do
108
+ icons = extracted_package.select {|file| file.start_with?('icon.iconset/') }
109
+ icons = icons.map {|i| i.gsub('icon.iconset/', '') }
110
+ icons.sort.must_equal(PushPackage::REQUIRED_ICONSET_FILES.sort)
111
+ end
112
+
113
+ it 'should have a website.json file' do
114
+ extracted_package.must_include('website.json')
115
+ end
116
+
117
+ it 'should have a valid signature' do
118
+ extracted_package.must_include('signature')
119
+ signature = File.read(tmp_path + '/signature')
120
+ p7 = OpenSSL::PKCS7.new(signature)
121
+ store = OpenSSL::X509::Store.new
122
+ store.add_cert(push_package.p12.certificate)
123
+ p7.verify(
124
+ [push_package.p12.certificate],
125
+ store,
126
+ File.read(tmp_path + '/manifest.json'),
127
+ OpenSSL::PKCS7::DETACHED
128
+ ).must_equal true
129
+ end
130
+ end
10
131
  end
data/spec/spec_helper.rb CHANGED
@@ -2,3 +2,6 @@ require 'minitest/spec'
2
2
  require 'minitest/autorun'
3
3
  require 'push_package'
4
4
 
5
+ def fixture_path(*paths)
6
+ File.join(File.dirname(__FILE__), 'fixtures', *paths)
7
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: push_package
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Natchev
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-10-29 00:00:00.000000000 Z
12
+ date: 2013-11-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -39,6 +39,20 @@ dependencies:
39
39
  - - ~>
40
40
  - !ruby/object:Gem::Version
41
41
  version: 10.0.3
42
+ - !ruby/object:Gem::Dependency
43
+ name: pry
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
42
56
  description: As of OSX 10.9 Safari can receive push notifications when it is closed.
43
57
  email:
44
58
  - stefan.natchev@gmail.com
@@ -56,6 +70,15 @@ files:
56
70
  - lib/push_package.rb
57
71
  - lib/push_package/version.rb
58
72
  - push_package.gemspec
73
+ - spec/fixtures/iconset/icon_128x128.png
74
+ - spec/fixtures/iconset/icon_128x128@2x.png
75
+ - spec/fixtures/iconset/icon_16x16.png
76
+ - spec/fixtures/iconset/icon_16x16@2x.png
77
+ - spec/fixtures/iconset/icon_32x32.png
78
+ - spec/fixtures/iconset/icon_32x32@2x.png
79
+ - spec/fixtures/manifest.json
80
+ - spec/fixtures/self-signed.p12
81
+ - spec/fixtures/signature
59
82
  - spec/push_package_spec.rb
60
83
  - spec/spec_helper.rb
61
84
  homepage: https://github.com/symmetricinfinity/push_package
@@ -83,5 +106,14 @@ signing_key:
83
106
  specification_version: 4
84
107
  summary: A gem for creating Safari push notification push packages.
85
108
  test_files:
109
+ - spec/fixtures/iconset/icon_128x128.png
110
+ - spec/fixtures/iconset/icon_128x128@2x.png
111
+ - spec/fixtures/iconset/icon_16x16.png
112
+ - spec/fixtures/iconset/icon_16x16@2x.png
113
+ - spec/fixtures/iconset/icon_32x32.png
114
+ - spec/fixtures/iconset/icon_32x32@2x.png
115
+ - spec/fixtures/manifest.json
116
+ - spec/fixtures/self-signed.p12
117
+ - spec/fixtures/signature
86
118
  - spec/push_package_spec.rb
87
119
  - spec/spec_helper.rb