session_test.rb 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. require 'test_helper'
  2. require 'timecop'
  3. class SessionTest < Test::Unit::TestCase
  4. def setup
  5. ShopifyAPI::Session.secret = 'secret'
  6. end
  7. test "not be valid without a url" do
  8. session = ShopifyAPI::Session.new(nil, "any-token")
  9. assert_not session.valid?
  10. end
  11. test "not be valid without token" do
  12. session = ShopifyAPI::Session.new("testshop.myshopify.com")
  13. assert_not session.valid?
  14. end
  15. test "be valid with any token and any url" do
  16. session = ShopifyAPI::Session.new("testshop.myshopify.com", "any-token")
  17. assert session.valid?
  18. end
  19. test "not raise error without params" do
  20. assert_nothing_raised do
  21. session = ShopifyAPI::Session.new("testshop.myshopify.com", "any-token")
  22. end
  23. end
  24. test "ignore everything but the subdomain in the shop" do
  25. assert_equal "https://testshop.myshopify.com/admin", ShopifyAPI::Session.new("http://user:pass@testshop.notshopify.net/path", "any-token").site
  26. end
  27. test "append the myshopify domain if not given" do
  28. assert_equal "https://testshop.myshopify.com/admin", ShopifyAPI::Session.new("testshop", "any-token").site
  29. end
  30. test "not raise error without params" do
  31. assert_nothing_raised do
  32. session = ShopifyAPI::Session.new("testshop.myshopify.com", "any-token")
  33. end
  34. end
  35. test "raise error if params passed but signature omitted" do
  36. assert_raises(ShopifyAPI::ValidationException) do
  37. session = ShopifyAPI::Session.new("testshop.myshopify.com")
  38. session.request_token({'code' => 'any-code'})
  39. end
  40. end
  41. test "setup api_key and secret for all sessions" do
  42. ShopifyAPI::Session.setup(:api_key => "My test key", :secret => "My test secret")
  43. assert_equal "My test key", ShopifyAPI::Session.api_key
  44. assert_equal "My test secret", ShopifyAPI::Session.secret
  45. end
  46. test "use 'https' protocol by default for all sessions" do
  47. assert_equal 'https', ShopifyAPI::Session.protocol
  48. end
  49. test "#temp reset ShopifyAPI::Base.site to original value" do
  50. ShopifyAPI::Session.setup(:api_key => "key", :secret => "secret")
  51. session1 = ShopifyAPI::Session.new('fakeshop.myshopify.com', 'token1')
  52. ShopifyAPI::Base.activate_session(session1)
  53. ShopifyAPI::Session.temp("testshop.myshopify.com", "any-token") {
  54. @assigned_site = ShopifyAPI::Base.site
  55. }
  56. assert_equal 'https://testshop.myshopify.com/admin', @assigned_site.to_s
  57. assert_equal 'https://fakeshop.myshopify.com/admin', ShopifyAPI::Base.site.to_s
  58. end
  59. test "create_permission_url returns correct url with single scope no redirect uri" do
  60. ShopifyAPI::Session.setup(:api_key => "My_test_key", :secret => "My test secret")
  61. session = ShopifyAPI::Session.new('http://localhost.myshopify.com')
  62. scope = ["write_products"]
  63. permission_url = session.create_permission_url(scope)
  64. assert_equal "https://localhost.myshopify.com/admin/oauth/authorize?client_id=My_test_key&scope=write_products", permission_url
  65. end
  66. test "create_permission_url returns correct url with single scope and redirect uri" do
  67. ShopifyAPI::Session.setup(:api_key => "My_test_key", :secret => "My test secret")
  68. session = ShopifyAPI::Session.new('http://localhost.myshopify.com')
  69. scope = ["write_products"]
  70. permission_url = session.create_permission_url(scope, "http://my_redirect_uri.com")
  71. 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
  72. end
  73. test "create_permission_url returns correct url with dual scope no redirect uri" do
  74. ShopifyAPI::Session.setup(:api_key => "My_test_key", :secret => "My test secret")
  75. session = ShopifyAPI::Session.new('http://localhost.myshopify.com')
  76. scope = ["write_products","write_customers"]
  77. permission_url = session.create_permission_url(scope)
  78. assert_equal "https://localhost.myshopify.com/admin/oauth/authorize?client_id=My_test_key&scope=write_products,write_customers", permission_url
  79. end
  80. test "create_permission_url returns correct url with no scope no redirect uri" do
  81. ShopifyAPI::Session.setup(:api_key => "My_test_key", :secret => "My test secret")
  82. session = ShopifyAPI::Session.new('http://localhost.myshopify.com')
  83. scope = []
  84. permission_url = session.create_permission_url(scope)
  85. assert_equal "https://localhost.myshopify.com/admin/oauth/authorize?client_id=My_test_key&scope=", permission_url
  86. end
  87. test "raise exception if code invalid in request token" do
  88. ShopifyAPI::Session.setup(:api_key => "My test key", :secret => "My test secret")
  89. session = ShopifyAPI::Session.new('http://localhost.myshopify.com')
  90. fake nil, :url => 'https://localhost.myshopify.com/admin/oauth/access_token',:method => :post, :status => 404, :body => '{"error" : "invalid_request"}'
  91. assert_raises(ShopifyAPI::ValidationException) do
  92. session.request_token(params={:code => "bad-code"})
  93. end
  94. assert_equal false, session.valid?
  95. end
  96. test "#temp reset ShopifyAPI::Base.site to original value when using a non-standard port" do
  97. ShopifyAPI::Session.setup(:api_key => "key", :secret => "secret")
  98. session1 = ShopifyAPI::Session.new('fakeshop.myshopify.com:3000', 'token1')
  99. ShopifyAPI::Base.activate_session(session1)
  100. end
  101. test "myshopify_domain supports non-standard ports" do
  102. begin
  103. ShopifyAPI::Session.setup(:api_key => "key", :secret => "secret", :myshopify_domain => 'localhost', port: '3000')
  104. session = ShopifyAPI::Session.new('fakeshop.localhost:3000', 'token1')
  105. ShopifyAPI::Base.activate_session(session)
  106. assert_equal 'https://fakeshop.localhost:3000/admin', ShopifyAPI::Base.site.to_s
  107. session = ShopifyAPI::Session.new('fakeshop', 'token1')
  108. ShopifyAPI::Base.activate_session(session)
  109. assert_equal 'https://fakeshop.localhost:3000/admin', ShopifyAPI::Base.site.to_s
  110. ensure
  111. ShopifyAPI::Session.myshopify_domain = "myshopify.com"
  112. ShopifyAPI::Session.port = nil
  113. end
  114. end
  115. test "return site for session" do
  116. session = ShopifyAPI::Session.new("testshop.myshopify.com", "any-token")
  117. assert_equal "https://testshop.myshopify.com/admin", session.site
  118. end
  119. test "return_token_if_signature_is_valid" do
  120. fake nil,
  121. url: 'https://testshop.myshopify.com/admin/oauth/access_token',
  122. method: :post,
  123. body: '{"access_token":"any-token"}'
  124. session = ShopifyAPI::Session.new("testshop.myshopify.com")
  125. params = { code: 'any-code', timestamp: Time.now }
  126. token = session.request_token(params.merge(hmac: generate_signature(params)))
  127. assert_equal "any-token", token
  128. assert_equal "any-token", session.token
  129. end
  130. test "extra parameters are stored in session" do
  131. fake nil,
  132. url: 'https://testshop.myshopify.com/admin/oauth/access_token',
  133. method: :post,
  134. body: '{"access_token":"any-token","foo":"example"}'
  135. session = ShopifyAPI::Session.new("testshop.myshopify.com")
  136. params = { code: 'any-code', timestamp: Time.now }
  137. assert session.request_token(params.merge(hmac: generate_signature(params)))
  138. assert_equal ({ "foo" => "example" }), session.extra
  139. end
  140. test "expires_in is automatically converted in expires_at" do
  141. fake nil,
  142. url: 'https://testshop.myshopify.com/admin/oauth/access_token',
  143. method: :post,
  144. body: '{"access_token":"any-token","expires_in":86393}'
  145. session = ShopifyAPI::Session.new("testshop.myshopify.com")
  146. Timecop.freeze do
  147. params = { code: 'any-code', timestamp: Time.now }
  148. assert session.request_token(params.merge(hmac: generate_signature(params)))
  149. expires_at = Time.now.utc + 86393
  150. assert_equal ({ "expires_at" => expires_at.to_i }), session.extra
  151. assert session.expires_at.is_a?(Time)
  152. assert_equal expires_at.to_i, session.expires_at.to_i
  153. assert_equal 86393, session.expires_in
  154. assert_equal false, session.expired?
  155. Timecop.travel(session.expires_at) do
  156. assert_equal true, session.expired?
  157. end
  158. end
  159. end
  160. test "raise error if signature does not match expected" do
  161. params = {:code => "any-code", :timestamp => Time.now}
  162. signature = generate_signature(params)
  163. params[:foo] = 'world'
  164. assert_raises(ShopifyAPI::ValidationException) do
  165. session = ShopifyAPI::Session.new("testshop.myshopify.com")
  166. session.request_token(params.merge(:hmac => signature))
  167. end
  168. end
  169. test "raise error if timestamp is too old" do
  170. params = {:code => "any-code", :timestamp => Time.now - 2.days}
  171. signature = generate_signature(params)
  172. params[:foo] = 'world'
  173. assert_raises(ShopifyAPI::ValidationException) do
  174. session = ShopifyAPI::Session.new("testshop.myshopify.com")
  175. session.request_token(params.merge(:hmac => signature))
  176. end
  177. end
  178. test "return true when the signature is valid and the keys of params are strings" do
  179. params = {"code" => "any-code", "timestamp" => Time.now}
  180. params["hmac"] = generate_signature(params)
  181. assert_equal true, ShopifyAPI::Session.validate_signature(params)
  182. end
  183. test "return true when validating signature of params with ampersand and equal sign characters" do
  184. ShopifyAPI::Session.secret = 'secret'
  185. params = {'a' => '1&b=2', 'c=3&d' => '4'}
  186. to_sign = "a=1%26b=2&c%3D3%26d=4"
  187. params['hmac'] = generate_signature(to_sign)
  188. assert_equal true, ShopifyAPI::Session.validate_signature(params)
  189. end
  190. test "return true when validating signature of params with percent sign characters" do
  191. ShopifyAPI::Session.secret = 'secret'
  192. params = {'a%3D1%26b' => '2%26c%3D3'}
  193. to_sign = "a%253D1%2526b=2%2526c%253D3"
  194. params['hmac'] = generate_signature(to_sign)
  195. assert_equal true, ShopifyAPI::Session.validate_signature(params)
  196. end
  197. private
  198. def make_sorted_params(params)
  199. sorted_params = params.with_indifferent_access.except(:signature, :hmac, :action, :controller).collect{|k,v|"#{k}=#{v}"}.sort.join('&')
  200. end
  201. def generate_signature(params)
  202. params = make_sorted_params(params) if params.is_a?(Hash)
  203. OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, ShopifyAPI::Session.secret, params)
  204. end
  205. end