123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640 |
- # frozen_string_literal: true
- require 'test_helper'
- require 'timecop'
- class SessionTest < Test::Unit::TestCase
- SECONDS_IN_A_DAY = 24 * 60 * 60
- def setup
- super
- ShopifyAPI::Session.secret = 'secret'
- end
- test "not be valid without a url" do
- session = ShopifyAPI::Session.new(domain: nil, token: "any-token", api_version: any_api_version)
- assert_not(session.valid?)
- end
- test "not be valid without token" do
- session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: nil, api_version: any_api_version)
- assert_not(session.valid?)
- end
- test "not be valid without an API version" do
- session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: "any-token", api_version: nil)
- assert_not(session.valid?)
- session = ShopifyAPI::Session.new(
- domain: "testshop.myshopify.com", token: "any-token", api_version: ShopifyAPI::ApiVersion::NullVersion
- )
- assert_not(session.valid?)
- end
- test "default to base API version" do
- session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: "any-token")
- assert(session.valid?)
- assert_equal(session.api_version, ShopifyAPI::Base.api_version)
- end
- test "can override the base API version" do
- different_api_version = '2020-01'
- session = ShopifyAPI::Session.new(
- domain: "testshop.myshopify.com", token: "any-token", api_version: different_api_version
- )
- assert(session.valid?)
- assert_equal(session.api_version, ShopifyAPI::ApiVersion.find_version(different_api_version))
- end
- test "be valid with any token, any url and version" do
- session = ShopifyAPI::Session.new(
- domain: "testshop.myshopify.com",
- token: "any-token",
- api_version: any_api_version
- )
- assert(session.valid?)
- end
- test "be valid with nil access_scopes" do
- session = ShopifyAPI::Session.new(
- domain: "testshop.myshopify.com",
- token: "any-token",
- api_version: any_api_version,
- access_scopes: nil
- )
- assert(session.valid?)
- end
- test "be valid with string of access_scopes" do
- session = ShopifyAPI::Session.new(
- domain: "testshop.myshopify.com",
- token: "any-token",
- api_version: any_api_version,
- access_scopes: "read_products, write_orders"
- )
- assert(session.valid?)
- end
- test "be valid with a collection of access_scopes" do
- session = ShopifyAPI::Session.new(
- domain: "testshop.myshopify.com",
- token: "any-token",
- api_version: any_api_version,
- access_scopes: %w(read_products write_orders)
- )
- assert(session.valid?)
- end
- test "not raise error without params" do
- assert_nothing_raised do
- ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: "any-token", api_version: any_api_version)
- end
- end
- test "ignore everything but the subdomain in the shop" do
- assert_equal_uri(
- "https://testshop.myshopify.com",
- ShopifyAPI::Session.new(
- domain: "http://user:pass@testshop.notshopify.net/path",
- token: "any-token",
- api_version: any_api_version
- ).site
- )
- end
- test "append the myshopify domain if not given" do
- assert_equal_uri(
- "https://testshop.myshopify.com",
- ShopifyAPI::Session.new(domain: "testshop", token: "any-token", api_version: any_api_version).site
- )
- end
- test "not raise error without params" do
- assert_nothing_raised do
- ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: "any-token", api_version: any_api_version)
- end
- end
- test "provides default nil access_scopes attribute" do
- session = ShopifyAPI::Session.new(
- domain: "testshop.myshopify.com",
- token: "any-token",
- api_version: any_api_version
- )
- assert_nil session.access_scopes
- end
- test "provides specified nil access_scopes attribute" do
- session = ShopifyAPI::Session.new(
- domain: "testshop.myshopify.com",
- token: "any-token",
- access_scopes: "read_products",
- api_version: any_api_version
- )
- assert_equal "read_products", session.access_scopes.to_s
- end
- test "session instantiation raises error if bad access scopes are provided" do
- assert_raises NoMethodError do
- ShopifyAPI::Session.new(
- domain: "testshop.myshopify.com",
- token: "any-token",
- access_scopes: { bad_input: "bad_input" },
- api_version: any_api_version
- )
- end
- end
- test "raise error if params passed but signature omitted" do
- assert_raises(ShopifyAPI::ValidationException) do
- session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: nil, api_version: any_api_version)
- session.request_token({ 'code' => 'any-code' })
- end
- end
- test "setup api_key and secret for all sessions" do
- ShopifyAPI::Session.setup(api_key: "My test key", secret: "My test secret")
- assert_equal("My test key", ShopifyAPI::Session.api_key)
- assert_equal("My test secret", ShopifyAPI::Session.secret)
- end
- test "#temp reset ShopifyAPI::Base values to original value" do
- session1 = ShopifyAPI::Session.new(domain: 'fakeshop.myshopify.com', token: 'token1', api_version: '2019-01')
- ShopifyAPI::Base.user = 'foo'
- ShopifyAPI::Base.password = 'bar'
- ShopifyAPI::Base.activate_session(session1)
- ShopifyAPI::Session.temp(domain: "testshop.myshopify.com", token: "any-token", api_version: :unstable) do
- @assigned_site = ShopifyAPI::Base.site
- @assigned_version = ShopifyAPI::Base.api_version
- @assigned_user = ShopifyAPI::Base.user
- @assigned_password = ShopifyAPI::Base.password
- end
- assert_equal('https://testshop.myshopify.com', @assigned_site.to_s)
- assert_equal('https://fakeshop.myshopify.com', ShopifyAPI::Base.site.to_s)
- assert_equal(ShopifyAPI::ApiVersion.new(handle: :unstable), @assigned_version)
- assert_equal(ShopifyAPI::ApiVersion.new(handle: '2019-01'), ShopifyAPI::Base.api_version)
- assert_nil(@assigned_user)
- assert_equal('foo', ShopifyAPI::Base.user)
- assert_nil(@assigned_password)
- assert_equal('bar', ShopifyAPI::Base.password)
- end
- test "#temp does not use basic auth values from Base.site" do
- ShopifyAPI::Base.site = 'https://user:pass@fakeshop.myshopify.com'
- ShopifyAPI::Session.temp(domain: "testshop.myshopify.com", token: "any-token", api_version: :unstable) do
- @assigned_site = ShopifyAPI::Base.site
- @assigned_user = ShopifyAPI::Base.user
- @assigned_password = ShopifyAPI::Base.password
- end
- assert_equal('https://testshop.myshopify.com', @assigned_site.to_s)
- assert_equal('https://fakeshop.myshopify.com', ShopifyAPI::Base.site.to_s)
- assert_nil(@assigned_user)
- assert_equal('user', ShopifyAPI::Base.user)
- assert_nil(@assigned_password)
- assert_equal('pass', ShopifyAPI::Base.password)
- end
- test "#with_session activates the session for the duration of the block" do
- session1 = ShopifyAPI::Session.new(domain: 'fakeshop.myshopify.com', token: 'token1', api_version: '2019-01')
- ShopifyAPI::Base.activate_session(session1)
- other_session = ShopifyAPI::Session.new(
- domain: "testshop.myshopify.com",
- token: "any-token",
- api_version: :unstable
- )
- ShopifyAPI::Session.with_session(other_session) do
- @assigned_site = ShopifyAPI::Base.site
- @assigned_version = ShopifyAPI::Base.api_version
- end
- assert_equal('https://testshop.myshopify.com', @assigned_site.to_s)
- assert_equal('https://fakeshop.myshopify.com', ShopifyAPI::Base.site.to_s)
- assert_equal(ShopifyAPI::ApiVersion.new(handle: :unstable), @assigned_version)
- assert_equal(ShopifyAPI::ApiVersion.new(handle: '2019-01'), ShopifyAPI::Base.api_version)
- end
- test "#with_session resets the activated session even if there an exception during the block" do
- session1 = ShopifyAPI::Session.new(domain: 'fakeshop.myshopify.com', token: 'token1', api_version: '2019-01')
- ShopifyAPI::Base.activate_session(session1)
- other_session = ShopifyAPI::Session.new(
- domain: "testshop.myshopify.com",
- token: "any-token",
- api_version: :unstable
- )
- assert_raises(StandardError) do
- ShopifyAPI::Session.with_session(other_session) { raise StandardError, "" }
- end
- assert_equal('https://fakeshop.myshopify.com', ShopifyAPI::Base.site.to_s)
- assert_equal(ShopifyAPI::ApiVersion.new(handle: '2019-01'), ShopifyAPI::Base.api_version)
- end
- test "#with_version will adjust the actvated api version for the duration of the block" do
- session1 = ShopifyAPI::Session.new(domain: 'fakeshop.myshopify.com', token: 'token1', api_version: '2019-01')
- ShopifyAPI::Base.activate_session(session1)
- ShopifyAPI::Session.with_version(:unstable) do
- @assigned_site = ShopifyAPI::Base.site
- @assigned_version = ShopifyAPI::Base.api_version
- end
- assert_equal('https://fakeshop.myshopify.com', @assigned_site.to_s)
- assert_equal('https://fakeshop.myshopify.com', ShopifyAPI::Base.site.to_s)
- assert_equal(ShopifyAPI::ApiVersion.new(handle: :unstable), @assigned_version)
- assert_equal(ShopifyAPI::ApiVersion.new(handle: '2019-01'), ShopifyAPI::Base.api_version)
- end
- test "create_permission_url requires redirect_uri" do
- ShopifyAPI::Session.setup(api_key: "My_test_key", secret: "My test secret")
- session = ShopifyAPI::Session.new(
- domain: 'http://localhost.myshopify.com',
- token: 'any-token',
- api_version: any_api_version
- )
- scope = ["write_products"]
- assert_raises(ArgumentError) do
- session.create_permission_url(scope)
- end
- end
- test "create_permission_url returns correct url with single scope and redirect uri" do
- ShopifyAPI::Session.setup(api_key: "My_test_key", secret: "My test secret")
- session = ShopifyAPI::Session.new(
- domain: 'http://localhost.myshopify.com',
- token: 'any-token',
- api_version: any_api_version
- )
- scope = ["write_products"]
- permission_url = session.create_permission_url(scope, "http://my_redirect_uri.com")
- assert_equal_uri(
- "https://localhost.myshopify.com/admin/oauth/authorize?client_id=My_test_key&" \
- "scope=write_products&redirect_uri=http://my_redirect_uri.com",
- permission_url
- )
- end
- test "create_permission_url returns correct url with dual scope" do
- ShopifyAPI::Session.setup(api_key: "My_test_key", secret: "My test secret")
- session = ShopifyAPI::Session.new(
- domain: 'http://localhost.myshopify.com',
- token: 'any-token',
- api_version: any_api_version
- )
- scope = ["write_products", "write_customers"]
- permission_url = session.create_permission_url(scope, "http://my_redirect_uri.com")
- assert_equal_uri(
- "https://localhost.myshopify.com/admin/oauth/authorize?client_id=My_test_key&" \
- "scope=write_products,write_customers&redirect_uri=http://my_redirect_uri.com",
- permission_url
- )
- end
- test "create_permission_url returns correct url with no scope" do
- ShopifyAPI::Session.setup(api_key: "My_test_key", secret: "My test secret")
- session = ShopifyAPI::Session.new(
- domain: 'http://localhost.myshopify.com',
- token: 'any-token',
- api_version: any_api_version
- )
- scope = []
- permission_url = session.create_permission_url(scope, "http://my_redirect_uri.com")
- assert_equal_uri(
- "https://localhost.myshopify.com/admin/oauth/authorize?client_id=My_test_key&" \
- "scope=&redirect_uri=http://my_redirect_uri.com",
- permission_url
- )
- end
- test "create_permission_url returns correct url with state" do
- ShopifyAPI::Session.setup(api_key: "My_test_key", secret: "My test secret")
- session = ShopifyAPI::Session.new(
- domain: 'http://localhost.myshopify.com',
- token: 'any-token',
- api_version: any_api_version
- )
- scope = []
- permission_url = session.create_permission_url(scope, "http://my_redirect_uri.com", state: "My nonce")
- assert_equal_uri(
- "https://localhost.myshopify.com/admin/oauth/authorize?client_id=My_test_key&" \
- "scope=&redirect_uri=http://my_redirect_uri.com&state=My+nonce",
- permission_url
- )
- end
- test "create_permission_url returns correct url with grant_options[]" do
- ShopifyAPI::Session.setup(api_key: "My_test_key", secret: "My test secret")
- session = ShopifyAPI::Session.new(
- domain: 'http://localhost.myshopify.com',
- token: 'any-token',
- api_version: any_api_version
- )
- scope = []
- permission_url = session.create_permission_url(scope, "http://my_redirect_uri.com", grant_options: "per-user")
- assert_equal_uri(
- "https://localhost.myshopify.com/admin/oauth/authorize?client_id=My_test_key&" \
- "scope=&redirect_uri=http://my_redirect_uri.com&grant_options[]=per-user",
- permission_url
- )
- end
- test "raise exception if code invalid in request token" do
- ShopifyAPI::Session.setup(api_key: "My test key", secret: "My test secret")
- session = ShopifyAPI::Session.new(
- domain: 'http://localhost.myshopify.com',
- token: nil,
- api_version: any_api_version
- )
- fake(
- nil,
- url: 'https://localhost.myshopify.com/admin/oauth/access_token',
- method: :post,
- status: 404,
- body: '{"error" : "invalid_request"}'
- )
- assert_raises(ShopifyAPI::ValidationException) do
- session.request_token(code: "bad-code")
- end
- assert_equal(false, session.valid?)
- end
- test "return site for session" do
- session = ShopifyAPI::Session.new(
- domain: "testshop.myshopify.com",
- token: "any-token",
- api_version: any_api_version
- )
- assert_equal_uri("https://testshop.myshopify.com", session.site)
- end
- test "return_token_if_signature_is_valid" do
- api_version = any_api_version
- fake(
- nil,
- url: "https://testshop.myshopify.com/admin/oauth/access_token",
- method: :post,
- body: '{"access_token":"any-token"}'
- )
- session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: nil, api_version: api_version)
- params = { code: 'any-code', timestamp: Time.now }
- token = session.request_token(params.merge(hmac: generate_signature(params)))
- assert_equal("any-token", token)
- assert_equal("any-token", session.token)
- end
- test "extra parameters are stored in session" do
- api_version = ShopifyAPI::ApiVersion.new(handle: :unstable)
- fake(
- nil,
- url: "https://testshop.myshopify.com/admin/oauth/access_token",
- method: :post,
- body: '{"access_token":"any-token","foo":"example"}'
- )
- session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: nil, api_version: api_version)
- params = { code: 'any-code', timestamp: Time.now }
- assert(session.request_token(params.merge(hmac: generate_signature(params))))
- assert_equal({ "foo" => "example" }, session.extra)
- end
- test "expires_in is automatically converted in expires_at" do
- api_version = any_api_version
- fake(
- nil,
- url: "https://testshop.myshopify.com/admin/oauth/access_token",
- method: :post,
- body: '{"access_token":"any-token","expires_in":86393}'
- )
- session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: nil, api_version: api_version)
- Timecop.freeze do
- params = { code: 'any-code', timestamp: Time.now }
- assert(session.request_token(params.merge(hmac: generate_signature(params))))
- expires_at = Time.now.utc + 86393
- assert_equal({ "expires_at" => expires_at.to_i }, session.extra)
- assert(session.expires_at.is_a?(Time))
- assert_equal(expires_at.to_i, session.expires_at.to_i)
- assert_equal(86393, session.expires_in)
- assert_equal(false, session.expired?)
- Timecop.travel(session.expires_at) do
- assert_equal(true, session.expired?)
- end
- end
- end
- test "raise error if signature does not match expected" do
- params = { code: "any-code", timestamp: Time.now }
- signature = generate_signature(params)
- params[:foo] = 'world'
- assert_raises(ShopifyAPI::ValidationException) do
- session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: nil, api_version: any_api_version)
- session.request_token(params.merge(hmac: signature))
- end
- end
- test "raise error if timestamp is too old" do
- params = { code: "any-code", timestamp: Time.now - 2 * SECONDS_IN_A_DAY }
- signature = generate_signature(params)
- params[:foo] = 'world'
- assert_raises(ShopifyAPI::ValidationException) do
- session = ShopifyAPI::Session.new(domain: "testshop.myshopify.com", token: nil, api_version: any_api_version)
- session.request_token(params.merge(hmac: signature))
- end
- end
- test "return true when the signature is valid and the keys of params are strings" do
- params = { 'code' => 'any-code', 'timestamp' => Time.now }
- params[:hmac] = generate_signature(params)
- assert_equal(true, ShopifyAPI::Session.validate_signature(params))
- end
- test "return true when validating signature of params with ampersand and equal sign characters" do
- ShopifyAPI::Session.secret = 'secret'
- params = { 'a' => '1&b=2', 'c=3&d' => '4' }
- to_sign = 'a=1%26b=2&c%3D3%26d=4'
- params[:hmac] = generate_signature(to_sign)
- assert_equal(true, ShopifyAPI::Session.validate_signature(params))
- end
- test "return true when validating signature of params with percent sign characters" do
- ShopifyAPI::Session.secret = 'secret'
- params = { 'a%3D1%26b' => '2%26c%3D3' }
- to_sign = 'a%253D1%2526b=2%2526c%253D3'
- params[:hmac] = generate_signature(to_sign)
- assert_equal(true, ShopifyAPI::Session.validate_signature(params))
- end
- test "url is aliased to domain to minimize the upgrade changes" do
- session = ShopifyAPI::Session.new(
- domain: "http://testshop.myshopify.com",
- token: "any-token",
- api_version: any_api_version
- )
- assert_equal('testshop.myshopify.com', session.url)
- end
- test "#hash returns the same value for equal Sessions" do
- session = ShopifyAPI::Session.new(
- domain: "http://testshop.myshopify.com",
- token: "any-token",
- api_version: '2019-01',
- extra: { foo: "bar" }
- )
- other_session = ShopifyAPI::Session.new(
- domain: "http://testshop.myshopify.com",
- token: "any-token",
- api_version: '2019-01',
- extra: { foo: "bar" }
- )
- assert_equal(session.hash, other_session.hash)
- end
- test "equality verifies domain" do
- session = ShopifyAPI::Session.new(
- domain: "http://testshop.myshopify.com",
- token: "any-token",
- api_version: '2019-01',
- extra: { foo: "bar" }
- )
- other_session = ShopifyAPI::Session.new(
- domain: "http://testshop.myshopify.com",
- token: "any-token",
- api_version: '2019-01',
- extra: { foo: "bar" }
- )
- different_session = ShopifyAPI::Session.new(
- domain: "http://another_testshop.myshopify.com",
- token: "any-token",
- api_version: '2019-01',
- extra: { foo: "bar" }
- )
- assert_equal(session, other_session)
- refute_equal(session, different_session)
- end
- test "equality verifies token" do
- session = ShopifyAPI::Session.new(
- domain: "http://testshop.myshopify.com",
- token: "any-token",
- api_version: '2019-01',
- extra: { foo: "bar" }
- )
- different_session = ShopifyAPI::Session.new(
- domain: "http://testshop.myshopify.com",
- token: "very-different-token",
- api_version: '2019-01',
- extra: { foo: "bar" }
- )
- refute_equal(session, different_session)
- end
- test "equality verifies api_version" do
- session = ShopifyAPI::Session.new(
- domain: "http://testshop.myshopify.com",
- token: "any-token",
- api_version: '2019-01',
- extra: { foo: "bar" }
- )
- different_session = ShopifyAPI::Session.new(
- domain: "http://testshop.myshopify.com",
- token: "any-token",
- api_version: :unstable,
- extra: { foo: "bar" }
- )
- refute_equal(session, different_session)
- end
- test "equality verifies extra" do
- session = ShopifyAPI::Session.new(
- domain: "http://testshop.myshopify.com",
- token: "any-token",
- api_version: '2019-01',
- extra: { foo: "bar" }
- )
- different_session = ShopifyAPI::Session.new(
- domain: "http://testshop.myshopify.com",
- token: "any-token",
- api_version: '2019-01',
- extra: { bar: "other-bar" }
- )
- refute_equal(session, different_session)
- end
- test "equality verifies other is a Session" do
- session = ShopifyAPI::Session.new(
- domain: "http://testshop.myshopify.com",
- token: "any-token",
- api_version: '2019-01',
- extra: { foo: "bar" }
- )
- different_session = nil
- refute_equal(session, different_session)
- end
- test "#eql? and #hash are implemented" do
- session = ShopifyAPI::Session.new(
- domain: "http://testshop.myshopify.com",
- token: "any-token",
- api_version: '2019-01',
- extra: { foo: "bar" }
- )
- other_session = ShopifyAPI::Session.new(
- domain: "http://testshop.myshopify.com",
- token: "any-token",
- api_version: '2019-01',
- extra: { foo: "bar" }
- )
- different_session = ShopifyAPI::Session.new(
- domain: "http://another_testshop.myshopify.com",
- token: "any-token",
- api_version: '2019-01',
- extra: { foo: "bar" }
- )
- assert_equal([session, different_session], [session, other_session, different_session].uniq)
- end
- private
- def assert_equal_uri(expected, actual)
- assert_equal(Addressable::URI.parse(expected), Addressable::URI.parse(actual))
- end
- def make_sorted_params(params)
- params.with_indifferent_access.except(
- :signature, :hmac, :action, :controller
- ).collect { |k, v| "#{k}=#{v}" }.sort.join('&')
- end
- def generate_signature(params)
- params = make_sorted_params(params) if params.is_a?(Hash)
- OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('SHA256'), ShopifyAPI::Session.secret, params)
- end
- def any_api_version
- version_name = ['2019-01', :unstable].sample(1).first
- ShopifyAPI::ApiVersion.find_version(version_name)
- end
- end
|