oauth_simple 0.1.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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