rails3-opensocial 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,117 @@
1
+ # Copyright (c) 2008 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
+
16
+ module OpenSocial #:nodoc:
17
+
18
+ # Acts as a wrapper for an OpenSocial appdata entry.
19
+ #
20
+ # The AppData class takes a person's ID and input JSON as initialization
21
+ # parameters, and iterates through each of the key/value pairs of that JSON.
22
+ # For each key that is found, an attr_accessor is constructed (except for
23
+ # :id which is preconstructed, and required), allowing direct access to the
24
+ # value. Each value is stored in the attr_accessor, either as a String,
25
+ # Fixnum, Hash, or Array.
26
+ #
27
+
28
+
29
+ class AppData < Base
30
+ attr_accessor :id
31
+
32
+ # Initializes the AppData entry based on the provided id and json fragment.
33
+ # If no JSON is provided, an empty object (with only an ID) is created.
34
+ def initialize(id, json)
35
+ @id = id
36
+
37
+ if json
38
+ json.each do |key, value|
39
+ begin
40
+ self.send("#{key}=", value)
41
+ rescue NoMethodError
42
+ add_attr(key)
43
+ self.send("#{key}=", value)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ # Provides the ability to request a Collection of AppData for a given
51
+ # user or set of users.
52
+ #
53
+ # The FetchAppData wraps a simple request to an OpenSocial
54
+ # endpoint for a Collection of AppData. As parameters, it accepts
55
+ # a user ID and selector. This request may be used, standalone, by calling
56
+ # send, or bundled into an RpcRequest.
57
+ #
58
+
59
+
60
+ class FetchAppDataRequest < Request
61
+ # Defines the service fragment for use in constructing the request URL or
62
+ # JSON
63
+ SERVICE = 'appdata'
64
+
65
+ # Initializes a request to fetch appdata for the specified user and
66
+ # group, or the default (@me, @self). A valid Connection is not necessary
67
+ # if the request is to be used as part of an RpcRequest.
68
+ def initialize(connection = nil, guid = '@me', selector = '@self',
69
+ aid = '@app')
70
+ super(connection, guid, selector, aid)
71
+ end
72
+
73
+ # Sends the request, passing in the appropriate SERVICE and specified
74
+ # instance variables. Accepts an unescape parameter, defaulting to true,
75
+ # if the returned data should be unescaped.
76
+ def send(unescape = true)
77
+ json = send_request(SERVICE, @guid, @selector, @pid, unescape)
78
+
79
+ return parse_response(json['entry'])
80
+ end
81
+
82
+ # Selects the appropriate fragment from the JSON response in order to
83
+ # create a native object.
84
+ def parse_rpc_response(response)
85
+ return parse_response(response['data'])
86
+ end
87
+
88
+ # Converts the request into a JSON fragment that can be used as part of a
89
+ # larger RpcRequest.
90
+ def to_json(*a)
91
+ value = {
92
+ 'method' => SERVICE + GET,
93
+ 'params' => {
94
+ 'userId' => ["#{@guid}"],
95
+ 'groupId' => "#{@selector}",
96
+ 'appId' => "#{@pid}",
97
+ 'fields' => []
98
+ },
99
+ 'id' => @key
100
+ }.to_json(*a)
101
+ end
102
+
103
+ private
104
+
105
+ # Converts the JSON response into a Collection of AppData entries, indexed
106
+ # by id.
107
+ def parse_response(response)
108
+ appdata = Collection.new
109
+ response.each do |key, value|
110
+ data = AppData.new(key, value)
111
+ appdata[key] = data
112
+ end
113
+
114
+ return appdata
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,93 @@
1
+ # Copyright (c) 2007 Blaine Cook, Larry Halff, Pelle Braendgaard
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ #
22
+ # Includes modifications by Robin Luckey from:
23
+ # http://github.com/robinluckey/oauth/tree/master/lib%2Foauth%2Frequest_proxy%2Faction_controller_request.rb
24
+
25
+ require 'rubygems'
26
+ require 'active_support'
27
+ require 'oauth/request_proxy/action_controller_request'
28
+ require 'uri'
29
+
30
+ module OAuth::RequestProxy #:nodoc: all
31
+ class ActionControllerRequest < OAuth::RequestProxy::Base
32
+ # proxies ActionController::AbstractRequest
33
+ if ActionController.const_defined?(:AbstractRequest)
34
+ proxies ActionController::AbstractRequest
35
+ else
36
+ proxies ActionController::Request
37
+ end
38
+
39
+
40
+ def method
41
+ request.method.to_s.upcase
42
+ end
43
+
44
+ def uri
45
+ uri = URI.parse(request.protocol + request.host + request.port_string + request.path)
46
+ uri.query = nil
47
+ uri.to_s
48
+ end
49
+
50
+ def parameters
51
+ if options[:clobber_request]
52
+ options[:parameters] || {}
53
+ else
54
+ params = request_params.merge(query_params).merge(header_params)
55
+ params.stringify_keys! if params.respond_to?(:stringify_keys!)
56
+ params.merge(options[:parameters] || {})
57
+ end
58
+ end
59
+
60
+ # Override from OAuth::RequestProxy::Base to avoid roundtrip
61
+ # conversion to Hash or Array and thus preserve the original
62
+ # parameter names
63
+ def parameters_for_signature
64
+ params = []
65
+ params << options[:parameters].to_query if options[:parameters]
66
+
67
+ unless options[:clobber_request]
68
+ params << header_params.to_query
69
+ params << CGI.unescape(request.query_string) unless request.query_string.blank?
70
+ if request.content_type == Mime::Type.lookup("application/x-www-form-urlencoded")
71
+ params << CGI.unescape(request.raw_post)
72
+ end
73
+ end
74
+
75
+ params.
76
+ join('&').split('&').
77
+ reject { |kv| kv =~ /^oauth_signature=.*/}.
78
+ reject(&:blank?).
79
+ map { |p| p.split('=') }
80
+ end
81
+
82
+ protected
83
+
84
+ def query_params
85
+ request.query_parameters
86
+ end
87
+
88
+ def request_params
89
+ request.request_parameters
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,114 @@
1
+ # Copyright (c) 2008 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 'rubygems'
16
+ require 'oauth'
17
+ require 'oauth/consumer'
18
+
19
+
20
+ module OpenSocial #:nodoc:
21
+
22
+ # Provides helper classes to be used in verifying and validating the user.
23
+ # In particular, support is provided for:
24
+ #
25
+ # * Verification of signed makeRequest using OAuth/HMAC-SHA1
26
+ # class ExampleController < ApplicationController
27
+ # OpenSocial::Auth::CONSUMER_KEY = '623061448914'
28
+ # OpenSocial::Auth::CONSUMER_SECRET = 'uynAeXiWTisflWX99KU1D2q5'
29
+ #
30
+ # include OpenSocial::Auth
31
+ #
32
+ # before_filter :validate
33
+ #
34
+ # def return_private_data
35
+ # end
36
+ # end
37
+ #
38
+ # * Request for an OAuth request token
39
+ #
40
+ # * Request for an OAuth access token, when supplied with a request token
41
+ #
42
+
43
+
44
+ module Auth
45
+
46
+ # Validates an incoming request by using the OAuth library and the supplied
47
+ # key and secret.
48
+ def validate(key = CONSUMER_KEY, secret = CONSUMER_SECRET)
49
+ consumer = OAuth::Consumer.new(key, secret)
50
+ begin
51
+ signature = OAuth::Signature.build(request) do
52
+ [nil, consumer.secret]
53
+ end
54
+ pass = signature.verify
55
+ rescue OAuth::Signature::UnknownSignatureMethod => e
56
+ logger.error 'An unknown signature method was supplied: ' + e.to_s
57
+ end
58
+ return pass
59
+ end
60
+
61
+ # Gets an OAuth request token, and redirects the user to authorize the app
62
+ # to access data on their behalf.
63
+ def get_oauth_token(key, secret, container, callback)
64
+ consumer = OAuth::Consumer.new(key, secret, {
65
+ :site => container[:base_uri],
66
+ :request_token_path => container[:request_token_path],
67
+ :authorize_path => container[:authorize_path],
68
+ :access_token_path => container[:access_token_path],
69
+ :http_method => container[:http_method]
70
+ })
71
+ request_token = consumer.get_request_token
72
+
73
+ session[:token] = request_token.token
74
+ session[:secret] = request_token.secret
75
+
76
+ redirect_to request_token.authorize_url + '&oauth_callback=' + CGI.escape(callback)
77
+ end
78
+
79
+ # If neccesary, swaps an existing request token and secret for an access
80
+ # token, storing it in the Connection class, and returning the access token
81
+ # and secret for later use.
82
+ def get_access_token(connection, token, secret)
83
+ if (token && secret)
84
+ consumer = OAuth::Consumer.new(connection.consumer_key,
85
+ connection.consumer_secret,
86
+ connection.container)
87
+
88
+ if connection.consumer_token.token.empty? &&
89
+ connection.consumer_token.secret.empty?
90
+ connection.consumer_token = OAuth::Token.new(token, secret)
91
+
92
+ uri = URI.parse(connection.container[:base_uri] +
93
+ connection.container[:access_token_path])
94
+ http = Net::HTTP.new(uri.host, uri.port)
95
+ req = Net::HTTP::Get.new(uri.request_uri)
96
+ connection.sign!(http, req)
97
+
98
+ resp = http.get(req.path)
99
+
100
+ matches = resp.body.match(/oauth_token=(.*?)&oauth_token_secret=(.*)/)
101
+ access_token = matches[1]
102
+ access_secret = matches[2]
103
+ end
104
+
105
+ reusable_token = OAuth::AccessToken.new(consumer, access_token, access_secret)
106
+ connection.consumer_token = reusable_token
107
+
108
+ return access_token, access_secret
109
+ end
110
+
111
+ return nil, nil
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,53 @@
1
+ # Copyright (c) 2008 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 'net/http'
16
+ require 'uri'
17
+
18
+ # Use json/pure if you opt-out of including the validation code in
19
+ # opensocial/auth. This gem adds standard to_json behavior for the
20
+ # request classes, instead of using ActiveSupport.
21
+ require 'rubygems'
22
+
23
+
24
+ module OpenSocial #:nodoc:
25
+
26
+ # Provides base functionality for the OpenSocial child classes.
27
+ #
28
+
29
+
30
+ class Base
31
+
32
+ # Creates an attr_accessor for the specified variable name.
33
+ def add_attr(name)
34
+ self.class.class_eval "attr_accessor :#{name}"
35
+ end
36
+ end
37
+
38
+ # Wraps the functionality of an OpenSocial collection as defined by the
39
+ # specification. In practical uses, a Collection serves as a Hash (usually
40
+ # index by ID) with the added benefit of being convertable to an Array, when
41
+ # it's necessary to iterate over all of the values.
42
+ #
43
+
44
+
45
+ class Collection < Hash
46
+
47
+ # Converts the Collection to an Array by returning each of the values from
48
+ # key/value pairs.
49
+ def to_array
50
+ values
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,132 @@
1
+ # Copyright (c) 2008 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 'oauth/consumer'
16
+
17
+
18
+ module OpenSocial #:nodoc:
19
+
20
+ # Describes a connection to an OpenSocial container, including the ability to
21
+ # declare an authorization mechanism and appropriate credentials.
22
+ #
23
+
24
+
25
+ class Connection
26
+ ORKUT = { :endpoint => 'http://sandbox.orkut.com/social',
27
+ :rest => 'rest/',
28
+ :rpc => 'rpc/' }
29
+ IGOOGLE = { :endpoint => 'http://gmodules.com/api',
30
+ :rest => '',
31
+ :rpc => 'rpc' }
32
+ MYSPACE = { :endpoint => 'http://api.myspace.com/v2',
33
+ :rest => '',
34
+ :rpc => '',
35
+ :base_uri => 'http://api.myspace.com',
36
+ :request_token_path => '/request_token',
37
+ :authorize_path => '/authorize',
38
+ :access_token_path => '/access_token',
39
+ :http_method => :get }
40
+
41
+ AUTH_HMAC = 0
42
+ AUTH_ST = 1
43
+
44
+ DEFAULT_OPTIONS = { :container => ORKUT,
45
+ :st => '',
46
+ :consumer_key => '',
47
+ :consumer_secret => '',
48
+ :consumer_token => OAuth::Token.new('', ''),
49
+ :xoauth_requestor_id => '',
50
+ :auth => AUTH_HMAC }
51
+
52
+ # Defines the container that will be used in requests.
53
+ attr_accessor :container
54
+
55
+ # Defines the security token, for when OAuth is not in use.
56
+ attr_accessor :st
57
+
58
+ # Defines the consumer key for OAuth.
59
+ attr_accessor :consumer_key
60
+
61
+ # Defines the consumer secret for OAuth.
62
+ attr_accessor :consumer_secret
63
+
64
+ # Defines the consumer token for OAuth.
65
+ attr_accessor :consumer_token
66
+
67
+ # Defines the ID of the requestor (required by some implementations when
68
+ # using OAuth).
69
+ attr_accessor :xoauth_requestor_id
70
+
71
+ # Defines the authentication scheme: HMAC or security token.
72
+ attr_accessor :auth
73
+
74
+ # Initializes the Connection using the supplied options hash, or the
75
+ # defaults. Verifies that the supplied authentication type has proper
76
+ # (ie. non-blank) credentials, and that the authentication type is known.
77
+ def initialize(options = {})
78
+ options = DEFAULT_OPTIONS.merge(options)
79
+ options.each do |key, value|
80
+ self.send("#{key}=", value)
81
+ end
82
+
83
+ if @auth == AUTH_HMAC && !has_valid_hmac_double?
84
+ raise ArgumentError.new('Connection authentication is set to ' +
85
+ 'HMAC-SHA1, but a valid consumer_key and' +
86
+ 'secret pair was not supplied.')
87
+ elsif @auth == AUTH_ST && @st.empty?
88
+ raise ArgumentError.new('Connection authentication is set to ' +
89
+ 'security token, but a security token was ' +
90
+ 'not supplied.')
91
+ elsif ![AUTH_HMAC, AUTH_ST].include?(@auth)
92
+ raise ArgumentError.new('Connection authentication is set to an ' +
93
+ 'unknown value.')
94
+ end
95
+ end
96
+
97
+ # Constructs a URI to the OpenSocial endpoint given a service, guid,
98
+ # selector, and pid.
99
+ def service_uri(service, guid, selector, pid, extra_fields = {})
100
+ uri = [@container[:endpoint], service, guid, selector, pid].compact.
101
+ join('/')
102
+
103
+ if @auth == AUTH_HMAC && !xoauth_requestor_id.empty?
104
+ uri << '?xoauth_requestor_id=' + @xoauth_requestor_id
105
+ elsif @auth == AUTH_ST
106
+ uri << '?st=' + self.st
107
+ end
108
+
109
+ extra_fields.each do |name, value|
110
+ uri << "&#{name}=#{value}"
111
+ end
112
+
113
+ URI.parse(uri)
114
+ end
115
+
116
+ # Signs a request using OAuth.
117
+ def sign!(http, req)
118
+ if @auth == AUTH_HMAC
119
+ consumer = OAuth::Consumer.new(@consumer_key, @consumer_secret)
120
+ req.oauth!(http, consumer, @consumer_token, :scheme => 'query_string')
121
+ end
122
+ end
123
+
124
+ private
125
+
126
+ # Verifies that the consumer key, consumer secret and requestor id are all
127
+ # non-blank.
128
+ def has_valid_hmac_double?
129
+ return (!@consumer_key.empty? && !@consumer_secret.empty?)
130
+ end
131
+ end
132
+ end