twimock 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +125 -0
- data/Rakefile +6 -0
- data/db/.gitkeep +0 -0
- data/lib/twimock/access_token.rb +31 -0
- data/lib/twimock/api/account/verify_credentials.rb +40 -0
- data/lib/twimock/api/application.rb +29 -0
- data/lib/twimock/api/intent/sessions.rb +60 -0
- data/lib/twimock/api/oauth/access_token.rb +65 -0
- data/lib/twimock/api/oauth/authenticate.rb +51 -0
- data/lib/twimock/api/oauth/request_token.rb +49 -0
- data/lib/twimock/api/oauth.rb +83 -0
- data/lib/twimock/api.rb +35 -0
- data/lib/twimock/application.rb +21 -0
- data/lib/twimock/auth_hash.rb +8 -0
- data/lib/twimock/config.rb +90 -0
- data/lib/twimock/database/table.rb +359 -0
- data/lib/twimock/database.rb +133 -0
- data/lib/twimock/errors.rb +13 -0
- data/lib/twimock/omniauth/strategies/twitter.rb +28 -0
- data/lib/twimock/omniauth_twitter.rb +36 -0
- data/lib/twimock/request_token.rb +23 -0
- data/lib/twimock/user.rb +58 -0
- data/lib/twimock/version.rb +3 -0
- data/lib/twimock.rb +39 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/api_spec_helper.rb +30 -0
- data/spec/support/omniauth_twitter_helper.rb +26 -0
- data/spec/support/tables_helper.rb +54 -0
- data/spec/support/test_application_helper.rb +9 -0
- data/spec/twimock/access_token_spec.rb +128 -0
- data/spec/twimock/api/account/verify_credentials_spec.rb +125 -0
- data/spec/twimock/api/application_spec.rb +27 -0
- data/spec/twimock/api/intent/sessions_spec.rb +184 -0
- data/spec/twimock/api/oauth/access_token_spec.rb +185 -0
- data/spec/twimock/api/oauth/authenticate_spec.rb +96 -0
- data/spec/twimock/api/oauth/request_token_spec.rb +123 -0
- data/spec/twimock/api_spec.rb +81 -0
- data/spec/twimock/application_spec.rb +120 -0
- data/spec/twimock/auth_hash_spec.rb +7 -0
- data/spec/twimock/config_spec.rb +192 -0
- data/spec/twimock/database/table_spec.rb +769 -0
- data/spec/twimock/database_spec.rb +261 -0
- data/spec/twimock/omniauth_twitter_spec.rb +129 -0
- data/spec/twimock/request_token_spec.rb +140 -0
- data/spec/twimock/user_spec.rb +271 -0
- data/spec/twimock_spec.rb +76 -0
- data/twimock.gemspec +38 -0
- data/view/authenticate.html.erb +23 -0
- metadata +343 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 98d87b06d405b5e1b62b1e556b0364db89fd791f
|
4
|
+
data.tar.gz: 51a1cc2ac6c7bf7bc616330947265a52f3736462
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1bada1bcbe144100f5d8358303b6e9214cface5cd33052e5f5f4f0f8154a06ac45b24b606b658dc09f2bc38748ef4f808b186d5b6c8242e37a11e7b562568d29
|
7
|
+
data.tar.gz: 46af02913e36302f7eb310e1eda72711fa7857bedd0dc7cce99f72e5dc9e3a2c0038ac101d96975272ea588f562b17910645393a5da4871d14533eb813904fd6
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
tmp/
|
19
|
+
db/*.sqlite3
|
20
|
+
vendor/bundle/
|
21
|
+
vendor/bundler/
|
22
|
+
log/*.log
|
23
|
+
.ruby-*
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 ogawatti
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/twimock.svg)](http://badge.fury.io/rb/twimock)
|
2
|
+
[![Build Status](https://travis-ci.org/ogawatti/twimock.svg?branch=master)](https://travis-ci.org/ogawatti/twimock)
|
3
|
+
[![Coverage Status](https://coveralls.io/repos/ogawatti/twimock/badge.png?branch=master)](https://coveralls.io/r/ogawatti/twimock?branch=master)
|
4
|
+
[<img src="https://gemnasium.com/ogawatti/twimock.png" />](https://gemnasium.com/ogawatti/twimock)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/ogawatti/twimock.png)](https://codeclimate.com/github/ogawatti/twimock)
|
6
|
+
|
7
|
+
# Twimock
|
8
|
+
|
9
|
+
This gem is used to mock the communication part of the twitter api.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
gem 'twimock'
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install twimock
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
### For Rails App Settings (config/initializers/twimock.rb)
|
28
|
+
|
29
|
+
# Create Twimock Application & User
|
30
|
+
application = Twimock::Application.create!
|
31
|
+
user = Twimock::User.create!
|
32
|
+
|
33
|
+
# Associate App and User
|
34
|
+
user.generate_access_token(app.id)
|
35
|
+
|
36
|
+
# Twimock Setting
|
37
|
+
Twimock::Config.host = 'example.com'
|
38
|
+
Twimock::Config.port = 3000
|
39
|
+
Twimock::Config.callback_url = '/users/auth/twitter/callback'
|
40
|
+
|
41
|
+
# Enable Twimock
|
42
|
+
Twimock::API.on
|
43
|
+
Twimock::OmniAuthTwitter.on
|
44
|
+
|
45
|
+
# Add Rack Middleware for twimock
|
46
|
+
[ Twimock::API::OAuth::Authenticate, Twimock::API::Intent::Sessions ].each do |middleware|
|
47
|
+
Rails.application.config.middleware.use middleware
|
48
|
+
end
|
49
|
+
|
50
|
+
### Create Apps and Users at once by yaml file
|
51
|
+
|
52
|
+
require 'twimock'
|
53
|
+
|
54
|
+
filename = File.expand_path('../test_users.yml', __FILE__)
|
55
|
+
Twimock::Config.load_users(filename)
|
56
|
+
|
57
|
+
Twimock::Application.find_by_id(1).api_key #=> avb0vlu767yhu37hti5qq9hcc
|
58
|
+
Twimock::User.find_by_id(1).name #=> testuser01
|
59
|
+
|
60
|
+
yaml file see below.
|
61
|
+
|
62
|
+
---
|
63
|
+
- :id: 1
|
64
|
+
:api_key: avb0vlu767yhu37hti5qq9hcc
|
65
|
+
:api_secret: e85vl7fc4susiyjjp0pncz0hf2xtf3vm29gj7hhp2ktv28wunl
|
66
|
+
:users:
|
67
|
+
- :id: 1
|
68
|
+
:name: testuser01
|
69
|
+
:password: r3xkhy64w
|
70
|
+
:access_token: 6697725737-9ntcnith1wq7zgphnisxu6bqybl019bms05t8l9
|
71
|
+
:access_token_secret: 22ogzdkn5kqtlihr3u5vwplrlh8noie61pr6ndeangrpt
|
72
|
+
:application_id: 1
|
73
|
+
- :id: 2
|
74
|
+
:name: testuser02
|
75
|
+
:password: 5ush05lp0
|
76
|
+
:access_token: 6891305263-xpvu78zd1p76s3cp6jwrudgb0g0sffxe9hp7mdj
|
77
|
+
:access_token_secret: or1xkqs96tim8n7vhc77yxo2i6ed9a6bmhru0zozjao80
|
78
|
+
:application_id: 1
|
79
|
+
- :id: 2
|
80
|
+
:api_key: w6cb9sj17fyf5g1rr4fl5ignp
|
81
|
+
:api_secret: 2vrdpujwvl3421qatn8qah9ishpia9khq7mprnkfx49mldo0k6
|
82
|
+
:users:
|
83
|
+
- :id: 3
|
84
|
+
:name: testuser03
|
85
|
+
:password: ylgi4lth
|
86
|
+
:access_token: 6932630251-1sshumnflh0abshkgaf2scxa6l02cr8tyi2kt00
|
87
|
+
:access_token_secret: txncllipm1wl0g21wvtc750lqz2dleu6e0lqg62vt7eam
|
88
|
+
:application_id: 2
|
89
|
+
|
90
|
+
### User Model
|
91
|
+
|
92
|
+
require 'twimock'
|
93
|
+
|
94
|
+
# Create
|
95
|
+
user = Twimock::User.new
|
96
|
+
user.name = "twimock_test_user"
|
97
|
+
user.save!
|
98
|
+
|
99
|
+
user = Twimock::User.new(name: "hoge", password: "fuga")
|
100
|
+
user.name #=> "hoge"
|
101
|
+
user.save!
|
102
|
+
|
103
|
+
user = Twimock::User.create!(name: "hogehoge", password: "fugafuga")
|
104
|
+
user.name #=> "hogehoge"
|
105
|
+
user.password #=> "fugafuga"
|
106
|
+
|
107
|
+
# Find
|
108
|
+
Twimock::User.find_by_id(1)
|
109
|
+
Twimock::User.find_by_name("testuser01")
|
110
|
+
Twimock::User.where(name: "testuser02")
|
111
|
+
Twimock::User.all
|
112
|
+
Twimock::User.first
|
113
|
+
Twimock::User.last
|
114
|
+
|
115
|
+
# Delete
|
116
|
+
user = User.last
|
117
|
+
user.destroy
|
118
|
+
|
119
|
+
## Contributing
|
120
|
+
|
121
|
+
1. Fork it
|
122
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
123
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
124
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
125
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/db/.gitkeep
ADDED
File without changes
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'faker'
|
2
|
+
require 'twimock/database/table'
|
3
|
+
|
4
|
+
module Twimock
|
5
|
+
class AccessToken < Database::Table
|
6
|
+
TABLE_NAME = :access_tokens
|
7
|
+
COLUMN_NAMES = [:id, :string, :secret, :application_id, :user_id, :created_at]
|
8
|
+
|
9
|
+
def initialize(options={})
|
10
|
+
opts = Hashie::Mash.new(options)
|
11
|
+
id = opts.id.to_i
|
12
|
+
@id = id if id > 0
|
13
|
+
app_id = opts.application_id.to_i
|
14
|
+
@application_id = app_id if app_id > 0
|
15
|
+
user_id = opts.user_id.to_i
|
16
|
+
@user_id = user_id if user_id > 0
|
17
|
+
|
18
|
+
@string = generate_string(opts.string)
|
19
|
+
@secret = opts.secret || Faker::Lorem.characters(45)
|
20
|
+
@created_at = opts.created_at
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def generate_string(string=nil)
|
26
|
+
return string if string
|
27
|
+
return "#{@user_id}-#{Faker::Lorem.characters(39)}" if @user_id
|
28
|
+
return Faker::Lorem.characters(50)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'twimock/api/oauth'
|
2
|
+
require 'twimock/user'
|
3
|
+
|
4
|
+
module Twimock
|
5
|
+
module API
|
6
|
+
# OAuth 1.1, OAuth Echo で利用するAPI
|
7
|
+
# ユーザ情報を取得する
|
8
|
+
module Account
|
9
|
+
class VerifyCredentials < OAuth
|
10
|
+
METHOD = "GET"
|
11
|
+
PATH = "/1.1/account/verify_credentials.json"
|
12
|
+
AUTHORIZATION_REGEXP = /OAuth oauth_consumer_key=\"(.*)\", oauth_nonce=\"(.*)\", oauth_signature=\"(.*)\", oauth_signature_method=\"(.*)\", oauth_timestamp=\"(.*)\", oauth_token=\"(.*)\", oauth_version=\"(.*)\".*/
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
return super unless called?(env)
|
16
|
+
|
17
|
+
begin
|
18
|
+
authorization_header = env["authorization"] || env["HTTP_AUTHORIZATION"]
|
19
|
+
oauth = parse_authorization_header(authorization_header)
|
20
|
+
|
21
|
+
raise Twimock::Errors::InvalidConsumerKey.new if !validate_consumer_key(oauth.consumer_key)
|
22
|
+
application = Twimock::Application.find_by_api_key(oauth.consumer_key)
|
23
|
+
raise Twimock::Errors::InvalidAccessToken.new if !validate_access_token(oauth.token, application.id)
|
24
|
+
access_token = Twimock::AccessToken.find_by_string(oauth.token)
|
25
|
+
user = Twimock::User.find_by_id(access_token.user_id)
|
26
|
+
rescue Twimock::Errors::InvalidAccessToken, Twimock::Errors::InvalidConsumerKey => @error
|
27
|
+
return unauthorized
|
28
|
+
rescue => @error
|
29
|
+
return internal_server_error
|
30
|
+
end
|
31
|
+
|
32
|
+
status = '200 OK'
|
33
|
+
body = user.info.to_json
|
34
|
+
header = { "Content-Length" => body.bytesize.to_s }
|
35
|
+
[ status, header, [ body ] ]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'excon'
|
2
|
+
|
3
|
+
module Twimock
|
4
|
+
module API
|
5
|
+
# Rack Application
|
6
|
+
# Net::HTTP は ShamRack で偽装されるため, Excon (Socket) で通信する
|
7
|
+
class Application
|
8
|
+
def call(env)
|
9
|
+
request(env)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def request(env)
|
15
|
+
rackreq = Rack::Request.new(env)
|
16
|
+
connection = Excon.new(rackreq.url)
|
17
|
+
|
18
|
+
options = {}
|
19
|
+
options[:method] = rackreq.request_method
|
20
|
+
options[:path] = rackreq.path
|
21
|
+
options[:headers] = rackreq.env.select{|k,v| k !~ /^rack\./}
|
22
|
+
options[:body] = rackreq.body.read
|
23
|
+
|
24
|
+
res = connection.request(options)
|
25
|
+
[ res.status, res.headers, [ res.body ] ]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'erb'
|
3
|
+
require 'json'
|
4
|
+
require 'addressable/uri'
|
5
|
+
require 'twimock/errors'
|
6
|
+
|
7
|
+
module Twimock
|
8
|
+
module API
|
9
|
+
# POST https://twitter.com/intent/sessions
|
10
|
+
# body: { 'session[username_or_email]' => "xxx", 'session[password]' => "xxx", oauth_token: "xxx" }
|
11
|
+
module Intent
|
12
|
+
class Sessions < OAuth
|
13
|
+
METHOD = "POST"
|
14
|
+
PATH = "/intent/sessions"
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
return super unless called?(env)
|
18
|
+
begin
|
19
|
+
request = Rack::Request.new(env)
|
20
|
+
body = query_string_to_hash(request.body.read)
|
21
|
+
@oauth_token = body.oauth_token
|
22
|
+
@username_or_email = body["session[username_or_email]"]
|
23
|
+
@password = body["session[password]"]
|
24
|
+
|
25
|
+
if !validate_request_token(@oauth_token)
|
26
|
+
raise Twimock::Errors::InvalidRequestToken.new
|
27
|
+
elsif !(user = Twimock::User.find_by_tiwtter_id_or_email(@username_or_email))
|
28
|
+
raise Twimock::Errors::InvalidUsernameOrEmail.new
|
29
|
+
elsif @password.blank? || @password != user.password
|
30
|
+
raise Twimock::Errors::InvalidPassword.new
|
31
|
+
end
|
32
|
+
request_token = Twimock::RequestToken.find_by_string(@oauth_token)
|
33
|
+
request_token.user_id = user.id
|
34
|
+
request_token.save!
|
35
|
+
|
36
|
+
uri = Addressable::URI.new
|
37
|
+
uri.query_values = { oauth_token: request_token.string,
|
38
|
+
oauth_verifier: request_token.verifier }
|
39
|
+
callback_url = Twimock::Config.callback_url + "?" + uri.query
|
40
|
+
|
41
|
+
status = 302
|
42
|
+
body = ""
|
43
|
+
header = { "Content-Length" => body.bytesize.to_s,
|
44
|
+
"Location" => callback_url }
|
45
|
+
[ status, header, [ body ] ]
|
46
|
+
rescue Twimock::Errors::InvalidUsernameOrEmail, Twimock::Errors::InvalidPassword => @error
|
47
|
+
response = unauthorized
|
48
|
+
response[0] = 302
|
49
|
+
response[1].merge!( {"Location" => "/oauth/authenticate?oauth_token=#{@oauth_token}" })
|
50
|
+
response
|
51
|
+
rescue Twimock::Errors::InvalidRequestToken => @error
|
52
|
+
return unauthorized
|
53
|
+
rescue => @error
|
54
|
+
internal_server_error
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'twimock/api/oauth'
|
2
|
+
require 'twimock/user'
|
3
|
+
|
4
|
+
module Twimock
|
5
|
+
module API
|
6
|
+
class OAuth
|
7
|
+
# OAuth 1.1 で利用するAPI
|
8
|
+
# Access Token を取得する
|
9
|
+
class AccessToken < OAuth
|
10
|
+
METHOD = "POST"
|
11
|
+
PATH = "/oauth/access_token"
|
12
|
+
AUTHORIZATION_REGEXP = /OAuth oauth_body_hash=\"(.*)\", oauth_consumer_key=\"(.*)\", oauth_nonce=\"(.*)\", oauth_signature=\"(.*)\", oauth_signature_method=\"(.*)\", oauth_timestamp=\"(.*)\", oauth_token=\"(.*)\", oauth_verifier=\"(.*)\", oauth_version=\"(.*)\"/
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
return super unless called?(env)
|
16
|
+
begin
|
17
|
+
authorization_header = env["authorization"] || env["HTTP_AUTHORIZATION"]
|
18
|
+
oauth = parse_authorization_header(authorization_header)
|
19
|
+
consumer_key = oauth.consumer_key
|
20
|
+
request_token = oauth.token
|
21
|
+
|
22
|
+
raise Twimock::Errors::InvalidConsumerKey.new if !validate_consumer_key(consumer_key)
|
23
|
+
application = Twimock::Application.find_by_api_key(consumer_key)
|
24
|
+
if !validate_request_token(request_token, application.id)
|
25
|
+
raise Twimock::Errors::InvalidRequestToken.new
|
26
|
+
end
|
27
|
+
request_token = Twimock::RequestToken.find_by_string(request_token)
|
28
|
+
user = Twimock::User.find_by_id(request_token.user_id)
|
29
|
+
access_tokens = Twimock::AccessToken.where(user_id: user.id)
|
30
|
+
unless access_token = access_tokens.find{|at| at.application_id == application.id }
|
31
|
+
access_token = user.generate_access_token(application.id)
|
32
|
+
end
|
33
|
+
rescue Twimock::Errors::InvalidConsumerKey, Twimock::Errors::InvalidRequestToken => @error
|
34
|
+
return unauthorized
|
35
|
+
rescue => @error
|
36
|
+
return internal_server_error
|
37
|
+
end
|
38
|
+
|
39
|
+
status = "200 OK"
|
40
|
+
params = {
|
41
|
+
oauth_token: access_token.string,
|
42
|
+
oauth_token_secret: access_token.secret,
|
43
|
+
user_id: user.id,
|
44
|
+
screen_name: user.twitter_id
|
45
|
+
}
|
46
|
+
body = params.inject([]){|a, (k, v)| a << "#{k}=#{v}"}.join('&')
|
47
|
+
header = { "Content-Length" => body.bytesize.to_s }
|
48
|
+
|
49
|
+
[ status, header, [ body ] ]
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def validate_request_token(request_token, application_id)
|
55
|
+
return false unless super(request_token)
|
56
|
+
|
57
|
+
request_token = Twimock::RequestToken.find_by_string(request_token)
|
58
|
+
return false unless request_token.application_id == application_id
|
59
|
+
return false unless User.find_by_id(request_token.user_id)
|
60
|
+
true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
module Twimock
|
5
|
+
module API
|
6
|
+
# OAuthでブラウザ認証するAPI
|
7
|
+
# GET http://api.twimock.com/authenticate?oauth_token=xxx
|
8
|
+
class OAuth
|
9
|
+
class Authenticate < OAuth
|
10
|
+
METHOD = "GET"
|
11
|
+
PATH = "/oauth/authenticate"
|
12
|
+
VIEW_DIRECTORY = File.expand_path("../../../../../view", __FILE__)
|
13
|
+
VIEW_FILE_NAME = "authenticate.html.erb"
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
return super unless called?(env)
|
17
|
+
begin
|
18
|
+
request = Rack::Request.new(env)
|
19
|
+
@oauth_token = request.params["oauth_token"]
|
20
|
+
|
21
|
+
if !validate_request_token(@oauth_token)
|
22
|
+
raise Twimock::Errors::InvalidRequestToken.new
|
23
|
+
end
|
24
|
+
|
25
|
+
status = 200
|
26
|
+
body = Twimock::API::OAuth::Authenticate.view(@oauth_token)
|
27
|
+
header = { "Content-Length" => body.bytesize.to_s }
|
28
|
+
[ status, header, [ body ] ]
|
29
|
+
rescue Twimock::Errors::InvalidRequestToken => @error
|
30
|
+
unauthorized
|
31
|
+
rescue => @error
|
32
|
+
internal_server_error
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.view(oauth_token)
|
37
|
+
@action_url = Twimock::API::Intent::Sessions::PATH
|
38
|
+
@oauth_token = oauth_token
|
39
|
+
erb = ERB.new(File.read(filepath))
|
40
|
+
erb.result(binding)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def self.filepath
|
46
|
+
File.join(VIEW_DIRECTORY, VIEW_FILE_NAME)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'twimock/api/oauth'
|
2
|
+
require 'twimock/application'
|
3
|
+
|
4
|
+
module Twimock
|
5
|
+
module API
|
6
|
+
# Twitter OAuth で利用するAPI
|
7
|
+
# Request Token を発行する
|
8
|
+
class OAuth
|
9
|
+
class RequestToken < OAuth
|
10
|
+
METHOD = "POST"
|
11
|
+
PATH = "/oauth/request_token"
|
12
|
+
AUTHORIZATION_REGEXP = /OAuth oauth_callback=\"(.*)\", oauth_consumer_key=\"(.*)\", oauth_nonce=\"(.*)\", oauth_signature=\"(.*)\", oauth_signature_method=\"(.*)\", oauth_timestamp=\"(.*)\", oauth_version=\"(.*)\".*/
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
return super unless called?(env)
|
16
|
+
begin
|
17
|
+
authorization_header = env["authorization"] || env["HTTP_AUTHORIZATION"]
|
18
|
+
oauth = parse_authorization_header(authorization_header)
|
19
|
+
consumer_key = oauth.consumer_key
|
20
|
+
|
21
|
+
raise Twimock::Errors::InvalidConsumerKey.new if !validate_consumer_key(consumer_key)
|
22
|
+
application = Twimock::Application.find_by_api_key(consumer_key)
|
23
|
+
rescue Twimock::Errors::InvalidConsumerKey => @error
|
24
|
+
return unauthorized
|
25
|
+
rescue => @error
|
26
|
+
return internal_server_error
|
27
|
+
end
|
28
|
+
|
29
|
+
request_token = create_request_token(application.id)
|
30
|
+
status = "200 OK"
|
31
|
+
params = { oauth_token: request_token.string,
|
32
|
+
oauth_token_secret: request_token.secret,
|
33
|
+
oauth_callback_confirmed: true }
|
34
|
+
body = params.inject([]){|a, (k, v)| a << "#{k}=#{v}"}.join('&')
|
35
|
+
header = { "Content-Length" => body.bytesize.to_s }
|
36
|
+
[ status, header, [ body ] ]
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def create_request_token(application_id)
|
42
|
+
request_token = Twimock::RequestToken.new(application_id: application_id)
|
43
|
+
request_token.save!
|
44
|
+
request_token
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'twimock/api/oauth/access_token'
|
2
|
+
require 'twimock/api/oauth/request_token'
|
3
|
+
require 'twimock/api/oauth/authenticate'
|
4
|
+
require 'twimock/api/intent/sessions'
|
5
|
+
require 'twimock/api/account/verify_credentials'
|
6
|
+
require 'twimock/errors'
|
7
|
+
|
8
|
+
module Twimock
|
9
|
+
module API
|
10
|
+
class OAuth
|
11
|
+
def initialize(app)
|
12
|
+
@app = app
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
@app.call(env)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def validate_consumer_key(consumer_key)
|
22
|
+
return false if consumer_key.blank?
|
23
|
+
return false unless application = Twimock::Application.find_by_api_key(consumer_key)
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate_request_token(request_token)
|
28
|
+
return false if request_token.blank?
|
29
|
+
return false unless request_token = Twimock::RequestToken.find_by_string(request_token)
|
30
|
+
return false unless request_token.application_id
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate_access_token(access_token_string, application_id)
|
35
|
+
return false if access_token_string.blank?
|
36
|
+
return false unless access_token = Twimock::AccessToken.find_by_string(access_token_string)
|
37
|
+
return false unless access_token.application_id
|
38
|
+
return false unless access_token.application_id == application_id
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
def called?(env)
|
43
|
+
request = Rack::Request.new(env)
|
44
|
+
request.request_method == self.class::METHOD && request.path == self.class::PATH
|
45
|
+
end
|
46
|
+
|
47
|
+
def unauthorized
|
48
|
+
generate_error_response(401)
|
49
|
+
end
|
50
|
+
|
51
|
+
def internal_server_error
|
52
|
+
generate_error_response(500)
|
53
|
+
end
|
54
|
+
|
55
|
+
def parse_authorization_header(authorization_header)
|
56
|
+
authorization = case authorization_header
|
57
|
+
when Array then authorization_header.first
|
58
|
+
when String then authorization_header
|
59
|
+
else ""
|
60
|
+
end
|
61
|
+
|
62
|
+
oauth = Hashie::Mash.new
|
63
|
+
authorization.scan(/oauth_(\w+)=\"([\w%-.]+)\"/) do |key, value|
|
64
|
+
oauth[key] = value
|
65
|
+
end
|
66
|
+
oauth
|
67
|
+
end
|
68
|
+
|
69
|
+
def query_string_to_hash(query_string)
|
70
|
+
ary = URI.decode(query_string).split("&").inject([]){|a, s| a << s.split("=")}
|
71
|
+
Hashie::Mash.new(Hash[ary])
|
72
|
+
end
|
73
|
+
|
74
|
+
def generate_error_response(status)
|
75
|
+
error_code = @error.class.to_s.split("::").last
|
76
|
+
body = { error: { code: error_code } }.to_json
|
77
|
+
header = { "Content-Type" => "application/json; charset=utf-8",
|
78
|
+
"Content-Length" => body.bytesize.to_s }
|
79
|
+
[ status, header, [ body ] ]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/twimock/api.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'twimock/api/application'
|
2
|
+
require 'twimock/api/oauth'
|
3
|
+
require 'sham_rack'
|
4
|
+
|
5
|
+
module Twimock
|
6
|
+
module API
|
7
|
+
extend self
|
8
|
+
|
9
|
+
HOSTNAME = "api.twitter.com"
|
10
|
+
PORT = 443
|
11
|
+
MIDDLEWARES = [ OAuth::AccessToken, OAuth::RequestToken, Account::VerifyCredentials ]
|
12
|
+
|
13
|
+
def on
|
14
|
+
ShamRack.at(HOSTNAME, PORT){|env| app.call(env) } unless on?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def off
|
19
|
+
ShamRack.unmount_all
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def on?
|
24
|
+
!ShamRack.application_for(HOSTNAME, PORT).nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
# Rack Application
|
28
|
+
def app
|
29
|
+
app = Twimock::API::Application.new
|
30
|
+
MIDDLEWARES.inject(app) do |app, klass|
|
31
|
+
app = klass.new(app)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'faker'
|
2
|
+
require 'twimock/database/table'
|
3
|
+
require 'twimock/access_token'
|
4
|
+
require 'twimock/request_token'
|
5
|
+
|
6
|
+
module Twimock
|
7
|
+
class Application < Database::Table
|
8
|
+
TABLE_NAME = :applications
|
9
|
+
COLUMN_NAMES = [:id, :api_key, :api_secret, :created_at]
|
10
|
+
CHILDREN = [ Twimock::AccessToken, Twimock::RequestToken ]
|
11
|
+
|
12
|
+
# WANT : DBに登録済みの値と重複しないようにする(id, api_secret)
|
13
|
+
def initialize(options={})
|
14
|
+
opts = Hashie::Mash.new(options)
|
15
|
+
@id = ( opts.id.to_i > 0 ) ? opts.id.to_i : Faker::Number.number(10).to_i
|
16
|
+
@api_key = opts.api_key || Faker::Lorem.characters(25)
|
17
|
+
@api_secret = opts.api_secret || Faker::Lorem.characters(50)
|
18
|
+
@created_at = opts.created_at
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|