warden_oauth_provider 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.textile +58 -0
- data/Rakefile +2 -0
- data/lib/generators/warden_oauth_provider/install/install_generator.rb +27 -0
- data/lib/generators/warden_oauth_provider/install/templates/migration.rb +45 -0
- data/lib/warden_oauth_provider.rb +19 -0
- data/lib/warden_oauth_provider/client_application.rb +21 -0
- data/lib/warden_oauth_provider/nonce.rb +15 -0
- data/lib/warden_oauth_provider/provider_strategy.rb +82 -0
- data/lib/warden_oauth_provider/token/access.rb +11 -0
- data/lib/warden_oauth_provider/token/base.rb +32 -0
- data/lib/warden_oauth_provider/token/request.rb +29 -0
- data/lib/warden_oauth_provider/token_strategy.rb +40 -0
- data/lib/warden_oauth_provider/version.rb +3 -0
- data/spec/access_token_spec.rb +214 -0
- data/spec/all_steps_spec.rb +79 -0
- data/spec/authorize_spec.rb +43 -0
- data/spec/client_application_spec.rb +41 -0
- data/spec/helpers/factories.rb +16 -0
- data/spec/helpers/request_helper.rb +87 -0
- data/spec/nonce_spec.rb +23 -0
- data/spec/oauth_request_spec.rb +161 -0
- data/spec/request_token_spec.rb +169 -0
- data/spec/spec_helper.rb +66 -0
- data/spec/token_spec.rb +222 -0
- data/spec/token_strategy_spec.rb +158 -0
- data/warden_oauth_provider.gemspec +27 -0
- metadata +214 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (C) 2011 by Edwin Vlieg, Berend van Bruijnsvoort and BlueTools B.V.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
h1. WardenOauthProvider
|
2
|
+
|
3
|
+
This gem allows you to start an oauth server and allow your customers to consume your application through oauth. It is based on Warden and can easily be added to the Warden authentication stack. It uses the "oauth gem":http://rubygems.org/gems/oauth to implement the oauth protocol for Warden.
|
4
|
+
|
5
|
+
h2. Installation
|
6
|
+
|
7
|
+
# Add this gem to your Gemfile
|
8
|
+
<pre>gem 'warden_oauth_provider'</pre>
|
9
|
+
# Run the generator to create a migration for the required database tables
|
10
|
+
<pre>$ rails generate warden_oauth_provider
|
11
|
+
$ rake db:migrate</pre>
|
12
|
+
# Make sure you have installed the Warden gem for your authentication
|
13
|
+
# Add the @:oauth_provider@ strategy to your Warden middleware and define the oauth paths
|
14
|
+
<pre>YourApp::Application.config.middleware.use Warden::Manager do |manager|
|
15
|
+
manager.default_strategies :oauth_provider, :http_basic, :password
|
16
|
+
manager.failure_app = SessionsController
|
17
|
+
manager.oauth_request_token_path = "/oauth/request_token"
|
18
|
+
manager.oauth_access_token_path = "/oauth/access_token"
|
19
|
+
end</pre>
|
20
|
+
|
21
|
+
At this point your application responds on the @/oauth/request_token@ and @/oauth/access_token@ paths and provides request and access tokens based on the request. Before you can make any requests, you should create a client application.
|
22
|
+
|
23
|
+
h3. Creating client applications
|
24
|
+
|
25
|
+
Before a client can connect to the oauth provider, it should be registered as a client application in the database. This can be done through a Rails console or you can create a dedicated controller for this purpose:
|
26
|
+
|
27
|
+
<pre>WardenOauthProvider::ClientApplication.create!(:name => "My client application", :url => "http://myapplication.com", :callback_url => "http://myapplication.com/callback")</pre>
|
28
|
+
|
29
|
+
The @:callback_url@ is an optional argument, because the callback url can also be provided when requesting a request token. The @key@ and @secret@ attributes are automatically filled and are the consumer key and consumer secret that should be used to connect to the oauth server.
|
30
|
+
|
31
|
+
h3. Creating the authorize interface
|
32
|
+
|
33
|
+
During the oauth process, the end-user is redirected to your application to authorize the oauth request. You should write create controller, views and routes for this. You use the @WardenOauthProvider::TokenStrategy@ to verify and authorize the token:
|
34
|
+
|
35
|
+
<pre>def authorize
|
36
|
+
@token = WardenOauthProvider::Token::Request.find_by_token(params[:oauth_token])
|
37
|
+
if request.post?
|
38
|
+
if params[:authorize] == "1" # Something based on your user interface
|
39
|
+
if warden.authenticate?(:oauth_token, :scope => :oauth_token)
|
40
|
+
redirect_to env['oauth.redirect_url']
|
41
|
+
else
|
42
|
+
# Render a template to display failure
|
43
|
+
render :authorize_failure
|
44
|
+
end
|
45
|
+
else
|
46
|
+
# Render a template to display failure
|
47
|
+
render :authorize_failure
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end</pre>
|
51
|
+
|
52
|
+
h2. Reporting bugs
|
53
|
+
|
54
|
+
Please report bugs in this gem via Github Issues: https://github.com/bluetools/warden_oauth_provider/issues
|
55
|
+
|
56
|
+
h2. License
|
57
|
+
|
58
|
+
This code is free to use under the terms of the MIT license and stated in the LICENSE file.
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
class WardenOauthProvider::InstallGenerator < Rails::Generators::Base
|
5
|
+
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
|
8
|
+
def self.source_root
|
9
|
+
@source_root ||= File.join(File.dirname(__FILE__), 'templates')
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.next_migration_number(dirname) #:nodoc:
|
13
|
+
next_migration_number = current_migration_number(dirname) + 1
|
14
|
+
if ActiveRecord::Base.timestamped_migrations
|
15
|
+
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
|
16
|
+
else
|
17
|
+
"%.3d" % next_migration_number
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_migration_file
|
22
|
+
if defined?(ActiveRecord)
|
23
|
+
migration_template 'migration.rb', 'db/migrate/create_oauth_tables.rb'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class CreateOauthTables < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :client_applications do |t|
|
4
|
+
t.string :name
|
5
|
+
t.string :url
|
6
|
+
t.string :support_url
|
7
|
+
t.string :callback_url
|
8
|
+
t.string :key, :limit => 20
|
9
|
+
t.string :secret, :limit => 40
|
10
|
+
t.integer :user_id
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
add_index :client_applications, :key, :unique => true
|
15
|
+
|
16
|
+
create_table :oauth_tokens do |t|
|
17
|
+
t.integer :user_id
|
18
|
+
t.string :type, :limit => 20
|
19
|
+
t.integer :client_application_id
|
20
|
+
t.string :token, :limit => 20
|
21
|
+
t.string :secret, :limit => 40
|
22
|
+
t.string :callback_url
|
23
|
+
t.string :verifier, :limit => 20
|
24
|
+
t.timestamp :authorized_at, :invalidated_at
|
25
|
+
t.timestamps
|
26
|
+
end
|
27
|
+
add_index :oauth_tokens, :token, :unique => true
|
28
|
+
|
29
|
+
create_table :oauth_nonces do |t|
|
30
|
+
t.string :nonce
|
31
|
+
t.integer :timestamp
|
32
|
+
|
33
|
+
t.timestamps
|
34
|
+
end
|
35
|
+
add_index :oauth_nonces,[:nonce, :timestamp], :unique => true
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.down
|
40
|
+
drop_table :client_applications
|
41
|
+
drop_table :oauth_tokens
|
42
|
+
drop_table :oauth_nonces
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'warden'
|
2
|
+
require 'oauth'
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
require 'warden_oauth_provider/provider_strategy'
|
6
|
+
require 'warden_oauth_provider/token_strategy'
|
7
|
+
|
8
|
+
require 'warden_oauth_provider/token/base'
|
9
|
+
require 'warden_oauth_provider/token/request'
|
10
|
+
require 'warden_oauth_provider/token/access'
|
11
|
+
|
12
|
+
Warden::Strategies.add(:oauth_provider, WardenOauthProvider::ProviderStrategy)
|
13
|
+
Warden::Strategies.add(:oauth_token, WardenOauthProvider::TokenStrategy)
|
14
|
+
|
15
|
+
module Warden
|
16
|
+
class Config
|
17
|
+
hash_accessor :oauth_request_token_path, :oauth_access_token_path
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module WardenOauthProvider
|
2
|
+
class ClientApplication < ActiveRecord::Base
|
3
|
+
|
4
|
+
has_many :tokens, :class_name => "WardenOauthProvider::Token::Base", :dependent => :destroy
|
5
|
+
has_many :access_tokens
|
6
|
+
has_many :oauth_tokens
|
7
|
+
|
8
|
+
validates_presence_of :name, :url, :key, :secret
|
9
|
+
validates_uniqueness_of :key
|
10
|
+
|
11
|
+
before_validation(:on => :create) do
|
12
|
+
self.key = OAuth::Helper.generate_key(40)[0,40]
|
13
|
+
self.secret = OAuth::Helper.generate_key(40)[0,40]
|
14
|
+
end
|
15
|
+
|
16
|
+
validates_format_of :url, :with => /\Ahttp(s?):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/i
|
17
|
+
validates_format_of :support_url, :with => /\Ahttp(s?):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/i, :allow_blank=>true
|
18
|
+
validates_format_of :callback_url, :with => /\Ahttp(s?):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/i, :allow_blank=>true
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module WardenOauthProvider
|
2
|
+
class Nonce < ActiveRecord::Base
|
3
|
+
set_table_name "oauth_nonces"
|
4
|
+
|
5
|
+
validates_presence_of :nonce, :timestamp
|
6
|
+
validates_uniqueness_of :nonce, :scope => :timestamp
|
7
|
+
|
8
|
+
# Remembers a nonce and it's associated timestamp. It returns false if it has already been used
|
9
|
+
def self.remember(nonce, timestamp)
|
10
|
+
Nonce.create!(:nonce => nonce, :timestamp => timestamp)
|
11
|
+
rescue ActiveRecord::RecordInvalid
|
12
|
+
false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'oauth/request_proxy/rack_request'
|
2
|
+
require 'oauth/signature/plaintext'
|
3
|
+
|
4
|
+
require 'warden_oauth_provider/client_application'
|
5
|
+
require 'warden_oauth_provider/nonce'
|
6
|
+
|
7
|
+
module WardenOauthProvider
|
8
|
+
|
9
|
+
class ProviderStrategy < Warden::Strategies::Base
|
10
|
+
include OAuth::Helper
|
11
|
+
|
12
|
+
def valid?
|
13
|
+
oauth_request.oauth_parameters.length > 1
|
14
|
+
end
|
15
|
+
|
16
|
+
def authenticate!
|
17
|
+
fail!("Invalid signature or nonce") and return if !verify_request
|
18
|
+
|
19
|
+
case request.path
|
20
|
+
when warden.config.oauth_request_token_path
|
21
|
+
|
22
|
+
# Return a request token for the client application
|
23
|
+
request_token = WardenOauthProvider::Token::Request.create!(:client_application => client_application, :callback_url => oauth_request.oauth_callback)
|
24
|
+
custom! [200, {}, ["oauth_token=#{escape(request_token.token)}&oauth_token_secret=#{escape(request_token.secret)}&oauth_callback_confirmed=true"]]
|
25
|
+
when warden.config.oauth_access_token_path
|
26
|
+
|
27
|
+
# Exchange the access token and return it
|
28
|
+
if access_token = (current_token && current_token.exchange!(oauth_request.oauth_verifier))
|
29
|
+
custom! [200, {}, ["oauth_token=#{escape(access_token.token)}&oauth_token_secret=#{escape(access_token.secret)}"]]
|
30
|
+
else
|
31
|
+
fail!("Request token exchange failed")
|
32
|
+
end
|
33
|
+
else
|
34
|
+
|
35
|
+
# Validate the current token as an access token and allow access to the resources
|
36
|
+
if current_token and current_token.is_a?(WardenOauthProvider::Token::Access)
|
37
|
+
success!(current_token.user)
|
38
|
+
else
|
39
|
+
fail!("Invalid access token")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def request
|
47
|
+
@request ||= Rack::Request.new(env)
|
48
|
+
end
|
49
|
+
|
50
|
+
def oauth_request
|
51
|
+
@oauth_request ||= OAuth::RequestProxy.proxy(request)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Find the signature for the current request and match it with the information in the database.
|
55
|
+
# Also adds the current client application and token to the environment
|
56
|
+
def signature
|
57
|
+
@signature ||= OAuth::Signature.build(oauth_request, :consumer => client_application, :token => current_token)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Verify the request by checking the nonce and the signature
|
61
|
+
def verify_request
|
62
|
+
WardenOauthProvider::Nonce.remember(oauth_request.nonce, oauth_request.timestamp) && signature && signature.verify
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the current token in the database, based on the token provided in the request
|
66
|
+
def current_token
|
67
|
+
return nil if oauth_request.token.nil? or client_application.nil?
|
68
|
+
env['oauth.token'] ||= client_application.tokens.validated.find_by_token(oauth_request.token)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the current client application, based on the consumer key in the request
|
72
|
+
def client_application
|
73
|
+
env['oauth.client_application'] ||= WardenOauthProvider::ClientApplication.find_by_key(oauth_request.consumer_key)
|
74
|
+
end
|
75
|
+
|
76
|
+
def warden
|
77
|
+
env['warden']
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module WardenOauthProvider
|
2
|
+
module Token
|
3
|
+
class Base < ActiveRecord::Base
|
4
|
+
set_table_name "oauth_tokens"
|
5
|
+
|
6
|
+
belongs_to :client_application
|
7
|
+
belongs_to :user
|
8
|
+
|
9
|
+
validates_uniqueness_of :token
|
10
|
+
validates_presence_of :client_application, :token
|
11
|
+
|
12
|
+
scope :validated, where("authorized_at IS NOT NULL and invalidated_at IS NULL")
|
13
|
+
|
14
|
+
before_validation(:on => :create) do
|
15
|
+
self.token = OAuth::Helper.generate_key(40)[0,40]
|
16
|
+
self.secret = OAuth::Helper.generate_key(40)[0,40]
|
17
|
+
end
|
18
|
+
|
19
|
+
def invalidated?
|
20
|
+
invalidated_at != nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def invalidate!
|
24
|
+
update_attribute(:invalidated_at, Time.now)
|
25
|
+
end
|
26
|
+
|
27
|
+
def authorized?
|
28
|
+
authorized_at != nil && !invalidated?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module WardenOauthProvider
|
2
|
+
module Token
|
3
|
+
class Request < Base
|
4
|
+
|
5
|
+
def authorize!(user)
|
6
|
+
return false if authorized? or user.nil?
|
7
|
+
self.user = user
|
8
|
+
self.authorized_at = Time.now
|
9
|
+
self.verifier = OAuth::Helper.generate_key(20)[0,20]
|
10
|
+
self.save
|
11
|
+
end
|
12
|
+
|
13
|
+
def exchange!(verifier)
|
14
|
+
return false unless authorized?
|
15
|
+
return false if self.verifier != verifier
|
16
|
+
self::class.transaction do
|
17
|
+
access_token = WardenOauthProvider::Token::Access.create!(:user => user, :client_application => client_application)
|
18
|
+
invalidate!
|
19
|
+
access_token
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# TODO: check what the requirements for OOB are
|
24
|
+
def oob?
|
25
|
+
self.callback_url.blank? || self.callback_url == 'oob'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module WardenOauthProvider
|
2
|
+
class TokenStrategy < Warden::Strategies::Base
|
3
|
+
|
4
|
+
def valid?
|
5
|
+
true
|
6
|
+
end
|
7
|
+
|
8
|
+
def authenticate!
|
9
|
+
request_token = WardenOauthProvider::Token::Request.find_by_token(request.params["oauth_token"])
|
10
|
+
|
11
|
+
if !request_token or request_token.invalidated?
|
12
|
+
fail!
|
13
|
+
else
|
14
|
+
if request_token.authorize!(env['warden'].user)
|
15
|
+
redirect_url = URI.parse(request_token.oob? ? request_token.client_application.callback_url : request_token.callback_url)
|
16
|
+
redirect_url.query ||= ""
|
17
|
+
redirect_url.query += "&" unless redirect_url.query.blank?
|
18
|
+
redirect_url.query += "oauth_token=#{request_token.token}&oauth_verifier=#{request_token.verifier}"
|
19
|
+
env['oauth.redirect_url'] = redirect_url.to_s
|
20
|
+
|
21
|
+
success!(env['warden'].user)
|
22
|
+
else
|
23
|
+
fail!("Token authorization failed!")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# This is just a single check for the token, don't store any result in the session
|
29
|
+
def store?
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def request
|
36
|
+
@request ||= Rack::Request.new(env)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Access token" do
|
4
|
+
|
5
|
+
context "Success" do
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
@user = Factory(:user)
|
9
|
+
@client_application = Factory.create(:client_application)
|
10
|
+
@request_token = Factory.create(:request_token, :client_application => @client_application)
|
11
|
+
@request_token.authorize!(@user)
|
12
|
+
|
13
|
+
auth_str = oauth_header({
|
14
|
+
:realm => "MoneyBird",
|
15
|
+
:oauth_consumer_key => @client_application.key,
|
16
|
+
:oauth_token => @request_token.token,
|
17
|
+
:oauth_signature_method => "PLAINTEXT",
|
18
|
+
:oauth_timestamp => Time.now.to_i,
|
19
|
+
:oauth_nonce => Time.now.to_f,
|
20
|
+
:oauth_verifier => @request_token.verifier,
|
21
|
+
:oauth_signature => @client_application.secret + "%26" + @request_token.secret
|
22
|
+
})
|
23
|
+
|
24
|
+
env = env_with_params("/oauth/access_token", {}, {
|
25
|
+
"HTTP_AUTHORIZATION" => auth_str
|
26
|
+
})
|
27
|
+
@response = setup_rack.call(env)
|
28
|
+
@oauth_response = Hash[*@response.last.first.split("&").collect { |v| v.split("=") }.flatten]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should have an oauth access token" do
|
32
|
+
@oauth_response.keys.should include("oauth_token")
|
33
|
+
@oauth_response["oauth_token"].should_not be_nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should have an oauth access token secret" do
|
37
|
+
@oauth_response.keys.should include("oauth_token_secret")
|
38
|
+
@oauth_response["oauth_token_secret"].should_not be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should have stored an access token with the token and secret" do
|
42
|
+
WardenOauthProvider::Token::Access.where(:token => @oauth_response["oauth_token"], :secret => @oauth_response["oauth_token_secret"]).count.should == 1
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
context "Success GET" do
|
48
|
+
|
49
|
+
before(:all) do
|
50
|
+
@user = Factory(:user)
|
51
|
+
@client_application = Factory.create(:client_application)
|
52
|
+
@request_token = Factory.create(:request_token, :client_application => @client_application)
|
53
|
+
@request_token.authorize!(@user)
|
54
|
+
|
55
|
+
auth_params = {
|
56
|
+
:realm => "MoneyBird",
|
57
|
+
:oauth_consumer_key => @client_application.key,
|
58
|
+
:oauth_token => @request_token.token,
|
59
|
+
:oauth_signature_method => "PLAINTEXT",
|
60
|
+
:oauth_timestamp => Time.now.to_i,
|
61
|
+
:oauth_nonce => Time.now.to_f,
|
62
|
+
:oauth_verifier => @request_token.verifier,
|
63
|
+
:oauth_signature => @client_application.secret + "&" + @request_token.secret
|
64
|
+
}
|
65
|
+
|
66
|
+
env = env_with_params("/oauth/access_token", auth_params, {})
|
67
|
+
@response = setup_rack.call(env)
|
68
|
+
@oauth_response = Hash[*@response.last.first.split("&").collect { |v| v.split("=") }.flatten]
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should have an oauth access token" do
|
72
|
+
@oauth_response.keys.should include("oauth_token")
|
73
|
+
@oauth_response["oauth_token"].should_not be_nil
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should have an oauth access token secret" do
|
77
|
+
@oauth_response.keys.should include("oauth_token_secret")
|
78
|
+
@oauth_response["oauth_token_secret"].should_not be_nil
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should have stored an access token with the token and secret" do
|
82
|
+
WardenOauthProvider::Token::Access.where(:token => @oauth_response["oauth_token"], :secret => @oauth_response["oauth_token_secret"]).count.should == 1
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
context "Failure" do
|
88
|
+
|
89
|
+
before(:all) do
|
90
|
+
@user = Factory(:user)
|
91
|
+
@client_application = Factory.create(:client_application)
|
92
|
+
@request_token = Factory.create(:request_token, :client_application => @client_application)
|
93
|
+
@request_token.authorize!(@user)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should response with a 401 if the second request contains the same nonce" do
|
97
|
+
auth_str = oauth_header({
|
98
|
+
:realm => "MoneyBird",
|
99
|
+
:oauth_consumer_key => @client_application.key,
|
100
|
+
:oauth_token => @request_token.token,
|
101
|
+
:oauth_signature_method => "PLAINTEXT",
|
102
|
+
:oauth_timestamp => Time.now.to_i,
|
103
|
+
:oauth_nonce => Time.now.to_f,
|
104
|
+
:oauth_verifier => @request_token.verifier,
|
105
|
+
:oauth_signature => @client_application.secret + "%26" + @request_token.secret
|
106
|
+
})
|
107
|
+
env1 = env_with_params("/oauth/access_token", {}, {
|
108
|
+
"HTTP_AUTHORIZATION" => auth_str
|
109
|
+
})
|
110
|
+
env2 = env_with_params("/oauth/access_token", {}, {
|
111
|
+
"HTTP_AUTHORIZATION" => auth_str
|
112
|
+
})
|
113
|
+
|
114
|
+
@response1 = setup_rack.call(env1)
|
115
|
+
@response2 = setup_rack.call(env2)
|
116
|
+
@response2.first.should == 401
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should response with a 401 if the consumer key is invalid" do
|
120
|
+
auth_str = oauth_header({
|
121
|
+
:realm => "MoneyBird",
|
122
|
+
:oauth_consumer_key => @client_application.key + "invalid",
|
123
|
+
:oauth_token => @request_token.token,
|
124
|
+
:oauth_signature_method => "PLAINTEXT",
|
125
|
+
:oauth_timestamp => Time.now.to_i,
|
126
|
+
:oauth_nonce => Time.now.to_f,
|
127
|
+
:oauth_verifier => @request_token.verifier,
|
128
|
+
:oauth_signature => @client_application.secret + "%26" + @request_token.secret
|
129
|
+
})
|
130
|
+
env = env_with_params("/oauth/access_token", {}, {
|
131
|
+
"HTTP_AUTHORIZATION" => auth_str
|
132
|
+
})
|
133
|
+
|
134
|
+
@response = setup_rack.call(env)
|
135
|
+
@response.first.should == 401
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should response with a 401 if the token is invalid" do
|
139
|
+
auth_str = oauth_header({
|
140
|
+
:realm => "MoneyBird",
|
141
|
+
:oauth_consumer_key => @client_application.key,
|
142
|
+
:oauth_token => @request_token.token + "invalid",
|
143
|
+
:oauth_signature_method => "PLAINTEXT",
|
144
|
+
:oauth_timestamp => Time.now.to_i,
|
145
|
+
:oauth_nonce => Time.now.to_f,
|
146
|
+
:oauth_verifier => @request_token.verifier,
|
147
|
+
:oauth_signature => @client_application.secret + "%26" + @request_token.secret
|
148
|
+
})
|
149
|
+
env = env_with_params("/oauth/access_token", {}, {
|
150
|
+
"HTTP_AUTHORIZATION" => auth_str
|
151
|
+
})
|
152
|
+
|
153
|
+
@response = setup_rack.call(env)
|
154
|
+
@response.first.should == 401
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should response with a 401 if the verifier is invalid" do
|
158
|
+
auth_str = oauth_header({
|
159
|
+
:realm => "MoneyBird",
|
160
|
+
:oauth_consumer_key => @client_application.key,
|
161
|
+
:oauth_token => @request_token.token,
|
162
|
+
:oauth_signature_method => "PLAINTEXT",
|
163
|
+
:oauth_timestamp => Time.now.to_i,
|
164
|
+
:oauth_nonce => Time.now.to_f,
|
165
|
+
:oauth_verifier => @request_token.verifier + "invalid",
|
166
|
+
:oauth_signature => @client_application.secret + "%26" + @request_token.secret
|
167
|
+
})
|
168
|
+
env = env_with_params("/oauth/access_token", {}, {
|
169
|
+
"HTTP_AUTHORIZATION" => auth_str
|
170
|
+
})
|
171
|
+
|
172
|
+
@response = setup_rack.call(env)
|
173
|
+
@response.first.should == 401
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should response with a 401 if the signature is invalid" do
|
177
|
+
auth_str = oauth_header({
|
178
|
+
:realm => "MoneyBird",
|
179
|
+
:oauth_consumer_key => @client_application.key,
|
180
|
+
:oauth_token => @request_token.token,
|
181
|
+
:oauth_signature_method => "PLAINTEXT",
|
182
|
+
:oauth_timestamp => Time.now.to_i,
|
183
|
+
:oauth_nonce => Time.now.to_f,
|
184
|
+
:oauth_verifier => @request_token.verifier,
|
185
|
+
:oauth_signature => @client_application.secret + "%26" + @request_token.secret + "invalid"
|
186
|
+
})
|
187
|
+
env = env_with_params("/oauth/access_token", {}, {
|
188
|
+
"HTTP_AUTHORIZATION" => auth_str
|
189
|
+
})
|
190
|
+
|
191
|
+
@response = setup_rack.call(env)
|
192
|
+
@response.first.should == 401
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should response with a 401 if consumer key or signature are invalid" do
|
196
|
+
auth_str = oauth_header({
|
197
|
+
:realm => "MoneyBird",
|
198
|
+
:oauth_consumer_key => @client_application.key + "invalid",
|
199
|
+
:oauth_token => @request_token.token + "invalid",
|
200
|
+
:oauth_signature_method => "PLAINTEXT",
|
201
|
+
:oauth_timestamp => Time.now.to_i,
|
202
|
+
:oauth_nonce => Time.now.to_f,
|
203
|
+
:oauth_verifier => @request_token.verifier + "invalid",
|
204
|
+
:oauth_signature => @client_application.secret + "%26" + @request_token.secret + "invalid"
|
205
|
+
})
|
206
|
+
|
207
|
+
env = env_with_params("/oauth/access_token", {}, {
|
208
|
+
"HTTP_AUTHORIZATION" => auth_str
|
209
|
+
})
|
210
|
+
@response = setup_rack.call(env)
|
211
|
+
@response.first.should == 401
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|