shopify_app 7.2.0 → 8.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +5 -5
  2. data/.babelrc +5 -0
  3. data/.github/CODEOWNERS +1 -0
  4. data/{ISSUE_TEMPLATE.md → .github/ISSUE_TEMPLATE.md} +0 -0
  5. data/.github/probots.yml +2 -0
  6. data/.gitignore +5 -0
  7. data/.nvmrc +1 -0
  8. data/.rubocop.yml +10 -0
  9. data/.ruby-version +1 -0
  10. data/.travis.yml +24 -12
  11. data/CHANGELOG.md +151 -0
  12. data/Gemfile +2 -0
  13. data/README.md +167 -68
  14. data/app/assets/images/storage_access.svg +2 -0
  15. data/app/assets/javascripts/shopify_app/enable_cookies.js +3 -0
  16. data/app/assets/javascripts/shopify_app/itp_helper.js +40 -0
  17. data/app/assets/javascripts/shopify_app/partition_cookies.js +7 -0
  18. data/app/assets/javascripts/shopify_app/redirect.js +33 -0
  19. data/app/assets/javascripts/shopify_app/request_storage_access.js +3 -0
  20. data/app/assets/javascripts/shopify_app/storage_access.js +121 -0
  21. data/app/assets/javascripts/shopify_app/storage_access_redirect.js +17 -0
  22. data/app/assets/javascripts/shopify_app/top_level.js +2 -0
  23. data/app/assets/javascripts/shopify_app/top_level_interaction.js +11 -0
  24. data/app/controllers/shopify_app/authenticated_controller.rb +5 -2
  25. data/app/controllers/shopify_app/callback_controller.rb +92 -0
  26. data/app/controllers/shopify_app/sessions_controller.rb +120 -2
  27. data/app/controllers/shopify_app/webhooks_controller.rb +11 -3
  28. data/app/views/shopify_app/partials/_button_styles.html.erb +104 -0
  29. data/app/views/shopify_app/partials/_card_styles.html.erb +33 -0
  30. data/app/views/shopify_app/partials/_empty_state_styles.html.erb +129 -0
  31. data/app/views/shopify_app/partials/_layout_styles.html.erb +167 -0
  32. data/app/views/shopify_app/partials/_typography_styles.html.erb +35 -0
  33. data/app/views/shopify_app/sessions/enable_cookies.html.erb +59 -0
  34. data/app/views/shopify_app/sessions/new.html.erb +88 -60
  35. data/app/views/shopify_app/sessions/request_storage_access.html.erb +67 -0
  36. data/app/views/shopify_app/sessions/top_level_interaction.html.erb +63 -0
  37. data/app/views/shopify_app/shared/redirect.html.erb +22 -0
  38. data/config/locales/de.yml +22 -0
  39. data/config/locales/en.yml +12 -1
  40. data/config/locales/es.yml +21 -3
  41. data/config/locales/fr.yml +23 -0
  42. data/config/locales/it.yml +22 -0
  43. data/config/locales/ja.yml +17 -0
  44. data/config/locales/nl.yml +21 -0
  45. data/config/locales/pt-BR.yml +22 -0
  46. data/config/locales/zh-CN.yml +16 -0
  47. data/config/locales/zh-TW.yml +17 -0
  48. data/config/routes.rb +11 -1
  49. data/{QUICKSTART.md → docs/Quickstart.md} +26 -23
  50. data/docs/Releasing.md +18 -0
  51. data/docs/Troubleshooting.md +16 -0
  52. data/karma.conf.js +43 -0
  53. data/lib/generators/shopify_app/add_after_authenticate_job/add_after_authenticate_job_generator.rb +45 -0
  54. data/lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb +10 -0
  55. data/lib/generators/shopify_app/home_controller/templates/home_controller.rb +1 -0
  56. data/lib/generators/shopify_app/home_controller/templates/index.html.erb +14 -0
  57. data/lib/generators/shopify_app/home_controller/templates/shopify_app_ready_script.html.erb +1 -5
  58. data/lib/generators/shopify_app/install/install_generator.rb +10 -16
  59. data/lib/generators/shopify_app/install/templates/_flash_messages.html.erb +13 -9
  60. data/lib/generators/shopify_app/install/templates/embedded_app.html.erb +1 -1
  61. data/lib/generators/shopify_app/install/templates/shopify_app.rb +5 -3
  62. data/lib/generators/shopify_app/install/templates/shopify_provider.rb +19 -4
  63. data/lib/generators/shopify_app/rotate_shopify_token_job/rotate_shopify_token_job_generator.rb +16 -0
  64. data/lib/generators/shopify_app/rotate_shopify_token_job/templates/rotate_shopify_token.rake +17 -0
  65. data/lib/generators/shopify_app/rotate_shopify_token_job/templates/rotate_shopify_token_job.rb +42 -0
  66. data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +5 -9
  67. data/lib/generators/shopify_app/shop_model/templates/db/migrate/{create_shops.rb → create_shops.erb} +1 -1
  68. data/lib/generators/shopify_app/shop_model/templates/shop.rb +0 -1
  69. data/lib/shopify_app/configuration.rb +27 -8
  70. data/lib/shopify_app/{app_proxy_verification.rb → controller_concerns/app_proxy_verification.rb} +2 -7
  71. data/lib/shopify_app/controller_concerns/embedded_app.rb +19 -0
  72. data/lib/shopify_app/controller_concerns/itp.rb +45 -0
  73. data/lib/shopify_app/controller_concerns/localization.rb +22 -0
  74. data/lib/shopify_app/controller_concerns/login_protection.rb +135 -0
  75. data/lib/shopify_app/{webhook_verification.rb → controller_concerns/webhook_verification.rb} +11 -12
  76. data/lib/shopify_app/engine.rb +10 -0
  77. data/lib/shopify_app/{scripttags_manager_job.rb → jobs/scripttags_manager_job.rb} +2 -2
  78. data/lib/shopify_app/{webhooks_manager_job.rb → jobs/webhooks_manager_job.rb} +0 -0
  79. data/lib/shopify_app/{scripttags_manager.rb → managers/scripttags_manager.rb} +24 -8
  80. data/lib/shopify_app/{webhooks_manager.rb → managers/webhooks_manager.rb} +1 -1
  81. data/lib/shopify_app/session/in_memory_session_store.rb +27 -0
  82. data/lib/shopify_app/{shopify_session_repository.rb → session/session_repository.rb} +0 -0
  83. data/lib/shopify_app/{session_storage.rb → session/session_storage.rb} +9 -0
  84. data/lib/shopify_app/utils.rb +2 -2
  85. data/lib/shopify_app/version.rb +1 -1
  86. data/lib/shopify_app.rb +21 -16
  87. data/package-lock.json +23 -0
  88. data/package.json +28 -0
  89. data/service.yml +7 -0
  90. data/shipit.rubygems.yml +2 -0
  91. data/shopify_app.gemspec +6 -5
  92. data/translation.yml +7 -0
  93. data/webpack.config.js +24 -0
  94. data/yarn.lock +4594 -0
  95. metadata +92 -35
  96. data/Gemfile.rails50 +0 -5
  97. data/Gemfile.ruby22 +0 -6
  98. data/Gemfile.ruby22.rails50 +0 -9
  99. data/RELEASING +0 -13
  100. data/lib/generators/shopify_app/install/templates/shopify_session_repository.rb +0 -23
  101. data/lib/generators/shopify_app/shop_model/templates/shopify_session_repository.rb +0 -7
  102. data/lib/shopify_app/in_memory_session_store.rb +0 -25
  103. data/lib/shopify_app/login_protection.rb +0 -103
  104. data/lib/shopify_app/sessions_concern.rb +0 -101
  105. data/lib/shopify_app/shop.rb +0 -15
