webpush 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b5006a4c889349920fc642e5e126f9a2bb986fa8
4
- data.tar.gz: cc39101673ba4b9c52aa4482e1dbb554ccb51342
3
+ metadata.gz: 1e5f86e721e160b17bf21789f6c9d11a5d7ac297
4
+ data.tar.gz: 2f484b4300166b10772c1ebd9266228e9d4cd970
5
5
  SHA512:
6
- metadata.gz: 81560e7eabb65bac0fb4ce2972279654bdbcbca4719d815a40cd63994b20f59ba2430ad6835e6d5a0655df6897552344588948622399a405e9b4964bc2740b14
7
- data.tar.gz: 4785dc7d17f520a0df9891dd1ada08b57e59b19e972a9308190c3025ba7491f27052b0163c5f4dfd4fb7361066539bc98b325ef47e5e81bfa0bce0bca2309cc0
6
+ metadata.gz: 109c269ebbc2443613635450d497c1c1c7b5f24dffa96761c987bd77fb1fec1c252e7851a1dea19ee1e3d88fdd293ee58870408dc6391d0e82a6fbb6a3e1f6d4
7
+ data.tar.gz: ba9f2b0d91d175fca0e462870aa36366a2cbf4cde21b91acb729c36a6b55bf1c5fa6e50f7e2c13dec2bdc13128e0d4aa6e8a0853696493104e5da5c516bf2f64
data/.travis.yml CHANGED
@@ -2,3 +2,4 @@ language: ruby
2
2
  rvm:
3
3
  - 2.3.0
4
4
  before_install: gem install bundler -v 1.11.2
5
+ script: "bundle exec rake spec"
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # WebPush
2
2
 
