oauth_simple 0.1.0.pre

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.
File without changes
@@ -0,0 +1,93 @@
1
+ = oauth_simple [RubyGem]
2
+
3
+ == Example
4
+
5
+ Following example shows you how to obtain a request token from Twitter server:
6
+
7
+ require "oauth_simple"
8
+
9
+ req_uri_str = 'https://api.twitter.com/oauth/request_token'
10
+ req_method = 'POST'
11
+ consumer_sec = 'your_consumer_secret'
12
+
13
+ req_helper = OAuthSimple::RequestHelper.new(
14
+ URI.parse( uri_str ),
15
+ req_method,
16
+ "#{OAuthSimple::HelperFunctions.enc_perenc(consumer_sec)}&",
17
+ OAuthSimple::RequestParamList.new( [
18
+ [ 'oauth_consumer_key', 'your_consumer_key' ],
19
+ [ 'oauth_signature_method', 'HMAC-SHA1' ],
20
+ [ 'oauth_timestamp', OAuthSimple::HelperFunctions.create_timestamp_str() ],
21
+ [ 'oauth_nonce', OAuthSimple::HelperFunctions.create_nonce_str() ],
22
+ [ 'oauth_version', '1.0' ],
23
+ ] ),
24
+ )
25
+
26
+ require 'net/https'
27
+ http = Net::HTTP.new( req_helper.host, req_helper.port )
28
+ http.use_ssl = true # SSLを有効に
29
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER # 認証モードをセット
30
+ http.start do |http|
31
+ http.request_post( req_helper.qpath, req_helper.req_body,
32
+ { 'Authorization' => req_helper.oauth_header_str } ) do |res|
33
+ if res.code == '200'
34
+ res.read_body do |str|
35
+ puts 'str: ', str
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ Alternatively, you can use OAuthSimple::HTTP, which is a subclass of Net::HTTP.
42
+ Following example shows you how to obtain a temporaty credentials (request token)
43
+ from Twitter server,
44
+ how to obtain a token credentials (access token), and how to issue a authenticated request:
45
+
46
+ require 'net/https' # if you use ssl
47
+ require 'oauth_simple'
48
+ #
49
+ # OAuthSimple::HTTP is a subclass of Net::HTTP
50
+ http = OAuthSimple::HTTP.new( 'api.twitter.com', 443 )
51
+ #
52
+ # SSL setting
53
+ http.use_ssl = true
54
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER # 認証モードをセット
55
+ #
56
+ # OAuth setting (this feature is provided by OAuthSimple::HTTP)
57
+ http.use_oauth = true
58
+ http.set_oauth_client_credentials( 'YOUR_CLIENT_CREDENTIALS', 'YOUR_CLIENT_SECRET' )
59
+ http.set_oauth_signature_method( 'HMAC-SHA1' ) # at this time, only 'HMAC-SHA1' is supported
60
+ #
61
+ # connection start
62
+ http.start() do |http|
63
+ # == Obtaining Temporary Credentials ==
64
+ token, secret = http.request_oauth_temp_credentials( '/oauth/request_token', 'oob' ) do |res_failed|
65
+ # when response code is not '200', this block is called
66
+ raise res_failed.body
67
+ end
68
+ # token and secret are set to OAuthSimple::HTTP object automatically in the request_oauth_temp_credentials method,
69
+ # so you need not set them explicitly as follows
70
+ # http.set_oauth_user_credentials( token, secret )
71
+ #
72
+ # == Resource Owner Authorization ==
73
+ puts "access to https://api.twitter.com/oauth/authorize?oauth_token=#{OAuthSimple::HelperFunctions.enc_perenc(token)} " +
74
+ 'and input verifier'
75
+ $stdout << 'verifier : '
76
+ verifier = $stdin.gets.chomp
77
+ #
78
+ # == Obtaining Token Credentials ==
79
+ token, secret = http.request_oauth_token_credentials( '/oauth/access_token', verifier ) do |res_failed|
80
+ # when response code is not '200', this block is called
81
+ raise res_failed.body
82
+ end
83
+ # token and secret are set to OAuthSimple::HTTP object automatically in the request_oauth_token_credentials method,
84
+ # you need not set them explicitly as follows
85
+ # http.set_oauth_user_credentials( token, secret )
86
+ #
87
+ # == Authenticated Requests ==
88
+ http.set_oauth_user_credentials( token, secret )
89
+ http.request_get( '/1/statuses/home_timeline.json?include_entities=true' ) do |res|
90
+ p res.code
91
+ p res.body
92
+ end
93
+ end
@@ -0,0 +1,9 @@
1
+ # coding : utf-8
2
+
3
+ require 'oauth_simple/request_helper'
4
+ require 'oauth_simple/request_helper_factory'
5
+ require 'oauth_simple/request_param_list'
6
+ require 'oauth_simple/http'
7
+
8
+ module OAuthSimple
9
+ end
@@ -0,0 +1,83 @@
1
+ # coding : utf-8
2
+
3
+ require 'openssl'
4
+
5
+ module OAuthSimple
6
+ module HelperFunctions
7
+
8
+ # ====================
9
+ # MODULE FUNCTIONS
10
+ # ====================
11
+ module_function
12
+
13
+ # nonce 用にランダムに文字列生成するメソッド
14
+ NONCE_STRING_SOURCE = ('a'..'z').to_a() + ('A'..'Z').to_a() + ('0'..'9').to_a()
15
+ def create_nonce_str( length = 16 )
16
+ Array.new( length ).map{ NONCE_STRING_SOURCE[rand(NONCE_STRING_SOURCE.size)] }.join('')
17
+ end
18
+
19
+ def create_timestamp_str( time = Time.now )
20
+ ( time - Time.utc( 1970, 1, 1 ) ).to_i().to_s()
21
+ end
22
+
23
+ # HMAC-SHA1
24
+ # RSA-SHA1
25
+ def calc_signature( method, uri_str, param_list, secret_str )
26
+ params_str = param_list.get_normalized_params_str()
27
+ #sb_str = String.new()
28
+ #list.each do |item|
29
+ # if ( sb_str != "" ) then
30
+ # sb_str << "&"
31
+ # end
32
+ # sb_str << encode( item[0] ) << "=" << encode( item[1] )
33
+ #end
34
+ base_str = [ method, uri_str, params_str ].map{ |e| enc_perenc(e) }.join('&')
35
+ digest = OpenSSL::HMAC::digest( OpenSSL::Digest::SHA1.new(), secret_str, base_str )
36
+ return [digest].pack('m').gsub!( /\n/u, '' )
37
+ end
38
+
39
+ # param : String
40
+ # return : [ [ String, String or nil ], ... ]
41
+ def decode_from_percent_encoded_str( str )
42
+ str.split( '&', -1 ).map! do |s|
43
+ if s.empty?
44
+ [ '', nil ]
45
+ else
46
+ pair = s.split( '=', -1 ).map!{ |s| dec_perenc( s ) }
47
+ # TODO: pair の要素数は 1 以上 2 以下 ('=' がない場合など, 1 個だけの場合もある)
48
+ [ pair[0], pair[1] ]
49
+ end
50
+ end
51
+ end
52
+
53
+ # param : [ [ String, String or nil ], ... ]
54
+ # return : String
55
+ def encode_to_percent_encoded_str_pairs( str_pairs )
56
+ str_pairs.map do |pair|
57
+ pair[1].nil? ? enc_perenc( pair[0] )
58
+ : enc_perenc( pair[0] ) + '=' + enc_perenc( pair[1] )
59
+ end.join( '&' )
60
+ end
61
+
62
+ # TODO これで良いか?
63
+ # UTF-8 エンコードされたものをパーセントエンコードしているとみなしてデコードする
64
+ # パーセントエンコードする際には文字列を UTF-8 エンコードするのは OAuth 1.0 の仕様
65
+ # だが, デコード時はこれでよいか?
66
+ def dec_perenc( str )
67
+ str.gsub( /%[a-fA-F\d]{2}/u ){ |s| [s[1,2]].pack('H*') }.force_encoding( Encoding::UTF_8 )
68
+ end
69
+
70
+ def enc_perenc( str )
71
+ str.gsub( /[^a-zA-Z\d\-\._\~]/u ) do |s|
72
+ d_str = s.unpack("H*")[0].upcase()
73
+ e_str = String.new()
74
+ while ( d_str[0,2] != "" ) do
75
+ e_str << "%" << d_str[0,2]
76
+ d_str[0,2] = ""
77
+ end
78
+ e_str
79
+ end
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,267 @@
1
+ # coding : utf-8
2
+
3
+ require 'net/http'
4
+ require 'oauth_simple/helper_functions'
5
+ require 'oauth_simple/request_param_list'
6
+
7
+ module OAuthSimple
8
+
9
+ ###
10
+ # Subclass of Net::HTTP, which has feature of OAuth authentication
11
+ class HTTP < Net::HTTP
12
+
13
+ include HelperFunctions
14
+
15
+ module DefaultOAuthParamSettable
16
+ def set_default_oauth_client_credentials( key, secret )
17
+ @client_credentials = [ key, secret ]
18
+ end
19
+
20
+ def set_default_oauth_user_credentials( key, secret )
21
+ @user_credentials = [ key, secret ]
22
+ end
23
+
24
+ def set_default_oauth_signature_method( sig_met )
25
+ @signature_method = sig_met
26
+ end
27
+
28
+ def get_default_params
29
+ params = {}
30
+ params[:oauth_client_credentials] = @client_credentials if @client_credentials
31
+ params[:oauth_user_credentials ] = @user_credentials if @user_credentials
32
+ params[:signature_method ] = @signature_method if @signature_method
33
+ return params
34
+ end
35
+ end
36
+
37
+ # :stopdoc:
38
+ # 空のハッシュを表す定数
39
+ EMPTY_HASH = {}.freeze
40
+ # :startdoc:
41
+
42
+ def initialize( *args )
43
+ super
44
+ self.set_oauth_params_location( LOC_AUTHORIZATION_HEADER )
45
+ end
46
+
47
+ def self.create_subclass_with_default_oauth_params( oauth_params = EMPTY_HASH )
48
+ klass = Class.new( self ) do
49
+ def initialize( *args )
50
+ super
51
+ default_params = self.class.get_default_params
52
+ self.use_oauth = true
53
+ if default_params.has_key? :oauth_client_credentials
54
+ self.set_oauth_client_credentials( *default_params[:oauth_client_credentials] )
55
+ end
56
+ if default_params.has_key? :oauth_user_credentials
57
+ self.set_oauth_user_credentials( *default_params[:oauth_user_credentials] )
58
+ end
59
+ if default_params.has_key? :signature_method
60
+ # at this time, only 'HMAC-SHA1' is supported
61
+ self.set_oauth_signature_method( default_params[:signature_method] )
62
+ end
63
+ end
64
+ end
65
+ klass.extend DefaultOAuthParamSettable
66
+ # TODO oauth_params で渡されたパラメータをここでセット
67
+ #
68
+ return klass
69
+ end
70
+
71
+ # @consumer_key
72
+ # @token
73
+ # @signature_method
74
+ # (timestamp, nonce, version)
75
+ # -> signature
76
+ #
77
+ # @consumer_secret
78
+ # @token_secret
79
+
80
+ ###
81
+ # Override: Net::HTTP#transport_request
82
+ def transport_request( req )
83
+ if use_oauth?
84
+ req_method = req.method.upcase
85
+ uri_str_scheme = use_ssl? ? 'https' : 'http'
86
+ uri_str_host = addr_port.downcase # デフォルトでない場合ポート番号含む
87
+ # TODO: path 中の '#' はどのように扱われるべき?
88
+ qpath, uri_str_fragment = req.path.split( '#', 2 )
89
+ uri_str_path, query_str = qpath.split( '?', 2 )
90
+ # OAuth Header (基本的には自分で用意)
91
+ #req.get_fields( 'Authorization' )
92
+ # ...
93
+ # body parameters (必要な場合だけ)
94
+ # Protocol parameters can be transmitted in the HTTP request entity-
95
+ # body, but only if the following REQUIRED conditions are met:
96
+ # o The entity-body is single-part.
97
+ # o The entity-body follows the encoding requirements of the
98
+ # "application/x-www-form-urlencoded" content-type as defined by
99
+ # [W3C.REC-html40-19980424].
100
+ # o The HTTP request entity-header includes the "Content-Type" header
101
+ # field set to "application/x-www-form-urlencoded".
102
+ body_str = nil
103
+ if req.request_body_permitted?
104
+ content_type = req.content_type || 'application/x-www-form-urlencoded'
105
+ if content_type == 'application/x-www-form-urlencoded'
106
+ body_str = req.body
107
+ end
108
+ end
109
+
110
+ secret_str = [ @oauth_consumer_secret, @oauth_token_secret ].
111
+ map {|e| e.nil? ? '' : enc_perenc( e ) }.
112
+ join( '&' )
113
+
114
+ # for debug
115
+ #puts "request method - #{req_method }"
116
+ #puts "http or https - #{uri_str_scheme}"
117
+ #puts "host[:port] - #{uri_str_host }"
118
+ #puts "path - #{uri_str_path }"
119
+ #puts "query str - #{query_str.nil? ? '<nil>' : query_str}"
120
+ #puts "body str - #{body_str.nil? ? '<nil>' : body_str }"
121
+
122
+ p_params = RequestParamList.new()
123
+ {
124
+ 'oauth_consumer_key' => @oauth_consumer_key,
125
+ 'oauth_token' => @oauth_token,
126
+ 'oauth_signature_method' => @oauth_signature_method,
127
+ }.each_pair{|k,v| p_params.add( k, v ) if v }
128
+ p_params.add( 'oauth_timestamp' , create_timestamp_str() )
129
+ p_params.add( 'oauth_nonce' , create_nonce_str() )
130
+ p_params.add( 'oauth_version' , '1.0' )
131
+ if req.respond_to? :oauth_params
132
+ req.oauth_params.each_pair do |key,value|
133
+ p_params.add( key, value )
134
+ end
135
+ end
136
+
137
+ param_list = RequestParamList.new()
138
+ param_list.concat p_params
139
+ param_list.concat RequestParamList.from_percent_encoded_str query_str if query_str
140
+ param_list.concat RequestParamList.from_percent_encoded_str body_str if body_str
141
+
142
+ # signature の計算
143
+ uri_str = "#{uri_str_scheme}://#{uri_str_host}#{uri_str_path}"
144
+ signature = calc_signature( req_method, uri_str, param_list, secret_str )
145
+
146
+ case @oauth_params_loc
147
+ when LOC_AUTHORIZATION_HEADER
148
+ # Authorization Header
149
+ p_params.add( 'oauth_signature', signature )
150
+ req.add_field( 'Authorization', 'OAuth ' + p_params.to_header_string() )
151
+ when LOC_REQBODY_OR_REQQUERY
152
+ # req body or req query
153
+ raise 'not implemented yet'
154
+ when LOC_REQQUERY
155
+ # req query
156
+ raise 'not implemented yet'
157
+ else
158
+ # error
159
+ raise 'invalid location'
160
+ end
161
+ end
162
+ return super # 引数, block をそのまま継承先へ渡す
163
+ end
164
+
165
+ def use_oauth=( val )
166
+ @use_oauth = val
167
+ end
168
+
169
+ def use_oauth?
170
+ @use_oauth
171
+ end
172
+
173
+ def set_oauth_client_credentials( key, secret )
174
+ @oauth_consumer_key = key
175
+ @oauth_consumer_secret = secret
176
+ end
177
+
178
+ def set_oauth_user_credentials( token, secret )
179
+ @oauth_token = token
180
+ @oauth_token_secret = secret
181
+ end
182
+
183
+ def set_oauth_signature_method( sigmet )
184
+ if sigmet != 'HMAC-SHA1'
185
+ raise %q{at this time, only 'HMAC-SHA1' is supported}
186
+ end
187
+ @oauth_signature_method = sigmet
188
+ end
189
+
190
+ LOC_AUTHORIZATION_HEADER = :auth_header
191
+ LOC_REQBODY_OR_REQQUERY = :reqbody_or_reqquery
192
+ LOC_REQQUERY = :reqquery
193
+ def set_oauth_params_location( location )
194
+ @oauth_params_loc = location
195
+ end
196
+
197
+ # TODO: POST メソッド以外も使えるように
198
+ def request_oauth_temp_credentials( path, oauth_callback_uri, &block )
199
+ req = Post.new( path )
200
+ req.set_oauth_param( 'oauth_callback', oauth_callback_uri )
201
+ token = nil
202
+ secret = nil
203
+ request( req ) do |res|
204
+ if res.code == '200'
205
+ params = RequestParamList.from_percent_encoded_str res.body
206
+ token = params.get_values( 'oauth_token' )[0]
207
+ secret = params.get_values( 'oauth_token_secret' )[0]
208
+ else
209
+ if block
210
+ block.call res
211
+ else
212
+ raise 'error' # TODO
213
+ end
214
+ end
215
+ end
216
+
217
+ # Set credentials automatically
218
+ set_oauth_user_credentials( token, secret )
219
+ return token, secret
220
+ end
221
+
222
+ # TODO: POST メソッド以外も使えるように
223
+ def request_oauth_token_credentials( path, oauth_verifier, &block )
224
+ req = Post.new( path )
225
+ req.set_oauth_param( 'oauth_verifier', oauth_verifier )
226
+ token = nil
227
+ secret = nil
228
+ request( req ) do |res|
229
+ if res.code == '200'
230
+ params = RequestParamList.from_percent_encoded_str res.body
231
+ token = params.get_values( 'oauth_token' )[0]
232
+ secret = params.get_values( 'oauth_token_secret' )[0]
233
+ else
234
+ if block
235
+ block.call res
236
+ else
237
+ raise 'error'
238
+ end
239
+ end
240
+ end
241
+ set_oauth_user_credentials( token, secret )
242
+ return token, secret
243
+ end
244
+
245
+ module OAuthParamsHandler
246
+ def set_oauth_param( name, value )
247
+ # TODO: name must start with 'oauth_'
248
+ @oauth_params ||= {}
249
+ @oauth_params[ name ] = value
250
+ end
251
+ def get_oauth_param( name )
252
+ ( @oauth_params || {} )[ name ]
253
+ end
254
+ def oauth_params
255
+ @oauth_params || {}
256
+ end
257
+ end
258
+
259
+ class Get < Net::HTTP::Get
260
+ include OAuthParamsHandler
261
+ end
262
+ class Post < Net::HTTP::Post
263
+ include OAuthParamsHandler
264
+ end
265
+
266
+ end
267
+ end
@@ -0,0 +1,98 @@
1
+ # coding : utf-8
2
+
3
+ require 'oauth_simple/helper_functions'
4
+
5
+ module OAuthSimple
6
+ class RequestHelper
7
+
8
+ include HelperFunctions
9
+
10
+ ###
11
+ # req_uri : URI or String object
12
+ # req_method : String
13
+ def initialize( req_uri, req_method, oauth_secret, protocol_params, query_params = nil, body_params = nil )
14
+ # URI
15
+ @req_uri = req_uri # 後ろの処理で query は nil になる
16
+ # String or Symbol?
17
+ @req_method = req_method
18
+ # String or Symbol?
19
+ @sig_method = nil
20
+ # String
21
+ @secret_str = oauth_secret
22
+ # RequestParamList
23
+ @p_params = protocol_params
24
+ # RequestParamList or nil
25
+ q_params_list = []
26
+ if @req_uri.query
27
+ q_params_list << RequestParamList.from_percent_encoded_str( @req_uri.query )
28
+ @req_uri.query = nil
29
+ end
30
+ if query_params
31
+ q_params_list << query_params
32
+ end
33
+ @q_params = q_params_list.empty? ? nil : q_params_list.inject{|a,b| a.concat b}
34
+ # RequestParamList or nil
35
+ @b_params = body_params
36
+
37
+ params = RequestParamList.new()
38
+ params.concat @p_params
39
+ params.concat @q_params if @q_params
40
+ params.concat @b_params if @b_params
41
+
42
+ # URI の処理
43
+ # The scheme, authority, and path of the request resource URI [RFC3986]
44
+ # are included by constructing an "http" or "https" URI representing
45
+ # the request resource (without the query or fragment) as follows:
46
+ # 1. The scheme and host MUST be in lowercase.
47
+ uri_str = ''
48
+ # scheme
49
+ uri_str << @req_uri.scheme.downcase
50
+ uri_str << '://'
51
+ uri_str << @req_uri.host.downcase
52
+ # 3. The port MUST be included if it is not the default port for the
53
+ # scheme, and MUST be excluded if it is the default. Specifically,
54
+ # the port MUST be excluded when making an HTTP request [RFC2616]
55
+ # to port 80 or when making an HTTPS request [RFC2818] to port 443.
56
+ # All other non-default port numbers MUST be included.
57
+ if @req_uri.port != @req_uri.default_port
58
+ uri_str << ":#{@req_uri.port}"
59
+ end
60
+ uri_str << @req_uri.path
61
+
62
+ @p_params.add( 'oauth_signature', calc_signature( req_method, uri_str, params, @secret_str ) )
63
+ end
64
+
65
+ def host
66
+ @req_uri.host
67
+ end
68
+
69
+ def port
70
+ @req_uri.port
71
+ end
72
+
73
+ def request_method
74
+ @req_method
75
+ end
76
+
77
+ ###
78
+ # path + '?' + query
79
+ def qpath
80
+ @req_uri.path
81
+ end
82
+
83
+ def qpath_with_oauth_params
84
+
85
+ end
86
+
87
+ def req_body
88
+ end
89
+
90
+ def req_body_with_oauth_params
91
+ end
92
+
93
+ def oauth_header_str( realm_str = nil )
94
+ 'OAuth ' + @p_params.to_header_string()
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,72 @@
1
+ # coding : utf-8
2
+
3
+ require 'oauth_simple/helper_functions'
4
+
5
+ module OAuthSimple
6
+ class RequestHelperFactory
7
+
8
+ include HelperFunctions
9
+
10
+ ###
11
+ # consumer_secret
12
+ # token_secret
13
+ # consumer_key
14
+ # token
15
+ # clients MAY omit the parameter.
16
+ # signature_method
17
+ # timestamp
18
+ # MAY be omitted when using the "PLAINTEXT" signature method.
19
+ # nonce
20
+ # MAY be omitted when using the "PLAINTEXT" signature method.
21
+ # version
22
+ # OPTIONAL. If present, MUST be set to "1.0".
23
+ # :consumer_key, :consumer_secret, :token, :token_secret, :signature_method
24
+ # :use_default_timestamp, :use_default_nonce, :use_default_version
25
+ def initialize( args )
26
+ args = {
27
+ :use_default_timestamp => true,
28
+ :use_default_nonce => true,
29
+ :use_default_version => true,
30
+ }.merge args
31
+ # String object or nil
32
+ @consumer_key = args.delete( :consumer_key )
33
+ @consumer_secret = args.delete( :consumer_secret )
34
+ @token = args.delete( :token )
35
+ @token_secret = args.delete( :token_secret )
36
+ @signature_method = args.delete( :signature_method )
37
+ # boolean
38
+ @use_default_timestamp = args.delete( :use_default_timestamp )
39
+ @use_default_nonce = args.delete( :use_default_nonce )
40
+ @use_default_version = args.delete( :use_default_version )
41
+ # TODO: args に key が残っている場合, 警告を表示
42
+ end
43
+
44
+ ###
45
+ # create RequestHelper object
46
+ def create( req_uri, req_method, option_params )
47
+ # secret_str 生成
48
+ cons_sec = option_params[:consumer_secret] || @consumer_secret
49
+ tokn_sec = option_params[:token_secret ] || @token_secret
50
+ secret_str = "#{cons_sec}&#{tokn_sec}"
51
+
52
+ # protocol params
53
+ p_params = RequestParamList.new()
54
+ p_params.add( 'oauth_consumer_key' , @consumer_key ) if @consumer_key
55
+ p_params.add( 'oauth_token' , @token ) if @token
56
+ p_params.add( 'oauth_signature_method', @signature_method ) if @signature_method
57
+ p_params.add( 'oauth_timestamp' , create_timestamp_str() ) if @use_default_timestamp
58
+ p_params.add( 'oauth_nonce' , create_nonce_str() ) if @use_default_nonce
59
+ p_params.add( 'oauth_version' , '1.0' ) if @use_default_version
60
+ p_params2 = option_params.delete( :protocol_params )
61
+ p_params.concat p_params2 if p_params2
62
+
63
+ # query_params and body_params
64
+ q_params = option_params.delete( :query_params )
65
+ b_params = option_params.delete( :body_params )
66
+
67
+ # RequestHelper object 生成
68
+ RequestHelper.new( req_uri, req_method, secret_str, p_params, q_params, b_params )
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,144 @@
1
+ # coding : utf-8
2
+
3
+ require 'oauth_simple/helper_functions'
4
+
5
+ module OAuthSimple
6
+ class RequestParamList
7
+
8
+ include HelperFunctions
9
+
10
+ # instance variable
11
+ # @list : [ [ String obj, String obj or nil ], ... ]
12
+
13
+ def initialize( arg = Array.new() )
14
+ if ( arg.is_a? Array ) then
15
+ arg.each do |item|
16
+ if not item.is_a?( Array ) or item.length != 2 or not item[0].is_a?( String ) or not ( item[1].is_a?( String ) or item[1].nil? ) then
17
+ raise "引数として与えられた Array が正しい形式ではありません. " +
18
+ '引数として与えられた Array オブジェクトの各要素は, String オブジェクト ' +
19
+ '2 つ (または 2 つめは nil) からなる Array オブジェクトである必要があります'
20
+ end
21
+ end
22
+ @list = arg
23
+ elsif ( arg.is_a? Hash ) then
24
+ @list = Array.new()
25
+ arg.each do |key,val|
26
+ @list.push( [key, val] )
27
+ end
28
+ elsif ( arg.is_a? String ) then
29
+ @list = Array.new()
30
+ if ( /%(?![a-fA-F\d]{2})/u =~ arg ) then
31
+ # OAuth の仕様どおりではないが, URL エンコードの形式ならば受け付ける
32
+ raise "引数として与えられた String オブジェクトが正しく encode されたものではありません"
33
+ end
34
+ param_list = arg.split( /&/u )
35
+ param_list.each do |item|
36
+ if ( item.nil? || item == "" ) then
37
+ next
38
+ end
39
+ pair = item.split( /=/u )
40
+ @list.push( [decode( pair[0] ), decode( pair[1].to_s() )] )
41
+ end
42
+ else
43
+ raise "型エラー : ParameterList の初期化時に与えることができる引数の型は Array と String のみです"
44
+ end
45
+ end
46
+
47
+ def self.from_percent_encoded_str( str )
48
+ # HelperFunctions モジュールで定義されている... 関数呼び出しにはできない?
49
+ new HelperFunctions.decode_from_percent_encoded_str( str )
50
+ end
51
+
52
+ public
53
+ def +( other )
54
+ return ParameterList.new( self.get_list() + other.get_list() )
55
+ end
56
+
57
+ def concat( other )
58
+ @list.concat( other.get_list() )
59
+ return self
60
+ end
61
+
62
+ def add( name, value )
63
+ @list.push( [name, value] )
64
+ return nil
65
+ end
66
+
67
+ def get_values( name )
68
+ res_list = Array.new()
69
+ @list.each do |item|
70
+ if ( item[0] == name ) then
71
+ res_list.push( item[1] )
72
+ end
73
+ end
74
+ return res_list
75
+ end
76
+
77
+ alias :[] :get_values
78
+
79
+ def each()
80
+ @list.each do |item|
81
+ yield( item[0], item[1] )
82
+ end
83
+ end
84
+
85
+ def to_header_string()
86
+ #list = get_sorted_list()
87
+ sb_str = String.new()
88
+ @list.each do |item|
89
+ if ( sb_str != "" ) then
90
+ sb_str << ", "
91
+ end
92
+ sb_str << enc_perenc( item[0] ) << '="' << enc_perenc( item[1] ) << '"'
93
+ end
94
+ return sb_str
95
+ end
96
+
97
+ def to_query_string()
98
+ #list = get_sorted_list()
99
+ sb_str = String.new()
100
+ @list.map{ |e| enc_perenc( e[0] ) + ( e[1] ? "=#{enc_perenc( e[1] )}" : '' ) }.join( '&' )
101
+ end
102
+
103
+ =begin
104
+ def to_signature_string( method, url, key )
105
+ list = get_sorted_list()
106
+ sb_str = String.new()
107
+ list.each do |item|
108
+ if ( sb_str != "" ) then
109
+ sb_str << "&"
110
+ end
111
+ sb_str << encode( item[0] ) << "=" << encode( item[1] )
112
+ end
113
+ base_string = encode( method ) + "&" + encode( url ) + "&" + encode( sb_str )
114
+ digest = OpenSSL::HMAC::digest( OpenSSL::Digest::SHA1.new(), key, base_string )
115
+ sig = [digest].pack("m").gsub!( /\n/u, "" )
116
+ return sig
117
+ end
118
+ =end
119
+
120
+ protected
121
+ def get_list()
122
+ return @list
123
+ end
124
+
125
+ public
126
+ # http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2
127
+ def get_normalized_params_str()
128
+ @list.
129
+ map do |e| # [ key, val ]
130
+ [ enc_perenc( e[0] ), e[1] ? enc_perenc( e[1] ) : '' ]
131
+ end.
132
+ sort do |a,b|
133
+ case a[0] <=> b[0]
134
+ when 1 then rel = 1
135
+ when -1 then rel = -1
136
+ when 0 then rel = a[1] <=> b[1]
137
+ end
138
+ end.
139
+ map{ |e| "#{e[0]}=#{e[1]}" }.
140
+ join( '&' )
141
+ end
142
+
143
+ end
144
+ end
@@ -0,0 +1,13 @@
1
+ # coding: UTF-8
2
+
3
+ require 'rake/testtask'
4
+
5
+ task :default => [:test]
6
+
7
+ Rake::TestTask.new do |test|
8
+ # $LOAD_PATH に追加するパス (デフォルトで 'lib' は入っている)
9
+ test.libs << 'test'
10
+ # テスト対象ファイルの指定
11
+ test.test_files = Dir[ 'test/**/test_*.rb' ]
12
+ test.verbose = true
13
+ end
@@ -0,0 +1,4 @@
1
+ # coding: UTF-8
2
+
3
+ $LOAD_PATH.unshift File.dirname(__FILE__)
4
+ $LOAD_PATH.unshift File.join( File.dirname(__FILE__), '..', 'lib' )
@@ -0,0 +1,43 @@
1
+ # coding: UTF-8
2
+
3
+ require File.expand_path File.join( File.dirname(__FILE__), 'helper_path_setting' )
4
+
5
+ require 'uri'
6
+ require 'minitest/unit'
7
+ require 'minitest/autorun'
8
+
9
+ require 'oauth_simple'
10
+ require 'oauth_simple/http'
11
+
12
+ class TestHttp < MiniTest::Unit::TestCase
13
+
14
+ # OAuthSimple::HTTP is a subclass of Net::HTTP
15
+ MyHTTP = OAuthSimple::HTTP.create_subclass_with_default_oauth_params()
16
+ MyHTTP.set_default_oauth_client_credentials( 'key', 'secret' )
17
+ #MyHTTP.set_default_oauth_user_credentials( key, secret )
18
+ MyHTTP.set_default_oauth_signature_method( 'HMAC-SHA1' )
19
+
20
+ ###
21
+ # test by using OAuth Test Server : http://term.ie/oauth/example/
22
+ def test_getting_request_token
23
+
24
+ http = MyHTTP.new( 'term.ie' )
25
+ # connection start
26
+ http.start() do |http|
27
+ assert_equal( http.class, MyHTTP )
28
+ http.request_post( '/oauth/example/request_token.php', nil ) do |res|
29
+ assert_equal( '200', res.code )
30
+ assert_equal( 'oauth_token=requestkey&oauth_token_secret=requestsecret', res.body )
31
+ end
32
+
33
+ token, secret = http.request_oauth_temp_credentials( '/oauth/example/request_token.php', 'oob' )
34
+ assert_equal( 'requestkey' , token )
35
+ assert_equal( 'requestsecret', secret )
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ __END__
42
+
43
+ assert( .... )
@@ -0,0 +1,151 @@
1
+ # coding: UTF-8
2
+
3
+ require File.expand_path File.join( File.dirname(__FILE__), 'helper_path_setting' )
4
+
5
+ require 'uri'
6
+ require 'minitest/unit'
7
+ require 'minitest/autorun'
8
+
9
+ require 'oauth_simple'
10
+ require 'oauth_simple/http'
11
+
12
+ class TestMain < MiniTest::Unit::TestCase
13
+
14
+ def test_proxy
15
+ http = OAuthSimple::HTTP.Proxy( '96.32.133.3', '8085' ).new( 'api.twitter.com' )
16
+ assert( http.is_a? OAuthSimple::HTTP )
17
+ end
18
+
19
+ def test_sign_simple
20
+ base_str = 'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal'
21
+ secret_str = 'kd94hf93k423kf44&pfkkdhi9sl3r4s00'
22
+ signature = 'tR3+Ty81lMeYAr/Fid0kMTYa/WM='
23
+ digest = OpenSSL::HMAC::digest( OpenSSL::Digest::SHA1.new(), secret_str, base_str )
24
+ assert_equal( signature, [digest].pack('m').gsub!( /\n/u, '' ) )
25
+ base_str = 'POST&http%3A%2F%2Fexample.com%2Frequest&a2%3Dr%2520b%26a3%3D2%2520q%26a3%3Da%26b5%3D%253D%25253D%26c%2540%3D%26c2%3D%26oauth_consumer_key%3D9djdj82h48djs9d2%26oauth_nonce%3D7d8f3e4a%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D137131201%26oauth_token%3Dkkk9d7dh3k39sjv7'
26
+ secret_str = 'j49sk3j29djd&dh893hdasih9'
27
+ signature = 'r6%2FTJjbCOr97%2F%2BUU0NsvSne7s5g%3D'
28
+ digest = OpenSSL::HMAC::digest( OpenSSL::Digest::SHA1.new(), secret_str, base_str )
29
+ assert_equal( signature, OAuthSimple::HelperFunctions.enc_perenc( [digest].pack('m').gsub!( /\n/u, '' ) ) )
30
+ end
31
+
32
+ def test_nonce_string_creation
33
+ # 引数を与えなければ, 16 文字の文字列
34
+ nonce_str = OAuthSimple::HelperFunctions.create_nonce_str()
35
+ assert_equal( nonce_str.class, String )
36
+ assert_equal( nonce_str.length, 16 )
37
+ # 0 以上の整数を引数として与えると, その長さの文字列
38
+ [ 20, 0, 300, 432, 125432 ].each do |len|
39
+ nonce_str = OAuthSimple::HelperFunctions.create_nonce_str( len )
40
+ assert_equal( nonce_str.class, String )
41
+ assert_equal( nonce_str.length, len )
42
+ end
43
+ end
44
+
45
+ def test_helper
46
+ req_helper = OAuthSimple::RequestHelper.new(
47
+ URI.parse( 'https://api.twitter.com/oauth/request_token' ),
48
+ 'POST',
49
+ 'CONS_SECRET_XXXX&',
50
+ OAuthSimple::RequestParamList.new( [
51
+ # TwitVC
52
+ [ 'oauth_consumer_key', 'CONS_KEY_XXXX' ],
53
+ [ 'oauth_signature_method', 'HMAC-SHA1' ],
54
+ [ 'oauth_timestamp', ( Time.now() - Time.utc( 1970, 1, 1 ) ).to_i().to_s() ],
55
+ [ 'oauth_nonce', 'dfaeaveafefea' ],
56
+ [ 'oauth_version', '1.0' ],
57
+ ] ),
58
+ )
59
+ assert_equal( req_helper.host, 'api.twitter.com' )
60
+ assert_equal( req_helper.port, 443 )
61
+ end
62
+
63
+ # RFC5849 Sec. 3.1 の試験
64
+ # http://tools.ietf.org/html/rfc5849
65
+ #
66
+ # POST /request?b5=%3D%253D&a3=a&c%40=&a2=r%20b HTTP/1.1
67
+ # Host: example.com
68
+ # Content-Type: application/x-www-form-urlencoded
69
+ #
70
+ # c2&a3=2+q
71
+ #
72
+ # The client assigns values to the following protocol parameters using
73
+ # its client credentials, token credentials, the current timestamp, a
74
+ # uniquely generated nonce, and indicates that it will use the
75
+ # "HMAC-SHA1" signature method:
76
+ # oauth_consumer_key: 9djdj82h48djs9d2
77
+ # oauth_token: kkk9d7dh3k39sjv7
78
+ # oauth_signature_method: HMAC-SHA1
79
+ # oauth_timestamp: 137131201
80
+ # oauth_nonce: 7d8f3e4a
81
+ def test_sign
82
+ client_secret = 'j49sk3j29djd'
83
+ token_secret = 'dh893hdasih9'
84
+ req_helper = OAuthSimple::RequestHelper.new(
85
+ URI.parse( 'http://example.com/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b' ),
86
+ 'POST',
87
+ "#{client_secret}&#{token_secret}",
88
+ OAuthSimple::RequestParamList.new( [
89
+ [ 'oauth_consumer_key', '9djdj82h48djs9d2' ],
90
+ [ 'oauth_token', 'kkk9d7dh3k39sjv7' ],
91
+ [ 'oauth_signature_method', 'HMAC-SHA1' ],
92
+ [ 'oauth_timestamp', '137131201' ],
93
+ [ 'oauth_nonce', '7d8f3e4a' ],
94
+ ] ),
95
+ nil,
96
+ OAuthSimple::RequestParamList.new( [
97
+ [ 'c2', nil ],
98
+ [ 'a3', '2 q' ],
99
+ ] ),
100
+ )
101
+ #p req_helper.oauth_header_str
102
+ end
103
+
104
+ def test_factory
105
+ rhf = OAuthSimple::RequestHelperFactory.new(
106
+ :consumer_key => '9djdj82h48djs9d2',
107
+ :consumer_secret => 'j49sk3j29djd' ,
108
+ :token => 'kkk9d7dh3k39sjv7',
109
+ :token_secret => 'dh893hdasih9' ,
110
+ :signature_method => 'HMAC-SHA1' ,
111
+ )
112
+ req_helper = rhf.create(
113
+ URI.parse( 'http://example.com/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b' ),
114
+ 'POST',
115
+ :body_params => OAuthSimple::RequestParamList.new( [
116
+ [ 'c2', nil ],
117
+ [ 'a3', '2 q' ],
118
+ ] )
119
+ )
120
+ assert_equal( req_helper.class, OAuthSimple::RequestHelper )
121
+ #p req_helper.oauth_header_str
122
+ end
123
+
124
+ ###
125
+ # test by using OAuth Test Server : http://term.ie/oauth/example/
126
+ def test_getting_request_token
127
+ # OAuthSimple::HTTP is a subclass of Net::HTTP
128
+ http = OAuthSimple::HTTP.new( 'term.ie' )
129
+
130
+ # OAuth setting (this feature provided by OAuthSimple::HTTP)
131
+ http.use_oauth = true
132
+ http.set_oauth_client_credentials( 'key', 'secret' )
133
+ http.set_oauth_signature_method( 'HMAC-SHA1' ) # at this time, only 'HMAC-SHA1' is supported
134
+
135
+ # connection start
136
+ http.start() do |http|
137
+ assert_equal( http.class, OAuthSimple::HTTP )
138
+ http.request_post( '/oauth/example/request_token.php', nil ) do |res|
139
+ assert_equal( '200', res.code )
140
+ assert_equal( 'oauth_token=requestkey&oauth_token_secret=requestsecret', res.body )
141
+ end
142
+
143
+ token, secret = http.request_oauth_temp_credentials( '/oauth/example/request_token.php', 'oob' )
144
+ end
145
+ end
146
+
147
+ end
148
+
149
+ __END__
150
+
151
+ assert( .... )
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oauth_simple
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.pre
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - NOBUOKA Yu
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-11 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description:
15
+ email: nobuoka@vividcode.info
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files:
19
+ - README.rdoc
20
+ files:
21
+ - README.rdoc
22
+ - rakefile.rb
23
+ - .gemtest
24
+ - lib/oauth_simple/request_param_list.rb
25
+ - lib/oauth_simple/helper_functions.rb
26
+ - lib/oauth_simple/request_helper_factory.rb
27
+ - lib/oauth_simple/request_helper.rb
28
+ - lib/oauth_simple/http.rb
29
+ - lib/oauth_simple.rb
30
+ - test/helper_path_setting.rb
31
+ - test/test_http.rb
32
+ - test/test_main.rb
33
+ homepage: https://github.com/nobuoka/ruby-OAuthSimple
34
+ licenses: []
35
+ post_install_message:
36
+ rdoc_options:
37
+ - --charset=UTF-8
38
+ - --main
39
+ - README.rdoc
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>'
52
+ - !ruby/object:Gem::Version
53
+ version: 1.3.1
54
+ requirements: []
55
+ rubyforge_project:
56
+ rubygems_version: 1.8.24
57
+ signing_key:
58
+ specification_version: 3
59
+ summary: Helper for OAuth 1.0
60
+ test_files:
61
+ - test/test_main.rb