@@ -12,11 +12,11 @@ module ShopifyApp
12
12
  end
13
13
 
14
14
  def create_shop_migration
15
- copy_migration 'create_shops.rb'
15
+ migration_template 'db/migrate/create_shops.erb', 'db/migrate/create_shops.rb'
16
16
  end
17
17
 
18
- def create_session_storage_initializer
19
- copy_file 'shopify_session_repository.rb', 'config/initializers/shopify_session_repository.rb', force: true
18
+ def update_shopify_app_initializer
19
+ gsub_file 'config/initializers/shopify_app.rb', 'ShopifyApp::InMemorySessionStore', 'Shop'
20
20
  end
21
21
 
22
22
  def create_shop_fixtures
@@ -25,12 +25,8 @@ module ShopifyApp
25
25
 
26
26
  private
27
27
 
28
- def copy_migration(migration_name, config = {})
29
- migration_template(
30
- "db/migrate/#{migration_name}",
31
- "db/migrate/#{migration_name}",
32
- config
33
- )
28
+ def rails_migration_version
29
+ Rails.version.match(/\d\.\d/)[0]
34
30
  end
35
31
 
36
32
  # for generating a timestamp when using `create_migration`
@@ -1,4 +1,4 @@
1
- class CreateShops < ActiveRecord::Migration
1
+ class CreateShops < ActiveRecord::Migration[<%= rails_migration_version %>]
2
2
  def self.up
