session_test.rb 13 KB

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