Class: RubyDesk::Connector

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby_desk/connector.rb

Overview

Basic methods used for connecting with oDesk like signing request parameters and parsing response. This class is also responsible for authorizing user

Constant Summary collapse

ODESK_URL =
"www.odesk.com/"
ODESK_API_URL =
"#{ODESK_URL}api/"
ODESK_GDS_URL =
"#{ODESK_URL}gds/"
ODESK_AUTH_URL =
"#{ODESK_URL}services/api/auth/"
DEFAULT_OPTIONS =
{:secure=>true, :sign=>true, :format=>'json',
:base_url=>ODESK_API_URL, :auth=>true}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(api_key = nil, api_secret = nil, api_token = nil) ⇒ Connector

Returns a new instance of Connector.



22
23
24
25
26
# File 'lib/ruby_desk/connector.rb', line 22

def initialize(api_key=nil, api_secret=nil, api_token=nil)
  @api_key = api_key
  @api_secret = api_secret
  @api_token = api_token
end

Instance Attribute Details

#auth_userObject

Returns the value of attribute auth_user.



20
21
22
# File 'lib/ruby_desk/connector.rb', line 20

def auth_user
  @auth_user
end

#frob=(value) ⇒ Object (writeonly)

Sets the attribute frob

Parameters:

  • value

    the value to set the attribute frob to.



19
20
21
# File 'lib/ruby_desk/connector.rb', line 19

def frob=(value)
  @frob = value
end

Instance Method Details

#auth_urlObject

Returns the URL that authenticates the application for the current user. This is used for web applications only



134
135
136
137
138
139
# File 'lib/ruby_desk/connector.rb', line 134

def auth_url
  auth_call = prepare_api_call("", :params=>{:api_key=>@api_key},
    :base_url=>ODESK_AUTH_URL, :format=>nil, :method=>:get, :auth=>false)
  data = auth_call[:params].to_a.map{|pair| pair.join '='}.join('&')
  return auth_call[:url]+"?"+data
end

#check_tokenObject

Returns the authenticated user associated with the given authorization token.

Parameters

    * api_key
    * api_sig
    * api_token

Return Data

    * token


198
199
200
201
202
# File 'lib/ruby_desk/connector.rb', line 198

def check_token
  json = prepare_and_invoke_api_call 'auth/v1/keys/token', :method=>:get
  # TODO what to do with results?
  return json
end

#desktop_auth_urlObject

Returns a URL that the desktop user should visit to activate current frob. This method should not be called before a frob has been requested



143
144
145
146
147
148
149
# File 'lib/ruby_desk/connector.rb', line 143

def desktop_auth_url
  raise "Frob should be requested first. Use RubyDesk::Controller#get_frob()" unless @frob
  auth_call = prepare_api_call("", :params=>{:api_key=>@api_key, :frob=>@frob},
    :base_url=>ODESK_AUTH_URL, :format=>nil, :method=>:get, :auth=>false)
  data = auth_call[:params].to_a.map{|pair| pair.join '='}.join('&')
  return auth_call[:url]+"?"+data
end

#get_frobObject

Returns an authentication frob. Parameters

* api_key
* api_sig

Return Data

* frob


182
183
184
185
186
# File 'lib/ruby_desk/connector.rb', line 182

def get_frob
  json = prepare_and_invoke_api_call 'auth/v1/keys/frobs',
    :params=>{:api_key=>@api_key}, :method=>:post, :auth=>false
  @frob = json['frob']
end

#get_tokenObject

Returns an authentication frob.

Parameters
  * frob
  * api_key
  * api_sig

Return Data
  * token


166
167
168
169
170
171
172
# File 'lib/ruby_desk/connector.rb', line 166

def get_token
  json = prepare_and_invoke_api_call 'auth/v1/keys/tokens',
      :params=>{:frob=>@frob, :api_key=>@api_key}, :method=>:post,
      :auth=>false
  @auth_user = User.new(json['auth_user'])
  @api_token = json['token']
end

#invoke_api_call(api_call) ⇒ Object

invokes the given API call and returns body of the response as text



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/ruby_desk/connector.rb', line 79