3
3
  create_table :shops do |t|
4
4
  t.string :shopify_domain, null: false
@@ -1,4 +1,3 @@
1
1
  class Shop < ActiveRecord::Base
2
- include ShopifyApp::Shop
3
2
  include ShopifyApp::SessionStorage
4
3
  end
@@ -7,11 +7,18 @@ module ShopifyApp
7
7
  attr_accessor :application_name
8
8
  attr_accessor :api_key
9
9
  attr_accessor :secret
10
+ attr_accessor :old_secret
10
11
  attr_accessor :scope
11
12
  attr_accessor :embedded_app
12
13
  alias_method :embedded_app?, :embedded_app
13
14
  attr_accessor :webhooks
14
15
  attr_accessor :scripttags
16
+ attr_accessor :after_authenticate_job
17
+ attr_accessor :session_repository
18
+
19
+ # customise urls
20
+ attr_accessor :root_url
21
+ attr_accessor :login_url
15
22
 
16
23
  # customise ActiveJob queue names
17
24
  attr_accessor :scripttags_manager_queue_name
@@ -20,24 +27,36 @@ module ShopifyApp
20
27
  # configure myshopify domain for local shopify development
21
28
  attr_accessor :myshopify_domain
22
29
 
30
+ # allow namespacing webhook jobs
31
+ attr_accessor :webhook_jobs_namespace
32
+
23
33
  def initialize
34
+ @root_url = '/'
24
35
  @myshopify_domain = 'myshopify.com'
36
+ @scripttags_manager_queue_name = Rails.application.config.active_job.queue_name
37
+ @webhooks_manager_queue_name = Rails.application.config.active_job.queue_name
25
38
  end
26
39
 
27
- def has_webhooks?
28
- webhooks.present?
40
+ def login_url
41
+ @login_url || File.join(@root_url, 'login')
29
42
  end
30
43
 
31
- def has_scripttags?
32
- scripttags.present?
44
+ def session_repository=(klass)
45
+ if Rails.configuration.cache_classes
46
+ ShopifyApp::SessionRepository.storage = klass
47
+ else
48
+ ActiveSupport::Reloader.to_prepare do
49
+ ShopifyApp::SessionRepository.storage = klass
50
+ end
51
+ end
33
52
  end
34
53
 
35
- def scripttags_manager_queue_name
36
- @scripttags_manager_queue_name ||= Rails.application.config.active_job.queue_name
54
+ def has_webhooks?
55
+ webhooks.present?
37
56
  end
38
57
 
39
- def webhooks_manager_queue_name
40
- @webhooks_manager_queue_name ||= Rails.application.config.active_job.queue_name
58
+ def has_scripttags?
59
+ scripttags.present?
41
60
  end
