diff --git a/README.markdown b/README.markdown index ec5f06b..2fafc81 100644 --- a/README.markdown +++ b/README.markdown @@ -47,6 +47,7 @@ Dropbox::API::Config.app_key = YOUR_APP_KEY Dropbox::API::Config.app_secret = YOUR_APP_SECRET Dropbox::API::Config.mode = "sandbox" # if you have a single-directory app # Dropbox::API::Config.mode = "dropbox" # if your app has access to the whole dropbox +Dropbox::API::Config.auth_type = "oauth" # options are "oauth" or "oauth2" (default is "oauth") ``` Dropbox::API::Client @@ -62,6 +63,8 @@ In order to create a Dropbox::API::Client object, you need to have the configura Second thing you need is to have the user authorize your app using OAuth. Here's a short intro on how to do this: +##### OAuth1 + ```ruby consumer = Dropbox::API::OAuth.consumer(:authorize) request_token = consumer.get_request_token @@ -77,12 +80,28 @@ oauth_verifier = params[:oauth_verifier] result = request_token.get_access_token(:oauth_verifier => oauth_verifier) ``` -Now that you have the oauth token and secret, you can create a new instance of the Dropbox::API::Client, like this: +Now that you have the OAuth1 token and secret, you can create a new instance of the Dropbox::API::Client, like this: ```ruby client = Dropbox::API::Client.new :token => result.token, :secret => result.secret ``` +##### OAuth2 +```ruby +consumer = ::Dropbox::API::OAuth2.consumer(:authorize) +authorize_uri = consumer.authorize_url(client_id: Dropbox::API::Config.app_key, response_type: 'code') +# Here the user goes to Dropbox, authorizes the app and is redirected, when +# they return, extract the &code query string parameter +consumer = ::Dropbox::API::OAuth2.consumer(:main) +access_token = consumer.auth_code.get_token(params[:code]) +``` + +Now that you have an authenticated OAuth2 access token, you can create a new instance of the Dropbox::API::Client, like this: + +```ruby +client = Dropbox::API::Client.new :token => access_token.token +``` + Rake-based authorization ------------------------ @@ -96,11 +115,11 @@ require "dropbox-api/tasks" Dropbox::API::Tasks.install ``` -You will notice that you have a new rake task - dropbox:authorize +You will notice that you have two new rake tasks - `dropbox:authorize` and `dropbox:authorize_oauth2` -When you call this Rake task, it will ask you to provide the app key and app secret. Afterwards it will present you with an authorize url on Dropbox. +When you call one of these Rake tasks, it will ask you to provide the app key and app secret. Afterwards it will present you with an authorize url on Dropbox. -Simply go to that url, authorize the app, then press enter in the console. +Simply go to that url, authorize the app, then follow the instructions in the console. The rake task will output valid ruby code which you can use to create a client. diff --git a/dropbox-api.gemspec b/dropbox-api.gemspec index 58709d8..0f1c321 100644 --- a/dropbox-api.gemspec +++ b/dropbox-api.gemspec @@ -17,6 +17,7 @@ Gem::Specification.new do |s| s.add_dependency 'multi_json', '~> 1.10' s.add_dependency 'oauth', '~> 0.4.7' s.add_dependency 'hashie', '~> 3.4.0' + s.add_dependency 'oauth2', '~> 1.0' s.add_development_dependency 'rspec','2.14.1' s.add_development_dependency 'rake', '10.1.0' diff --git a/lib/dropbox-api.rb b/lib/dropbox-api.rb index e7ea3cb..493d90a 100644 --- a/lib/dropbox-api.rb +++ b/lib/dropbox-api.rb @@ -11,6 +11,7 @@ module API require "dropbox-api/version" require "dropbox-api/util/config" require "dropbox-api/util/oauth" +require "dropbox-api/util/oauth2" require "dropbox-api/util/error" require "dropbox-api/util/util" require "dropbox-api/objects/object" diff --git a/lib/dropbox-api/connection.rb b/lib/dropbox-api/connection.rb index b57dce7..d4d18ae 100644 --- a/lib/dropbox-api/connection.rb +++ b/lib/dropbox-api/connection.rb @@ -15,8 +15,9 @@ def initialize(options = {}) @consumers = {} @tokens = {} Dropbox::API::Config.endpoints.each do |endpoint, url| - @consumers[endpoint] = Dropbox::API::OAuth.consumer(endpoint) - @tokens[endpoint] = Dropbox::API::OAuth.access_token(@consumers[endpoint], options) + auth_class = Dropbox::API::Config.auth_type == 'oauth2' ? Dropbox::API::OAuth2 : Dropbox::API::OAuth + @consumers[endpoint] = auth_class.consumer(endpoint) + @tokens[endpoint] = auth_class.access_token(@consumers[endpoint], options) end end diff --git a/lib/dropbox-api/connection/requests.rb b/lib/dropbox-api/connection/requests.rb index 4f770fb..9a888e8 100644 --- a/lib/dropbox-api/connection/requests.rb +++ b/lib/dropbox-api/connection/requests.rb @@ -8,7 +8,7 @@ module Requests def request(options = {}) response = yield raise Dropbox::API::Error::ConnectionFailed if !response - status = response.code.to_i + status = (response.respond_to?(:code) ? response.code : response.status).to_i case status when 400 parsed = MultiJson.decode(response.body) @@ -55,27 +55,47 @@ def handle_503(response) def get_raw(endpoint, path, data = {}, headers = {}) query = Dropbox::API::Util.query(data) + request_url = "#{Dropbox::API::Config.prefix}#{path}?#{URI.parse(URI.encode(query))}" request(:raw => true) do - token(endpoint).get "#{Dropbox::API::Config.prefix}#{path}?#{URI.parse(URI.encode(query))}", headers + if token.is_a?(::OAuth2::AccessToken) + token(endpoint).get request_url, :headers => headers, :raise_errors => false + else + token(endpoint).get request_url, headers + end end end def get(endpoint, path, data = {}, headers = {}) query = Dropbox::API::Util.query(data) + request_url = "#{Dropbox::API::Config.prefix}#{path}?#{URI.parse(URI.encode(query))}" request do - token(endpoint).get "#{Dropbox::API::Config.prefix}#{path}?#{URI.parse(URI.encode(query))}", headers + if token.is_a?(::OAuth2::AccessToken) + token(endpoint).get request_url, :headers => headers, :raise_errors => false + else + token(endpoint).get request_url, headers + end end end def post(endpoint, path, data = {}, headers = {}) + request_url = "#{Dropbox::API::Config.prefix}#{path}" request do - token(endpoint).post "#{Dropbox::API::Config.prefix}#{path}", data, headers + if token.is_a?(::OAuth2::AccessToken) + token(endpoint).post request_url, :body => data, :headers => headers, :raise_errors => false + else + token(endpoint).post request_url, data, headers + end end end def put(endpoint, path, data = {}, headers = {}) + request_url = "#{Dropbox::API::Config.prefix}#{path}" request do - token(endpoint).put "#{Dropbox::API::Config.prefix}#{path}", data, headers + if token.is_a?(::OAuth2::AccessToken) + token(endpoint).put request_url, :body => data, :headers => headers, :raise_errors => false + else + token(endpoint).put request_url, data, headers + end end end diff --git a/lib/dropbox-api/tasks.rb b/lib/dropbox-api/tasks.rb index 6d16cc3..9ccf5af 100644 --- a/lib/dropbox-api/tasks.rb +++ b/lib/dropbox-api/tasks.rb @@ -38,6 +38,36 @@ def self.install puts " client = Dropbox::API::Client.new(:token => '#{access_token.token}', :secret => '#{access_token.secret}')" puts "\n" end + + desc "Authorize wizard for Dropbox API OAuth2" + task :authorize_oauth2 do + require "oauth2" + require "dropbox-api" + require "cgi" + print "Enter dropbox app key: " + consumer_key = $stdin.gets.chomp + print "Enter dropbox app secret: " + consumer_secret = $stdin.gets.chomp + + Dropbox::API::Config.app_key = consumer_key + Dropbox::API::Config.app_secret = consumer_secret + + authorize_uri = ::Dropbox::API::OAuth2::AuthFlow.start + + puts "\nGo to this url and click 'Authorize' to get the token:" + puts authorize_uri + print "\nOnce you authorize the app on Dropbox, paste the code here and press enter:" + code = $stdin.gets.chomp + + access_token = ::Dropbox::API::OAuth2::AuthFlow.finish(code) + + puts "\nAuthorization complete!:\n\n" + puts " Dropbox::API::Config.app_key = '#{consumer_key}'" + puts " Dropbox::API::Config.app_secret = '#{consumer_secret}'" + puts " client = Dropbox::API::Client.new(:token => '#{access_token.token}')" + puts "\n" + end + end end diff --git a/lib/dropbox-api/util/config.rb b/lib/dropbox-api/util/config.rb index e518fd6..0a54845 100644 --- a/lib/dropbox-api/util/config.rb +++ b/lib/dropbox-api/util/config.rb @@ -9,6 +9,7 @@ class << self attr_accessor :app_key attr_accessor :app_secret attr_accessor :mode + attr_accessor :auth_type end self.endpoints = { @@ -20,6 +21,7 @@ class << self self.app_key = nil self.app_secret = nil self.mode = 'sandbox' + self.auth_type = 'oauth' end diff --git a/lib/dropbox-api/util/oauth2.rb b/lib/dropbox-api/util/oauth2.rb new file mode 100644 index 0000000..e7727ed --- /dev/null +++ b/lib/dropbox-api/util/oauth2.rb @@ -0,0 +1,37 @@ +module Dropbox + module API + module OAuth2 + + class << self + def consumer(endpoint) + if !Dropbox::API::Config.app_key or !Dropbox::API::Config.app_secret + raise Dropbox::API::Error::Config.new("app_key or app_secret not provided") + end + ::OAuth2::Client.new(Dropbox::API::Config.app_key, Dropbox::API::Config.app_secret, + :site => Dropbox::API::Config.endpoints[endpoint], + :authorize_url => "#{Dropbox::API::Config.prefix}/oauth2/authorize", + :token_url => "#{Dropbox::API::Config.prefix}/oauth2/token") + end + + def access_token(konsumer, options = {}) + ::OAuth2::AccessToken.new(konsumer, options[:token], options) + end + end + + module AuthFlow + def self.start + OAuth2.consumer(:authorize).authorize_url({ + client_id: Dropbox::API::Config.app_key, + response_type: 'code' + }) + end + + # Exchanges code for a token + def self.finish(code) + OAuth2.consumer(:main).auth_code.get_token(code) + end + end + end + end +end + diff --git a/spec/connection.sample.yml b/spec/connection.sample.yml index 973d372..d6ecd51 100644 --- a/spec/connection.sample.yml +++ b/spec/connection.sample.yml @@ -1,5 +1,6 @@ app_key: # CONSUMER KEY app_secret: # CONSUMER SECRET token: # ACCESS TOKEN -secret: # ACCESS SECRET -mode: # 'sandbox' or 'dropbox' \ No newline at end of file +secret: # ACCESS SECRET (for oauth1 only) +mode: # 'sandbox' or 'dropbox' +type: # 'oauth2' or 'oauth' (default) \ No newline at end of file diff --git a/spec/lib/dropbox-api/client_spec.rb b/spec/lib/dropbox-api/client_spec.rb index 0f13d37..5ab1997 100644 --- a/spec/lib/dropbox-api/client_spec.rb +++ b/spec/lib/dropbox-api/client_spec.rb @@ -222,8 +222,12 @@ @client.upload delete_filename, 'Some file' response = @client.delta cursor, files = response.cursor, response.entries - files.last.path.should == delete_filename - files.last.destroy + delta_file = files.last + if delta_file.path == Dropbox::Spec.test_dir + delta_file = files.at(-2) + end + delta_file.path.should == delete_filename + delta_file.destroy @client.upload filename, 'Another file' response = @client.delta(cursor) cursor, files = response.cursor, response.entries diff --git a/spec/lib/dropbox-api/connection_spec.rb b/spec/lib/dropbox-api/connection_spec.rb index c6df89c..c9cdb75 100644 --- a/spec/lib/dropbox-api/connection_spec.rb +++ b/spec/lib/dropbox-api/connection_spec.rb @@ -112,7 +112,11 @@ describe "#consumer" do it "returns an appropriate consumer object" do - @connection.consumer(:main).should be_a(::OAuth::Consumer) + if Dropbox::API::Config.auth_type == 'oauth2' + @connection.consumer(:main).should be_a(::OAuth2::Client) + else + @connection.consumer(:main).should be_a(::OAuth::Consumer) + end end end diff --git a/spec/support/config.rb b/spec/support/config.rb index 76f0cff..b29fb07 100644 --- a/spec/support/config.rb +++ b/spec/support/config.rb @@ -1,4 +1,5 @@ require "yaml" +require "oauth2" config = YAML.load_file "spec/connection.yml" @@ -6,8 +7,13 @@ Dropbox::API::Config.app_secret = config['app_secret'] Dropbox::API::Config.mode = config['mode'] -Dropbox::Spec.token = config['token'] -Dropbox::Spec.secret = config['secret'] +if ENV['AUTH'] == 'oauth2' || config['type'] == 'oauth2' + Dropbox::API::Config.auth_type = "oauth2" + Dropbox::Spec.token = config['token'] +else + Dropbox::Spec.token = config['token'] + Dropbox::Spec.secret = config['secret'] +end Dropbox::Spec.namespace = Time.now.to_i Dropbox::Spec.instance = Dropbox::API::Client.new(:token => Dropbox::Spec.token,