roman-warden_oauth 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/CHANGELOG +2 -0
- data/LICENSE +20 -0
- data/README.rdoc +60 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/lib/warden_oauth.rb +19 -0
- data/lib/warden_oauth/config.rb +43 -0
- data/lib/warden_oauth/errors.rb +9 -0
- data/lib/warden_oauth/manager.rb +72 -0
- data/lib/warden_oauth/strategy.rb +127 -0
- data/lib/warden_oauth/strategy_builder.rb +80 -0
- data/lib/warden_oauth/utils.rb +40 -0
- data/spec/application_runner.rb +11 -0
- data/spec/application_scenario.rb +35 -0
- data/spec/fixtures/authorize_request_token.txt +1 -0
- data/spec/fixtures/unauthorized_request_token.txt +1 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/warden_oauth/manager_spec.rb +48 -0
- data/spec/warden_oauth/strategy_spec.rb +121 -0
- metadata +138 -0
data/.document
ADDED
data/CHANGELOG
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Roman Gonzalez
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
= warden_oauth
|
2
|
+
|
3
|
+
warden_oauth enhances the Warden authentication framework, offering a simple interface for creating
|
4
|
+
oauth strategies.
|
5
|
+
|
6
|
+
== Getting Started
|
7
|
+
|
8
|
+
To get started you just have to require the warden_oauth libraries, and setup the
|
9
|
+
oauth services you would like to have on the <tt>Warden::Manager</tt> middleware declaration:
|
10
|
+
|
11
|
+
Warden::Manager do |manager|
|
12
|
+
manager.failure_app = FailureApp
|
13
|
+
manager.oauth(:twitter) do |twitter|
|
14
|
+
twitter.consumer_secret = <YOUR CONSUMER SECRET>
|
15
|
+
twitter.consumer_key = <YOUR CONSUMER KEY>
|
16
|
+
twitter.options :site => 'http://twitter.com'
|
17
|
+
end
|
18
|
+
manager.default_strategies(:twitter_oauth, :password, :other)
|
19
|
+
end
|
20
|
+
|
21
|
+
== Giving an Access Token fetcher
|
22
|
+
|
23
|
+
Users get identified on a system via an access_token and an access_secret, when a valid access_token is
|
24
|
+
recevied, warden_oauth calls a fetcher declared on <tt>Warden::Manager.access_token_user_finder</tt>.
|
25
|
+
|
26
|
+
Warden::Manager.access_token_user_finder(:twitter) do |access_token|
|
27
|
+
User.find_by_access_token_and_access_secret(access_token.token, access_token.secret)
|
28
|
+
end
|
29
|
+
|
30
|
+
If a user is returned, then this is the user that is going to be authenticated in the session, otherwise the
|
31
|
+
<tt>FailureApp</tt> will be called, you may check the <tt>env['warden.options'][:oauth][:access_token]</tt> to check
|
32
|
+
the original access_token and <em>create a new user</em> from there if desired.
|
33
|
+
|
34
|
+
== Strategy Class info
|
35
|
+
|
36
|
+
When you declare an oauth strategy on the <tt>Warden::Manager</tt> initialization, (e.g. manager.oauth(:service_name))
|
37
|
+
a <tt>Warden::OAuth::Strategy::ServiceName</tt> will be declared, at the same time this class will be registered as
|
38
|
+
<tt>:service_name_oauth</tt> on the <tt>Warden::Strategies</tt>.
|
39
|
+
|
40
|
+
So if we have a declaration like the one of the Getting Started section, we will have an Strategy class
|
41
|
+
called <tt>Warden::OAuth::Strategy::Twitter</tt>, and this will be registered as <tt>:twitter_oauth</tt>.
|
42
|
+
|
43
|
+
== Running the Strategy
|
44
|
+
|
45
|
+
In order to get the strategy running in the app, you have to specify a parameter called warden_oauth_provider
|
46
|
+
with the name of the oauth service you want to start. So for example, if you would like to boot the twitter
|
47
|
+
oauth example given on the "Getting Started" section you just have to specify the parameter on a protected
|
48
|
+
url.
|
49
|
+
|
50
|
+
In Rails:
|
51
|
+
|
52
|
+
link_to 'Twitter Authentication', url_for(login_path(:warden_oauth_provider => 'twitter'))
|
53
|
+
|
54
|
+
== Note on Patches/Pull Requests
|
55
|
+
|
56
|
+
For any error send an email to: romanandreg [at] gmail [dot] com
|
57
|
+
|
58
|
+
== Copyright
|
59
|
+
|
60
|
+
Copyright (c) 2009 Roman Gonzalez. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "warden_oauth"
|
8
|
+
gem.summary = %Q{OAuth Strategy generator for Warden Authentication Framework}
|
9
|
+
gem.description = %Q{
|
10
|
+
warden_oauth will help you create oauth authentication strategies using the oauth
|
11
|
+
helper method on the Warden::Manager config setup
|
12
|
+
}
|
13
|
+
gem.email = "romanandreg@gmail.com"
|
14
|
+
gem.homepage = "http://github.com/roman/warden_oauth"
|
15
|
+
gem.authors = ["Roman Gonzalez"]
|
16
|
+
gem.rubyforge_project = "warden_oauth"
|
17
|
+
gem.add_dependency('warden')
|
18
|
+
gem.add_dependency('oauth')
|
19
|
+
gem.add_development_dependency("rack-test")
|
20
|
+
gem.add_development_dependency("fakeweb")
|
21
|
+
gem.add_development_dependency("rspec")
|
22
|
+
gem.add_development_dependency("yard")
|
23
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
24
|
+
end
|
25
|
+
Jeweler::RubyforgeTasks.new do |rubyforge|
|
26
|
+
rubyforge.doc_task = "yardoc"
|
27
|
+
end
|
28
|
+
rescue LoadError
|
29
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
30
|
+
end
|
31
|
+
|
32
|
+
require 'spec/rake/spectask'
|
33
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
34
|
+
spec.libs << 'lib' << 'spec'
|
35
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
36
|
+
end
|
37
|
+
|
38
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
39
|
+
spec.libs << 'lib' << 'spec'
|
40
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
41
|
+
spec.rcov = true
|
42
|
+
end
|
43
|
+
|
44
|
+
task :spec => :check_dependencies
|
45
|
+
|
46
|
+
task :default => :spec
|
47
|
+
|
48
|
+
begin
|
49
|
+
require 'yard'
|
50
|
+
YARD::Rake::YardocTask.new
|
51
|
+
rescue LoadError
|
52
|
+
task :yardoc do
|
53
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
54
|
+
end
|
55
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
data/lib/warden_oauth.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "rack"
|
2
|
+
require "warden"
|
3
|
+
require "oauth"
|
4
|
+
|
5
|
+
module Warden
|
6
|
+
module OAuth
|
7
|
+
|
8
|
+
base_path = File.dirname(__FILE__) + "/warden_oauth"
|
9
|
+
|
10
|
+
require base_path + "/errors"
|
11
|
+
autoload :Utils, base_path + '/utils'
|
12
|
+
autoload :StrategyBuilder, base_path + '/strategy_builder'
|
13
|
+
autoload :Strategy, base_path + '/strategy'
|
14
|
+
autoload :Config, base_path + "/config"
|
15
|
+
require base_path + "/manager"
|
16
|
+
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Warden
|
2
|
+
module OAuth
|
3
|
+
|
4
|
+
#
|
5
|
+
# Holds all the information of the OAuth service.
|
6
|
+
#
|
7
|
+
class Config
|
8
|
+
attr_accessor :provider_name
|
9
|
+
|
10
|
+
def consumer_key(key = nil)
|
11
|
+
unless key.nil?
|
12
|
+
@consumer_key = key
|
13
|
+
end
|
14
|
+
@consumer_key
|
15
|
+
end
|
16
|
+
alias_method :consumer_key=, :consumer_key
|
17
|
+
|
18
|
+
def consumer_secret(secret = nil)
|
19
|
+
unless secret.nil?
|
20
|
+
@consumer_secret = secret
|
21
|
+
end
|
22
|
+
@consumer_secret
|
23
|
+
end
|
24
|
+
alias_method :consumer_secret=, :consumer_secret
|
25
|
+
|
26
|
+
def options(options = nil)
|
27
|
+
unless options.nil?
|
28
|
+
@options = options
|
29
|
+
end
|
30
|
+
@options
|
31
|
+
end
|
32
|
+
alias_method :options=, :options
|
33
|
+
|
34
|
+
def check_requirements
|
35
|
+
if @consumer_key.nil? || @consumer_secret.nil?
|
36
|
+
raise Warden::OAuth::ConfigError.new("You need to specify the consumer key and the consumer secret")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Warden
|
2
|
+
module OAuth
|
3
|
+
|
4
|
+
#
|
5
|
+
# Holds all the extensions made to Warden::Manager in order to create OAuth
|
6
|
+
# consumers.
|
7
|
+
#
|
8
|
+
module Manager
|
9
|
+
|
10
|
+
def self.included(base) #:nodoc:
|
11
|
+
base.extend(ClassMethods)
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# Helps to setup a new OAuth client authentication, to get started you need to define
|
16
|
+
# a service name, and then on the block assign the different values required in order
|
17
|
+
# to boot the OAuth process.
|
18
|
+
# @param [Symbol] service An identifier of the OAuth service
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
#
|
22
|
+
# Warden::Manager.oauth(:twitter) do
|
23
|
+
# consumer_key "<YOUR CONSUMER KEY>"
|
24
|
+
# consumer_secret "<YOUR CONSUMER SECRET>"
|
25
|
+
# options :site => 'http://twitter.com'
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
def oauth(service, &block)
|
29
|
+
config = Warden::OAuth::Config.new
|
30
|
+
if block_given?
|
31
|
+
if block.arity == 1
|
32
|
+
yield config
|
33
|
+
else
|
34
|
+
config.instance_eval(&block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
config.check_requirements
|
38
|
+
config.provider_name = service
|
39
|
+
Warden::OAuth::Strategy.build(service, config)
|
40
|
+
end
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
|
44
|
+
#
|
45
|
+
# Assigns a block that handles how to find a User given an access_token.
|
46
|
+
# @param [Symbol] oauth_service The identifier specified on Warden::Manager.oauth
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# Warden::Manager.access_token_user_finder(:twitter) do |access_token|
|
50
|
+
# # Find user with access_token
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
def access_token_user_finder(oauth_service, &block)
|
54
|
+
raise Warden::OAuth::AccessTokenFinderMissing.new("You need to specify a block for Warden::Manager.acess_token_user_finder") unless block_given?
|
55
|
+
raise Warden::OAuth::AccessTokenFinderMissing.new("You need to specify a block for Warden::Manager.access_token_user_finder, this must receive one parameter") if block.arity != 1
|
56
|
+
@find_user_by_access_token ||= {}
|
57
|
+
@find_user_by_access_token[oauth_service] = block
|
58
|
+
end
|
59
|
+
|
60
|
+
def find_user_by_access_token(oauth_service, access_token) #:nodoc:
|
61
|
+
raise Warden::OAuth::AccessTokenFinderMissing.new("You need to specify a block for Warden::Manager.acess_token_user_finder") if @find_user_by_access_token.nil?
|
62
|
+
@find_user_by_access_token[oauth_service].call(access_token)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
Warden::Manager.send(:include, Warden::OAuth::Manager)
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Warden
|
2
|
+
module OAuth
|
3
|
+
|
4
|
+
#
|
5
|
+
# Holds all the main logic of the OAuth authentication, all the generated
|
6
|
+
# OAuth classes will extend from this class
|
7
|
+
#
|
8
|
+
class Strategy < Warden::Strategies::Base
|
9
|
+
extend StrategyBuilder
|
10
|
+
|
11
|
+
######################
|
12
|
+
### Strategy Logic ###
|
13
|
+
######################
|
14
|
+
|
15
|
+
#
|
16
|
+
# An OAuth strategy will be valid to execute if:
|
17
|
+
# * A 'warden_oauth_provider' parameter is given, with the name of the OAuth service
|
18
|
+
# * A 'oauth_token' is being receive on the request (response from an OAuth provider)
|
19
|
+
#
|
20
|
+
def valid?
|
21
|
+
(params.include?('warden_oauth_provider') && params['warden_oauth_provider'] == config.provider_name.to_s) ||
|
22
|
+
params.include?('oauth_token')
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
#
|
27
|
+
# Manages the OAuth authentication process, there can be 3 outcomes from this Strategy:
|
28
|
+
# 1. The OAuth credentials are invalid and the FailureApp is called
|
29
|
+
# 2. The OAuth credentials are valid, but there is no user associated to them. In this case
|
30
|
+
# the FailureApp is called, but the env['warden.options'][:oauth][:access_token] will be
|
31
|
+
# available.
|
32
|
+
# 3. The OAuth credentials are valid, and the user is authenticated successfuly
|
33
|
+
#
|
34
|
+
# @note
|
35
|
+
# If you want to signup users with the twitter credentials, you can manage the creation of a new
|
36
|
+
# user in the FailureApp with the given access_token
|
37
|
+
#
|
38
|
+
def authenticate!
|
39
|
+
if params.include?('warden_oauth_provider')
|
40
|
+
store_request_token_on_session
|
41
|
+
redirect!(request_token.authorize_url)
|
42
|
+
|
43
|
+
elsif params.include?('oauth_token')
|
44
|
+
load_request_token_from_session
|
45
|
+
if missing_stored_token?
|
46
|
+
fail!("There is no OAuth authentication in progress")
|
47
|
+
elsif !stored_token_match_recieved_token?
|
48
|
+
fail!("Received OAuth token didn't match stored OAuth token")
|
49
|
+
else
|
50
|
+
user = Warden::Manager.find_user_by_access_token(config.provider_name , access_token)
|
51
|
+
if user.nil?
|
52
|
+
fail!("User with access token not found")
|
53
|
+
throw_error_with_oauth_info
|
54
|
+
else
|
55
|
+
success!(user)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def fail!(msg) #:nodoc:
|
63
|
+
self.errors.add(service_param_name.to_sym, msg)
|
64
|
+
super
|
65
|
+
end
|
66
|
+
|
67
|
+
###################
|
68
|
+
### OAuth Logic ###
|
69
|
+
###################
|
70
|
+
|
71
|
+
def consumer
|
72
|
+
@consumer ||= ::OAuth::Consumer.new(config.consumer_key, config.consumer_secret, config.options)
|
73
|
+
end
|
74
|
+
|
75
|
+
def request_token
|
76
|
+
host_with_port = Warden::OAuth::Utils.host_with_port(request)
|
77
|
+
@request_token ||= consumer.get_request_token(:oauth_callback => host_with_port)
|
78
|
+
end
|
79
|
+
|
80
|
+
def access_token
|
81
|
+
@access_token ||= request_token.get_access_token(:oauth_verifier => params['oauth_verifier'])
|
82
|
+
end
|
83
|
+
|
84
|
+
protected
|
85
|
+
|
86
|
+
def throw_error_with_oauth_info
|
87
|
+
throw(:warden, :oauth => {
|
88
|
+
self.config.provider_name => {
|
89
|
+
:provider => config.provider_name,
|
90
|
+
:access_token => access_token,
|
91
|
+
:consumer_key => config.consumer_key,
|
92
|
+
:consumer_secret => config.consumer_secret
|
93
|
+
}
|
94
|
+
})
|
95
|
+
end
|
96
|
+
|
97
|
+
def store_request_token_on_session
|
98
|
+
session[:request_token] = request_token.token
|
99
|
+
session[:request_secret] = request_token.secret
|
100
|
+
end
|
101
|
+
|
102
|
+
def load_request_token_from_session
|
103
|
+
token = session.delete(:request_token)
|
104
|
+
secret = session.delete(:request_secret)
|
105
|
+
@request_token = ::OAuth::RequestToken.new(consumer, token, secret)
|
106
|
+
end
|
107
|
+
|
108
|
+
def missing_stored_token?
|
109
|
+
!request_token
|
110
|
+
end
|
111
|
+
|
112
|
+
def stored_token_match_recieved_token?
|
113
|
+
request_token.token == params['oauth_token']
|
114
|
+
end
|
115
|
+
|
116
|
+
def service_param_name
|
117
|
+
'%s_oauth' % config.provider_name
|
118
|
+
end
|
119
|
+
|
120
|
+
def config
|
121
|
+
self.class::CONFIG
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Warden
|
2
|
+
module OAuth
|
3
|
+
|
4
|
+
#
|
5
|
+
# Handles the creation an registration of OAuth strategies based on configuration parameters
|
6
|
+
# via the Warden::Manager.oauth method
|
7
|
+
#
|
8
|
+
module StrategyBuilder
|
9
|
+
extend self
|
10
|
+
|
11
|
+
#
|
12
|
+
# Manages the creation and registration of the OAuth strategy specified
|
13
|
+
# on the keyword
|
14
|
+
#
|
15
|
+
# @param [Symbol] name of the oauth service
|
16
|
+
# @param [Walruz::Config] configuration specified on the declaration of the oauth service
|
17
|
+
#
|
18
|
+
def build(keyword, config)
|
19
|
+
strategy_class = self.create_oauth_strategy_class(keyword)
|
20
|
+
self.register_oauth_strategy_class(keyword, strategy_class)
|
21
|
+
self.set_oauth_service_info(strategy_class, config)
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Creates the OAuth Strategy class from the keyword specified on the declaration of the
|
26
|
+
# oauth service. This class will be namespaced inside Warden::OAuth::Strategy
|
27
|
+
#
|
28
|
+
# @param [Symbol] name of the OAuth service
|
29
|
+
# @return [Class] The class representing the Warden strategy
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
#
|
33
|
+
# self.create_oauth_strategy_class(:twitter) #=> Warden::OAuth::Strategy::Twitter
|
34
|
+
# # will create a class Warden::OAuth::Strategy::Twitter that extends from
|
35
|
+
# # Warden::OAuth::Strategy
|
36
|
+
#
|
37
|
+
def create_oauth_strategy_class(keyword)
|
38
|
+
class_name = Warden::OAuth::Utils.camelize(keyword.to_s)
|
39
|
+
if self.const_defined?(class_name)
|
40
|
+
self.const_get(class_name)
|
41
|
+
else
|
42
|
+
self.const_set(class_name, Class.new(self))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Registers the generated OAuth Strategy in the Warden::Strategies collection, the label
|
48
|
+
# of the strategy will be the given oauth service name plus an '_oauth' postfix
|
49
|
+
#
|
50
|
+
# @param [Symbol] name of the OAuth service
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# manager.oauth(:twitter) { |twitter| ... } # will register a strategy :twitter_oauth
|
54
|
+
#
|
55
|
+
def register_oauth_strategy_class(keyword, strategy_class)
|
56
|
+
keyword_name = "%s_oauth" % keyword.to_s
|
57
|
+
if Warden::Strategies[keyword_name.to_sym].nil?
|
58
|
+
Warden::Strategies.add(keyword_name.to_sym, strategy_class)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Defines a CONFIG constant in the generated class that will hold the configuration information
|
64
|
+
# (consumer_key, consumer_secret and options) of the oauth service.
|
65
|
+
#
|
66
|
+
# @param [Class] strategy class that will hold the configuration info
|
67
|
+
# @param [Warden::OAuth::Config] configuration info of the oauth service
|
68
|
+
#
|
69
|
+
def set_oauth_service_info(strategy_class, config)
|
70
|
+
strategy_class.const_set("CONFIG", config) unless strategy_class.const_defined?("CONFIG")
|
71
|
+
end
|
72
|
+
|
73
|
+
protected :create_oauth_strategy_class,
|
74
|
+
:register_oauth_strategy_class,
|
75
|
+
:set_oauth_service_info
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Warden
|
2
|
+
module OAuth
|
3
|
+
|
4
|
+
#
|
5
|
+
# Contains methods from Rails to avoid unnecessary dependencies
|
6
|
+
#
|
7
|
+
module Utils
|
8
|
+
|
9
|
+
#
|
10
|
+
# Fetched from ActiveSupport::Inflector.camelize to avoid dependencies
|
11
|
+
#
|
12
|
+
def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
|
13
|
+
if first_letter_in_uppercase
|
14
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
15
|
+
else
|
16
|
+
lower_case_and_underscored_word.first.downcase + camelize(lower_case_and_underscored_word)[1..-1]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Fetched from ActionController::Request to avoid dependencies
|
22
|
+
#
|
23
|
+
def host_with_port(request)
|
24
|
+
url = request.scheme + "://"
|
25
|
+
url << request.host
|
26
|
+
|
27
|
+
if request.scheme == "https" && request.port != 443 ||
|
28
|
+
request.scheme == "http" && request.port != 80
|
29
|
+
url << ":#{request.port}"
|
30
|
+
end
|
31
|
+
|
32
|
+
url
|
33
|
+
end
|
34
|
+
|
35
|
+
module_function :camelize, :host_with_port
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'rubygems'
|
4
|
+
require 'warden_oauth'
|
5
|
+
require File.dirname(__FILE__) + "/application_scenario"
|
6
|
+
|
7
|
+
Warden::Manager.access_token_user_finder do |access_token|
|
8
|
+
nil
|
9
|
+
end
|
10
|
+
|
11
|
+
Rack::Handler::Mongrel.run $app, :Port => '4567'
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class ClientApp
|
2
|
+
|
3
|
+
def self.call(env)
|
4
|
+
env['warden'].authenticate!
|
5
|
+
[200, {"Content-Type" => 'text/plain'}, "Welcome"]
|
6
|
+
end
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
class ErrorApp
|
11
|
+
|
12
|
+
def self.call(env)
|
13
|
+
if env['warden.options'][:oauth].nil?
|
14
|
+
[401, {'Content-Type' => 'text/plain'}, "You are not authenticated"]
|
15
|
+
else
|
16
|
+
access_token = env['warden.options'][:oauth][:access_token]
|
17
|
+
[401, {'Content-Type' => 'text/plain'}, "No user with the given access token"]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
$app = Rack::Builder.new do
|
24
|
+
use Rack::Session::Cookie
|
25
|
+
use Warden::Manager do |manager|
|
26
|
+
manager.oauth(:example) do |example|
|
27
|
+
example.consumer_key "aCOTnTeKniyifcwwF3Mo"
|
28
|
+
example.consumer_secret "dEu91qxWfO0Z4Be1tHGuZ63FzHoUm9mk4Z8rzKa8"
|
29
|
+
example.options :site => 'http://localhost:3000'
|
30
|
+
end
|
31
|
+
manager.default_strategies :example_oauth
|
32
|
+
manager.failure_app = ErrorApp
|
33
|
+
end
|
34
|
+
run ClientApp
|
35
|
+
end if $app.nil?
|
@@ -0,0 +1 @@
|
|
1
|
+
oauth_token=SylltB94pocC6hex8kr9&oauth_verifier=omPxEkKnnx9ygnu7dd6f
|
@@ -0,0 +1 @@
|
|
1
|
+
oauth_token=SylltB94pocC6hex8kr9&oauth_token_secret=WkU0NDgdOlKeB7arFWJyAQjlaVZEEcH0lhdy1kFs&oauth_callback_confirmed=true
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'rubygems'
|
4
|
+
require 'warden_oauth'
|
5
|
+
require 'spec'
|
6
|
+
require 'fakeweb'
|
7
|
+
require 'spec/autorun'
|
8
|
+
|
9
|
+
require File.dirname(__FILE__) + "/application_scenario"
|
10
|
+
|
11
|
+
Spec::Runner.configure do |config|
|
12
|
+
config.before(:all) do
|
13
|
+
FakeWeb.allow_net_connect = false
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe Warden::Manager do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
failure_app = lambda { |env| "Failure" }
|
7
|
+
@manager = Warden::Manager.new(nil, :failure_app => failure_app)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should respond to an `oauth` message" do
|
11
|
+
@manager.should respond_to(:oauth)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#oauth" do
|
15
|
+
|
16
|
+
describe "when initialize" do
|
17
|
+
|
18
|
+
it "should require setting the consumer_key" do
|
19
|
+
lambda do
|
20
|
+
@manager.oauth(:service) do |service|
|
21
|
+
service.consumer_secret "ABC"
|
22
|
+
end
|
23
|
+
end.should raise_error(Warden::OAuth::ConfigError, "You need to specify the consumer key and the consumer secret")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should require setting the consumer_secret" do
|
27
|
+
lambda do
|
28
|
+
@manager.oauth(:service) do |service|
|
29
|
+
service.consumer_key "ABC"
|
30
|
+
end
|
31
|
+
end.should raise_error(Warden::OAuth::ConfigError, "You need to specify the consumer key and the consumer secret")
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should create a new instance of strategy" do
|
35
|
+
@manager.oauth(:service) do |service|
|
36
|
+
service.consumer_key "ABC"
|
37
|
+
service.consumer_secret "123"
|
38
|
+
end
|
39
|
+
lambda do
|
40
|
+
Warden::OAuth::Strategy::Service
|
41
|
+
end.should_not raise_error(NameError)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
require "rack/test"
|
3
|
+
|
4
|
+
describe Warden::OAuth::Strategy do
|
5
|
+
|
6
|
+
def fixture_response(name)
|
7
|
+
filename = File.dirname(__FILE__) + "/../fixtures/%s.txt" % name
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '.build' do
|
11
|
+
before(:each) do
|
12
|
+
@config = Warden::OAuth::Config.new
|
13
|
+
@config.consumer_key "ABC"
|
14
|
+
@config.consumer_secret "123"
|
15
|
+
@config.options :site => 'http://service.com'
|
16
|
+
Warden::OAuth::Strategy.send(:remove_const, "Service") if Warden::OAuth::Strategy.const_defined?("Service")
|
17
|
+
Warden::Strategies.clear!
|
18
|
+
Warden::OAuth::Strategy.build(:service, @config)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should create a new instance that extends from Warden::OAuth::Strategy" do
|
22
|
+
Warden::OAuth::Strategy.const_defined?("Service").should be_true
|
23
|
+
(Warden::OAuth::Strategy::Service < Warden::OAuth::Strategy).should be_true
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should register the oauth service key on the Warden strategies with `_oauth` appended" do
|
27
|
+
Warden::Strategies[:service_oauth].should_not be_nil
|
28
|
+
Warden::OAuth::Strategy::Service.should_not be_nil
|
29
|
+
Warden::Strategies[:service_oauth].should == Warden::OAuth::Strategy::Service
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should assign the oauth_service config as a constant" do
|
33
|
+
Warden::OAuth::Strategy::Service::CONFIG.should_not be_nil
|
34
|
+
Warden::OAuth::Strategy::Service::CONFIG.should == @config
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "when invoking the strategy" do
|
40
|
+
|
41
|
+
before(:each) do
|
42
|
+
@request = Rack::MockRequest.new($app)
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "without warden_oauth_service nor oauth_token parameter" do
|
46
|
+
|
47
|
+
before(:each) do
|
48
|
+
@response = @request.get("/")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should render the failure app response" do
|
52
|
+
@response.body.should == "You are not authenticated"
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "with a warden_oauth_provider parameter" do
|
58
|
+
|
59
|
+
before(:each) do
|
60
|
+
FakeWeb.register_uri(:post, 'http://localhost:3000/oauth/request_token',
|
61
|
+
:body => fixture_response("unauthorized_request_token"))
|
62
|
+
@response = @request.get("/", :input => 'warden_oauth_provider=example')
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should redirect to the authorize url" do
|
66
|
+
@response.headers['Location'].should =~ %r"http://localhost:3000/oauth/authorize"
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "when receiving a valid oauth response" do
|
72
|
+
include Rack::Test::Methods
|
73
|
+
|
74
|
+
def app
|
75
|
+
$app
|
76
|
+
end
|
77
|
+
|
78
|
+
before(:each) do
|
79
|
+
Warden::Manager.access_token_user_finder(:example) do |access_token|
|
80
|
+
Object.new if access_token.token == 'ABC' && access_token.secret == '123'
|
81
|
+
end
|
82
|
+
FakeWeb.register_uri(:post, 'http://localhost:3000/oauth/request_token',
|
83
|
+
:body => fixture_response("unauthorized_request_token"))
|
84
|
+
get "/", 'warden_oauth_provider' => 'example'
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "and the user is not found" do
|
88
|
+
|
89
|
+
before(:each) do
|
90
|
+
FakeWeb.register_uri(:post, 'http://localhost:3000/oauth/access_token',
|
91
|
+
:body => 'oauth_token=ABD&oauth_token_secret=122')
|
92
|
+
get "/", 'oauth_token' => "SylltB94pocC6hex8kr9",
|
93
|
+
'oauth_verifier' => "omPxEkKnnx9ygnu7dd6f"
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should invoke the fail app" do
|
97
|
+
last_response.body.should == "No user with the given access token"
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "and the user is found" do
|
103
|
+
|
104
|
+
before(:each) do
|
105
|
+
FakeWeb.register_uri(:post, 'http://localhost:3000/oauth/access_token',
|
106
|
+
:body => 'oauth_token=ABC&oauth_token_secret=123')
|
107
|
+
get "/", 'oauth_token' => "SylltB94pocC6hex8kr9",
|
108
|
+
'oauth_verifier' => "omPxEkKnnx9ygnu7dd6f"
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should go to the desired app" do
|
112
|
+
last_response.body.should == "Welcome"
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
metadata
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: roman-warden_oauth
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Roman Gonzalez
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-09-16 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: warden
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: oauth
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rack-test
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: fakeweb
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
type: :development
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: yard
|
67
|
+
type: :development
|
68
|
+
version_requirement:
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
version:
|
75
|
+
description: warden_oauth will help you create oauth authentication strategies using the oauth helper method on the Warden::Manager config setup
|
76
|
+
email: romanandreg@gmail.com
|
77
|
+
executables: []
|
78
|
+
|
79
|
+
extensions: []
|
80
|
+
|
81
|
+
extra_rdoc_files:
|
82
|
+
- LICENSE
|
83
|
+
- README.rdoc
|
84
|
+
files:
|
85
|
+
- .document
|
86
|
+
- .gitignore
|
87
|
+
- CHANGELOG
|
88
|
+
- LICENSE
|
89
|
+
- README.rdoc
|
90
|
+
- Rakefile
|
91
|
+
- VERSION
|
92
|
+
- lib/warden_oauth.rb
|
93
|
+
- lib/warden_oauth/config.rb
|
94
|
+
- lib/warden_oauth/errors.rb
|
95
|
+
- lib/warden_oauth/manager.rb
|
96
|
+
- lib/warden_oauth/strategy.rb
|
97
|
+
- lib/warden_oauth/strategy_builder.rb
|
98
|
+
- lib/warden_oauth/utils.rb
|
99
|
+
- spec/application_runner.rb
|
100
|
+
- spec/application_scenario.rb
|
101
|
+
- spec/fixtures/authorize_request_token.txt
|
102
|
+
- spec/fixtures/unauthorized_request_token.txt
|
103
|
+
- spec/spec_helper.rb
|
104
|
+
- spec/warden_oauth/manager_spec.rb
|
105
|
+
- spec/warden_oauth/strategy_spec.rb
|
106
|
+
has_rdoc: false
|
107
|
+
homepage: http://github.com/roman/warden_oauth
|
108
|
+
licenses:
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options:
|
111
|
+
- --charset=UTF-8
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: "0"
|
119
|
+
version:
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: "0"
|
125
|
+
version:
|
126
|
+
requirements: []
|
127
|
+
|
128
|
+
rubyforge_project: warden_oauth
|
129
|
+
rubygems_version: 1.3.5
|
130
|
+
signing_key:
|
131
|
+
specification_version: 3
|
132
|
+
summary: OAuth Strategy generator for Warden Authentication Framework
|
133
|
+
test_files:
|
134
|
+
- spec/application_runner.rb
|
135
|
+
- spec/application_scenario.rb
|
136
|
+
- spec/spec_helper.rb
|
137
|
+
- spec/warden_oauth/manager_spec.rb
|
138
|
+
- spec/warden_oauth/strategy_spec.rb
|