42
61
  end
43
62
 
@@ -3,17 +3,12 @@ module ShopifyApp
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- if Rails.version >= '5.0'
7
- skip_before_action :verify_authenticity_token, raise: false
8
- else
9
- skip_before_action :verify_authenticity_token
10
- end
11
-
6
+ skip_before_action :verify_authenticity_token, raise: false
12
7
  before_action :verify_proxy_request
13
8
  end
14
9
 
15
10
  def verify_proxy_request
16
- return head :unauthorized unless query_string_valid?(request.query_string)
11
+ return head :forbidden unless query_string_valid?(request.query_string)
17
12
  end
18
13
 
19
14
  private
@@ -0,0 +1,19 @@
1
+ module ShopifyApp
2
+ module EmbeddedApp
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ if ShopifyApp.configuration.embedded_app?
7
+ after_action :set_esdk_headers
8
+ layout 'embedded_app'
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def set_esdk_headers
15
+ response.set_header('P3P', 'CP="Not used"')
16
+ response.headers.except!('X-Frame-Options')
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyApp
4
+ # Cookie management helpers required for ITP implementation
5
+ module Itp
6
+ private
7
+
8
+ def set_test_cookie
9
+ return unless ShopifyApp.configuration.embedded_app?
10
+ return unless user_agent_can_partition_cookies
11
+
12
+ session['shopify.cookies_persist'] = true
13
+ end
14
+
15
+ def set_top_level_oauth_cookie
16
+ session['shopify.top_level_oauth'] = true
17
+ end
18
+
19
+ def clear_top_level_oauth_cookie
20
+ session.delete('shopify.top_level_oauth')
21
+ end
22
+
23
+ def user_agent_is_mobile
24
+ user_agent = BrowserSniffer.new(request.user_agent).browser_info
25
+
26
+ user_agent[:name].to_s.match(/Shopify\sMobile/)
27
+ end
28
+
29
+ def user_agent_is_pos
30
+ user_agent = BrowserSniffer.new(request.user_agent).browser_info
31
+
32
+ user_agent[:name].to_s.match(/Shopify\sPOS/)
33
+ end
34
+
35
+ def user_agent_can_partition_cookies
36
+ user_agent = BrowserSniffer.new(request.user_agent).browser_info
37
+
38
+ is_safari = user_agent[:name].to_s.match(/Safari/)
39
+
40
+ return false unless is_safari
41
+
42
+ user_agent[:version].to_s.match(/12\.0/)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,22 @@
1
+ module ShopifyApp
2
+ module Localization
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_action :set_locale
7
+ end
8
+
9
+ private
10
+
11
+ def set_locale
12
+ if params[:locale]
13
+ session[:locale] = params[:locale]
14
+ else
15
+ session[:locale] ||= I18n.default_locale
16
+ end
17
+ I18n.locale = session[:locale]
18
+ rescue I18n::InvalidLocale
19
+ I18n.locale = I18n.default_locale
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'browser_sniffer'
4
+
5
+ module ShopifyApp
6
+ module LoginProtection
7
+ extend ActiveSupport::Concern
8
+ include ShopifyApp::Itp
9
+
10
+ class ShopifyDomainNotFound < StandardError; end
11
+
12
+ included do
13
+ after_action :set_test_cookie
14
+ rescue_from ActiveResource::UnauthorizedAccess, :with => :close_session
15
+ end
16
+
17
+ def shopify_session
18
+ return redirect_to_login unless shop_session
19
+ clear_top_level_oauth_cookie
20
+
21
+ begin
22
+ ShopifyAPI::Base.activate_session(shop_session)
23
+ yield
24
+ ensure
25
+ ShopifyAPI::Base.clear_session
26
+ end
27
+ end
28
+
29
+ def shop_session
30
+ return unless session[:shopify]
31
+ @shop_session ||= ShopifyApp::SessionRepository.retrieve(session[:shopify])
32
+ end
33
+
34
+ def login_again_if_different_shop
35
+ if shop_session && params[:shop] && params[:shop].is_a?(String) && (shop_session.url != params[:shop])
36
+ clear_shop_session
37
+ redirect_to_login
38
+ end
39
+ end
40
+
41
+ protected
42
+
43
+ def redirect_to_login
44
+ if request.xhr?
45
+ head :unauthorized
46
+ else
47
+ if request.get?
48
+ session[:return_to] = "#{request.path}?#{sanitized_params.to_query}"
49
+ end
50
+ redirect_to login_url
51
+ end
52
+ end
53
+
54
+ def close_session
55
+ clear_shop_session
56
+ redirect_to login_url
57
+ end
58
+
59
+ def clear_shop_session
60
+ session[:shopify] = nil
61
+ session[:shopify_domain] = nil
62
+ session[:shopify_user] = nil
63
+ end
64
+
65
+ def login_url(top_level: false)
66
+ url = ShopifyApp.configuration.login_url
67
+
68
+ query_params = login_url_params(top_level: top_level)
69
+
70
+ url = "#{url}?#{query_params.to_query}" if query_params.present?
71
+ url
72
+ end
73
+
74
+ def login_url_params(top_level:)
75
+ query_params = {}
76
+ query_params[:shop] = sanitized_params[:shop] if params[:shop].present?
77
+
78
+ has_referer_shop_name = referer_sanitized_shop_name.present?
79
+
80
+ if has_referer_shop_name
81
+ query_params[:shop] ||= referer_sanitized_shop_name
82
+ end
83
+
84
+ query_params[:top_level] = true if top_level
85
+ query_params
86
+ end
87
+
88
+ def fullpage_redirect_to(url)
89
+ if ShopifyApp.configuration.embedded_app?
90
+ render 'shopify_app/shared/redirect', layout: false, locals: { url: url, current_shopify_domain: current_shopify_domain }
91
+ else
92
+ redirect_to url
93
+ end
94
+ end
95
+
96
+ def current_shopify_domain
97
+ shopify_domain = sanitized_shop_name || session[:shopify_domain]
98
+ return shopify_domain if shopify_domain.present?
99
+
100
+ raise ShopifyDomainNotFound
101
+ end
102
+
103
+ def sanitized_shop_name
104
+ @sanitized_shop_name ||= sanitize_shop_param(params)
105
+ end
106
+
107
+ def referer_sanitized_shop_name
108
+ return unless request.referer.present?
109
+
110
+ @referer_sanitized_shop_name ||= begin
111
+ referer_uri = URI(request.referer)
112
+ query_params = Rack::Utils.parse_query(referer_uri.query)
113
+
114
+ sanitize_shop_param(query_params.with_indifferent_access)
115
+ end
116
+ end
117
+
118
+ def sanitize_shop_param(params)
119
+ return unless params[:shop].present?
120
+ ShopifyApp::Utils.sanitize_shop_domain(params[:shop])
121
+ end
122
+
123
+ def sanitized_params
124
+ request.query_parameters.clone.tap do |query_params|
125
+ if params[:shop].is_a?(String)
126
+ query_params[:shop] = sanitize_shop_param(params)
127
+ end
128
+ end
129
+ end
130
+
131
+ def return_address
132
+ session.delete(:return_to) || ShopifyApp.configuration.root_url
133
+ end
134
+ end
135
+ end
@@ -3,12 +3,7 @@ module ShopifyApp
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- if Rails.version >= '5.0'
7
- skip_before_action :verify_authenticity_token, raise: false
8
- else
9
- skip_before_action :verify_authenticity_token
10
- end
11
-
6
+ skip_before_action :verify_authenticity_token, raise: false
12
7
  before_action :verify_request
