duodealer_app 1.0.0

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.
Files changed (212) hide show
  1. checksums.yaml +7 -0
  2. data/.!66854!duodealer_app.gemspec +0 -0
  3. data/.babelrc +5 -0
  4. data/.gitignore +16 -0
  5. data/.nvmrc +1 -0
  6. data/.rubocop.yml +263 -0
  7. data/.ruby-version +1 -0
  8. data/.travis.yml +27 -0
  9. data/Gemfile +8 -0
  10. data/LICENSE +19 -0
  11. data/README.md +553 -0
  12. data/Rakefile +6 -0
  13. data/app/assets/images/storage_access.svg +2 -0
  14. data/app/assets/javascripts/duodealer_app/enable_cookies.js +3 -0
  15. data/app/assets/javascripts/duodealer_app/itp_helper.js +40 -0
  16. data/app/assets/javascripts/duodealer_app/partition_cookies.js +8 -0
  17. data/app/assets/javascripts/duodealer_app/redirect.js +33 -0
  18. data/app/assets/javascripts/duodealer_app/request_storage_access.js +3 -0
  19. data/app/assets/javascripts/duodealer_app/storage_access.js +153 -0
  20. data/app/assets/javascripts/duodealer_app/storage_access_redirect.js +17 -0
  21. data/app/assets/javascripts/duodealer_app/top_level.js +2 -0
  22. data/app/assets/javascripts/duodealer_app/top_level_interaction.js +11 -0
  23. data/app/controllers/concerns/duodealer_app/authenticated.rb +15 -0
  24. data/app/controllers/concerns/duodealer_app/authenticated.rb-e +15 -0
  25. data/app/controllers/duodealer_app/authenticated_controller.rb +9 -0
  26. data/app/controllers/duodealer_app/authenticated_controller.rb-e +9 -0
  27. data/app/controllers/duodealer_app/callback_controller.rb +104 -0
  28. data/app/controllers/duodealer_app/callback_controller.rb-e +104 -0
  29. data/app/controllers/duodealer_app/extension_verification_controller.rb +19 -0
  30. data/app/controllers/duodealer_app/extension_verification_controller.rb-e +19 -0
  31. data/app/controllers/duodealer_app/sessions_controller.rb +159 -0
  32. data/app/controllers/duodealer_app/sessions_controller.rb-e +159 -0
  33. data/app/controllers/duodealer_app/webhooks_controller.rb +37 -0
  34. data/app/controllers/duodealer_app/webhooks_controller.rb-e +37 -0
  35. data/app/views/duodealer_app/partials/_button_styles.html.erb +104 -0
  36. data/app/views/duodealer_app/partials/_button_styles.html.erb-e +104 -0
  37. data/app/views/duodealer_app/partials/_card_styles.html.erb +33 -0
  38. data/app/views/duodealer_app/partials/_card_styles.html.erb-e +33 -0
  39. data/app/views/duodealer_app/partials/_empty_state_styles.html.erb +129 -0
  40. data/app/views/duodealer_app/partials/_empty_state_styles.html.erb-e +129 -0
  41. data/app/views/duodealer_app/partials/_layout_styles.html.erb +167 -0
  42. data/app/views/duodealer_app/partials/_layout_styles.html.erb-e +167 -0
  43. data/app/views/duodealer_app/partials/_typography_styles.html.erb +35 -0
  44. data/app/views/duodealer_app/partials/_typography_styles.html.erb-e +35 -0
  45. data/app/views/duodealer_app/sessions/enable_cookies.html.erb +75 -0
  46. data/app/views/duodealer_app/sessions/enable_cookies.html.erb-e +75 -0
  47. data/app/views/duodealer_app/sessions/new.html.erb +123 -0
  48. data/app/views/duodealer_app/sessions/new.html.erb-e +123 -0
  49. data/app/views/duodealer_app/sessions/request_storage_access.html.erb +68 -0
  50. data/app/views/duodealer_app/sessions/request_storage_access.html.erb-e +68 -0
  51. data/app/views/duodealer_app/sessions/top_level_interaction.html.erb +64 -0
  52. data/app/views/duodealer_app/sessions/top_level_interaction.html.erb-e +64 -0
  53. data/app/views/duodealer_app/shared/redirect.html.erb +23 -0
  54. data/app/views/duodealer_app/shared/redirect.html.erb-e +23 -0
  55. data/config/locales/cs.yml +23 -0
  56. data/config/locales/da.yml +20 -0
  57. data/config/locales/de.yml +22 -0
  58. data/config/locales/en.yml +15 -0
  59. data/config/locales/es.yml +22 -0
  60. data/config/locales/fi.yml +20 -0
  61. data/config/locales/fr.yml +23 -0
  62. data/config/locales/hi.yml +23 -0
  63. data/config/locales/it.yml +21 -0
  64. data/config/locales/ja.yml +17 -0
  65. data/config/locales/ko.yml +19 -0
  66. data/config/locales/ms.yml +22 -0
  67. data/config/locales/nb.yml +21 -0
  68. data/config/locales/nl.yml +21 -0
  69. data/config/locales/pl.yml +21 -0
  70. data/config/locales/pt-BR.yml +21 -0
  71. data/config/locales/pt-PT.yml +22 -0
  72. data/config/locales/sv.yml +21 -0
  73. data/config/locales/th.yml +20 -0
  74. data/config/locales/tr.yml +22 -0
  75. data/config/locales/zh-CN.yml +16 -0
  76. data/config/locales/zh-TW.yml +16 -0
  77. data/config/routes.rb +22 -0
  78. data/docs/.!20385!test-your-app.png +0 -0
  79. data/docs/.!20388!install-on-dev-shop.png +0 -0
  80. data/docs/.!62511!test-your-app.png +0 -0
  81. data/docs/.!62512!install-on-dev-shop.png +0 -0
  82. data/docs/.!62763!test-your-app.png +0 -0
  83. data/docs/.!62765!install-on-dev-shop.png +0 -0
  84. data/docs/.!63018!test-your-app.png +0 -0
  85. data/docs/.!63020!install-on-dev-shop.png +0 -0
  86. data/docs/.!63289!test-your-app.png +0 -0
  87. data/docs/.!63291!install-on-dev-shop.png +0 -0
  88. data/docs/.!63562!test-your-app.png +0 -0
  89. data/docs/.!63564!install-on-dev-shop.png +0 -0
  90. data/docs/.!63872!test-your-app.png +0 -0
  91. data/docs/.!63874!install-on-dev-shop.png +0 -0
  92. data/docs/.!64151!test-your-app.png +0 -0
  93. data/docs/.!64153!install-on-dev-shop.png +0 -0
  94. data/docs/.!64428!test-your-app.png +0 -0
  95. data/docs/.!64431!install-on-dev-shop.png +0 -0
  96. data/docs/.!64737!test-your-app.png +0 -0
  97. data/docs/.!64740!install-on-dev-shop.png +0 -0
  98. data/docs/.!65025!test-your-app.png +0 -0
  99. data/docs/.!65028!install-on-dev-shop.png +0 -0
  100. data/docs/.!65324!test-your-app.png +0 -0
  101. data/docs/.!65327!install-on-dev-shop.png +0 -0
  102. data/docs/.!65626!test-your-app.png +0 -0
  103. data/docs/.!65629!install-on-dev-shop.png +0 -0
  104. data/docs/.!65942!test-your-app.png +0 -0
  105. data/docs/.!65945!install-on-dev-shop.png +0 -0
  106. data/docs/.!66760!test-your-app.png +0 -0
  107. data/docs/.!66763!install-on-dev-shop.png +0 -0
  108. data/docs/.!67028!test-your-app.png +0 -0
  109. data/docs/.!67031!install-on-dev-shop.png +0 -0
  110. data/docs/.!67657!test-your-app.png +0 -0
  111. data/docs/.!67660!install-on-dev-shop.png +0 -0
  112. data/docs/.!68031!test-your-app.png +0 -0
  113. data/docs/.!68034!install-on-dev-shop.png +0 -0
  114. data/docs/.!68363!test-your-app.png +0 -0
  115. data/docs/.!68366!install-on-dev-shop.png +0 -0
  116. data/docs/Quickstart.md +103 -0
  117. data/docs/Releasing.md +17 -0
  118. data/docs/Troubleshooting.md +16 -0
  119. data/docs/install-on-dev-shop.png +0 -0
  120. data/docs/test-your-app.png +0 -0
  121. data/duodealer_app.gemspec +34 -0
  122. data/images/.!20334!app-proxy-screenshot.png +0 -0
  123. data/images/.!62504!app-proxy-screenshot.png +0 -0
  124. data/images/.!62754!app-proxy-screenshot.png +0 -0
  125. data/images/.!63008!app-proxy-screenshot.png +0 -0
  126. data/images/.!63277!app-proxy-screenshot.png +0 -0
  127. data/images/.!63548!app-proxy-screenshot.png +0 -0
  128. data/images/.!63855!app-proxy-screenshot.png +0 -0
  129. data/images/.!64132!app-proxy-screenshot.png +0 -0
  130. data/images/.!64407!app-proxy-screenshot.png +0 -0
  131. data/images/.!64714!app-proxy-screenshot.png +0 -0
  132. data/images/.!65000!app-proxy-screenshot.png +0 -0
  133. data/images/.!65296!app-proxy-screenshot.png +0 -0
  134. data/images/.!65594!app-proxy-screenshot.png +0 -0
  135. data/images/.!65908!app-proxy-screenshot.png +0 -0
  136. data/images/.!66724!app-proxy-screenshot.png +0 -0
  137. data/images/.!66989!app-proxy-screenshot.png +0 -0
  138. data/images/.!67614!app-proxy-screenshot.png +0 -0
  139. data/images/.!67986!app-proxy-screenshot.png +0 -0
  140. data/images/.!68314!app-proxy-screenshot.png +0 -0
  141. data/images/app-proxy-screenshot.png +0 -0
  142. data/karma.conf.js +44 -0
  143. data/lib/duodealer_app.rb +54 -0
  144. data/lib/duodealer_app/configuration.rb +85 -0
  145. data/lib/duodealer_app/controller_concerns/app_proxy_verification.rb +39 -0
  146. data/lib/duodealer_app/controller_concerns/embedded_app.rb +20 -0
  147. data/lib/duodealer_app/controller_concerns/itp.rb +44 -0
  148. data/lib/duodealer_app/controller_concerns/localization.rb +23 -0
  149. data/lib/duodealer_app/controller_concerns/login_protection.rb +180 -0
  150. data/lib/duodealer_app/controller_concerns/webhook_verification.rb +39 -0
  151. data/lib/duodealer_app/engine.rb +22 -0
  152. data/lib/duodealer_app/jobs/scripttags_manager_job.rb +17 -0
  153. data/lib/duodealer_app/jobs/webhooks_manager_job.rb +17 -0
  154. data/lib/duodealer_app/managers/scripttags_manager.rb +78 -0
  155. data/lib/duodealer_app/managers/webhooks_manager.rb +62 -0
  156. data/lib/duodealer_app/middleware/same_site_cookie_middleware.rb +69 -0
  157. data/lib/duodealer_app/session/in_memory_session_store.rb +29 -0
  158. data/lib/duodealer_app/session/session_repository.rb +33 -0
  159. data/lib/duodealer_app/session/session_storage.rb +31 -0
  160. data/lib/duodealer_app/session/storage_strategies/shop_storage_strategy.rb +25 -0
  161. data/lib/duodealer_app/session/storage_strategies/user_storage_strategy.rb +26 -0
  162. data/lib/duodealer_app/utils.rb +24 -0
  163. data/lib/duodealer_app/version.rb +5 -0
  164. data/lib/generators/duodealer_app/add_after_authenticate_job/add_after_authenticate_job_generator.rb +46 -0
  165. data/lib/generators/duodealer_app/add_after_authenticate_job/templates/after_authenticate_job.rb +10 -0
  166. data/lib/generators/duodealer_app/add_marketing_activity_extension/add_marketing_activity_extension_generator.rb +40 -0
  167. data/lib/generators/duodealer_app/add_marketing_activity_extension/templates/marketing_activities_controller.rb +62 -0
  168. data/lib/generators/duodealer_app/add_webhook/add_webhook_generator.rb +69 -0
  169. data/lib/generators/duodealer_app/add_webhook/templates/webhook_job.rb +8 -0
  170. data/lib/generators/duodealer_app/app_proxy_controller/app_proxy_controller_generator.rb +27 -0
  171. data/lib/generators/duodealer_app/app_proxy_controller/templates/app_proxy_controller.rb +9 -0
  172. data/lib/generators/duodealer_app/app_proxy_controller/templates/app_proxy_route.rb +10 -0
  173. data/lib/generators/duodealer_app/app_proxy_controller/templates/index.html.erb +19 -0
  174. data/lib/generators/duodealer_app/authenticated_controller/authenticated_controller_generator.rb +15 -0
  175. data/lib/generators/duodealer_app/authenticated_controller/templates/authenticated_controller.rb +5 -0
  176. data/lib/generators/duodealer_app/controllers/controllers_generator.rb +30 -0
  177. data/lib/generators/duodealer_app/duodealer_app_generator.rb +19 -0
  178. data/lib/generators/duodealer_app/home_controller/home_controller_generator.rb +27 -0
  179. data/lib/generators/duodealer_app/home_controller/templates/home_controller.rb +8 -0
  180. data/lib/generators/duodealer_app/home_controller/templates/index.html.erb +21 -0
  181. data/lib/generators/duodealer_app/install/install_generator.rb +83 -0
  182. data/lib/generators/duodealer_app/install/templates/_flash_messages.html.erb +3 -0
  183. data/lib/generators/duodealer_app/install/templates/duodealer_app.js +15 -0
  184. data/lib/generators/duodealer_app/install/templates/duodealer_app.rb +15 -0
  185. data/lib/generators/duodealer_app/install/templates/duodealer_app_index.js +2 -0
  186. data/lib/generators/duodealer_app/install/templates/duodealer_provider.rb +20 -0
  187. data/lib/generators/duodealer_app/install/templates/embedded_app.html.erb +41 -0
  188. data/lib/generators/duodealer_app/install/templates/flash_messages.js +26 -0
  189. data/lib/generators/duodealer_app/install/templates/omniauth.rb +2 -0
  190. data/lib/generators/duodealer_app/install/templates/session_store.rb +4 -0
  191. data/lib/generators/duodealer_app/install/templates/user_agent.rb +5 -0
  192. data/lib/generators/duodealer_app/rotate_duodealer_token_job/rotate_duodealer_token_job_generator.rb +16 -0
  193. data/lib/generators/duodealer_app/rotate_duodealer_token_job/templates/rotate_duodealer_token.rake +17 -0
  194. data/lib/generators/duodealer_app/rotate_duodealer_token_job/templates/rotate_duodealer_token_job.rb +42 -0
  195. data/lib/generators/duodealer_app/routes/routes_generator.rb +32 -0
  196. data/lib/generators/duodealer_app/routes/templates/routes.rb +11 -0
  197. data/lib/generators/duodealer_app/shop_model/shop_model_generator.rb +39 -0
  198. data/lib/generators/duodealer_app/shop_model/templates/db/migrate/create_shops.erb +15 -0
  199. data/lib/generators/duodealer_app/shop_model/templates/shop.rb +7 -0
  200. data/lib/generators/duodealer_app/shop_model/templates/shops.yml +3 -0
  201. data/lib/generators/duodealer_app/user_model/templates/db/migrate/create_users.erb +16 -0
  202. data/lib/generators/duodealer_app/user_model/templates/user.rb +7 -0
  203. data/lib/generators/duodealer_app/user_model/templates/users.yml +4 -0
  204. data/lib/generators/duodealer_app/user_model/user_model_generator.rb +39 -0
  205. data/lib/generators/duodealer_app/views/views_generator.rb +30 -0
  206. data/package-lock.json +7224 -0
  207. data/package.json +28 -0
  208. data/shipit.rubygems.yml +4 -0
  209. data/translation.yml +7 -0
  210. data/webpack.config.js +24 -0
  211. data/yarn.lock +5263 -0
  212. metadata +447 -0
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuodealerApp
4
+ # Performs login after OAuth completes
5
+ class CallbackController < ApplicationController
6
+ include DuodealerApp::LoginProtection
7
+
8
+ def callback
9
+ if auth_hash
10
+ login_shop
11
+ install_webhooks
12
+ install_scripttags
13
+ perform_after_authenticate_job
14
+
15
+ redirect_to return_address
16
+ else
17
+ flash[:error] = I18n.t("could_not_log_in")
18
+ redirect_to(login_url_with_optional_shop)
19
+ end
20
+ end
21
+
22
+ private
23
+ def login_shop
24
+ reset_session_options
25
+ set_duodealer_session
26
+ end
27
+
28
+ def auth_hash
29
+ request.env["omniauth.auth"]
30
+ end
31
+
32
+ def shop_name
33
+ auth_hash.uid
34
+ end
35
+
36
+ def associated_user
37
+ return if auth_hash["extra"].blank?
38
+
39
+ auth_hash["extra"]["associated_user"]
40
+ end
41
+
42
+ def token
43
+ auth_hash["credentials"]["token"]
44
+ end
45
+
46
+ def reset_session_options
47
+ request.session_options[:renew] = true
48
+ session.delete(:_csrf_token)
49
+ end
50
+
51
+ def set_duodealer_session
52
+ session_store = DuodealerAPI::Session.new(
53
+ domain: shop_name,
54
+ token: token,
55
+ api_version: DuodealerApp.configuration.api_version
56
+ )
57
+ session[:duodealer] = DuodealerApp::SessionRepository.store(session_store, user: associated_user)
58
+ session[:duodealer_domain] = shop_name
59
+ session[:duodealer_user] = associated_user
60
+
61
+ if DuodealerApp.configuration.per_user_tokens?
62
+ # Adds the user_session to the session to determine if the logged in user has changed
63
+ user_session = auth_hash&.extra&.session
64
+ raise IndexError, "Missing user session signature" if user_session.nil?
65
+ session[:user_session] = user_session
66
+ end
67
+ end
68
+
69
+ def install_webhooks
70
+ return unless DuodealerApp.configuration.has_webhooks?
71
+
72
+ WebhooksManager.queue(
73
+ shop_name,
74
+ token,
75
+ DuodealerApp.configuration.webhooks
76
+ )
77
+ end
78
+
79
+ def install_scripttags
80
+ return unless DuodealerApp.configuration.has_scripttags?
81
+
82
+ ScripttagsManager.queue(
83
+ shop_name,
84
+ token,
85
+ DuodealerApp.configuration.scripttags
86
+ )
87
+ end
88
+
89
+ def perform_after_authenticate_job
90
+ config = DuodealerApp.configuration.after_authenticate_job
91
+
92
+ return unless config && config[:job].present?
93
+
94
+ job = config[:job]
95
+ job = job.constantize if job.is_a?(String)
96
+
97
+ if config[:inline] == true
98
+ job.perform_now(shop_domain: session[:duodealer_domain])
99
+ else
100
+ job.perform_later(shop_domain: session[:duodealer_domain])
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuodealerApp
4
+ class ExtensionVerificationController < ApplicationController
5
+ protect_from_forgery with: :null_session
6
+ before_action :verify_request
7
+
8
+ private
9
+ def verify_request
10
+ hmac_header = request.headers["HTTP_X_DUODEALER_HMAC_SHA256"]
11
+ request_body = request.body.read
12
+ secret = DuodealerApp.configuration.secret
13
+ digest = OpenSSL::Digest.new("sha256")
14
+
15
+ expected_hmac = Base64.strict_encode64(OpenSSL::HMAC.digest(digest, secret, request_body))
16
+ head(:unauthorized) unless ActiveSupport::SecurityUtils.secure_compare(expected_hmac, hmac_header)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuodealerApp
4
+ class ExtensionVerificationController < ApplicationController
5
+ protect_from_forgery with: :null_session
6
+ before_action :verify_request
7
+
8
+ private
9
+ def verify_request
10
+ hmac_header = request.headers["HTTP_X_DUODEALER_HMAC_SHA256"]
11
+ request_body = request.body.read
12
+ secret = DuodealerApp.configuration.secret
13
+ digest = OpenSSL::Digest.new("sha256")
14
+
15
+ expected_hmac = Base64.strict_encode64(OpenSSL::HMAC.digest(digest, secret, request_body))
16
+ head(:unauthorized) unless ActiveSupport::SecurityUtils.secure_compare(expected_hmac, hmac_header)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuodealerApp
4
+ class SessionsController < ApplicationController # rubocop:disable Metrics/ClassLength
5
+ include DuodealerApp::LoginProtection
6
+
7
+ layout false, only: :new
8
+ after_action only: [:new, :create] do |controller|
9
+ controller.response.headers.except!("X-Frame-Options")
10
+ end
11
+
12
+ def new
13
+ authenticate if sanitized_shop_name.present?
14
+ end
15
+
16
+ def create
17
+ authenticate
18
+ end
19
+
20
+ def enable_cookies
21
+ return unless validate_shop
22
+
23
+ render(:enable_cookies, layout: false, locals: {
24
+ does_not_have_storage_access_url: top_level_interaction_path(
25
+ shop: sanitized_shop_name,
26
+ return_to: params[:return_to]
27
+ ),
28
+ has_storage_access_url: login_url_with_optional_shop(top_level: true),
29
+ app_target_url: granted_storage_access_path(
30
+ shop: sanitized_shop_name,
31
+ return_to: params[:return_to]
32
+ ),
33
+ current_duodealer_domain: current_duodealer_domain
34
+ })
35
+ end
36
+
37
+ def top_level_interaction
38
+ @url = login_url_with_optional_shop(top_level: true)
39
+ validate_shop
40
+ end
41
+
42
+ def granted_storage_access
43
+ return unless validate_shop
44
+
45
+ session["duodealer.granted_storage_access"] = true
46
+
47
+ copy_return_to_param_to_session
48
+
49
+ redirect_to(return_address_with_params({ shop: @shop }))
50
+ end
51
+
52
+ def destroy
53
+ reset_session
54
+ flash[:notice] = I18n.t(".logged_out")
55
+ redirect_to(login_url_with_optional_shop)
56
+ end
57
+
58
+ private
59
+ def authenticate
60
+ return render_invalid_shop_error if sanitized_shop_name.blank?
61
+ session["duodealer.omniauth_params"] = { shop: sanitized_shop_name }
62
+
63
+ copy_return_to_param_to_session
64
+
65
+ if user_agent_can_partition_cookies
66
+ authenticate_with_partitioning
67
+ else
68
+ authenticate_normally
69
+ end
70
+ end
71
+
72
+ def authenticate_normally
73
+ if request_storage_access?
74
+ redirect_to_request_storage_access
75
+ elsif authenticate_in_context?
76
+ authenticate_in_context
77
+ else
78
+ authenticate_at_top_level
79
+ end
80
+ end
81
+
82
+ def authenticate_with_partitioning
83
+ if session["duodealer.cookies_persist"]
84
+ clear_top_level_oauth_cookie
85
+ authenticate_in_context
86
+ else
87
+ set_top_level_oauth_cookie
88
+ enable_cookie_access
89
+ end
90
+ end
91
+
92
+ def validate_shop
93
+ @shop = sanitized_shop_name
94
+ unless @shop
95
+ render_invalid_shop_error
96
+ return false
97
+ end
98
+
99
+ true
100
+ end
101
+
102
+ def copy_return_to_param_to_session
103
+ session[:return_to] = params[:return_to] if params[:return_to]
104
+ end
105
+
106
+ def render_invalid_shop_error
107
+ flash[:error] = I18n.t("invalid_shop_url")
108
+ redirect_to return_address
109
+ end
110
+
111
+ def enable_cookie_access
112
+ fullpage_redirect_to(enable_cookies_path(
113
+ shop: sanitized_shop_name,
114
+ return_to: session[:return_to]
115
+ ))
116
+ end
117
+
118
+ def authenticate_in_context
119
+ redirect_to "#{main_app.root_path}auth/duodealer"
120
+ end
121
+
122
+ def authenticate_at_top_level
123
+ fullpage_redirect_to(login_url_with_optional_shop(top_level: true))
124
+ end
125
+
126
+ def authenticate_in_context?
127
+ return true unless DuodealerApp.configuration.embedded_app?
128
+ params[:top_level]
129
+ end
130
+
131
+ def request_storage_access?
132
+ return false unless DuodealerApp.configuration.embedded_app?
133
+ return false if params[:top_level]
134
+ return false if user_agent_is_mobile
135
+ return false if user_agent_is_pos
136
+
137
+ !session["duodealer.granted_storage_access"]
138
+ end
139
+
140
+ def redirect_to_request_storage_access
141
+ render(
142
+ :request_storage_access,
143
+ layout: false,
144
+ locals: {
145
+ does_not_have_storage_access_url: top_level_interaction_path(
146
+ shop: sanitized_shop_name,
147
+ return_to: session[:return_to]
148
+ ),
149
+ has_storage_access_url: login_url_with_optional_shop(top_level: true),
150
+ app_target_url: granted_storage_access_path(
151
+ shop: sanitized_shop_name,
152
+ return_to: session[:return_to]
153
+ ),
154
+ current_duodealer_domain: current_duodealer_domain
155
+ }
156
+ )
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuodealerApp
4
+ class SessionsController < ApplicationController # rubocop:disable Metrics/ClassLength
5
+ include DuodealerApp::LoginProtection
6
+
7
+ layout false, only: :new
8
+ after_action only: [:new, :create] do |controller|
9
+ controller.response.headers.except!("X-Frame-Options")
10
+ end
11
+
12
+ def new
13
+ authenticate if sanitized_shop_name.present?
14
+ end
15
+
16
+ def create
17
+ authenticate
18
+ end
19
+
20
+ def enable_cookies
21
+ return unless validate_shop
22
+
23
+ render(:enable_cookies, layout: false, locals: {
24
+ does_not_have_storage_access_url: top_level_interaction_path(
25
+ shop: sanitized_shop_name,
26
+ return_to: params[:return_to]
27
+ ),
28
+ has_storage_access_url: login_url_with_optional_shop(top_level: true),
29
+ app_target_url: granted_storage_access_path(
30
+ shop: sanitized_shop_name,
31
+ return_to: params[:return_to]
32
+ ),
33
+ current_duodealer_domain: current_duodealer_domain
34
+ })
35
+ end
36
+
37
+ def top_level_interaction
38
+ @url = login_url_with_optional_shop(top_level: true)
39
+ validate_shop
40
+ end
41
+
42
+ def granted_storage_access
43
+ return unless validate_shop
44
+
45
+ session["duodealer.granted_storage_access"] = true
46
+
47
+ copy_return_to_param_to_session
48
+
49
+ redirect_to(return_address_with_params({ shop: @shop }))
50
+ end
51
+
52
+ def destroy
53
+ reset_session
54
+ flash[:notice] = I18n.t(".logged_out")
55
+ redirect_to(login_url_with_optional_shop)
56
+ end
57
+
58
+ private
59
+ def authenticate
60
+ return render_invalid_shop_error if sanitized_shop_name.blank?
61
+ session["duodealer.omniauth_params"] = { shop: sanitized_shop_name }
62
+
63
+ copy_return_to_param_to_session
64
+
65
+ if user_agent_can_partition_cookies
66
+ authenticate_with_partitioning
67
+ else
68
+ authenticate_normally
69
+ end
70
+ end
71
+
72
+ def authenticate_normally
73
+ if request_storage_access?
74
+ redirect_to_request_storage_access
75
+ elsif authenticate_in_context?
76
+ authenticate_in_context
77
+ else
78
+ authenticate_at_top_level
79
+ end
80
+ end
81
+
82
+ def authenticate_with_partitioning
83
+ if session["duodealer.cookies_persist"]
84
+ clear_top_level_oauth_cookie
85
+ authenticate_in_context
86
+ else
87
+ set_top_level_oauth_cookie
88
+ enable_cookie_access
89
+ end
90
+ end
91
+
92
+ def validate_shop
93
+ @shop = sanitized_shop_name
94
+ unless @shop
95
+ render_invalid_shop_error
96
+ return false
97
+ end
98
+
99
+ true
100
+ end
101
+
102
+ def copy_return_to_param_to_session
103
+ session[:return_to] = params[:return_to] if params[:return_to]
104
+ end
105
+
106
+ def render_invalid_shop_error
107
+ flash[:error] = I18n.t("invalid_shop_url")
108
+ redirect_to return_address
109
+ end
110
+
111
+ def enable_cookie_access
112
+ fullpage_redirect_to(enable_cookies_path(
113
+ shop: sanitized_shop_name,
114
+ return_to: session[:return_to]
115
+ ))
116
+ end
117
+
118
+ def authenticate_in_context
119
+ redirect_to "#{main_app.root_path}auth/duodealer"
120
+ end
121
+
122
+ def authenticate_at_top_level
123
+ fullpage_redirect_to(login_url_with_optional_shop(top_level: true))
124
+ end
125
+
126
+ def authenticate_in_context?
127
+ return true unless DuodealerApp.configuration.embedded_app?
128
+ params[:top_level]
129
+ end
130
+
131
+ def request_storage_access?
132
+ return false unless DuodealerApp.configuration.embedded_app?
133
+ return false if params[:top_level]
134
+ return false if user_agent_is_mobile
135
+ return false if user_agent_is_pos
136
+
137
+ !session["duodealer.granted_storage_access"]
138
+ end
139
+
140
+ def redirect_to_request_storage_access
141
+ render(
142
+ :request_storage_access,
143
+ layout: false,
144
+ locals: {
145
+ does_not_have_storage_access_url: top_level_interaction_path(
146
+ shop: sanitized_shop_name,
147
+ return_to: session[:return_to]
148
+ ),
149
+ has_storage_access_url: login_url_with_optional_shop(top_level: true),
150
+ app_target_url: granted_storage_access_path(
151
+ shop: sanitized_shop_name,
152
+ return_to: session[:return_to]
153
+ ),
154
+ current_duodealer_domain: current_duodealer_domain
155
+ }
156
+ )
157
+ end
158
+ end
159
+ end