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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd66d842458698a15b8be312434d4b07f3df1b8671eb5a21c56fb505a2c0aa45
4
- data.tar.gz: 4fe046f44e70debb6d73636e9e84ff8bcaa76398764117f099220ba3750e5134
3
+ metadata.gz: '0874200090d68b8be05ff9d0a17c96a91896d0065b810041a459da29d8a2a9a8'
4
+ data.tar.gz: 75ab0d47c9b75349bf6ac5d4aeba7d531a904ea1331de3df34d5145af922c931
5
5
  SHA512:
6
- metadata.gz: ae57c0e9ce4c3c3d709bda36580a6510cf6466d1de31e961167665ebe71e3e58a12bf96d1880da7f3288dfb884bca4aeee936e42acffbb7917e3799a7db42da7
7
- data.tar.gz: 5ce8ff4597bcd3c9c848d076b5ebbb11db1a47a8fdea11006d6e8fc825a87df675d935582352281b673691f3f2d88f27e3e8fe07c83b7eee9237923a04d557f7
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 unless Rails.env.production?
24
- query_hash = Rack::Utils.parse_query(request.query_string)
25
- signature = query_hash.delete("signature")
26
- sorted_params = query_hash.collect{ |k, v| "#{k}=#{Array(v).join(',')}" }.sort.join
27
- calculated_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ShopifyApp.configuration.secret, sorted_params)
28
- signature == calculated_signature
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 :verify_carrier_request_signature
6
+ before_action :verify_carrier_request
7
7
  end
8
8
 
9
9
  private
10
10
 
11
- def verify_carrier_request_signature
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 unless Rails.env.production?
19
- data = request.body.read.to_s
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 DiscoApp::WebhookService.is_valid_hmac?(request.body.read.to_s, ShopifyApp.configuration.secret, request.headers['HTTP_X_SHOPIFY_HMAC_SHA256'])
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
@@ -30,7 +30,7 @@ module DiscoApp::Concerns::SynchroniseCarrierServiceJob
30
30
  protected
31
31
 
32
32
  def carrier_service_name
33
- Rails.application.config.x.shopify_app_name
33
+ DiscoApp.configuration.app_name
34
34
  end
35
35
 
36
36
  def callback_url
@@ -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: Rails.configuration.x.shopify_app_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
- # give data and secret.
4
+ # given data and secret.
5
5
  def self.is_valid_hmac?(body, secret, hmac_to_verify)
6
- self.calculated_hmac(body, secret) == hmac_to_verify
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 <%= Rails.configuration.x.shopify_app_name %>!
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
@@ -1,3 +1,3 @@
1
1
  module DiscoApp
2
- VERSION = '0.8.3'
2
+ VERSION = '0.8.4'
3
3
  end
data/lib/disco_app.rb CHANGED
@@ -1,4 +1,6 @@
1
- require "disco_app/engine"
1
+ require 'disco_app/version'
2
+ require 'disco_app/configuration'
3
+ require 'disco_app/engine'
2
4
 
3
5
  module DiscoApp
4
6
  end
@@ -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 shopify_app:install and shopify_app:home_controller
114
- def shopify_app_install
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.7.1'
11
- gem 'oj', '~> 2.14.3'
12
- gem 'newrelic_rpm', '~> 3.14.1.311'
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
@@ -0,0 +1,8 @@
1
+ class ProxyController < ActionController::Base
2
+ include DiscoApp::AppProxyController
3
+
4
+ def index
5
+ render text: 'ok'
6
+ end
7
+
8
+ 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
@@ -2,6 +2,8 @@ Rails.application.routes.draw do
2
2
 
3
3
  root to: 'home#index'
4
4
 
5
+ get '/proxy', to: 'proxy#index'
6
+
5
7
  mount ShopifyApp::Engine, at: '/'
6
8
  mount DiscoApp::Engine, at: '/'
7
9
 
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.3
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