13
8
  end
14
9
 
@@ -20,12 +15,16 @@ module ShopifyApp
20
15
  end
21
16
 
22
17
  def hmac_valid?(data)
23
- secret = ShopifyApp.configuration.secret
24
- digest = OpenSSL::Digest.new('sha256')
25
- ActiveSupport::SecurityUtils.variable_size_secure_compare(
26
- shopify_hmac,
27
- Base64.encode64(OpenSSL::HMAC.digest(digest, secret, data)).strip
28
- )
18
+ secrets = [ShopifyApp.configuration.secret, ShopifyApp.configuration.old_secret].reject(&:blank?)
19
+
20
+ secrets.any? do |secret|
21
+ digest = OpenSSL::Digest.new('sha256')
22
+
23
+ ActiveSupport::SecurityUtils.secure_compare(
24
+ shopify_hmac,
25
+ Base64.strict_encode64(OpenSSL::HMAC.digest(digest, secret, data))
26
+ )
27
+ end
29
28
  end
30
29
 
31
30
  def shop_domain
@@ -2,5 +2,15 @@ module ShopifyApp
2
2
  class Engine < Rails::Engine
3
3
  engine_name 'shopify_app'
4
4
  isolate_namespace ShopifyApp
5
+
6
+ initializer "shopify_app.assets.precompile" do |app|
7
+ app.config.assets.precompile += %w[
8
+ shopify_app/redirect.js
9
+ shopify_app/top_level.js
10
+ shopify_app/enable_cookies.js
11
+ shopify_app/request_storage_access.js
12
+ storage_access.svg
13
+ ]
14
+ end
5
15
  end
