opensocial 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,87 @@
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
+
34
+ def method
35
+ request.method.to_s.upcase
36
+ end
37
+
38
+ def uri
39
+ uri = URI.parse(request.protocol + request.host + request.port_string + request.path)
40
+ uri.query = nil
41
+ uri.to_s
42
+ end
43
+
44
+ def parameters
45
+ if options[:clobber_request]
46
+ options[:parameters] || {}
47
+ else
48
+ params = request_params.merge(query_params).merge(header_params)
49
+ params.stringify_keys! if params.respond_to?(:stringify_keys!)
50
+ params.merge(options[:parameters] || {})
51
+ end
52
+ end
53
+
54
+ # Override from OAuth::RequestProxy::Base to avoid roundtrip
55
+ # conversion to Hash or Array and thus preserve the original
56
+ # parameter names
57
+ def parameters_for_signature
58
+ params = []
59
+ params << options[:parameters].to_query if options[:parameters]
60
+
61
+ unless options[:clobber_request]
62
+ params << header_params.to_query
63
+ params << CGI.unescape(request.query_string) unless request.query_string.blank?
64
+ if request.content_type == Mime::Type.lookup("application/x-www-form-urlencoded")
65
+ params << CGI.unescape(request.raw_post)
66
+ end
67
+ end
68
+
69
+ params.
70
+ join('&').split('&').
71
+ reject { |kv| kv =~ /^oauth_signature=.*/}.
72
+ reject(&:blank?).
73
+ map { |p| p.split('=') }
74
+ end
75
+
76
+ protected
77
+
78
+ def query_params
79
+ request.query_parameters
80
+ end
81
+
82
+ def request_params
83
+ request.request_parameters
84
+ end
85
+
86
+ end
87
+ 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,54 @@
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
+ require 'json/add/rails'
23
+
24
+
25
+ module OpenSocial #:nodoc:
26
+
27
+ # Provides base functionality for the OpenSocial child classes.
28
+ #
29
+
30
+
31
+ class Base
32
+
33
+ # Creates an attr_accessor for the specified variable name.
34
+ def add_attr(name)
35
+ self.class.class_eval "attr_accessor :#{name}"
36
+ end
37
+ end
38
+
39
+ # Wraps the functionality of an OpenSocial collection as defined by the
40
+ # specification. In practical uses, a Collection serves as a Hash (usually
41
+ # index by ID) with the added benefit of being convertable to an Array, when
42
+ # it's necessary to iterate over all of the values.
43
+ #
44
+
45
+
46
+ class Collection < Hash
47
+
48
+ # Converts the Collection to an Array by returning each of the values from
49
+ # key/value pairs.
50
+ def to_array
51
+ values
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,146 @@
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
+ :content_type => 'application/json',
30
+ :post_body_signing => false,
31
+ :use_request_body_hash => true }
32
+ IGOOGLE = { :endpoint => 'http://www-opensocial-sandbox.googleusercontent.com/api',
33
+ :rest => '',
34
+ :rpc => 'rpc',
35
+ :content_type => 'application/json',
36
+ :post_body_signing => false,
37
+ :use_request_body_hash => true }
38
+ MYSPACE = { :endpoint => 'http://api.myspace.com/v2',
39
+ :rest => '',
40
+ :rpc => '',
41
+ :base_uri => 'http://api.myspace.com',
42
+ :request_token_path => '/request_token',
43
+ :authorize_path => '/authorize',
44
+ :access_token_path => '/access_token',
45
+ :http_method => :get,
46
+ :content_type => 'application/x-www-form-urlencoded',
47
+ :post_body_signing => true,
48
+ :use_request_body_hash => false }
49
+
50
+ AUTH_HMAC = 0
51
+ AUTH_ST = 1
52
+
53
+ DEFAULT_OPTIONS = { :container => ORKUT,
54
+ :st => '',
55
+ :consumer_key => '',
56
+ :consumer_secret => '',
57
+ :consumer_token => OAuth::Token.new('', ''),
58
+ :xoauth_requestor_id => '',
59
+ :auth => AUTH_HMAC }
60
+
61
+ # Defines the container that will be used in requests.
62
+ attr_accessor :container
63
+
64
+ # Defines the security token, for when OAuth is not in use.
65
+ attr_accessor :st
66
+
67
+ # Defines the consumer key for OAuth.
68
+ attr_accessor :consumer_key
69
+
70
+ # Defines the consumer secret for OAuth.
71
+ attr_accessor :consumer_secret
72
+
73
+ # Defines the consumer token for OAuth.
74
+ attr_accessor :consumer_token
75
+
76
+ # Defines the ID of the requestor (required by some implementations when
77
+ # using OAuth).
78
+ attr_accessor :xoauth_requestor_id
79
+
80
+ # Defines the authentication scheme: HMAC or security token.
81
+ attr_accessor :auth
82
+
83
+ # Defines the content-type when sending a request body
84
+ attr_accessor :content_type
85
+
86
+ # Defines whether or not to sign the request body (treating the body as a
87
+ # large query parameter for the purposes of the signature base string)
88
+ attr_accessor :post_body_signing
89
+
90
+ # Defines whether or not to sign the body using a request body hash
91
+ attr_accessor :use_request_body_hash
92
+
93
+ # Initializes the Connection using the supplied options hash, or the
94
+ # defaults. Verifies that the supplied authentication type has proper
95
+ # (ie. non-blank) credentials, and that the authentication type is known.
96
+ def initialize(options = {})
97
+ options = DEFAULT_OPTIONS.merge(options)
98
+ options.each do |key, value|
99
+ self.send("#{key}=", value)
100
+ end
101
+
102
+ if @auth == AUTH_HMAC && !has_valid_hmac_double?
103
+ raise ArgumentError.new('Connection authentication is set to ' +
104
+ 'HMAC-SHA1, but a valid consumer_key and' +
105
+ 'secret pair was not supplied.')
106
+ elsif @auth == AUTH_ST && @st.empty?
107
+ raise ArgumentError.new('Connection authentication is set to ' +
108
+ 'security token, but a security token was ' +
109
+ 'not supplied.')
110
+ elsif ![AUTH_HMAC, AUTH_ST].include?(@auth)
111
+ raise ArgumentError.new('Connection authentication is set to an ' +
112
+ 'unknown value.')
113
+ end
114
+ end
115
+
116
+ # Constructs a URI to the OpenSocial endpoint given a service, guid,
117
+ # selector, and pid.
118
+ def service_uri(service, guid, selector, pid)
119
+ uri = [@container[:endpoint], service, guid, selector, pid].compact.
120
+ join('/')
121
+
122
+ if @auth == AUTH_HMAC && !xoauth_requestor_id.empty?
123
+ uri << '?xoauth_requestor_id=' + @xoauth_requestor_id
124
+ elsif @auth == AUTH_ST
125
+ uri << '?st=' + self.st
126
+ end
127
+ URI.parse(uri)
128
+ end
129
+
130
+ # Signs a request using OAuth.
131
+ def sign!(http, req)
132
+ if @auth == AUTH_HMAC
133
+ consumer = OAuth::Consumer.new(@consumer_key, @consumer_secret)
134
+ req.oauth!(http, consumer, nil, :scheme => 'query_string')
135
+ end
136
+ end
137
+
138
+ private
139
+
140
+ # Verifies that the consumer key, consumer secret and requestor id are all
141
+ # non-blank.
142
+ def has_valid_hmac_double?
143
+ return (!@consumer_key.empty? && !@consumer_secret.empty?)
144
+ end
145
+ end
146
+ end