3
+ [![Code Climate](https://codeclimate.com/github/zaru/webpush/badges/gpa.svg)](https://codeclimate.com/github/zaru/webpush)
4
+ [![Build Status](https://travis-ci.org/zaru/webpush.svg?branch=master)](https://travis-ci.org/zaru/webpush)
5
+
3
6
  This Gem will send the Web Push API. It supports the encryption necessary to payload.
4
7
 
5
8
  Payload is supported by Chrome50+, Firefox48+.
@@ -22,6 +25,8 @@ Or install it yourself as:
22
25
 
23
26
  ## Usage
24
27
 
28
+ ### using the payload
29
+
25
30
  ```ruby
26
31
  message = {
27
32
  title: "title",
@@ -30,14 +35,23 @@ message = {
30
35
  }
31
36
 
32
37
  Webpush.payload_send(
33
- message: JSON.generate(message),
34
38
  endpoint: "https://android.googleapis.com/gcm/send/eah7hak....",
39
+ message: JSON.generate(message),
35
40
  p256dh: "BO/aG9nYXNkZmFkc2ZmZHNmYWRzZmFl...",
36
41
  auth: "aW1hcmthcmFpa3V6ZQ==",
37
42
  api_key: "[GoogleDeveloper APIKEY]" # optional, not used in Firefox.
38
43
  )
39
44
  ```
40
45
 
46
+ ### not use the payload
47
+
48
+ ```ruby
49
+ Webpush.payload_send(
50
+ endpoint: "https://android.googleapis.com/gcm/send/eah7hak....",
51
+ api_key: "[GoogleDeveloper APIKEY]" # optional, not used in Firefox.
52
+ )
53
+ ```
54
+
41
55
  ### ServiceWorker sample
42
56
 
43
57
  see. https://github.com/zaru/web-push-sample
data/Rakefile CHANGED
@@ -3,4 +3,4 @@ require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task default: :spec
data/bin/rspec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require "pathname"
10
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require "rubygems"
14
+ require "bundler/setup"
15
+
16
+ load Gem.bin_path("rspec-core", "rspec")
@@ -3,6 +3,8 @@ module Webpush
3
3
  extend self
4
4
 
5
5
  def encrypt(message, p256dh, auth)
6
+ assert_arguments(message, p256dh, auth)
7
+
6
8
  group_name = "prime256v1"
7
9
  salt = Random.new.bytes(16)
8
10
 
@@ -18,15 +20,15 @@ module Webpush
18
20
 
19
21
  client_auth_token = Base64.urlsafe_decode64(auth)
20
22
 
21
- prk = HKDF.new(shared_secret, :salt => client_auth_token, :algorithm => 'SHA256', :info => "Content-Encoding: auth\0").next_bytes(32)
23
+ prk = HKDF.new(shared_secret, salt: client_auth_token, algorithm: 'SHA256', info: "Content-Encoding: auth\0").next_bytes(32)
22
24
 
23
25
  context = create_context(client_public_key_bn, server_public_key_bn)
24
26
 
25
27
  content_encryption_key_info = create_info('aesgcm', context)
26
- content_encryption_key = HKDF.new(prk, :salt => salt, :info => content_encryption_key_info).next_bytes(16)
28
+ content_encryption_key = HKDF.new(prk, salt: salt, info: content_encryption_key_info).next_bytes(16)
27
29
 
28
30
  nonce_info = create_info('nonce', context)
29
- nonce = HKDF.new(prk, :salt => salt, :info => nonce_info).next_bytes(12)
31
+ nonce = HKDF.new(prk, salt: salt, info: nonce_info).next_bytes(12)
30
32
 
31
33
  ciphertext = encrypt_payload(message, content_encryption_key, nonce)
32
34
 
@@ -40,9 +42,9 @@ module Webpush
40
42
 
41
43
  private
42
44
 
43
- def create_context(clientPublicKey, serverPublicKey)
44
- c = convert16bit(clientPublicKey)
45
- s = convert16bit(serverPublicKey)
45
+ def create_context(client_public_key, server_public_key)
46
+ c = convert16bit(client_public_key)
47
+ s = convert16bit(server_public_key)
46
48
  context = "\0"
47
49
  context += [c.bytesize].pack("n*")
48
50
  context += c
@@ -77,5 +79,15 @@ module Webpush
77
79
  def convert16bit(key)
78
80
  [key.to_s(16)].pack("H*")
79
81
  end
82
+
83
+ def assert_arguments(message, p256dh, auth)
84
+ raise ArgumentError, "message cannot be blank" if blank?(message)
85
+ raise ArgumentError, "p256dh cannot be blank" if blank?(p256dh)
86
+ raise ArgumentError, "auth cannot be blank" if blank?(auth)
87
+ end
88
+
89
+ def blank?(value)
90
+ value.nil? || value.empty?
91
+ end
80
92
  end
81
93
  end
@@ -0,0 +1,75 @@
1
+ module Webpush
2
+ class Request
3
+ def initialize(endpoint, options = {})
4
+ @endpoint = endpoint
5
+ @options = default_options.merge(options)
6
+ @payload = @options.delete(:payload) || {}
7
+ end
8
+
9
+ def perform
10
+ uri = URI.parse(@endpoint)
11
+ http = Net::HTTP.new(uri.host, uri.port)
12
+ http.use_ssl = true
13
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
14
+ req = Net::HTTP::Post.new(uri.request_uri, headers)
15
+ req.body = body
16
+ res = http.request(req)
17
+ res.code == "201"
18
+ rescue
19
+ false
20
+ end
21
+
22
+ def headers
23
+ headers = {}
24
+ headers["Content-Type"] = "application/octet-stream"
25
+ headers["Ttl"] = ttl
26
+
27
+ if encrypted_payload?
28
+ headers["Content-Encoding"] = "aesgcm"
29
+ headers["Encryption"] = "salt=#{salt_param}"
30
+ headers["Crypto-Key"] = "dh=#{dh_param}"
31
+ end
32
+
33
+ headers["Authorization"] = "key=#{api_key}" if api_key?
34
+
35
+ headers
36
+ end
37
+
38
+ def body
39
+ @payload.fetch(:ciphertext, "")
40
+ end
41
+
42
+ private
43
+
44
+ def ttl
45
+ @options.fetch(:ttl).to_s
46
+ end
47
+
48
+ def api_key
49
+ @options.fetch(:api_key, nil)
50
+ end
51
+
52
+ def api_key?
53
+ !(api_key.nil? || api_key.empty?)
54
+ end
55
+
56
+ def encrypted_payload?
57
+ [:ciphertext, :server_public_key_bn, :salt].all? { |key| @payload.has_key?(key) }
58
+ end
59
+
60
+ def dh_param
61
+ Base64.urlsafe_encode64(@payload.fetch(:server_public_key_bn)).delete('=')
62
+ end
63
+
64
+ def salt_param
65
+ Base64.urlsafe_encode64(@payload.fetch(:salt)).delete('=')
66
+ end
67
+
68
+ def default_options
69
+ {
70
+ api_key: nil,
71
+ ttl: 60*60*24*7*4 # 4 weeks
72
+ }
73
+ end
74
+ end
75
+ end
@@ -1,3 +1,3 @@
1
1
  module Webpush
2
- VERSION = "0.1.6"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/webpush.rb CHANGED
@@ -6,6 +6,7 @@ require 'json'
6
6
 
7
7
  require 'webpush/version'
8
8
  require 'webpush/encryption'
9
+ require 'webpush/request'
9
10
 
10
11
  module Webpush
11
12
 
@@ -14,36 +15,31 @@ module Webpush
14
15
  TEMP_GCM_URL = 'https://gcm-http.googleapis.com/gcm'
15
16
 
16
17
  class << self
17
- def payload_send(message:, endpoint:, p256dh:, auth:, api_key: "")
18
+ # Deliver the payload to the required endpoint given by the JavaScript
19
+ # PushSubscription. Including an optional message requires p256dh and
20
+ # auth keys from the PushSubscription.
21
+ #
22
+ # @param endpoint [String] the required PushSubscription url
23
+ # @param message [String] the optional payload
24
+ # @param p256dh [String] the user's public ECDH key given by the PushSubscription
25
+ # @param auth [String] the user's private ECDH key given by the PushSubscription
26
+ # @param options [Hash<Symbol,String>] additional options for the notification
27
+ # @option options [String] :api_key required for Google, omit for Firefox
28
+ # @option options [#to_s] :ttl Time-to-live in seconds
29
+ def payload_send(endpoint:, message: "", p256dh: "", auth: "", **options)
18
30
  endpoint = endpoint.gsub(GCM_URL, TEMP_GCM_URL)
19
31
 
20
- payload = Webpush::Encryption.encrypt(message, p256dh, auth)
21
- push_server_post(endpoint, payload, api_key)
32
+ payload = build_payload(message, p256dh, auth)
33
+
34
+ Webpush::Request.new(endpoint, options.merge(payload: payload)).perform
22
35
  end
23
36
 
24
37
  private
25
38
 
26
- def push_server_post(endpoint, payload, api_key = "")
27
- begin
28
- uri = URI.parse(endpoint)
29
- http = Net::HTTP.new(uri.host, uri.port)
30
- http.use_ssl = true
31
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
32
- header = {
33
- "Content-Type" => "application/octet-stream",
34
- "Content-Encoding" => "aesgcm",
35
- "Encryption" => "salt=#{Base64.urlsafe_encode64(payload[:salt]).delete('=')}",
36
- "Crypto-Key" => "dh=#{Base64.urlsafe_encode64(payload[:server_public_key_bn]).delete('=')}",
37
- "Ttl" => "2419200"
38
- }
39
- header["Authorization"] = "key=#{api_key}" unless api_key.empty?
40
- req = Net::HTTP::Post.new(uri.request_uri, header)
41
- req.body = payload[:ciphertext]
42
- res = http.request(req)
43
- return ("201" == res.code) ? true : false
44
- rescue
45
- return false
46
- end
39
+ def build_payload(message, p256dh, auth)
40
+ return {} if message.nil? || message.empty?
41
+
42
+ Webpush::Encryption.encrypt(message, p256dh, auth)
47
43
  end
48
44
  end
49
45
  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.1.6
4
+ version: 0.2.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: 2016-05-12 00:00:00.000000000 Z
11
+ date: 2016-05-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hkdf
@@ -123,9 +123,11 @@ files:
123
123
  - Rakefile
124
124
  - bin/console
125
125
  - bin/rake
126
+ - bin/rspec
126
127
  - bin/setup
127
128
  - lib/webpush.rb
128
129
  - lib/webpush/encryption.rb
130
+ - lib/webpush/request.rb
129
131
  - lib/webpush/version.rb
130
132
  - webpush.gemspec
131
133
  homepage: https://github.com/zaru/webpush