webpush 0.3.8 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +30 -0
- data/.travis.yml +10 -4
- data/CHANGELOG.md +7 -0
- data/README.md +1 -0
- data/Rakefile +2 -2
- data/lib/webpush.rb +23 -12
- data/lib/webpush/encryption.rb +23 -40
- data/lib/webpush/errors.rb +3 -2
- data/lib/webpush/railtie.rb +2 -0
- data/lib/webpush/request.rb +54 -53
- data/lib/webpush/vapid_key.rb +4 -2
- data/lib/webpush/version.rb +3 -1
- data/webpush.gemspec +15 -16
- metadata +11 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fbb0e5a0419639817533b0ce1b45c0b222fafa5
|
4
|
+
data.tar.gz: b3f72a12240ef2496c27aefc7a88a896fd1f2aa4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ef773e38114e36f2d903ffcb892ffde10e5ca837908e76df1f2c95e31da9044f80e6add328e769ec70ddd8a43dcfcaff0122298346903dda8a730962e8e7f4c
|
7
|
+
data.tar.gz: 7e242909ab6e783f81a7553a193e384aa8b956537087de172838e50199d7dd37c7c78204248932b5574022c0624ef3ff7d6bc730f1e65b6f03858a993a7582e6
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require: rubocop-performance
|
2
|
+
|
3
|
+
AllCops:
|
4
|
+
Exclude:
|
5
|
+
- 'bin/**/*'
|
6
|
+
|
7
|
+
Metrics/AbcSize:
|
8
|
+
Max: 20
|
9
|
+
|
10
|
+
Metrics/ClassLength:
|
11
|
+
Max: 100
|
12
|
+
|
13
|
+
Metrics/ModuleLength:
|
14
|
+
Max: 100
|
15
|
+
|
16
|
+
Metrics/LineLength:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Metrics/BlockLength:
|
20
|
+
Exclude:
|
21
|
+
- spec/**/*_spec.rb
|
22
|
+
|
23
|
+
Lint/AmbiguousBlockAssociation:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
Style/Documentation:
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
Style/IndentHeredoc:
|
30
|
+
Enabled: false
|
data/.travis.yml
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
env:
|
2
|
+
global:
|
3
|
+
- CC_TEST_REPORTER_ID=155202524386dfebe0c3267a5c868b5417ff4cc2cde8ed301fb36b177d46a458
|
1
4
|
language: ruby
|
2
5
|
rvm:
|
3
6
|
- 2.2
|
@@ -6,12 +9,15 @@ rvm:
|
|
6
9
|
- 2.5
|
7
10
|
- 2.6
|
8
11
|
before_install:
|
9
|
-
-
|
10
|
-
if [[ ${RUBY_VERSION} =~ ^ruby-2.3 ]]; then
|
11
|
-
gem uninstall -i /home/travis/.rvm/gems/${RUBY_VERSION}@global bundler -x
|
12
|
-
fi
|
12
|
+
- gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true
|
13
13
|
- gem install bundler -v 1.17.3
|
14
|
+
before_script:
|
15
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
16
|
+
- chmod +x ./cc-test-reporter
|
17
|
+
- ./cc-test-reporter before-build
|
14
18
|
script: "bundle exec rake spec"
|
19
|
+
after_script:
|
20
|
+
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
15
21
|
addons:
|
16
22
|
code_climate:
|
17
23
|
repo_token: 155202524386dfebe0c3267a5c868b5417ff4cc2cde8ed301fb36b177d46a458
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## [v0.3.8](https://github.com/zaru/webpush/tree/v0.3.8) (2019-04-16)
|
4
|
+
[Full Changelog](https://github.com/zaru/webpush/compare/v0.3.7...v0.3.8)
|
5
|
+
|
6
|
+
**Merged pull requests:**
|
7
|
+
|
8
|
+
- Fix authorization header [\#72](https://github.com/zaru/webpush/pull/72) ([xronos-i-am](https://github.com/xronos-i-am))
|
9
|
+
|
3
10
|
## [v0.3.7](https://github.com/zaru/webpush/tree/v0.3.7) (2019-03-06)
|
4
11
|
[Full Changelog](https://github.com/zaru/webpush/compare/v0.3.6...v0.3.7)
|
5
12
|
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# WebPush
|
2
2
|
|
3
3
|
[![Code Climate](https://codeclimate.com/github/zaru/webpush/badges/gpa.svg)](https://codeclimate.com/github/zaru/webpush)
|
4
|
+
[![Test Coverage](https://codeclimate.com/github/zaru/webpush/badges/coverage.svg)](https://codeclimate.com/github/zaru/webpush/coverage)
|
4
5
|
[![Build Status](https://travis-ci.org/zaru/webpush.svg?branch=master)](https://travis-ci.org/zaru/webpush)
|
5
6
|
[![Gem Version](https://badge.fury.io/rb/webpush.svg)](https://badge.fury.io/rb/webpush)
|
6
7
|
|
data/Rakefile
CHANGED
data/lib/webpush.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'openssl'
|
2
4
|
require 'base64'
|
3
5
|
require 'hkdf'
|
@@ -11,6 +13,10 @@ require 'webpush/encryption'
|
|
11
13
|
require 'webpush/request'
|
12
14
|
require 'webpush/railtie' if defined?(Rails)
|
13
15
|
|
16
|
+
# Push API implementation
|
17
|
+
#
|
18
|
+
# https://tools.ietf.org/html/rfc8030
|
19
|
+
# https://www.w3.org/TR/push-api/
|
14
20
|
module Webpush
|
15
21
|
class << self
|
16
22
|
# Deliver the payload to the required endpoint given by the JavaScript
|
@@ -28,21 +34,16 @@ module Webpush
|
|
28
34
|
# @param options [Hash<Symbol,String>] additional options for the notification
|
29
35
|
# @option options [#to_s] :ttl Time-to-live in seconds
|
30
36
|
# @option options [#to_s] :urgency Urgency can be very-low, low, normal, high
|
31
|
-
|
32
|
-
|
33
|
-
endpoint: endpoint,
|
34
|
-
keys: {
|
35
|
-
p256dh: p256dh,
|
36
|
-
auth: auth
|
37
|
-
}
|
38
|
-
}
|
37
|
+
# rubocop:disable Metrics/ParameterLists
|
38
|
+
def payload_send(message: '', endpoint:, p256dh: '', auth: '', vapid: {}, **options)
|
39
39
|
Webpush::Request.new(
|
40
40
|
message: message,
|
41
|
-
subscription: subscription,
|
41
|
+
subscription: subscription(endpoint, p256dh, auth),
|
42
42
|
vapid: vapid,
|
43
43
|
**options
|
44
44
|
).perform
|
45
45
|
end
|
46
|
+
# rubocop:enable Metrics/ParameterLists
|
46
47
|
|
47
48
|
# Generate a VapidKey instance to obtain base64 encoded public and private keys
|
48
49
|
# suitable for VAPID protocol JSON web token signing
|
@@ -59,11 +60,21 @@ module Webpush
|
|
59
60
|
def decode64(str)
|
60
61
|
# For Ruby < 2.3, Base64.urlsafe_decode64 strict decodes and will raise errors if encoded value is not properly padded
|
61
62
|
# Implementation: http://ruby-doc.org/stdlib-2.3.0/libdoc/base64/rdoc/Base64.html#method-i-urlsafe_decode64
|
62
|
-
if !str.end_with?(
|
63
|
-
str = str.ljust((str.length + 3) & ~3, "=")
|
64
|
-
end
|
63
|
+
str = str.ljust((str.length + 3) & ~3, '=') if !str.end_with?('=') && str.length % 4 != 0
|
65
64
|
|
66
65
|
Base64.urlsafe_decode64(str)
|
67
66
|
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def subscription(endpoint, p256dh, auth)
|
71
|
+
{
|
72
|
+
endpoint: endpoint,
|
73
|
+
keys: {
|
74
|
+
p256dh: p256dh,
|
75
|
+
auth: auth
|
76
|
+
}
|
77
|
+
}
|
78
|
+
end
|
68
79
|
end
|
69
80
|
end
|
data/lib/webpush/encryption.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Webpush
|
2
4
|
module Encryption
|
3
5
|
extend self
|
4
6
|
|
7
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
5
8
|
def encrypt(message, p256dh, auth)
|
6
9
|
assert_arguments(message, p256dh, auth)
|
7
10
|
|
8
|
-
group_name =
|
11
|
+
group_name = 'prime256v1'
|
9
12
|
salt = Random.new.bytes(16)
|
10
13
|
|
11
14
|
server = OpenSSL::PKey::EC.new(group_name)
|
@@ -20,75 +23,55 @@ module Webpush
|
|
20
23
|
|
21
24
|
client_auth_token = Webpush.decode64(auth)
|
22
25
|
|
23
|
-
|
26
|
+
info = "WebPush: info\0" + client_public_key_bn.to_s(2) + server_public_key_bn.to_s(2)
|
27
|
+
content_encryption_key_info = "Content-Encoding: aes128gcm\0"
|
28
|
+
nonce_info = "Content-Encoding: nonce\0"
|
24
29
|
|
25
|
-
|
30
|
+
prk = HKDF.new(shared_secret, salt: client_auth_token, algorithm: 'SHA256', info: info).next_bytes(32)
|
26
31
|
|
27
|
-
content_encryption_key_info = create_info('aesgcm', context)
|
28
32
|
content_encryption_key = HKDF.new(prk, salt: salt, info: content_encryption_key_info).next_bytes(16)
|
29
33
|
|
30
|
-
nonce_info = create_info('nonce', context)
|
31
34
|
nonce = HKDF.new(prk, salt: salt, info: nonce_info).next_bytes(12)
|
32
35
|
|
33
36
|
ciphertext = encrypt_payload(message, content_encryption_key, nonce)
|
34
37
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
server_public_key_bn: convert16bit(server_public_key_bn),
|
39
|
-
server_public_key: server_public_key_bn.to_s(2),
|
40
|
-
shared_secret: shared_secret
|
41
|
-
}
|
42
|
-
end
|
38
|
+
serverkey16bn = convert16bit(server_public_key_bn)
|
39
|
+
rs = ciphertext.bytesize
|
40
|
+
raise ArgumentError, "encrypted payload is too big" if rs > 4096
|
43
41
|
|
44
|
-
|
42
|
+
aes128gcmheader = "#{salt}" + [rs].pack('N*') + [serverkey16bn.bytesize].pack('C*') + serverkey16bn
|
45
43
|
|
46
|
-
|
47
|
-
c = convert16bit(client_public_key)
|
48
|
-
s = convert16bit(server_public_key)
|
49
|
-
context = "\0"
|
50
|
-
context += [c.bytesize].pack("n*")
|
51
|
-
context += c
|
52
|
-
context += [s.bytesize].pack("n*")
|
53
|
-
context += s
|
54
|
-
context
|
44
|
+
aes128gcmheader + ciphertext
|
55
45
|
end
|
46
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
47
|
+
|
48
|
+
private
|
56
49
|
|
57
50
|
def encrypt_payload(plaintext, content_encryption_key, nonce)
|
58
51
|
cipher = OpenSSL::Cipher.new('aes-128-gcm')
|
59
52
|
cipher.encrypt
|
60
53
|
cipher.key = content_encryption_key
|
61
54
|
cipher.iv = nonce
|
62
|
-
padding = cipher.update("\0\0")
|
63
55
|
text = cipher.update(plaintext)
|
64
|
-
|
65
|
-
e_text =
|
56
|
+
padding = cipher.update("\2\0")
|
57
|
+
e_text = text + padding + cipher.final
|
66
58
|
e_tag = cipher.auth_tag
|
67
59
|
|
68
60
|
e_text + e_tag
|
69
61
|
end
|
70
62
|
|
71
|
-
def create_info(type, context)
|
72
|
-
info = "Content-Encoding: "
|
73
|
-
info += type
|
74
|
-
info += "\0"
|
75
|
-
info += "P-256"
|
76
|
-
info += context
|
77
|
-
info
|
78
|
-
end
|
79
|
-
|
80
63
|
def convert16bit(key)
|
81
|
-
[key.to_s(16)].pack(
|
64
|
+
[key.to_s(16)].pack('H*')
|
82
65
|
end
|
83
66
|
|
84
67
|
def assert_arguments(message, p256dh, auth)
|
85
|
-
raise ArgumentError,
|
86
|
-
raise ArgumentError,
|
87
|
-
raise ArgumentError,
|
68
|
+
raise ArgumentError, 'message cannot be blank' if blank?(message)
|
69
|
+
raise ArgumentError, 'p256dh cannot be blank' if blank?(p256dh)
|
70
|
+
raise ArgumentError, 'auth cannot be blank' if blank?(auth)
|
88
71
|
end
|
89
72
|
|
90
73
|
def blank?(value)
|
91
74
|
value.nil? || value.empty?
|
92
75
|
end
|
93
76
|
end
|
94
|
-
end
|
77
|
+
end
|
data/lib/webpush/errors.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Webpush
|
2
4
|
class Error < RuntimeError; end
|
3
5
|
|
4
6
|
class ConfigurationError < Error; end
|
5
7
|
|
6
|
-
class ResponseError < Error
|
8
|
+
class ResponseError < Error
|
7
9
|
attr_reader :response, :host
|
8
10
|
|
9
11
|
def initialize(response, host)
|
@@ -24,5 +26,4 @@ module Webpush
|
|
24
26
|
class TooManyRequests < ResponseError; end
|
25
27
|
|
26
28
|
class PushServiceError < ResponseError; end
|
27
|
-
|
28
29
|
end
|
data/lib/webpush/railtie.rb
CHANGED
data/lib/webpush/request.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'jwt'
|
2
4
|
require 'base64'
|
3
5
|
|
4
6
|
module Webpush
|
5
7
|
# It is temporary URL until supported by the GCM server.
|
6
|
-
GCM_URL = 'https://android.googleapis.com/gcm/send'
|
7
|
-
TEMP_GCM_URL = 'https://
|
8
|
+
GCM_URL = 'https://android.googleapis.com/gcm/send'.freeze
|
9
|
+
TEMP_GCM_URL = 'https://fcm.googleapis.com/fcm'.freeze
|
8
10
|
|
11
|
+
# rubocop:disable Metrics/ClassLength
|
9
12
|
class Request
|
10
|
-
def initialize(message:
|
13
|
+
def initialize(message: '', subscription:, vapid:, **options)
|
11
14
|
endpoint = subscription.fetch(:endpoint)
|
12
15
|
@endpoint = endpoint.gsub(GCM_URL, TEMP_GCM_URL)
|
13
16
|
@payload = build_payload(message, subscription)
|
@@ -15,6 +18,7 @@ module Webpush
|
|
15
18
|
@options = default_options.merge(options)
|
16
19
|
end
|
17
20
|
|
21
|
+
# rubocop:disable Metrics/AbcSize
|
18
22
|
def perform
|
19
23
|
http = Net::HTTP.new(uri.host, uri.port)
|
20
24
|
http.use_ssl = true
|
@@ -25,63 +29,46 @@ module Webpush
|
|
25
29
|
req = Net::HTTP::Post.new(uri.request_uri, headers)
|
26
30
|
req.body = body
|
27
31
|
resp = http.request(req)
|
28
|
-
|
29
|
-
if resp.is_a?(Net::HTTPGone) # 410
|
30
|
-
raise ExpiredSubscription.new(resp, uri.host)
|
31
|
-
elsif resp.is_a?(Net::HTTPNotFound) # 404
|
32
|
-
raise InvalidSubscription.new(resp, uri.host)
|
33
|
-
elsif resp.is_a?(Net::HTTPUnauthorized) || resp.is_a?(Net::HTTPForbidden) || # 401, 403
|
34
|
-
resp.is_a?(Net::HTTPBadRequest) && resp.message == "UnauthorizedRegistration" # 400, Google FCM
|
35
|
-
raise Unauthorized.new(resp, uri.host)
|
36
|
-
elsif resp.is_a?(Net::HTTPRequestEntityTooLarge) # 413
|
37
|
-
raise PayloadTooLarge.new(resp, uri.host)
|
38
|
-
elsif resp.is_a?(Net::HTTPTooManyRequests) # 429, try again later!
|
39
|
-
raise TooManyRequests.new(resp, uri.host)
|
40
|
-
elsif resp.is_a?(Net::HTTPServerError) # 5xx
|
41
|
-
raise PushServiceError.new(resp, uri.host)
|
42
|
-
elsif !resp.is_a?(Net::HTTPSuccess) # unknown/unhandled response error
|
43
|
-
raise ResponseError.new(resp, uri.host)
|
44
|
-
end
|
32
|
+
verify_response(resp)
|
45
33
|
|
46
34
|
resp
|
47
35
|
end
|
36
|
+
# rubocop:enable Metrics/AbcSize
|
48
37
|
|
38
|
+
# rubocop:disable Metrics/MethodLength
|
49
39
|
def headers
|
50
40
|
headers = {}
|
51
|
-
headers[
|
52
|
-
headers[
|
53
|
-
headers[
|
54
|
-
|
55
|
-
if @payload
|
56
|
-
headers[
|
57
|
-
headers["
|
58
|
-
headers["Crypto-Key"] = "dh=#{dh_param}"
|
41
|
+
headers['Content-Type'] = 'application/octet-stream'
|
42
|
+
headers['Ttl'] = ttl
|
43
|
+
headers['Urgency'] = urgency
|
44
|
+
|
45
|
+
if @payload
|
46
|
+
headers['Content-Encoding'] = 'aes128gcm'
|
47
|
+
headers["Content-Length"] = @payload.length.to_s
|
59
48
|
end
|
60
49
|
|
61
50
|
if api_key?
|
62
|
-
headers[
|
51
|
+
headers['Authorization'] = "key=#{api_key}"
|
63
52
|
elsif vapid?
|
64
|
-
|
65
|
-
headers["Authorization"] = vapid_headers["Authorization"]
|
66
|
-
headers["Crypto-Key"] = [ headers["Crypto-Key"], vapid_headers["Crypto-Key"] ].compact.join(";")
|
53
|
+
headers["Authorization"] = build_vapid_header
|
67
54
|
end
|
68
55
|
|
69
56
|
headers
|
70
57
|
end
|
58
|
+
# rubocop:enable Metrics/MethodLength
|
59
|
+
|
60
|
+
def build_vapid_header
|
61
|
+
# https://tools.ietf.org/id/draft-ietf-webpush-vapid-03.html
|
71
62
|
|
72
|
-
def build_vapid_headers
|
73
63
|
vapid_key = vapid_pem ? VapidKey.from_pem(vapid_pem) : VapidKey.from_keys(vapid_public_key, vapid_private_key)
|
74
64
|
jwt = JWT.encode(jwt_payload, vapid_key.curve, 'ES256', jwt_header_fields)
|
75
65
|
p256ecdsa = vapid_key.public_key_for_push_header
|
76
66
|
|
77
|
-
|
78
|
-
'Authorization' => 'WebPush ' + jwt,
|
79
|
-
'Crypto-Key' => 'p256ecdsa=' + p256ecdsa,
|
80
|
-
}
|
67
|
+
"vapid t=#{jwt},k=#{p256ecdsa}"
|
81
68
|
end
|
82
69
|
|
83
70
|
def body
|
84
|
-
@payload
|
71
|
+
@payload || ''
|
85
72
|
end
|
86
73
|
|
87
74
|
private
|
@@ -98,32 +85,24 @@ module Webpush
|
|
98
85
|
@options.fetch(:urgency).to_s
|
99
86
|
end
|
100
87
|
|
101
|
-
def dh_param
|
102
|
-
trim_encode64(@payload.fetch(:server_public_key))
|
103
|
-
end
|
104
|
-
|
105
|
-
def salt_param
|
106
|
-
trim_encode64(@payload.fetch(:salt))
|
107
|
-
end
|
108
|
-
|
109
88
|
def jwt_payload
|
110
89
|
{
|
111
90
|
aud: audience,
|
112
91
|
exp: Time.now.to_i + expiration,
|
113
|
-
sub: subject
|
92
|
+
sub: subject
|
114
93
|
}
|
115
94
|
end
|
116
95
|
|
117
96
|
def jwt_header_fields
|
118
|
-
{
|
97
|
+
{ "typ": "JWT", "alg": "ES256" }
|
119
98
|
end
|
120
99
|
|
121
100
|
def audience
|
122
|
-
uri.scheme +
|
101
|
+
uri.scheme + '://' + uri.host
|
123
102
|
end
|
124
103
|
|
125
104
|
def expiration
|
126
|
-
@vapid_options.fetch(:expiration, 24*60*60)
|
105
|
+
@vapid_options.fetch(:expiration, 24 * 60 * 60)
|
127
106
|
end
|
128
107
|
|
129
108
|
def subject
|
@@ -144,13 +123,13 @@ module Webpush
|
|
144
123
|
|
145
124
|
def default_options
|
146
125
|
{
|
147
|
-
ttl: 60*60*24*7*4, # 4 weeks
|
126
|
+
ttl: 60 * 60 * 24 * 7 * 4, # 4 weeks
|
148
127
|
urgency: 'normal'
|
149
128
|
}
|
150
129
|
end
|
151
130
|
|
152
131
|
def build_payload(message, subscription)
|
153
|
-
return
|
132
|
+
return nil if message.nil? || message.empty?
|
154
133
|
|
155
134
|
encrypt_payload(message, subscription.fetch(:keys))
|
156
135
|
end
|
@@ -164,7 +143,7 @@ module Webpush
|
|
164
143
|
end
|
165
144
|
|
166
145
|
def api_key?
|
167
|
-
!(api_key.nil? || api_key.empty?) && @endpoint =~
|
146
|
+
!(api_key.nil? || api_key.empty?) && @endpoint =~ %r{\Ahttps://(android|gcm-http|fcm)\.googleapis\.com}
|
168
147
|
end
|
169
148
|
|
170
149
|
def vapid?
|
@@ -174,5 +153,27 @@ module Webpush
|
|
174
153
|
def trim_encode64(bin)
|
175
154
|
Webpush.encode64(bin).delete('=')
|
176
155
|
end
|
156
|
+
|
157
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Style/GuardClause
|
158
|
+
def verify_response(resp)
|
159
|
+
if resp.is_a?(Net::HTTPGone) # 410
|
160
|
+
raise ExpiredSubscription.new(resp, uri.host)
|
161
|
+
elsif resp.is_a?(Net::HTTPNotFound) # 404
|
162
|
+
raise InvalidSubscription.new(resp, uri.host)
|
163
|
+
elsif resp.is_a?(Net::HTTPUnauthorized) || resp.is_a?(Net::HTTPForbidden) || # 401, 403
|
164
|
+
resp.is_a?(Net::HTTPBadRequest) && resp.message == 'UnauthorizedRegistration' # 400, Google FCM
|
165
|
+
raise Unauthorized.new(resp, uri.host)
|
166
|
+
elsif resp.is_a?(Net::HTTPRequestEntityTooLarge) # 413
|
167
|
+
raise PayloadTooLarge.new(resp, uri.host)
|
168
|
+
elsif resp.is_a?(Net::HTTPTooManyRequests) # 429, try again later!
|
169
|
+
raise TooManyRequests.new(resp, uri.host)
|
170
|
+
elsif resp.is_a?(Net::HTTPServerError) # 5xx
|
171
|
+
raise PushServiceError.new(resp, uri.host)
|
172
|
+
elsif !resp.is_a?(Net::HTTPSuccess) # unknown/unhandled response error
|
173
|
+
raise ResponseError.new(resp, uri.host)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Style/GuardClause
|
177
177
|
end
|
178
|
+
# rubocop:enable Metrics/ClassLength
|
178
179
|
end
|
data/lib/webpush/vapid_key.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Webpush
|
2
4
|
# Class for abstracting the generation and encoding of elliptic curve public and private keys for use with the VAPID protocol
|
3
5
|
#
|
@@ -78,12 +80,12 @@ module Webpush
|
|
78
80
|
def to_pem
|
79
81
|
public_key = OpenSSL::PKey::EC.new curve
|
80
82
|
public_key.private_key = nil
|
81
|
-
|
83
|
+
|
82
84
|
curve.to_pem + public_key.to_pem
|
83
85
|
end
|
84
86
|
|
85
87
|
def inspect
|
86
|
-
"#<#{self.class}:#{object_id.to_s(16)} #{to_h.map { |k, v| ":#{k}=#{v}" }.join(
|
88
|
+
"#<#{self.class}:#{object_id.to_s(16)} #{to_h.map { |k, v| ":#{k}=#{v}" }.join(' ')}>"
|
87
89
|
end
|
88
90
|
|
89
91
|
private
|
data/lib/webpush/version.rb
CHANGED
data/webpush.gemspec
CHANGED
@@ -1,29 +1,28 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require 'webpush/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
6
|
+
spec.name = 'webpush'
|
8
7
|
spec.version = Webpush::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
8
|
+
spec.authors = ['zaru@sakuraba']
|
9
|
+
spec.email = ['zarutofu@gmail.com']
|
11
10
|
|
12
|
-
spec.summary =
|
13
|
-
spec.homepage =
|
11
|
+
spec.summary = 'Encryption Utilities for Web Push payload. '
|
12
|
+
spec.homepage = 'https://github.com/zaru/webpush'
|
14
13
|
|
15
14
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
|
-
spec.bindir =
|
15
|
+
spec.bindir = 'exe'
|
17
16
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
|
-
spec.require_paths = [
|
17
|
+
spec.require_paths = ['lib']
|
19
18
|
|
20
|
-
spec.add_dependency
|
21
|
-
spec.add_dependency
|
19
|
+
spec.add_dependency 'hkdf', '~> 0.2'
|
20
|
+
spec.add_dependency 'jwt', '~> 2.0'
|
22
21
|
|
23
|
-
spec.add_development_dependency
|
22
|
+
spec.add_development_dependency 'bundler', '~> 1.17.3'
|
24
23
|
spec.add_development_dependency 'pry'
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
28
|
-
spec.add_development_dependency
|
24
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
25
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
26
|
+
spec.add_development_dependency 'simplecov'
|
27
|
+
spec.add_development_dependency 'webmock', '~> 1.24'
|
29
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: webpush
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- zaru@sakuraba
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hkdf
|
@@ -95,33 +95,33 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '3.0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: simplecov
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
103
|
+
version: '0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
110
|
+
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: webmock
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
117
|
+
version: '1.24'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
124
|
+
version: '1.24'
|
125
125
|
description:
|
126
126
|
email:
|
127
127
|
- zarutofu@gmail.com
|
@@ -131,6 +131,7 @@ extra_rdoc_files: []
|
|
131
131
|
files:
|
132
132
|
- ".gitignore"
|
133
133
|
- ".rspec"
|
134
|
+
- ".rubocop.yml"
|
134
135
|
- ".travis.yml"
|
135
136
|
- CHANGELOG.md
|
136
137
|
- Gemfile
|