added basic json parser support and tests
[arvados.git] / lib / google / api_client / auth / oauth_1.rb
1 # Copyright 2010 Google Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 require 'addressable/uri'
16 require 'oauth'
17
18 module Google #:nodoc:
19   class APIClient #:nodoc:
20     ##
21     # An OAuth 1.0a handler.
22     class OAuth1
23
24       ##
25       # The default OAuth 1.0a configuration values.  These may be overrided
26       # simply by passing in the same key to the constructor.
27       DEFAULTS = {
28         :request_token_uri =>
29           'https://www.google.com/accounts/OAuthGetRequestToken',
30         :authorization_uri =>
31           'https://www.google.com/accounts/OAuthAuthorizeToken',
32         :access_token_uri =>
33           'https://www.google.com/accounts/OAuthGetAccessToken',
34         :scopes => [],
35         :callback => OAuth::OUT_OF_BAND,
36         :display_name => nil,
37         :consumer_key => 'anonymous',
38         :consumer_secret => 'anonymous'
39       }
40
41       ##
42       # A set of default configuration values specific to each service.  These
43       # may be overrided simply by passing in the same key to the constructor.
44       SERVICE_DEFAULTS = {
45         :buzz => {
46           :authorization_uri =>
47             'https://www.google.com/buzz/api/auth/OAuthAuthorizeToken',
48           :scopes => ['https://www.googleapis.com/auth/buzz']
49         },
50         :latitude => {
51           :authorization_uri =>
52             'https://www.google.com/latitude/apps/OAuthAuthorizeToken',
53           :scopes => ['https://www.googleapis.com/auth/latitude']
54         }
55       }
56
57       ##
58       # Creates a new OAuth 1.0a handler.  This object obtains the tokens from
59       # the provider and handles signing any requests to the API.
60       #
61       # @param [Hash] options
62       #   The configuration options.
63       #   <code>:service</code>::
64       #     The name of the service.
65       #   <code>:request_token_uri</code>::
66       #     The OAuth endpoint for obtaining a request token.
67       #   <code>:authorization_uri</code>::
68       #     The OAuth endpoint for obtaining user permission.
69       #   <code>:access_token_uri</code>::
70       #     The OAuth endpoint for obtaining an access token.
71       #   <code>:scopes</code>::
72       #     An <code>Array</code> of scopes that define the access being
73       #     requested to the API.
74       #   <code>:callback</code>::
75       #     The URI the user will be redirected to if access is granted to the
76       #     API.  For development purposes, the special value
77       #     <code>OAuth::OUT_OF_BAND</code> may also be used.
78       #   <code>:display_name</code>::
79       #     A human-readable service name to present to the user when they
80       #     visit the <code>:authorization_uri</code>.
81       #   <code>:consumer_key</code>::
82       #     The consumer key you registered with the Google Accounts API.
83       #   <code>:consumer_secret</code>::
84       #     The consumer secret issued to you when you registered with the
85       #     Google Accounts API.
86       #
87       # @return [Google::APIClient::OAuth1] The OAuth 1.0a handler.
88       def initialize(options={})
89         if options[:service] && SERVICE_DEFAULTS[options[:service]]
90           @options = DEFAULTS.merge(SERVICE_DEFAULTS[options[:service]])
91         else
92           @options = DEFAULTS.clone
93         end
94         @options.merge!(options)
95         @options[:request_token_uri] =
96           Addressable::URI.parse(@options[:request_token_uri])
97         @options[:authorization_uri] =
98           Addressable::URI.parse(@options[:authorization_uri])
99         @options[:access_token_uri] =
100           Addressable::URI.parse(@options[:access_token_uri])
101         if (@options[:request_token_uri].site !=
102             @options[:authorization_uri].site) ||
103             (@options[:request_token_uri].site !=
104             @options[:authorization_uri].site)
105           raise ArgumentError, 'All OAuth endpoints must be on the same site.'
106         end
107         @oauth_consumer = ::OAuth::Consumer.new(
108           @options[:consumer_key], @options[:consumer_secret], {
109             # This is an extremely unfortunate way to configure the consumer,
110             # but not worth forking or patching to resolve.  Yet.
111             :site               => @options[:request_token_uri].site,
112             :scheme             => :header,
113             :http_method        => :post,
114             :request_token_path => @options[:request_token_uri].request_uri,
115             :access_token_path  => @options[:access_token_uri].request_uri,
116             :authorize_path     => @options[:authorization_uri].request_uri
117           }
118         )
119       end
120
121       ##
122       # Returns the configuration of the handler.  Configuration options that
123       # are not recognized by the handler are ignored.
124       #
125       # @return [Hash] The configuration options.
126       def options
127         return @options
128       end
129
130       ##
131       # Returns the current request token.  Obtains a new request token if
132       # one hasn't already been obtained.
133       #
134       # @return [OAuth::RequestToken] The request token.
135       def request_token
136         oauth_parameters = {
137           :oauth_callback => @options[:callback]
138         }
139         app_parameters = {
140           :scope => @options[:scopes].join(' ')
141         }
142         if @options[:display_name]
143           app_parameters[:xoauth_displayname] = @options[:display_name]
144         end
145         return @request_token ||= @oauth_consumer.get_request_token(
146           oauth_parameters,
147           app_parameters
148         )
149       end
150
151       ##
152       # Sets the request token for the handler.
153       #
154       # @param [OAuth::RequestToken] new_request_token The request token.
155       def request_token=(new_request_token)
156         if new_request_token.kind_of?(OAuth::RequestToken)
157           @request_token = new_request_token
158         else
159           raise TypeError,
160             "Expected OAuth::RequestToken, got #{new_request_token.class}."
161         end
162       end
163
164       ##
165       # Returns the current access token.  Obtains a new access token if
166       # one hasn't already been obtained.  An request token must have already
167       # been obtained and authorized or this method will fail.
168       #
169       # @return [OAuth::AccessToken] The access token.
170       def access_token
171         return @access_token ||=
172           @oauth_consumer.get_access_token(self.request_token)
173       end
174
175       ##
176       # Sets the access token for the handler.
177       #
178       # @param [OAuth::AccessToken] new_access_token The access token.
179       def access_token=(new_access_token)
180         if new_access_token.kind_of?(OAuth::AccessToken)
181           @access_token = new_access_token
182         else
183           raise TypeError,
184             "Expected OAuth::AccessToken, got #{new_access_token.class}."
185         end
186       end
187
188       ##
189       # Returns the list of scopes for the handler.
190       #
191       # @return [Array] An <code>Array</code> of access scopes.
192       def scopes
193         return @options[:scopes]
194       end
195
196       ##
197       # Returns the callback for the handler.
198       #
199       # @return [String] The OAuth 1.0a callback for the consumer.
200       def callback
201         return @options[:callback]
202       end
203
204       ##
205       # Returns a human-readable service name to present to the user when they
206       # visit the <code>:authorization_uri</code>.
207       #
208       # @return [String] The display name for the consumer.
209       def display_name
210         return @options[:display_name]
211       end
212
213       ##
214       # Returns the consumer key.
215       #
216       # @return [String]
217       #   The consumer key you registered with the Google Accounts API.
218       def consumer_key
219         return @oauth_consumer.key
220       end
221
222       ##
223       # Returns the consumer key.
224       #
225       # @return [String]
226       #   The consumer secret issued to you when you registered with the
227       #   Google Accounts API.
228       def consumer_secret
229         return @oauth_consumer.secret
230       end
231
232       ##
233       # Returns the request token URI.
234       #
235       # @return [String]
236       #   The OAuth endpoint for obtaining a request token.
237       def request_token_uri
238         return @oauth_consumer.request_token_url
239       end
240
241       ##
242       # Returns the authorization endpoint URI.  This URI is used to construct
243       # the {#authorization_uri}.
244       #
245       # @return [String]
246       #   The OAuth endpoint for obtaining user permission.
247       def authorization_endpoint_uri
248         return @oauth_consumer.authorize_url
249       end
250
251       ##
252       # Builds the authorization URI that the user will be redirected to.
253       # Note that this value is derived from the
254       # {#authorization_endpoint_uri}.
255       #
256       # @param [Hash] parameters
257       #   The extra URI query parameters appended to the
258       #   {#authorization_endpoint_uri}.
259       #
260       # @return [String]
261       #   The URI to redirect the user to to obtain permission.
262       def authorization_uri(parameters={})
263         return self.request_token.authorize_url(parameters)
264       end
265
266       ##
267       # Returns the access token URI.
268       #
269       # @return [String]
270       #   The OAuth endpoint for obtaining an access token.
271       def access_token_uri
272         return @oauth_consumer.access_token_url
273       end
274     end
275   end
276 end