6
16
  end
@@ -2,12 +2,12 @@ module ShopifyApp
2
2
  class ScripttagsManagerJob < ActiveJob::Base
3
3
 
4
4
  queue_as do
5
- ShopifyApp.configuration.webhooks_manager_queue_name
5
+ ShopifyApp.configuration.scripttags_manager_queue_name
6
6
  end
7
7
 
8
8
  def perform(shop_domain:, shop_token:, scripttags:)
9
9
  ShopifyAPI::Session.temp(shop_domain, shop_token) do
10
- manager = ScripttagsManager.new(scripttags)
10
+ manager = ScripttagsManager.new(scripttags, shop_domain)
11
11
  manager.create_scripttags
12
12
  end
13
13
  end
@@ -6,14 +6,25 @@ module ShopifyApp
6
6
  ShopifyApp::ScripttagsManagerJob.perform_later(
7
7
  shop_domain: shop_domain,
8
8
  shop_token: shop_token,
9
- scripttags: scripttags
9
+ # Procs cannot be serialized so we interpolate now, if necessary
10
+ scripttags: build_src(scripttags, shop_domain)
10
11
  )
11
12
  end
12
13
 
13
- attr_reader :required_scripttags
14
+ def self.build_src(scripttags, domain)
15
+ scripttags.map do |tag|
16
+ next tag unless tag[:src].respond_to?(:call)
17
+ tag = tag.dup
18
+ tag[:src] = tag[:src].call(domain)
19
+ tag
20
+ end
21
+ end
22
+
23
+ attr_reader :required_scripttags, :shop_domain
14
24
 
15
- def initialize(scripttags)
25
+ def initialize(scripttags, shop_domain)
16
26
  @required_scripttags = scripttags
27
+ @shop_domain = shop_domain
17
28
  end
18
29
 
19
30
  def recreate_scripttags!
@@ -24,14 +35,15 @@ module ShopifyApp
24
35
  def create_scripttags
25
36
  return unless required_scripttags.present?
26
37
 
27
- required_scripttags.each do |scripttag|
38
+ expanded_scripttags.each do |scripttag|
28
39
  create_scripttag(scripttag) unless scripttag_exists?(scripttag[:src])
29
40
  end
30
41
  end
31
42
 
32
43
  def destroy_scripttags
33
- ShopifyAPI::ScriptTag.all.each do |scripttag|
34
- ShopifyAPI::ScriptTag.delete(scripttag.id) if is_required_scripttag?(scripttag)
44
+ scripttags = expanded_scripttags
45
+ ShopifyAPI::ScriptTag.all.each do |tag|
46
+ ShopifyAPI::ScriptTag.delete(tag.id) if is_required_scripttag?(scripttags, tag)
35
47
  end
