push_package 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/Gemfile.lock +9 -1
- data/README.md +13 -1
- data/lib/push_package.rb +75 -1
- data/lib/push_package/version.rb +2 -2
- data/push_package.gemspec +1 -0
- data/spec/fixtures/iconset/icon_128x128.png +0 -0
- data/spec/fixtures/iconset/icon_128x128@2x.png +0 -0
- data/spec/fixtures/iconset/icon_16x16.png +0 -0
- data/spec/fixtures/iconset/icon_16x16@2x.png +0 -0
- data/spec/fixtures/iconset/icon_32x32.png +0 -0
- data/spec/fixtures/iconset/icon_32x32@2x.png +0 -0
- data/spec/fixtures/manifest.json +1 -0
- data/spec/fixtures/self-signed.p12 +0 -0
- data/spec/fixtures/signature +0 -0
- data/spec/push_package_spec.rb +121 -0
- data/spec/spec_helper.rb +3 -0
- metadata +34 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YzMyYmUwZDI3OTNhMjQ5ZmMzYTljZDk0M2Y1YmU0NTUwM2M3MTA2OA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YmQwOGVjMjRiMTUwZTYzMGM0MGEzM2I1MzcxODczMGRkNGQ0ZGU4Zg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MWQzY2NkYzBhZjhjMGI3MzJkMjg4YTZkYzUwYTNiMmE4NzQ5MmJiZDE4NDJi
|
10
|
+
MTdkZGQyY2I0ZDUzZjdmMzc0MjJmYzI4MTk4YjBkMjY0NjU4M2RkZmUyNzJi
|
11
|
+
NzJhNTJkOGVkN2JkODkzYWMyNDQxMDgyYmEwMGE0ZjUyZmZjMTg=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
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.
|
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
|
-
|
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
|
-
|
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
|
data/lib/push_package/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = '0.0.
|
1
|
+
class PushPackage
|
2
|
+
VERSION = '0.0.2'.freeze
|
3
3
|
end
|
data/push_package.gemspec
CHANGED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -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
|
data/spec/push_package_spec.rb
CHANGED
@@ -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
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.
|
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-
|
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
|