Skip to content

Commit 075feb7

Browse files
committed
Added the OAuth signing method
1 parent 56c3924 commit 075feb7

3 files changed

Lines changed: 117 additions & 0 deletions

File tree

lib/ruby-smugmug.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
path = File.expand_path("../smugmug", __FILE__)
22
require "#{path}/version"
33
require "#{path}/exceptions"
4+
require "#{path}/http"
5+
require "#{path}/client"
6+

lib/smugmug/client.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module SmugMug
2+
class Client
3+
4+
##
5+
# Creates a new client instance that can be used to access the SMugMug API
6+
# @param [Hash] args
7+
# @option args [String] :api_key Your SmugMug API key
8+
# @option args [String] :oauth_secret Your SmugMug OAuth secret key
9+
# @option args [Hash] :user Configuration for the user you are requesting/updating data for
10+
# * :token [String] OAuth token for the user
11+
# * :secret [String] OAuth secret token for the user
12+
# @option args [String, Optional :user_agent Helps SmugMug identify API calls
13+
# @option args [Hash, Optional] :http Additional configuration for the HTTP requests
14+
# * :verify_mode [Integer, Optional] How to verify the SSL certificate when connecting through HTTPS, either OpenSSL::SSL::VERIFY_PEER or OpenSSL::SSL::VERIFY_NONE, defaults to OpenSSL::SSL::VERIFY_NONE
15+
# * :ca_file [String, Optional] Path to the CA certification file in PEM format
16+
# * :ca_path [String, Optional] Path to the directory containing CA certifications in PEM format
17+
#
18+
# @raise [ArgumentError]
19+
def initialize(args)
20+
raise ArgumentError, "API Key required" unless args.has_key?(:api_key)
21+
raise ArgumentError, "API OAuth secret required" unless args.has_key?(:oauth_secret)
22+
raise ArgumentError, "Must specify the users OAuth datA" unless args[:user].is_a?(Hash)
23+
raise ArgumentError, "Users OAuth token required" unless args[:user][:token]
24+
raise ArgumentError, "Users OAuth secret token required" unless args[:user][:secret]
25+
26+
@http = HTTP.new(args)
27+
end
28+
end
29+
end

lib/smugmug/http.rb

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
require "cgi"
2+
require "openssl"
3+
require "base64"
4+
require "net/http"
5+
6+
module SmugMug
7+
class HTTP
8+
API_URI = URI("https://api.smugmug.com/services/api/json/1.3.0")
9+
10+
##
11+
# Creates a new HTTP wrapper to handle the network portions of the API requests
12+
# @param [Hash] args Same as [SmugMug::HTTP]
13+
#
14+
def initialize(args)
15+
@config = args
16+
@headers = {"User-Agent" => args.delete(:user_agent) || "Ruby-SmugMug v#{SmugMug::VERSION}"}
17+
@digest = OpenSSL::Digest::Digest.new("SHA1")
18+
end
19+
20+
def request(api, args)
21+
args[:method] = "smugmug.#{api}"
22+
23+
http = ::Net::HTTP.new(API_URI.host, API_URI.port)
24+
http.set_debug_output(@config[:debug_output]) if @config[:debug_output]
25+
26+
# Configure HTTPS if needed
27+
if API_URI.scheme == "https"
28+
http.use_ssl = true
29+
30+
if @config[:http] and @config[:http][:verify_mode]
31+
http.verify_mode = @config[:http][:verify_mode]
32+
http.ca_file = @config[:http][:ca_file]
33+
http.ca_path = @config[:http][:ca_path]
34+
else
35+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
36+
end
37+
end
38+
39+
# Parse post data
40+
postdata = self.sign_request("POST", API_URI, args)
41+
42+
response = http.request_post(API_URI.request_uri, postdata, @headers)
43+
end
44+
45+
##
46+
# Generates an OAuth signature and updates the args with the required fields
47+
# @param [String] method HTTP method that the request is sent as
48+
# @param [String] uri Full URL of the request
49+
# @param [Hash] form_args Args to be passed to the server
50+
def sign_request(method, uri, form_args)
51+
# Convert non-string keys to strings so the sort works
52+
args = {}
53+
form_args.each do |key, value|
54+
next unless value and value != ""
55+
56+
key = key.to_s unless key.is_a?(String)
57+
args[key] = value
58+
end
59+
60+
# Add the necessary OAuth args
61+
args["oauth_version"] = "1.0"
62+
args["oauth_consumer_key"] = @config[:api_key]
63+
args["oauth_nonce"] = Digest::MD5.hexdigest("#{Time.now.to_f}#{rand(10 ** 30)}")
64+
args["oauth_signature_method"] = "HMAC-SHA1"
65+
args["oauth_timestamp"] = Time.now.utc.to_i
66+
args["oauth_token"] = @config[:user][:token]
67+
68+
# Sort the params
69+
sorted_args = []
70+
args.sort.each do |key, value|
71+
sorted_args.push("#{key.to_s}=#{URI::encode_www_form_component(value.to_s)}")
72+
end
73+
74+
postdata = sorted_args.join("&")
75+
76+
# Final string to hash
77+
sig_base = "#{method}&#{URI::encode_www_form_component("#{uri.scheme}://#{uri.host}#{uri.path}")}&#{URI::encode_www_form_component(postdata)}"
78+
79+
signature = OpenSSL::HMAC.digest(@digest, "#{@config[:oauth_secret]}&#{@config[:user][:secret]}", sig_base)
80+
signature = Base64.encode64(signature).chomp
81+
82+
"#{postdata}&oauth_signature=#{URI::encode_www_form_component(signature)}"
83+
end
84+
end
85+
end

0 commit comments

Comments
 (0)