session.rb 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. require 'openssl'
  2. require 'rack'
  3. module ShopifyAPI
  4. class ValidationException < StandardError
  5. end
  6. class Session
  7. cattr_accessor :api_key, :secret, :protocol, :myshopify_domain, :port
  8. self.protocol = 'https'
  9. self.myshopify_domain = 'myshopify.com'
  10. attr_accessor :url, :token, :name
  11. class << self
  12. def setup(params)
  13. params.each { |k,value| public_send("#{k}=", value) }
  14. end
  15. def temp(domain, token, &block)
  16. session = new(domain, token)
  17. original_site = ShopifyAPI::Base.site.to_s
  18. original_token = ShopifyAPI::Base.headers['X-Shopify-Access-Token']
  19. original_session = new(original_site, original_token)
  20. begin
  21. ShopifyAPI::Base.activate_session(session)
  22. yield
  23. ensure
  24. ShopifyAPI::Base.activate_session(original_session)
  25. end
  26. end
  27. def prepare_url(url)
  28. return nil if url.blank?
  29. # remove http:// or https://
  30. url = url.strip.gsub(/\Ahttps?:\/\//, '')
  31. # extract host, removing any username, password or path
  32. shop = URI.parse("https://#{url}").host
  33. # extract subdomain of .myshopify.com
  34. if idx = shop.index(".")
  35. shop = shop.slice(0, idx)
  36. end
  37. return nil if shop.empty?
  38. shop = "#{shop}.#{myshopify_domain}"
  39. port ? "#{shop}:#{port}" : shop
  40. rescue URI::InvalidURIError
  41. nil
  42. end
  43. def validate_signature(params)
  44. params = params.with_indifferent_access
  45. return false unless signature = params[:hmac]
  46. calculated_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new(), secret, encoded_params_for_signature(params))
  47. Rack::Utils.secure_compare(calculated_signature, signature)
  48. end
  49. private
  50. def encoded_params_for_signature(params)
  51. params = params.except(:signature, :hmac, :action, :controller)
  52. params.map{|k,v| "#{URI.escape(k.to_s, '&=%')}=#{URI.escape(v.to_s, '&%')}"}.sort.join('&')
  53. end
  54. end
  55. def initialize(url, token = nil)
  56. self.url = self.class.prepare_url(url)
  57. self.token = token
  58. end
  59. def create_permission_url(scope, redirect_uri = nil)
  60. params = {:client_id => api_key, :scope => scope.join(',')}
  61. params[:redirect_uri] = redirect_uri if redirect_uri
  62. "#{site}/oauth/authorize?#{parameterize(params)}"
  63. end
  64. def request_token(params)
  65. return token if token
  66. unless self.class.validate_signature(params) && params[:timestamp].to_i > 24.hours.ago.utc.to_i
  67. raise ShopifyAPI::ValidationException, "Invalid Signature: Possible malicious login"
  68. end
  69. code = params['code']
  70. response = access_token_request(code)
  71. if response.code == "200"
  72. token = JSON.parse(response.body)['access_token']
  73. else
  74. raise RuntimeError, response.msg
  75. end
  76. end
  77. def shop
  78. Shop.current
  79. end
  80. def site
  81. "#{protocol}://#{url}/admin"
  82. end
  83. def valid?
  84. url.present? && token.present?
  85. end
  86. private
  87. def parameterize(params)
  88. URI.escape(params.collect{|k,v| "#{k}=#{v}"}.join('&'))
  89. end
  90. def access_token_request(code)
  91. uri = URI.parse("#{protocol}://#{url}/admin/oauth/access_token")
  92. https = Net::HTTP.new(uri.host, uri.port)
  93. https.use_ssl = true
  94. request = Net::HTTP::Post.new(uri.request_uri)
  95. request.set_form_data({"client_id" => api_key, "client_secret" => secret, "code" => code})
  96. https.request(request)
  97. end
  98. end
  99. end