def invoke_api_call(api_call)
  url = URI.parse(api_call[:url])
  http = Net::HTTP.new(url.host, url.port)
  http.use_ssl = true
			http.verify_mode = OpenSSL::SSL::VERIFY_NONE

  # Concatenate parameters to form data
  data = api_call[:params].to_a.map{|pair| pair.map{|x| URI.escape(x.to_s)}.join '='}.join('&')
  headers = {
    'Content-Type' => 'application/x-www-form-urlencoded'
  }

  RubyDesk.logger.info "URL: #{api_call[:url]}"
  RubyDesk.logger.info "method: #{api_call[:method]}"
  RubyDesk.logger.info "Params: #{data}"

  case api_call[:method]
    when :get, 'get' then
      resp, data = http.request(Net::HTTP::Get.new(url.path+"?"+data, headers))
    when :post, 'post' then
      resp, data = http.request(Net::HTTP::Post.new(url.path, headers), data)
    when :delete, 'delete' then
      resp, data = http.request(Net::HTTP::Delete.new(url.path, headers), data)
  end

  RubyDesk.logger.info "Response code: #{resp.code}"
  RubyDesk.logger.info "Returned data: #{data}"

  case resp.code
    when "200" then return data
    when "400" then raise RubyDesk::BadRequest, data
    when "401", "403" then raise RubyDesk::UnauthorizedError, data
    when "404" then raise RubyDesk::PageNotFound, data
    when "500" then raise RubyDesk::ServerError, data
    else raise RubyDesk::Error, data
  end

end

#logout_urlObject

return the URL that logs user out of odesk applications



152
153
154
155
156
# File 'lib/ruby_desk/connector.rb', line 152

def logout_url
  logout_call = prepare_api_call("", :base_url=>ODESK_AUTH_URL,
    :secure=>false, :auth=>false, :format=>nil)
  return logout_call[:url]
end

#prepare_and_invoke_api_call(path, options = {}) ⇒ Object

Prepares an API call with the given arguments then invokes it and returns its body



119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/ruby_desk/connector.rb', line 119

def prepare_and_invoke_api_call(path, options = {})
  api_call = prepare_api_call(path, options)
  data = invoke_api_call(api_call)

  parsed_data = case options[:format]
    when 'json' then JSON.parse(data)
    when 'xml' then REXML::Document.new(data)
    else JSON.parse(data) rescue REXML::Document.new(data) rescue data
  end
  RubyDesk.logger.info "Parsed data: #{parsed_data.inspect}"
  return parsed_data
end

#prepare_api_call(path, options = {}) ⇒ Object

Returns the correct URL to go to to invoke the given api path: the path of the API to call. e.g. ‘auth’ options:

  • :secure=>false: Whether a secure connection is required or not.

  • :sign=>true: Whether you need to sign the parameters or not. If :scure is false, parameters are not signed regardless of this option.

  • :params=>{}: a hash of parameters that needs to be appended

  • :auth=>true: when true indicates that this call need authentication.

    This forces adding :api_token, :api_key and :api_sig to parameters.
    This means that parameters are automatically signed regardless of other options
    


64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/ruby_desk/connector.rb', line 64

def prepare_api_call(path, options = {})
  options = DEFAULT_OPTIONS.merge(options)
  params = options[:params] || {}
  if options[:auth]
    params[:api_token] ||= @api_token
    params[:api_key] ||= @api_key
  end
  params[:api_sig] = sign(params) if (options[:secure] && options[:sign]) || options[:auth]
  url = (options[:secure] ? "https" : "http") + "://"
  url << options[:base_url] << path
  url << ".#{options[:format]}" if options[:format]
  return {:url=>url, :params=> params, :method=>options[:method]}
end

#revoke_tokenObject

Revokes the given aut Parameters

* api_key
* api_sig
* api_token


210
211
212
213
# File 'lib/ruby_desk/connector.rb', line 210

def revoke_token
  prepare_and_invoke_api_call 'auth/v1/keys/token', :method=>:delete
  @api_token = nil
end

#sign(params) ⇒ Object

Sign the given parameters and returns the signature



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/ruby_desk/connector.rb', line 29

def sign(params)
  RubyDesk.logger.debug {"Params to sign: #{params.inspect}"}
  # sort parameters by its names (keys)
  sorted_params = params.sort { |a, b| a.to_s <=> b.to_s}

  RubyDesk.logger.debug {"Sorted params: #{sorted_params.inspect}"}
  
  # Unescape escaped params
  sorted_params.map! do |k, v|
    [k, URI.unescape(v)]
  end

  # concatenate secret with names, values
  concatenated = @api_secret + sorted_params.join

  RubyDesk.logger.debug {"concatenated: #{concatenated}"}

  # Calculate and return md5 of concatenated string
  md5 = Digest::MD5.hexdigest(concatenated)

  RubyDesk.logger.debug {"md5: #{md5}"}

  return md5
end