Skip to content
Browse files

Implementing all the bases for version 0.0.1

  • Loading branch information...
1 parent 4412c4a commit 3f628527fc9c39608213e7e3f4de1c04ea4ed1fd @roman committed Sep 2, 2009
View
18 lib/warden_oauth.rb
@@ -0,0 +1,18 @@
+require "rack"
+require "warden"
+require "oauth"
+
+module Warden
+ module OAuth
+
+ base_path = File.dirname(__FILE__) + "/warden_oauth"
+
+ require base_path + "/errors"
+ autoload :Utils, base_path + '/utils'
+ autoload :Strategy, base_path + '/strategy'
+ autoload :Config, base_path + "/config"
+ require base_path + "/manager"
+
+
+ end
+end
View
28 lib/warden_oauth/config.rb
@@ -0,0 +1,28 @@
+module Warden
+ module OAuth
+
+ class Config
+ attr_accessor :provider_name
+
+ def consumer_key(key = nil)
+ @consumer_key ||= key
+ end
+
+ def consumer_secret(secret = nil)
+ @consumer_secret ||= secret
+ end
+
+ def options(options = nil)
+ @options ||= options
+ end
+
+ def check_requirements
+ if @consumer_key.nil? || @consumer_secret.nil?
+ raise Warden::OAuth::ConfigError.new("You need to specify the consumer key and the consumer secret")
+ end
+ end
+
+ end
+
+ end
+end
View
9 lib/warden_oauth/errors.rb
@@ -0,0 +1,9 @@
+module Warden
+ module OAuth
+
+ class ConfigError < ArgumentError; end
+ class ServiceAlreadyRegistered < Exception; end
+ class AccessTokenFinderMissing < Exception; end
+
+ end
+end
View
40 lib/warden_oauth/manager.rb
@@ -0,0 +1,40 @@
+module Warden
+ module OAuth
+
+ module Manager
+
+ def self.included(base)
+ base.extend(ClassMethods)
+ end
+
+ def oauth(service)
+ #@config[:oauth_services] ||= {}
+ config = Warden::OAuth::Config.new
+ yield config
+ config.check_requirements
+ config.provider_name = service
+ Warden::OAuth::Strategy.build(service, config)
+ #@config[:oauth_services][service] = config
+ end
+
+ module ClassMethods
+
+ def access_token_user_finder(&block)
+ raise Warden::OAuth::AccessTokenFinderMissing.new("You need to specify a block for Warden::Manager.acess_token_user_finder") unless block_given?
+ 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
+ @find_user_by_access_token = block
+ end
+
+ def find_user_by_access_token(access_token)
+ 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?
+ @find_user_by_access_token.call(access_token)
+ end
+
+ end
+
+ end
+
+ end
+end
+
+Warden::Manager.send(:include, Warden::OAuth::Manager)
View
127 lib/warden_oauth/strategy.rb
@@ -0,0 +1,127 @@
+module Warden
+ module OAuth
+
+ class Strategy < Warden::Strategies::Base
+
+ ###################################
+ ### Setup and Subclass Creation ###
+ ###################################
+
+ def self.build(keyword, config)
+ strategy_class = self.create_oauth_strategy_class(keyword)
+ self.register_oauth_strategy_class(keyword, strategy_class)
+ self.set_oauth_service_info(strategy_class, config)
+ end
+
+ def self.create_oauth_strategy_class(keyword)
+ class_name = Warden::OAuth::Utils.camelize(keyword.to_s)
+ if self.const_defined?(class_name)
+ self.const_get(class_name)
+ else
+ self.const_set(class_name, Class.new(self))
+ end
+ end
+
+ def self.register_oauth_strategy_class(keyword, strategy_class)
+ keyword_name = "%s_oauth" % keyword.to_s
+ if Warden::Strategies[keyword_name.to_sym].nil?
+ Warden::Strategies.add(keyword_name.to_sym, strategy_class)
+ end
+ end
+
+ def self.set_oauth_service_info(strategy_class, config)
+ strategy_class.const_set("CONFIG", config) unless strategy_class.const_defined?("CONFIG")
+ end
+
+ class << self
+ protected :create_oauth_strategy_class,
+ :register_oauth_strategy_class,
+ :set_oauth_service_info
+ end
+
+ ######################
+ ### Strategy Logic ###
+ ######################
+
+ def valid?
+ (params.include?('warden_oauth_provider') && params['warden_oauth_provider'] == config.provider_name.to_s) ||
+ params.include?('oauth_token')
+ end
+
+ def authenticate!
+ if params.include?('warden_oauth_provider')
+ store_request_token_on_session
+ redirect!(request_token.authorize_url)
+
+ elsif params.include?('oauth_token')
+ load_request_token_from_session
+ if missing_stored_token?
+ fail!("There is no OAuth authentication in progress")
+ elsif !stored_token_match_recieved_token?
+ fail!("Received OAuth token didn't match stored OAuth token")
+ else
+ user = Warden::Manager.find_user_by_access_token(access_token)
+ if user.nil?
+ fail!("User with access token not found")
+ throw(:warden, :oauth => { :access_token => access_token })
+ else
+ success!(user)
+ end
+ end
+ end
+
+ end
+
+ def fail!(msg)
+ self.errors.add(service_param_name.to_sym, msg)
+ super
+ end
+
+ ###################
+ ### OAuth Logic ###
+ ###################
+
+ def consumer
+ @consumer ||= ::OAuth::Consumer.new(config.consumer_key, config.consumer_secret, config.options)
+ end
+
+ def request_token
+ host_with_port = Warden::OAuth::Utils.host_with_port(request)
+ @request_token ||= consumer.get_request_token(:oauth_callback => host_with_port)
+ end
+
+ def access_token
+ @access_token ||= request_token.get_access_token(:oauth_verifier => params['oauth_verifier'])
+ end
+
+ def store_request_token_on_session
+ session[:request_token] = request_token.token
+ session[:request_secret] = request_token.secret
+ end
+
+ def load_request_token_from_session
+ token = session.delete(:request_token)
+ secret = session.delete(:request_secret)
+ @request_token = ::OAuth::RequestToken.new(consumer, token, secret)
+ end
+
+ def missing_stored_token?
+ !request_token
+ end
+
+ def stored_token_match_recieved_token?
+ request_token.token == params['oauth_token']
+ end
+
+ def service_param_name
+ '%s_oauth' % config.provider_name
+ end
+
+ def config
+ self.class::CONFIG
+ end
+
+ end
+
+ end
+end
View
32 lib/warden_oauth/utils.rb
@@ -0,0 +1,32 @@
+module Warden
+ module OAuth
+
+ module Utils
+
+ # Fetched from ActiveSupport to avoid dependencies
+ def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
+ if first_letter_in_uppercase
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
+ else
+ lower_case_and_underscored_word.first.downcase + camelize(lower_case_and_underscored_word)[1..-1]
+ end
+ end
+
+ def host_with_port(request)
+ url = request.scheme + "://"
+ url << request.host
+
+ if request.scheme == "https" && request.port != 443 ||
+ request.scheme == "http" && request.port != 80
+ url << ":#{request.port}"
+ end
+
+ url
+ end
+
+ module_function :camelize, :host_with_port
+
+ end
+
+ end
+end
View
11 spec/application_runner.rb
@@ -0,0 +1,11 @@
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
+require 'rubygems'
+require 'warden_oauth'
+require File.dirname(__FILE__) + "/application_scenario"
+
+Warden::Manager.access_token_user_finder do |access_token|
+ nil
+end
+
+Rack::Handler::Mongrel.run $app, :Port => '4567'
View
35 spec/application_scenario.rb
@@ -0,0 +1,35 @@
+class ClientApp
+
+ def self.call(env)
+ env['warden'].authenticate!
+ [200, {"Content-Type" => 'text/plain'}, "Welcome"]
+ end
+
+end
+
+class ErrorApp
+
+ def self.call(env)
+ if env['warden.options'][:oauth].nil?
+ [401, {'Content-Type' => 'text/plain'}, "You are not authenticated"]
+ else
+ access_token = env['warden.options'][:oauth][:access_token]
+ [401, {'Content-Type' => 'text/plain'}, "No user with the given access token"]
+ end
+ end
+
+end
+
+$app = Rack::Builder.new do
+ use Rack::Session::Cookie
+ use Warden::Manager do |manager|
+ manager.oauth(:example) do |example|
+ example.consumer_key "aCOTnTeKniyifcwwF3Mo"
+ example.consumer_secret "dEu91qxWfO0Z4Be1tHGuZ63FzHoUm9mk4Z8rzKa8"
+ example.options :site => 'http://localhost:3000'
+ end
+ manager.default_strategies :example_oauth
+ manager.failure_app = ErrorApp
+ end
+ run ClientApp
+end if $app.nil?
View
1 spec/fixtures/authorize_request_token.txt
@@ -0,0 +1 @@
+oauth_token=SylltB94pocC6hex8kr9&oauth_verifier=omPxEkKnnx9ygnu7dd6f
View
1 spec/fixtures/unauthorized_request_token.txt
@@ -0,0 +1 @@
+oauth_token=SylltB94pocC6hex8kr9&oauth_token_secret=WkU0NDgdOlKeB7arFWJyAQjlaVZEEcH0lhdy1kFs&oauth_callback_confirmed=true
View
8 spec/spec_helper.rb
@@ -1,9 +1,15 @@
$LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
+require 'rubygems'
require 'warden_oauth'
require 'spec'
+require 'fakeweb'
require 'spec/autorun'
+require File.dirname(__FILE__) + "/application_scenario"
+
Spec::Runner.configure do |config|
-
+ config.before(:all) do
+ FakeWeb.allow_net_connect = false
+ end
end
View
48 spec/warden_oauth/manager_spec.rb
@@ -0,0 +1,48 @@
+require File.dirname(__FILE__) + "/../spec_helper"
+
+describe Warden::Manager do
+
+ before(:each) do
+ failure_app = lambda { |env| "Failure" }
+ @manager = Warden::Manager.new(nil, :failure_app => failure_app)
+ end
+
+ it "should respond to an `oauth` message" do
+ @manager.should respond_to(:oauth)
+ end
+
+ describe "#oauth" do
+
+ describe "when initialize" do
+
+ it "should require setting the consumer_key" do
+ lambda do
+ @manager.oauth(:service) do |service|
+ service.consumer_secret "ABC"
+ end
+ end.should raise_error(Warden::OAuth::ConfigError, "You need to specify the consumer key and the consumer secret")
+ end
+
+ it "should require setting the consumer_secret" do
+ lambda do
+ @manager.oauth(:service) do |service|
+ service.consumer_key "ABC"
+ end
+ end.should raise_error(Warden::OAuth::ConfigError, "You need to specify the consumer key and the consumer secret")
+ end
+
+ it "should create a new instance of strategy" do
+ @manager.oauth(:service) do |service|
+ service.consumer_key "ABC"
+ service.consumer_secret "123"
+ end
+ lambda do
+ Warden::OAuth::Strategy::Service
+ end.should_not raise_error(NameError)
+ end
+
+ end
+
+ end
+
+end
View
121 spec/warden_oauth/strategy_spec.rb
@@ -0,0 +1,121 @@
+require File.dirname(__FILE__) + "/../spec_helper"
+require "rack/test"
+
+describe Warden::OAuth::Strategy do
+
+ def fixture_response(name)
+ filename = File.dirname(__FILE__) + "/../fixtures/%s.txt" % name
+ end
+
+ describe '.build' do
+ before(:each) do
+ @config = Warden::OAuth::Config.new
+ @config.consumer_key "ABC"
+ @config.consumer_secret "123"
+ @config.options :site => 'http://service.com'
+ Warden::OAuth::Strategy.send(:remove_const, "Service") if Warden::OAuth::Strategy.const_defined?("Service")
+ Warden::Strategies.clear!
+ Warden::OAuth::Strategy.build(:service, @config)
+ end
+
+ it "should create a new instance that extends from Warden::OAuth::Strategy" do
+ Warden::OAuth::Strategy.const_defined?("Service").should be_true
+ (Warden::OAuth::Strategy::Service < Warden::OAuth::Strategy).should be_true
+ end
+
+ it "should register the oauth service key on the Warden strategies with `_oauth` appended" do
+ Warden::Strategies[:service_oauth].should_not be_nil
+ Warden::OAuth::Strategy::Service.should_not be_nil
+ Warden::Strategies[:service_oauth].should == Warden::OAuth::Strategy::Service
+ end
+
+ it "should assign the oauth_service config as a constant" do
+ Warden::OAuth::Strategy::Service::CONFIG.should_not be_nil
+ Warden::OAuth::Strategy::Service::CONFIG.should == @config
+ end
+
+ end
+
+ describe "when invoking the strategy" do
+
+ before(:each) do
+ @request = Rack::MockRequest.new($app)
+ end
+
+ describe "without warden_oauth_service nor oauth_token parameter" do
+
+ before(:each) do
+ @response = @request.get("/")
+ end
+
+ it "should render the failure app response" do
+ @response.body.should == "You are not authenticated"
+ end
+
+ end
+
+ describe "with a warden_oauth_provider parameter" do
+
+ before(:each) do
+ FakeWeb.register_uri(:post, 'http://localhost:3000/oauth/request_token',
+ :body => fixture_response("unauthorized_request_token"))
+ @response = @request.get("/", :input => 'warden_oauth_provider=example')
+ end
+
+ it "should redirect to the authorize url" do
+ @response.headers['Location'].should =~ %r"http://localhost:3000/oauth/authorize"
+ end
+
+ end
+
+ describe "when receiving a valid oauth response" do
+ include Rack::Test::Methods
+
+ def app
+ $app
+ end
+
+ before(:each) do
+ Warden::Manager.access_token_user_finder do |access_token|
+ Object.new if access_token.token == 'ABC' && access_token.secret == '123'
+ end
+ FakeWeb.register_uri(:post, 'http://localhost:3000/oauth/request_token',
+ :body => fixture_response("unauthorized_request_token"))
+ get "/", 'warden_oauth_provider' => 'example'
+ end
+
+ describe "and the user is not found" do
+
+ before(:each) do
+ FakeWeb.register_uri(:post, 'http://localhost:3000/oauth/access_token',
+ :body => 'oauth_token=ABD&oauth_token_secret=122')
+ get "/", 'oauth_token' => "SylltB94pocC6hex8kr9",
+ 'oauth_verifier' => "omPxEkKnnx9ygnu7dd6f"
+ end
+
+ it "should invoke the fail app" do
+ last_response.body.should == "No user with the given access token"
+ end
+
+ end
+
+ describe "and the user is found" do
+
+ before(:each) do
+ FakeWeb.register_uri(:post, 'http://localhost:3000/oauth/access_token',
+ :body => 'oauth_token=ABC&oauth_token_secret=123')
+ get "/", 'oauth_token' => "SylltB94pocC6hex8kr9",
+ 'oauth_verifier' => "omPxEkKnnx9ygnu7dd6f"
+ end
+
+ it "should go to the desired app" do
+ last_response.body.should == "Welcome"
+ end
+
+ end
+
+ end
+
+ end
+
+end
View
7 spec/warden_oauth_spec.rb
@@ -1,7 +0,0 @@
-require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
-
-describe "WardenOauth" do
- it "fails" do
- fail "hey buddy, you should probably rename this file and start specing for real"
- end
-end

0 comments on commit 3f62852

Please sign in to comment.
Something went wrong with that request. Please try again.