session_test.rb 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. require 'test_helper'
  2. require 'timecop'
  3. class SessionTest < Test::Unit::TestCase
  4. def setup
  5. super
  6. ShopifyAPI::Session.secret = 'secret'
  7. end
  8. test "not be valid without a url" do
  9. session = ShopifyAPI::Session.new(domain: nil, token: "any-token", api_version: any_api_version)
  10. assert_not session.valid?
  11. end
  12. test "not be valid without token" do
  13. session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: nil, api_version: any_api_version)
  14. assert_not session.valid?
  15. end
  16. test "not be valid without an api version" do
  17. session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: "any-token", api_version: nil)
  18. assert_not session.valid?
  19. session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: "any-token", api_version: ShopifyAPI::ApiVersion::NullVersion)
  20. assert_not session.valid?
  21. end
  22. test "be valid with any token, any url and version" do
  23. session = ShopifyAPI::Session.new(
  24. domain: "testshop.myshopify.com",
  25. token: "any-token",
  26. api_version: any_api_version
  27. )
  28. assert session.valid?
  29. end
  30. test "not raise error without params" do
  31. assert_nothing_raised do
  32. ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: "any-token", api_version: any_api_version)
  33. end
  34. end
  35. test "ignore everything but the subdomain in the shop" do
  36. assert_equal(
  37. "https://testshop.myshopify.com",
  38. ShopifyAPI::Session.new(
  39. domain: "http://user:pass@testshop.notshopify.net/path",
  40. token: "any-token",
  41. api_version: any_api_version
  42. ).site
  43. )
  44. end
  45. test "append the myshopify domain if not given" do
  46. assert_equal(
  47. "https://testshop.myshopify.com",
  48. ShopifyAPI::Session.new(domain: "testshop", token: "any-token", api_version: any_api_version).site
  49. )
  50. end
  51. test "not raise error without params" do
  52. assert_nothing_raised do
  53. ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: "any-token", api_version: any_api_version)
  54. end
  55. end
  56. test "raise error if params passed but signature omitted" do
  57. assert_raises(ShopifyAPI::ValidationException) do
  58. session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: nil, api_version: any_api_version)
  59. session.request_token({'code' => 'any-code'})
  60. end
  61. end
  62. test "setup api_key and secret for all sessions" do
  63. ShopifyAPI::Session.setup(:api_key => "My test key", :secret => "My test secret")
  64. assert_equal "My test key", ShopifyAPI::Session.api_key
  65. assert_equal "My test secret", ShopifyAPI::Session.secret
  66. end
  67. test "#temp reset ShopifyAPI::Base.site to original value" do
  68. session1 = ShopifyAPI::Session.new(domain: 'fakeshop.myshopify.com', token: 'token1', api_version: '2019-01')
  69. ShopifyAPI::Base.activate_session(session1)
  70. ShopifyAPI::Session.temp(domain: "testshop.myshopify.com", token: "any-token", api_version: :unstable) do
  71. @assigned_site = ShopifyAPI::Base.site
  72. @assigned_version = ShopifyAPI::Base.api_version
  73. end
  74. assert_equal('https://testshop.myshopify.com', @assigned_site.to_s)
  75. assert_equal('https://fakeshop.myshopify.com', ShopifyAPI::Base.site.to_s)
  76. assert_equal(ShopifyAPI::ApiVersion.new(handle: :unstable), @assigned_version)
  77. assert_equal(ShopifyAPI::ApiVersion.new(handle: '2019-01'), ShopifyAPI::Base.api_version)
  78. end
  79. test "#with_session activates the session for the duration of the block" do
  80. session1 = ShopifyAPI::Session.new(domain: 'fakeshop.myshopify.com', token: 'token1', api_version: '2019-01')
  81. ShopifyAPI::Base.activate_session(session1)
  82. other_session = ShopifyAPI::Session.new(
  83. domain: "testshop.myshopify.com",
  84. token: "any-token",
  85. api_version: :unstable
  86. )
  87. ShopifyAPI::Session.with_session(other_session) do
  88. @assigned_site = ShopifyAPI::Base.site
  89. @assigned_version = ShopifyAPI::Base.api_version
  90. end
  91. assert_equal('https://testshop.myshopify.com', @assigned_site.to_s)
  92. assert_equal('https://fakeshop.myshopify.com', ShopifyAPI::Base.site.to_s)
  93. assert_equal(ShopifyAPI::ApiVersion.new(handle: :unstable), @assigned_version)
  94. assert_equal(ShopifyAPI::ApiVersion.new(handle: '2019-01'), ShopifyAPI::Base.api_version)
  95. end
  96. test "#with_session resets the activated session even if there an exception during the block" do
  97. session1 = ShopifyAPI::Session.new(domain: 'fakeshop.myshopify.com', token: 'token1', api_version: '2019-01')
  98. ShopifyAPI::Base.activate_session(session1)
  99. other_session = ShopifyAPI::Session.new(
  100. domain: "testshop.myshopify.com",
  101. token: "any-token",
  102. api_version: :unstable
  103. )
  104. assert_raises StandardError do
  105. ShopifyAPI::Session.with_session(other_session) { raise StandardError, "" }
  106. end
  107. assert_equal('https://fakeshop.myshopify.com', ShopifyAPI::Base.site.to_s)
  108. assert_equal(ShopifyAPI::ApiVersion.new(handle: '2019-01'), ShopifyAPI::Base.api_version)
  109. end
  110. test "#with_version will adjust the actvated api version for the duration of the block" do
  111. session1 = ShopifyAPI::Session.new(domain: 'fakeshop.myshopify.com', token: 'token1', api_version: '2019-01')
  112. ShopifyAPI::Base.activate_session(session1)
  113. ShopifyAPI::Session.with_version(:unstable) do
  114. @assigned_site = ShopifyAPI::Base.site
  115. @assigned_version = ShopifyAPI::Base.api_version
  116. end
  117. assert_equal('https://fakeshop.myshopify.com', @assigned_site.to_s)
  118. assert_equal('https://fakeshop.myshopify.com', ShopifyAPI::Base.site.to_s)
  119. assert_equal(ShopifyAPI::ApiVersion.new(handle: :unstable), @assigned_version)
  120. assert_equal(ShopifyAPI::ApiVersion.new(handle: '2019-01'), ShopifyAPI::Base.api_version)
  121. end
  122. test "create_permission_url returns correct url with single scope no redirect uri" do
  123. ShopifyAPI::Session.setup(:api_key => "My_test_key", :secret => "My test secret")
  124. session = ShopifyAPI::Session.new(
  125. domain: 'http://localhost.myshopify.com',
  126. token: 'any-token',
  127. api_version: any_api_version
  128. )
  129. scope = ["write_products"]
  130. permission_url = session.create_permission_url(scope)
  131. assert_equal "https://localhost.myshopify.com/admin/oauth/authorize?client_id=My_test_key&scope=write_products", permission_url
  132. end
  133. test "create_permission_url returns correct url with single scope and redirect uri" do
  134. ShopifyAPI::Session.setup(:api_key => "My_test_key", :secret => "My test secret")
  135. session = ShopifyAPI::Session.new(
  136. domain: 'http://localhost.myshopify.com',
  137. token: 'any-token',
  138. api_version: any_api_version
  139. )
  140. scope = ["write_products"]
  141. permission_url = session.create_permission_url(scope, "http://my_redirect_uri.com")
  142. assert_equal "https://localhost.myshopify.com/admin/oauth/authorize?client_id=My_test_key&scope=write_products&redirect_uri=http://my_redirect_uri.com", permission_url
  143. end
  144. test "create_permission_url returns correct url with dual scope no redirect uri" do
  145. ShopifyAPI::Session.setup(:api_key => "My_test_key", :secret => "My test secret")
  146. session = ShopifyAPI::Session.new(
  147. domain: 'http://localhost.myshopify.com',
  148. token: 'any-token',
  149. api_version: any_api_version
  150. )
  151. scope = ["write_products","write_customers"]
  152. permission_url = session.create_permission_url(scope)
  153. assert_equal "https://localhost.myshopify.com/admin/oauth/authorize?client_id=My_test_key&scope=write_products,write_customers", permission_url
  154. end
  155. test "create_permission_url returns correct url with no scope no redirect uri" do
  156. ShopifyAPI::Session.setup(:api_key => "My_test_key", :secret => "My test secret")
  157. session = ShopifyAPI::Session.new(
  158. domain: 'http://localhost.myshopify.com',
  159. token: 'any-token',
  160. api_version: any_api_version
  161. )
  162. scope = []
  163. permission_url = session.create_permission_url(scope)
  164. assert_equal "https://localhost.myshopify.com/admin/oauth/authorize?client_id=My_test_key&scope=", permission_url
  165. end
  166. test "raise exception if code invalid in request token" do
  167. ShopifyAPI::Session.setup(:api_key => "My test key", :secret => "My test secret")
  168. session = ShopifyAPI::Session.new(
  169. domain: 'http://localhost.myshopify.com',
  170. token: nil,
  171. api_version: any_api_version
  172. )
  173. fake(
  174. nil,
  175. url: 'https://localhost.myshopify.com/admin/oauth/access_token',
  176. method: :post,
  177. status: 404,
  178. body: '{"error" : "invalid_request"}'
  179. )
  180. assert_raises(ShopifyAPI::ValidationException) do
  181. session.request_token(code: "bad-code")
  182. end
  183. assert_equal false, session.valid?
  184. end
  185. test "return site for session" do
  186. session = ShopifyAPI::Session.new(
  187. domain: "testshop.myshopify.com",
  188. token: "any-token",
  189. api_version: any_api_version
  190. )
  191. assert_equal "https://testshop.myshopify.com", session.site
  192. end
  193. test "return_token_if_signature_is_valid" do
  194. api_version = any_api_version
  195. fake nil,
  196. url: "https://testshop.myshopify.com/admin/oauth/access_token",
  197. method: :post,
  198. body: '{"access_token":"any-token"}'
  199. session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: nil, api_version: api_version)
  200. params = { code: 'any-code', timestamp: Time.now }
  201. token = session.request_token(params.merge(hmac: generate_signature(params)))
  202. assert_equal "any-token", token
  203. assert_equal "any-token", session.token
  204. end
  205. test "extra parameters are stored in session" do
  206. api_version = ShopifyAPI::ApiVersion.new(handle: :unstable)
  207. fake nil,
  208. url: "https://testshop.myshopify.com/admin/oauth/access_token",
  209. method: :post,
  210. body: '{"access_token":"any-token","foo":"example"}'
  211. session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: nil, api_version: api_version)
  212. params = { code: 'any-code', timestamp: Time.now }
  213. assert session.request_token(params.merge(hmac: generate_signature(params)))
  214. assert_equal ({ "foo" => "example" }), session.extra
  215. end
  216. test "expires_in is automatically converted in expires_at" do
  217. api_version = any_api_version
  218. fake nil,
  219. url: "https://testshop.myshopify.com/admin/oauth/access_token",
  220. method: :post,
  221. body: '{"access_token":"any-token","expires_in":86393}'
  222. session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: nil, api_version: api_version)
  223. Timecop.freeze do
  224. params = { code: 'any-code', timestamp: Time.now }
  225. assert session.request_token(params.merge(hmac: generate_signature(params)))
  226. expires_at = Time.now.utc + 86393
  227. assert_equal ({ "expires_at" => expires_at.to_i }), session.extra
  228. assert session.expires_at.is_a?(Time)
  229. assert_equal expires_at.to_i, session.expires_at.to_i
  230. assert_equal 86393, session.expires_in
  231. assert_equal false, session.expired?
  232. Timecop.travel(session.expires_at) do
  233. assert_equal true, session.expired?
  234. end
  235. end
  236. end
  237. test "raise error if signature does not match expected" do
  238. params = {:code => "any-code", :timestamp => Time.now}
  239. signature = generate_signature(params)
  240. params[:foo] = 'world'
  241. assert_raises(ShopifyAPI::ValidationException) do
  242. session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: nil, api_version: any_api_version)
  243. session.request_token(params.merge(:hmac => signature))
  244. end
  245. end
  246. test "raise error if timestamp is too old" do
  247. params = {:code => "any-code", :timestamp => Time.now - 2.days}
  248. signature = generate_signature(params)
  249. params[:foo] = 'world'
  250. assert_raises(ShopifyAPI::ValidationException) do
  251. session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: nil, api_version: any_api_version)
  252. session.request_token(params.merge(:hmac => signature))
  253. end
  254. end
  255. test "return true when the signature is valid and the keys of params are strings" do
  256. params = { 'code' => 'any-code', 'timestamp' => Time.now }
  257. params[:hmac] = generate_signature(params)
  258. assert_equal true, ShopifyAPI::Session.validate_signature(params)
  259. end
  260. test "return true when validating signature of params with ampersand and equal sign characters" do
  261. ShopifyAPI::Session.secret = 'secret'
  262. params = { 'a' => '1&b=2', 'c=3&d' => '4' }
  263. to_sign = 'a=1%26b=2&c%3D3%26d=4'
  264. params[:hmac] = generate_signature(to_sign)
  265. assert_equal true, ShopifyAPI::Session.validate_signature(params)
  266. end
  267. test "return true when validating signature of params with percent sign characters" do
  268. ShopifyAPI::Session.secret = 'secret'
  269. params = { 'a%3D1%26b' => '2%26c%3D3' }
  270. to_sign = 'a%253D1%2526b=2%2526c%253D3'
  271. params[:hmac] = generate_signature(to_sign)
  272. assert_equal true, ShopifyAPI::Session.validate_signature(params)
  273. end
  274. test "url is aliased to domain to minimize the upgrade changes" do
  275. session = ShopifyAPI::Session.new(
  276. domain: "http://testshop.myshopify.com",
  277. token: "any-token",
  278. api_version: any_api_version
  279. )
  280. assert_equal('testshop.myshopify.com', session.url)
  281. end
  282. private
  283. def make_sorted_params(params)
  284. params.with_indifferent_access.except(
  285. :signature, :hmac, :action, :controller
  286. ).collect { |k, v| "#{k}=#{v}" }.sort.join('&')
  287. end
  288. def generate_signature(params)
  289. params = make_sorted_params(params) if params.is_a?(Hash)
  290. OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, ShopifyAPI::Session.secret, params)
  291. end
  292. def any_api_version
  293. version_name = ['2019-01', :unstable].sample(1).first
  294. ShopifyAPI::ApiVersion.find_version(version_name)
  295. end
  296. end