disco_app 0.8.3 → 0.8.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/disco_app/app_proxy_controller.rb +7 -6
- data/app/controllers/disco_app/carrier_request_controller.rb +4 -9
- data/app/controllers/disco_app/webhooks_controller.rb +6 -2
- data/app/jobs/disco_app/concerns/synchronise_carrier_service_job.rb +1 -1
- data/app/models/disco_app/concerns/shop.rb +1 -1
- data/app/models/disco_app/concerns/synchronises_with_shopify.rb +26 -0
- data/app/services/disco_app/carrier_request_service.rb +15 -0
- data/app/services/disco_app/proxy_service.rb +17 -0
- data/app/services/disco_app/webhook_service.rb +2 -2
- data/app/views/disco_app/charges/new.html.erb +1 -1
- data/lib/disco_app/configuration.rb +33 -0
- data/lib/disco_app/version.rb +1 -1
- data/lib/disco_app.rb +3 -1
- data/lib/generators/disco_app/disco_app_generator.rb +4 -11
- data/lib/generators/disco_app/monitorify/monitorify_generator.rb +3 -3
- data/lib/generators/disco_app/templates/initializers/disco_app.rb +14 -1
- data/test/controllers/proxy_controller_test.rb +42 -0
- data/test/dummy/app/controllers/proxy_controller.rb +8 -0
- data/test/dummy/config/application.rb +0 -3
- data/test/dummy/config/initializers/disco_app.rb +14 -1
- data/test/dummy/config/routes.rb +2 -0
- metadata +9 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0874200090d68b8be05ff9d0a17c96a91896d0065b810041a459da29d8a2a9a8'
|
4
|
+
data.tar.gz: 75ab0d47c9b75349bf6ac5d4aeba7d531a904ea1331de3df34d5145af922c931
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 43e23ef9eab56ee15dcc179cd86d6b0afafd5b8d059c4c82a98efaa64e6a3d19ffd9584652946ccbd24c3d16372bc14f1903217035c00437e91336a14240f3c8
|
7
|
+
data.tar.gz: b0dcf3ee172de718935d8a8ee530fffc6eaba51fc1b14dc344f4092d22209d614a52e13f6b4c0fc1140ae639b87f6acdf8427698e27b4a07523ad4752dd3c037
|
@@ -4,6 +4,7 @@ module DiscoApp
|
|
4
4
|
|
5
5
|
included do
|
6
6
|
before_action :verify_proxy_signature
|
7
|
+
before_action :shopify_shop
|
7
8
|
after_action :add_liquid_header
|
8
9
|
|
9
10
|
rescue_from ActiveRecord::RecordNotFound do |exception|
|
@@ -20,12 +21,12 @@ module DiscoApp
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def proxy_signature_is_valid?
|
23
|
-
return true
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
return true if DiscoApp.configuration.skip_proxy_verification?
|
25
|
+
DiscoApp::ProxyService.proxy_signature_is_valid?(request.query_string, ShopifyApp.configuration.secret)
|
26
|
+
end
|
27
|
+
|
28
|
+
def shopify_shop
|
29
|
+
@shop = Shop.find_by_shopify_domain!(params[:shop])
|
29
30
|
end
|
30
31
|
|
31
32
|
def add_liquid_header
|
@@ -3,25 +3,20 @@ module DiscoApp
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
-
before_action :
|
6
|
+
before_action :verify_carrier_request
|
7
7
|
end
|
8
8
|
|
9
9
|
private
|
10
10
|
|
11
|
-
def
|
11
|
+
def verify_carrier_request
|
12
12
|
unless carrier_request_signature_is_valid?
|
13
13
|
head :unauthorized
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
17
|
def carrier_request_signature_is_valid?
|
18
|
-
return true
|
19
|
-
|
20
|
-
hmac_header = request.headers['HTTP_X_SHOPIFY_HMAC_SHA256']
|
21
|
-
digest = OpenSSL::Digest.new('sha256')
|
22
|
-
calculated_hmac = Base64.encode64(OpenSSL::HMAC.digest(digest, ShopifyApp.configuration.secret, data)).strip
|
23
|
-
request.body.rewind
|
24
|
-
calculated_hmac == hmac_header
|
18
|
+
return true if DiscoApp.configuration.skip_carrier_request_verification?
|
19
|
+
DiscoApp::CarrierRequestService.is_valid_hmac?(request.body.read.to_s, ShopifyApp.configuration.secret, request.headers['HTTP_X_SHOPIFY_HMAC_SHA256'])
|
25
20
|
end
|
26
21
|
|
27
22
|
end
|
@@ -30,13 +30,17 @@ module DiscoApp
|
|
30
30
|
|
31
31
|
private
|
32
32
|
|
33
|
-
# Verify a webhook request.
|
34
33
|
def verify_webhook
|
35
|
-
unless
|
34
|
+
unless webhook_is_valid?
|
36
35
|
head :unauthorized
|
37
36
|
end
|
38
37
|
request.body.rewind
|
39
38
|
end
|
40
39
|
|
40
|
+
def webhook_is_valid?
|
41
|
+
return true if DiscoApp.configuration.skip_webhook_verification?
|
42
|
+
DiscoApp::WebhookService.is_valid_hmac?(request.body.read.to_s, ShopifyApp.configuration.secret, request.headers['HTTP_X_SHOPIFY_HMAC_SHA256'])
|
43
|
+
end
|
44
|
+
|
41
45
|
end
|
42
46
|
end
|
@@ -32,7 +32,7 @@ module DiscoApp::Concerns::Shop
|
|
32
32
|
def new_charge_attributes
|
33
33
|
{
|
34
34
|
type: Rails.configuration.x.shopify_charges_default_type,
|
35
|
-
name:
|
35
|
+
name: DiscoApp.configuration.app_name,
|
36
36
|
price: Rails.configuration.x.shopify_charges_default_price,
|
37
37
|
trial_days: Rails.configuration.x.shopify_charges_default_trial_days,
|
38
38
|
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module DiscoApp::Concerns::SynchronisesWithShopify
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
class_methods do
|
5
|
+
|
6
|
+
def synchronise_from_shopify(shop, data)
|
7
|
+
data = data.with_indifferent_access
|
8
|
+
|
9
|
+
instance = self.find_or_create_by!(id: data[:id]) do |instance|
|
10
|
+
instance.shop = shop
|
11
|
+
instance.data = data
|
12
|
+
end
|
13
|
+
|
14
|
+
instance.update(data: data)
|
15
|
+
|
16
|
+
instance
|
17
|
+
end
|
18
|
+
|
19
|
+
def synchronise_deletion_from_shopify(shop, data)
|
20
|
+
data = data.with_indifferent_access
|
21
|
+
self.destroy_all(shop: shop, id: data[:id])
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class DiscoApp::CarrierRequestService
|
2
|
+
|
3
|
+
# Return true iff the provided hmac_to_verify matches that calculated from the
|
4
|
+
# given data and secret.
|
5
|
+
def self.is_valid_hmac?(body, secret, hmac_to_verify)
|
6
|
+
ActiveSupport::SecurityUtils.secure_compare(self.calculated_hmac(body, secret), hmac_to_verify.to_s)
|
7
|
+
end
|
8
|
+
|
9
|
+
# Calculate the HMAC for the given data and secret.
|
10
|
+
def self.calculated_hmac(body, secret)
|
11
|
+
digest = OpenSSL::Digest.new('sha256')
|
12
|
+
Base64.encode64(OpenSSL::HMAC.digest(digest, secret, body)).strip
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class DiscoApp::ProxyService
|
2
|
+
|
3
|
+
# Return true iff the signature provided in the given query string matches
|
4
|
+
# that calculated from the remaining query parameters and the given secret.
|
5
|
+
def self.proxy_signature_is_valid?(query_string, secret)
|
6
|
+
query_hash = Rack::Utils.parse_query(query_string)
|
7
|
+
signature = query_hash.delete('signature').to_s
|
8
|
+
ActiveSupport::SecurityUtils.variable_size_secure_compare(self.calculated_signature(query_hash, secret), signature)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Return the calculated signature for the given query hash and secret.
|
12
|
+
def self.calculated_signature(query_hash, secret)
|
13
|
+
sorted_params = query_hash.collect{ |k, v| "#{k}=#{Array(v).join(',')}" }.sort.join
|
14
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret, sorted_params)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
class DiscoApp::WebhookService
|
2
2
|
|
3
3
|
# Return true iff the provided hmac_to_verify matches that calculated from the
|
4
|
-
#
|
4
|
+
# given data and secret.
|
5
5
|
def self.is_valid_hmac?(body, secret, hmac_to_verify)
|
6
|
-
self.calculated_hmac(body, secret)
|
6
|
+
ActiveSupport::SecurityUtils.secure_compare(self.calculated_hmac(body, secret), hmac_to_verify.to_s)
|
7
7
|
end
|
8
8
|
|
9
9
|
# Calculate the HMAC for the given data and secret.
|
@@ -27,7 +27,7 @@
|
|
27
27
|
<% else %>
|
28
28
|
<div class="alert alert-success">
|
29
29
|
<p>
|
30
|
-
Thanks for installing <%=
|
30
|
+
Thanks for installing <%= DiscoApp.configuration.app_name %>!
|
31
31
|
</p>
|
32
32
|
<p>
|
33
33
|
Before we start setting things up, we need you to authorize a charge for the application.
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module DiscoApp
|
2
|
+
|
3
|
+
class Configuration
|
4
|
+
|
5
|
+
# Required configuration.
|
6
|
+
attr_accessor :app_name
|
7
|
+
|
8
|
+
# Set the below if using an application proxy.
|
9
|
+
attr_accessor :app_proxy_prefix
|
10
|
+
|
11
|
+
# Optional configuration, usually useful for development environments.
|
12
|
+
attr_accessor :skip_proxy_verification
|
13
|
+
alias_method :skip_proxy_verification?, :skip_proxy_verification
|
14
|
+
attr_accessor :skip_webhook_verification
|
15
|
+
alias_method :skip_webhook_verification?, :skip_webhook_verification
|
16
|
+
attr_accessor :skip_carrier_request_verification
|
17
|
+
alias_method :skip_carrier_request_verification?, :skip_carrier_request_verification
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.configuration
|
22
|
+
@configuration ||= Configuration.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.configuration=(config)
|
26
|
+
@configuration = config
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.configure
|
30
|
+
yield configuration
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/lib/disco_app/version.rb
CHANGED
data/lib/disco_app.rb
CHANGED
@@ -33,6 +33,7 @@ class DiscoAppGenerator < Rails::Generators::Base
|
|
33
33
|
gem 'bootstrap-sass', '~> 3.3.5.1'
|
34
34
|
gem 'activerecord-session_store', '~> 0.1.2'
|
35
35
|
gem 'activeresource', github: 'shopify/activeresource', tag: '4.2-threadsafe'
|
36
|
+
gem 'rails-bigint-pk', '~> 1.2.0'
|
36
37
|
|
37
38
|
# Add gems for development and testing only.
|
38
39
|
gem_group :development, :test do
|
@@ -92,15 +93,6 @@ class DiscoAppGenerator < Rails::Generators::Base
|
|
92
93
|
application "routes.default_url_options[:host] = ENV['DEFAULT_HOST']\n"
|
93
94
|
application "# Set the default host for absolute URL routing purposes"
|
94
95
|
|
95
|
-
# Add loading of the default application proxy prefix to set up support for
|
96
|
-
# reverse routings absolute proxy URLS.
|
97
|
-
application "config.x.shopify_app_proxy_prefix = ENV['SHOPIFY_APP_PROXY_PREFIX']\n"
|
98
|
-
application "# Set the application proxy path for absolute URL routing purposes"
|
99
|
-
|
100
|
-
# Add the Shopify application name to the configuration.
|
101
|
-
application "config.x.shopify_app_name = ENV['SHOPIFY_APP_NAME']\n"
|
102
|
-
application "# Set the name of the application"
|
103
|
-
|
104
96
|
# Copy over the default puma configuration.
|
105
97
|
copy_file 'config/puma.rb', 'config/puma.rb'
|
106
98
|
end
|
@@ -110,10 +102,11 @@ class DiscoAppGenerator < Rails::Generators::Base
|
|
110
102
|
route "mount DiscoApp::Engine, at: '/'"
|
111
103
|
end
|
112
104
|
|
113
|
-
# Run
|
114
|
-
def
|
105
|
+
# Run generators.
|
106
|
+
def run_generators
|
115
107
|
generate 'shopify_app:install'
|
116
108
|
generate 'shopify_app:home_controller'
|
109
|
+
generate 'bigint_pk:install'
|
117
110
|
end
|
118
111
|
|
119
112
|
# Copy template files to the appropriate location. In some cases, we'll be
|
@@ -7,9 +7,9 @@ module DiscoApp
|
|
7
7
|
# Install the Rollbar, OJ and New Relic gems.
|
8
8
|
def install_gems
|
9
9
|
# Add gem to Gemfile
|
10
|
-
gem 'rollbar', '~> 2.
|
11
|
-
gem 'oj', '~> 2.14.
|
12
|
-
gem 'newrelic_rpm', '~> 3.14.1.
|
10
|
+
gem 'rollbar', '~> 2.8.0'
|
11
|
+
gem 'oj', '~> 2.14.5'
|
12
|
+
gem 'newrelic_rpm', '~> 3.14.1.314'
|
13
13
|
|
14
14
|
# Install gem.
|
15
15
|
Bundler.with_clean_env do
|
@@ -1 +1,14 @@
|
|
1
|
-
DiscoApp::Engine.routes.default_url_options[:host] = ENV['DEFAULT_HOST']
|
1
|
+
DiscoApp::Engine.routes.default_url_options[:host] = ENV['DEFAULT_HOST']
|
2
|
+
|
3
|
+
DiscoApp.configure do |config|
|
4
|
+
# Required configuration.
|
5
|
+
config.app_name = ENV['SHOPIFY_APP_NAME']
|
6
|
+
|
7
|
+
# Set the below if using an application proxy.
|
8
|
+
config.app_proxy_prefix = ENV['SHOPIFY_APP_PROXY_PREFIX']
|
9
|
+
|
10
|
+
# Optional configuration, usually useful for development environments.
|
11
|
+
config.skip_proxy_verification = ENV['SKIP_PROXY_VERIFICATION']
|
12
|
+
config.skip_webhook_verification = ENV['SKIP_WEBHOOK_VERIFICATION']
|
13
|
+
config.skip_carrier_request_verification = ENV['SKIP_CARRIER_REQUEST_VERIFICATION']
|
14
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ProxyControllerTest < ActionController::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@shop = disco_app_shops(:widget_store)
|
7
|
+
@secret = ShopifyApp.configuration.secret
|
8
|
+
end
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
@shop = nil
|
12
|
+
@secret = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
test 'app proxy request without authentication information returns unauthorized' do
|
16
|
+
get(:index)
|
17
|
+
assert_response :unauthorized
|
18
|
+
end
|
19
|
+
|
20
|
+
test 'app proxy request with incorrect authentication information returns unauthorized' do
|
21
|
+
get(:index, proxy_params(shop: @shop.shopify_domain).merge(signature: 'invalid_signature'))
|
22
|
+
assert_response :unauthorized
|
23
|
+
end
|
24
|
+
|
25
|
+
test 'app proxy request with correct authentication information returns ok and has shop context' do
|
26
|
+
get(:index, proxy_params(shop: @shop.shopify_domain))
|
27
|
+
assert_response :ok
|
28
|
+
assert_equal @shop, assigns(:shop)
|
29
|
+
end
|
30
|
+
|
31
|
+
test 'app proxy request with correct authentication information but unknown shop returns 404' do
|
32
|
+
get(:index, proxy_params(shop: 'unknown.myshopify.com'))
|
33
|
+
assert_response :not_found
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def proxy_params(**params)
|
39
|
+
params.merge(signature: DiscoApp::ProxyService.calculated_signature(params, @secret))
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -21,9 +21,6 @@ module Dummy
|
|
21
21
|
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
|
22
22
|
# config.i18n.default_locale = :de
|
23
23
|
|
24
|
-
# Set the name of the application
|
25
|
-
config.x.shopify_app_name = ENV['SHOPIFY_APP_NAME']
|
26
|
-
|
27
24
|
# Set the default host for absolute URL routing purposes
|
28
25
|
routes.default_url_options[:host] = ENV['DEFAULT_HOST']
|
29
26
|
|
@@ -1 +1,14 @@
|
|
1
|
-
DiscoApp::Engine.routes.default_url_options[:host] = ENV['DEFAULT_HOST']
|
1
|
+
DiscoApp::Engine.routes.default_url_options[:host] = ENV['DEFAULT_HOST']
|
2
|
+
|
3
|
+
DiscoApp.configure do |config|
|
4
|
+
# Required configuration.
|
5
|
+
config.app_name = ENV['SHOPIFY_APP_NAME']
|
6
|
+
|
7
|
+
# Set the below if using an application proxy.
|
8
|
+
config.app_proxy_prefix = ENV['SHOPIFY_APP_PROXY_PREFIX']
|
9
|
+
|
10
|
+
# Optional configuration, usually useful for development environments.
|
11
|
+
config.skip_proxy_verification = ENV['SKIP_PROXY_VERIFICATION']
|
12
|
+
config.skip_webhook_verification = ENV['SKIP_WEBHOOK_VERIFICATION']
|
13
|
+
config.skip_carrier_request_verification = ENV['SKIP_CARRIER_REQUEST_VERIFICATION']
|
14
|
+
end
|
data/test/dummy/config/routes.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: disco_app
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gavin Ballard
|
@@ -301,11 +301,14 @@ files:
|
|
301
301
|
- app/models/disco_app/concerns/plan.rb
|
302
302
|
- app/models/disco_app/concerns/shop.rb
|
303
303
|
- app/models/disco_app/concerns/subscription.rb
|
304
|
+
- app/models/disco_app/concerns/synchronises_with_shopify.rb
|
304
305
|
- app/models/disco_app/plan.rb
|
305
306
|
- app/models/disco_app/session_storage.rb
|
306
307
|
- app/models/disco_app/shop.rb
|
307
308
|
- app/models/disco_app/subscription.rb
|
309
|
+
- app/services/disco_app/carrier_request_service.rb
|
308
310
|
- app/services/disco_app/charges_service.rb
|
311
|
+
- app/services/disco_app/proxy_service.rb
|
309
312
|
- app/services/disco_app/subscription_service.rb
|
310
313
|
- app/services/disco_app/webhook_service.rb
|
311
314
|
- app/views/disco_app/charges/activate.html.erb
|
@@ -333,6 +336,7 @@ files:
|
|
333
336
|
- db/migrate/20160112233706_create_disco_app_sessions.rb
|
334
337
|
- db/migrate/20160113194418_add_shop_id_to_disco_app_sessions.rb
|
335
338
|
- lib/disco_app.rb
|
339
|
+
- lib/disco_app/configuration.rb
|
336
340
|
- lib/disco_app/engine.rb
|
337
341
|
- lib/disco_app/session.rb
|
338
342
|
- lib/disco_app/support/file_fixtures.rb
|
@@ -362,12 +366,14 @@ files:
|
|
362
366
|
- test/controllers/disco_app/install_controller_test.rb
|
363
367
|
- test/controllers/disco_app/webhooks_controller_test.rb
|
364
368
|
- test/controllers/home_controller_test.rb
|
369
|
+
- test/controllers/proxy_controller_test.rb
|
365
370
|
- test/disco_app_test.rb
|
366
371
|
- test/dummy/Rakefile
|
367
372
|
- test/dummy/app/assets/javascripts/application.js
|
368
373
|
- test/dummy/app/assets/stylesheets/application.scss
|
369
374
|
- test/dummy/app/controllers/application_controller.rb
|
370
375
|
- test/dummy/app/controllers/home_controller.rb
|
376
|
+
- test/dummy/app/controllers/proxy_controller.rb
|
371
377
|
- test/dummy/app/helpers/application_helper.rb
|
372
378
|
- test/dummy/app/jobs/disco_app/app_uninstalled_job.rb
|
373
379
|
- test/dummy/app/models/disco_app/shop.rb
|
@@ -459,6 +465,7 @@ test_files:
|
|
459
465
|
- test/dummy/app/assets/javascripts/application.js
|
460
466
|
- test/dummy/app/jobs/disco_app/app_uninstalled_job.rb
|
461
467
|
- test/dummy/app/controllers/home_controller.rb
|
468
|
+
- test/dummy/app/controllers/proxy_controller.rb
|
462
469
|
- test/dummy/app/controllers/application_controller.rb
|
463
470
|
- test/dummy/app/helpers/application_helper.rb
|
464
471
|
- test/dummy/app/models/disco_app/shop.rb
|
@@ -499,6 +506,7 @@ test_files:
|
|
499
506
|
- test/jobs/disco_app/app_installed_job_test.rb
|
500
507
|
- test/controllers/disco_app/install_controller_test.rb
|
501
508
|
- test/controllers/disco_app/webhooks_controller_test.rb
|
509
|
+
- test/controllers/proxy_controller_test.rb
|
502
510
|
- test/controllers/home_controller_test.rb
|
503
511
|
- test/integration/navigation_test.rb
|
504
512
|
- test/models/disco_app/plan_test.rb
|