disco_app 0.8.3 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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