36
48
 
37
49
  @current_scripttags = nil
@@ -39,8 +51,12 @@ module ShopifyApp
39
51
 
40
52
  private
41
53
 
42
- def is_required_scripttag?(scripttag)
43
- required_scripttags.map{ |w| w[:src] }.include? scripttag.src
54
+ def expanded_scripttags
55
+ self.class.build_src(required_scripttags, shop_domain)
56
+ end
57
+
58
+ def is_required_scripttag?(scripttags, tag)
59
+ scripttags.map{ |w| w[:src] }.include? tag.src
44
60
  end
45
61
 
46
62
  def create_scripttag(attributes)
@@ -30,7 +30,7 @@ module ShopifyApp
30
30
  end
31
31
 
32
32
  def destroy_webhooks
33
- ShopifyAPI::Webhook.all.each do |webhook|
33
+ ShopifyAPI::Webhook.all.to_a.each do |webhook|
34
34
  ShopifyAPI::Webhook.delete(webhook.id) if is_required_webhook?(webhook)
35
35
  end
36
36
 
@@ -0,0 +1,27 @@
1
+ module ShopifyApp
2
+ class InMemorySessionStore
3
+ class EnvironmentError < StandardError; end
4
+
5
+ def self.retrieve(id)
6
+ repo[id]
7
+ end
8
+
9
+ def self.store(session)
10
+ id = SecureRandom.uuid
11
+ repo[id] = session
12
+ id
13
+ end
14
+
15
+ def self.clear
16
+ @@repo = nil
17
+ end
18
+
19
+ def self.repo
20
+ if Rails.env.production?
21
+ raise EnvironmentError.new("Cannot use InMemorySessionStore in a Production environment. \
22
+ Please initialize ShopifyApp with a model that can store and retrieve sessions")
23
+ end
24
+ @@repo ||= {}
25
+ end
26
+ end
27
+ end
@@ -2,6 +2,15 @@ module ShopifyApp
2
2
  module SessionStorage
3
3
  extend ActiveSupport::Concern
4
4
 
5
+ included do
6
+ validates :shopify_domain, presence: true, uniqueness: true
7
+ validates :shopify_token, presence: true
8
+ end
9
+
10
+ def with_shopify_session(&block)
11
+ ShopifyAPI::Session.temp(shopify_domain, shopify_token, &block)
12
+ end
13
+
5
14
  class_methods do
6
15
  def store(session)
7
16
  shop = self.find_or_initialize_by(shopify_domain: session.url)
@@ -2,12 +2,12 @@ module ShopifyApp
2
2
  module Utils
3
3
 
4
4
  def self.sanitize_shop_domain(shop_domain)
5
- name = shop_domain.to_s.strip
5
+ name = shop_domain.to_s.downcase.strip
6
6
  name += ".#{ShopifyApp.configuration.myshopify_domain}" if !name.include?("#{ShopifyApp.configuration.myshopify_domain}") && !name.include?(".")
7
7
  name.sub!(%r|https?://|, '')
8
8
 
9
9
  u = URI("http://#{name}")
10
- u.host && u.host.ends_with?(".#{ShopifyApp.configuration.myshopify_domain}") ? u.host : nil
10
+ u.host if u.host&.match(/^[a-z0-9][a-z0-9\-]*[a-z0-9]\.#{Regexp.escape(ShopifyApp.configuration.myshopify_domain)}$/)
11
11
  rescue URI::InvalidURIError
12
12
  nil
13
13
  end
@@ -1,3 +1,3 @@
1
1
  module ShopifyApp
2
- VERSION = '7.2.0'
2
+ VERSION = '8.5.0'.freeze
3
3
  end
data/lib/shopify_app.rb CHANGED
@@ -10,21 +10,26 @@ require 'shopify_app/configuration'
10
10
  # engine
11
11
  require 'shopify_app/engine'
12
12
 
13
- # jobs
14
- require 'shopify_app/webhooks_manager_job'
15
- require 'shopify_app/scripttags_manager_job'
16
-
17
- # helpers and concerns
18
- require 'shopify_app/shop'
19
- require 'shopify_app/session_storage'
20
- require 'shopify_app/sessions_concern'
21
- require 'shopify_app/login_protection'
22
- require 'shopify_app/webhooks_manager'
23
- require 'shopify_app/scripttags_manager'
24
- require 'shopify_app/webhook_verification'
25
- require 'shopify_app/app_proxy_verification'
13
+ # utils
26
14
  require 'shopify_app/utils'
27
15
 
28
- # session repository
29
- require 'shopify_app/shopify_session_repository'
30
- require 'shopify_app/in_memory_session_store'
16
+ # controller concerns
17
+ require 'shopify_app/controller_concerns/localization'
18
+ require 'shopify_app/controller_concerns/itp'
19
+ require 'shopify_app/controller_concerns/login_protection'
20
+ require 'shopify_app/controller_concerns/embedded_app'
21
+ require 'shopify_app/controller_concerns/webhook_verification'
22
+ require 'shopify_app/controller_concerns/app_proxy_verification'
23
+
24
+ # jobs
25
+ require 'shopify_app/jobs/webhooks_manager_job'
26
+ require 'shopify_app/jobs/scripttags_manager_job'
27
+
28
+ # managers
29
+ require 'shopify_app/managers/webhooks_manager'
30
+ require 'shopify_app/managers/scripttags_manager'
31
+
32
+ # session
33
+ require 'shopify_app/session/session_storage'
34
+ require 'shopify_app/session/session_repository'
35
+ require 'shopify_app/session/in_memory_session_store'
data/package-lock.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "shopify_app",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 1,
5
+ "requires": true,
6
+ "dependencies": {
7
+ "karma-mocha": {
8
+ "version": "1.3.0",
9
+ "resolved": "https://registry.npmjs.org/karma-mocha/-/karma-mocha-1.3.0.tgz",
10
+ "integrity": "sha1-7qrH/8DiAetjxGdEDStpx883eL8=",
11
+ "dev": true,
12
+ "requires": {
13
+ "minimist": "1.2.0"
14
+ }
15
+ },
16
+ "minimist": {
17
+ "version": "1.2.0",
18
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
19
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
20
+ "dev": true
21
+ }
22
+ }
23
+ }
data/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "shopify_app",
3
+ "version": "8.4.2",
4
+ "repository": "git@github.com:Shopify/shopify_app.git",
5
+ "author": "Shopify",
6
+ "license": "MIT",
7
+ "dependencies": {},
8
+ "devDependencies": {
9
+ "babel-loader": "^8.0.2",
10
+ "babel-preset-shopify": "^16.5.0",
11
+ "chai": "^4.1.2",
12
+ "karma": "^3.0.0",
13
+ "karma-chai-sinon": "^0.1.5",
14
+ "karma-chrome-launcher": "^2.2.0",
15
+ "karma-cli": "^1.0.1",
16
+ "karma-mocha": "^1.3.0",
17
+ "karma-mocha-clean-reporter": "^0.0.1",
18
+ "karma-mocha-debug": "^0.1.2",
19
+ "karma-webpack": "^3.0.5",
20
+ "mocha-debug": "^0.0.1",
21
+ "sinon": "^6.3.4",
22
+ "sinon-chai": "^3.2.0",
23
+ "webpack": "^4.20.2"
24
+ },
25
+ "scripts": {
26
+ "test": "./node_modules/.bin/karma start --browsers ChromeHeadless --single-run"
27
+ }
28
+ }
data/service.yml ADDED
@@ -0,0 +1,7 @@
1
+ audience: partner
2
+ classification: library
3
+ org_line: App & Partner Platform
4
+ owners:
5
+ - Shopify/app-partner-dev-tools-education
6
+ slack_channels:
7
+ - dev-tools-education
data/shipit.rubygems.yml CHANGED
@@ -1 +1,3 @@
1
1
  # using the default shipit config
2
+ hide:
3
+ - code-review/policial