|
|
|
@ -1,7 +1,8 @@
|
|
|
|
|
# encoding: utf-8
|
|
|
|
|
|
|
|
|
|
require 'dropbox_sdk'
|
|
|
|
|
require 'dropbox_api'
|
|
|
|
|
require_relative '../base_oauth'
|
|
|
|
|
require_relative '../../../../../lib/dropbox_api/endpoints/auth/token/revoke'
|
|
|
|
|
|
|
|
|
|
module CartoDB
|
|
|
|
|
module Datasources
|
|
|
|
@ -62,46 +63,24 @@ module CartoDB
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Return the url to be displayed or sent the user to to authenticate and get authorization code
|
|
|
|
|
# @param use_callback_flow : bool
|
|
|
|
|
# Older implementations had a use_callback_flow parameter that became deprecated. Not implemented.
|
|
|
|
|
# @throws AuthError
|
|
|
|
|
def get_auth_url(use_callback_flow=true)
|
|
|
|
|
if use_callback_flow
|
|
|
|
|
@auth_flow = DropboxOAuth2Flow.new(@app_key, @app_secret, @callback_url, {}, :csrf_token)
|
|
|
|
|
else
|
|
|
|
|
@auth_flow = DropboxOAuth2FlowNoRedirect.new(@app_key, @app_secret)
|
|
|
|
|
end
|
|
|
|
|
service_name = service_name_for_user(DATASOURCE_NAME, @user)
|
|
|
|
|
@auth_flow.start(CALLBACK_STATE_DATA_PLACEHOLDER.sub('user', @user.username).sub('service', service_name))
|
|
|
|
|
rescue DropboxError, ArgumentError => ex
|
|
|
|
|
def get_auth_url
|
|
|
|
|
authenticator.authorize_url redirect_uri: @callback_url, state: state
|
|
|
|
|
rescue => ex
|
|
|
|
|
raise AuthError.new("get_auth_url(#{use_callback_flow}): #{ex.message}", DATASOURCE_NAME)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Validate authorization code and store token
|
|
|
|
|
# @param auth_code : string
|
|
|
|
|
# @return string : Access token
|
|
|
|
|
# @throws AuthError
|
|
|
|
|
def validate_auth_code(auth_code)
|
|
|
|
|
@auth_flow = DropboxOAuth2FlowNoRedirect.new(@app_key, @app_secret)
|
|
|
|
|
data = @auth_flow.finish(auth_code)
|
|
|
|
|
@access_token = data[0] # Only keep the access token
|
|
|
|
|
@auth_flow = nil
|
|
|
|
|
@client = DropboxClient.new(@access_token)
|
|
|
|
|
@access_token
|
|
|
|
|
rescue DropboxError, ArgumentError => ex
|
|
|
|
|
raise AuthError.new("validate_auth_code(): #{ex.message}", DATASOURCE_NAME)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Validates the authorization callback
|
|
|
|
|
# @param params : mixed
|
|
|
|
|
def validate_callback(params)
|
|
|
|
|
session = {csrf_token: params[:state].split('|').first.presence }
|
|
|
|
|
@auth_flow = DropboxOAuth2Flow.new(@app_key, @app_secret, @callback_url, session, :csrf_token)
|
|
|
|
|
data = @auth_flow.finish(params)
|
|
|
|
|
@access_token = data[0] # Only keep the access token
|
|
|
|
|
@auth_flow = nil
|
|
|
|
|
@client = DropboxClient.new(@access_token)
|
|
|
|
|
raise "state doesn't match" unless params[:state] == state
|
|
|
|
|
auth_bearer = authenticator.get_token(params[:code], redirect_uri: @callback_url)
|
|
|
|
|
@access_token = auth_bearer.token
|
|
|
|
|
|
|
|
|
|
@client = DropboxApi::Client.new(@access_token)
|
|
|
|
|
@access_token
|
|
|
|
|
rescue DropboxError, ArgumentError => ex
|
|
|
|
|
rescue => ex
|
|
|
|
|
raise AuthError.new("validate_callback(#{params.inspect}): #{ex.message}", DATASOURCE_NAME)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
@ -111,7 +90,7 @@ module CartoDB
|
|
|
|
|
# @throws AuthError
|
|
|
|
|
def token=(token)
|
|
|
|
|
@access_token = token
|
|
|
|
|
@client = DropboxClient.new(@access_token)
|
|
|
|
|
@client = DropboxApi::Client.new(@access_token)
|
|
|
|
|
rescue => ex
|
|
|
|
|
handle_error(ex, "token= : #{ex.message}")
|
|
|
|
|
end
|
|
|
|
@ -133,9 +112,9 @@ module CartoDB
|
|
|
|
|
self.filter = filter
|
|
|
|
|
|
|
|
|
|
@formats.each do |search_query|
|
|
|
|
|
response = @client.search('/', search_query)
|
|
|
|
|
response.each do |item|
|
|
|
|
|
all_results.push(format_item_data(item))
|
|
|
|
|
response = @client.search(search_query, '')
|
|
|
|
|
response.matches.each do |item|
|
|
|
|
|
all_results.push(format_item_data(item.resource))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
all_results
|
|
|
|
@ -150,8 +129,11 @@ module CartoDB
|
|
|
|
|
# @throws AuthError
|
|
|
|
|
# @throws DataDownloadError
|
|
|
|
|
def get_resource(id)
|
|
|
|
|
contents, = @client.get_file_and_metadata(id)
|
|
|
|
|
contents
|
|
|
|
|
file_contents = ''
|
|
|
|
|
@client.download(id) do |chunk|
|
|
|
|
|
file_contents << chunk
|
|
|
|
|
end
|
|
|
|
|
file_contents
|
|
|
|
|
rescue => ex
|
|
|
|
|
handle_error(ex, "get_resource() #{id}: #{ex.message}")
|
|
|
|
|
end
|
|
|
|
@ -164,7 +146,7 @@ module CartoDB
|
|
|
|
|
def get_resource_metadata(id)
|
|
|
|
|
raise DropboxPermissionError.new('No Dropbox client', DATASOURCE_NAME) unless @client.present?
|
|
|
|
|
|
|
|
|
|
response = @client.metadata(id)
|
|
|
|
|
response = @client.get_metadata(id)
|
|
|
|
|
item_data = format_item_data(response)
|
|
|
|
|
|
|
|
|
|
item_data.to_hash
|
|
|
|
@ -214,17 +196,13 @@ module CartoDB
|
|
|
|
|
# @throws AuthError
|
|
|
|
|
def token_valid?
|
|
|
|
|
# Any call would do, we just want to see if communicates or refuses the token
|
|
|
|
|
@client.account_info
|
|
|
|
|
raise "Current account not found" unless @client.get_current_account
|
|
|
|
|
true
|
|
|
|
|
rescue DropboxError => ex
|
|
|
|
|
error_code = ex.http_response.code.to_i
|
|
|
|
|
raise AuthError.new("token_valid? : #{ex.message}") unless (error_code == 401 || error_code == 403)
|
|
|
|
|
false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Revokes current set token
|
|
|
|
|
def revoke_token
|
|
|
|
|
@client.disable_access_token
|
|
|
|
|
@client.revoke
|
|
|
|
|
true
|
|
|
|
|
rescue => ex
|
|
|
|
|
raise AuthError.new("revoke_token: #{ex.message}", DATASOURCE_NAME)
|
|
|
|
@ -239,7 +217,7 @@ module CartoDB
|
|
|
|
|
# @throws AuthError
|
|
|
|
|
# @throws mixed
|
|
|
|
|
def handle_error(original_exception, message)
|
|
|
|
|
if original_exception.kind_of? DropboxError
|
|
|
|
|
if original_exception.kind_of? DropboxApi::Errors::BasicError
|
|
|
|
|
error_code = original_exception.http_response.code.to_i
|
|
|
|
|
if error_code == 401 || error_code == 403
|
|
|
|
|
raise TokenExpiredOrInvalidError.new(message, DATASOURCE_NAME)
|
|
|
|
@ -256,19 +234,28 @@ module CartoDB
|
|
|
|
|
# Formats all data to comply with our desired format
|
|
|
|
|
# @param item_data Hash : Single item returned from Dropbox API
|
|
|
|
|
# @return { :id, :title, :url, :service, :size }
|
|
|
|
|
def format_item_data(item_data)
|
|
|
|
|
filename = item_data.fetch('path').split('/').last
|
|
|
|
|
def format_item_data(resource)
|
|
|
|
|
path = resource.path_display
|
|
|
|
|
filename = path.split('/').last
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
id: item_data.fetch('path'),
|
|
|
|
|
id: path,
|
|
|
|
|
title: filename,
|
|
|
|
|
filename: filename,
|
|
|
|
|
service: DATASOURCE_NAME,
|
|
|
|
|
checksum: checksum_of(item_data.fetch('rev')),
|
|
|
|
|
size: item_data.fetch('bytes').to_i
|
|
|
|
|
checksum: checksum_of(resource.rev),
|
|
|
|
|
size: resource.size
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def authenticator
|
|
|
|
|
@authenticator ||= DropboxApi::Authenticator.new(@app_key, @app_secret)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def state
|
|
|
|
|
service_name = service_name_for_user(DATASOURCE_NAME, @user)
|
|
|
|
|
CALLBACK_STATE_DATA_PLACEHOLDER.sub('user', @user.username).sub('service', service_name)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|