Permalink
Browse files

Initial Commit

  • Loading branch information...
0 parents commit 54c7be766dc07d9af4ce2b91fb3622b97e33430f @jrallison committed May 30, 2009
4 CHANGELOG.rdoc
@@ -0,0 +1,4 @@
+= Authlogic OAuth
+
+Authlogic OAuth
+
20 MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2009 John Allison (johnallison.me)
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0 Manifest.txt
No changes.
13 README.rdoc
@@ -0,0 +1,13 @@
+= Authlogic OAuth
+
+Authlogic OAuth is an extension of the Authlogic library to add OAuth support. One use case for authentication with OAuth is allowing users to log in with their Twitter credentials.
+
+== Helpful links
+
+* <b>Authlogic:</b> http://github.com/binarylogic/authlogic
+* <b>Live example:</b> *coming soon*
+
+== Install and use
+
+Directions coming soon.
+
20 Rakefile
@@ -0,0 +1,20 @@
+ENV['RDOCOPT'] = "-S -f html -T hanna"
+
+require "rubygems"
+require "hoe"
+require File.dirname(__FILE__) << "/lib/authlogic_oauth/version"
+
+Hoe.new("AuthlogicOauth", AuthlogicOauth::Version::STRING) do |p|
+ p.name = "authlogic_oauth"
+ p.author = "John Allison"
+ p.email = 'jrallison@gmail.com'
+ p.summary = "blah."
+ p.description = "blah."
+ p.url = "http://github.com/jrallison/authlogic_oauth"
+ p.history_file = "CHANGELOG.rdoc"
+ p.readme_file = "README.rdoc"
+ p.extra_rdoc_files = ["CHANGELOG.rdoc", "README.rdoc"]
+ p.remote_rdoc_dir = ''
+ p.test_globs = ["test/*/test_*.rb", "test/*_test.rb", "test/*/*_test.rb"]
+ p.extra_deps = %w(activesupport)
+end
1 init.rb
@@ -0,0 +1 @@
+File.dirname(__FILE__) + "/rails/init.rb"
7 lib/authlogic_oauth.rb
@@ -0,0 +1,7 @@
+require File.dirname(__FILE__) + "/authlogic_oauth/version"
+require File.dirname(__FILE__) + "/authlogic_oauth/oauth_process"
+require File.dirname(__FILE__) + "/authlogic_oauth/acts_as_authentic"
+require File.dirname(__FILE__) + "/authlogic_oauth/session"
+
+ActiveRecord::Base.send(:include, AuthlogicOauth::ActsAsAuthentic)
+Authlogic::Session::Base.send(:include, AuthlogicOauth::Session)
116 lib/authlogic_oauth/acts_as_authentic.rb
@@ -0,0 +1,116 @@
+module AuthlogicOauth
+ module ActsAsAuthentic
+ def self.included(klass)
+ klass.class_eval do
+ extend Config
+ add_acts_as_authentic_module(Methods, :prepend)
+ end
+ end
+
+ module Config
+ # The name of the oauth token field in the database.
+ #
+ # * <tt>Default:</tt> :oauth_token
+ # * <tt>Accepts:</tt> Symbol
+ def oauth_token_field(value = nil)
+ rw_config(:oauth_token_field, value, :oauth_token)
+ end
+ alias_method :oauth_token_field=, :oauth_token_field
+
+ # The name of the oauth token secret field in the database.
+ #
+ # * <tt>Default:</tt> :oauth_secret
+ # * <tt>Accepts:</tt> Symbol
+ def oauth_secret_field(value = nil)
+ rw_config(:oauth_secret_field, value, :oauth_secret)
+ end
+ alias_method :oauth_secret_field=, :oauth_secret_field
+ end
+
+ module Methods
+ include OauthProcess
+
+ # Set up some simple validations
+ def self.included(klass)
+ klass.class_eval do
+ alias_method "#{oauth_token_field.to_s}=".to_sym, :oauth_token=
+ alias_method "#{oauth_secret_field.to_s}=".to_sym, :oauth_secret=
+ end
+
+ return if !klass.column_names.include?(klass.oauth_token_field.to_s)
+
+ klass.class_eval do
+ validate :validate_by_oauth, :if => :authenticating_with_oauth?
+
+ validates_uniqueness_of klass.oauth_token_field, :scope => validations_scope, :if => :using_oauth?
+ validates_presence_of klass.oauth_secret_field, :scope => validations_scope, :if => :using_oauth?
+
+ validates_length_of_password_field_options validates_length_of_password_field_options.merge(:if => :validate_password_with_oauth?)
+ validates_confirmation_of_password_field_options validates_confirmation_of_password_field_options.merge(:if => :validate_password_with_oauth?)
+ validates_length_of_password_confirmation_field_options validates_length_of_password_confirmation_field_options.merge(:if => :validate_password_with_oauth?)
+ validates_length_of_login_field_options validates_length_of_login_field_options.merge(:if => :validate_password_with_oauth?)
+ validates_format_of_login_field_options validates_format_of_login_field_options.merge(:if => :validate_password_with_oauth?)
+ end
+
+ # email needs to be optional for oauth
+ klass.validate_email_field = false
+ end
+
+ def save(perform_validation = true, &block)
+ if perform_validation && block_given? && redirecting_to_oauth_server?
+ redirect_to_oauth
+ return false
+ end
+
+ result = super
+ yield(result) if block_given?
+ result
+ end
+
+ # Set the oauth fields
+ def oauth_token=(value)
+ write_attribute(oauth_token_field, value.blank? ? nil : value)
+ end
+
+ def oauth_secret=(value)
+ write_attribute(oauth_secret_field, value.blank? ? nil : value)
+ end
+
+ private
+
+ def authenticating_with_oauth?
+ !session_class.controller.params[:register_with_oauth].blank? || oauth_response
+ end
+
+ def authenticate_with_oauth
+ access_token = generate_access_token
+
+ self.oauth_token = access_token.token
+ self.oauth_secret = access_token.secret
+ end
+
+ def access_token
+ OAuth::AccessToken.new(oauth,
+ read_attribute(oauth_token_field),
+ read_attribute(oauth_secret_field))
+ end
+
+ def using_oauth?
+ respond_to?(oauth_token_field) && !oauth_token.blank?
+ end
+
+ def validate_password_with_oauth?
+ !using_oauth? && require_password?
+ end
+
+ def oauth_token_field
+ self.class.oauth_token_field
+ end
+
+ def oauth_secret_field
+ self.class.oauth_secret_field
+ end
+
+ end
+ end
+end
61 lib/authlogic_oauth/oauth_process.rb
@@ -0,0 +1,61 @@
+module AuthlogicOauth
+ module OauthProcess
+
+ private
+
+ def validate_by_oauth
+ validate_email_field = false
+
+ if oauth_response.blank?
+ redirect_to_oauth
+ else
+ authenticate_with_oauth
+ end
+ end
+
+ def redirecting_to_oauth_server?
+ authenticating_with_oauth? && oauth_response.blank?
+ end
+
+ def redirect_to_oauth
+ request = oauth.get_request_token
+ oauth_controller.session[:oauth_request_token] = request.token
+ oauth_controller.session[:oauth_request_token_secret] = request.secret
+
+ # Send to oauth authorize url and redirect back to the current action
+ oauth_controller.session[:oauth_redirect] = build_callback_url
+ oauth_controller.redirect_to request.authorize_url
+ end
+
+ def build_callback_url
+ { :controller => oauth_controller.controller_name, :action => oauth_controller.action_name }
+ end
+
+ def request_token
+ OAuth::RequestToken.new(oauth,
+ oauth_controller.session[:oauth_request_token],
+ oauth_controller.session[:oauth_request_token_secret])
+ end
+
+ def generate_access_token
+ request_token.get_access_token
+ end
+
+ def oauth_response
+ oauth_controller.params[:oauth_token]
+ end
+
+ def oauth_controller
+ is_auth_session? ? controller : session_class.controller
+ end
+
+ def oauth
+ is_auth_session? ? self.class.oauth_consumer : session_class.oauth_consumer
+ end
+
+ def is_auth_session?
+ self.is_a?(Authlogic::Session::Base)
+ end
+
+ end
+end
73 lib/authlogic_oauth/session.rb
@@ -0,0 +1,73 @@
+module AuthlogicOauth
+ # This module is responsible for adding oauth
+ # to the Authlogic::Session::Base class.
+ module Session
+ def self.included(klass)
+ klass.class_eval do
+ extend Config
+ include Methods
+ end
+ end
+
+ module Config
+ # * <tt>Default:</tt> :find_by_oauth_token
+ # * <tt>Accepts:</tt> Symbol
+ def find_by_oauth_method(value = nil)
+ rw_config(:find_by_oauth_method, value, :find_by_oauth_token)
+ end
+ alias_method :find_by_oauth_method=, :find_by_oauth_method
+ end
+
+ module Methods
+ include OauthProcess
+
+ def self.included(klass)
+ klass.class_eval do
+ validate :validate_by_oauth, :if => :authenticating_with_oauth?
+ end
+ end
+
+ # Hooks into credentials so that you can pass a user who has already has an oauth access token.
+ def credentials=(value)
+ super
+ values = value.is_a?(Array) ? value : [value]
+ hash = values.first.is_a?(Hash) ? values.first.with_indifferent_access : nil
+ self.record = hash[:priority_record] if !hash.nil? && hash.key?(:priority_record)
+ end
+
+ def record=(record)
+ @record = record
+ end
+
+ # Clears out the block if we are authenticating with oauth,
+ # so that we can redirect without a DoubleRender error.
+ def save(&block)
+ block = nil if redirecting_to_oauth_server?
+ super(&block)
+ end
+
+ private
+
+ def authenticating_with_oauth?
+ !controller.params[:login_with_oauth].blank? || oauth_response
+ end
+
+ def authenticate_with_oauth
+ if @record
+ self.attempted_record = record
+ else
+ self.attempted_record = search_for_record(find_by_oauth_method, generate_access_token.token)
+ #errors.add_to_base("Unable to authenticate with Twitter.")
+ end
+
+ if !attempted_record
+ errors.add_to_base("Could not find user in our database, have you registered with your oauth account?")
+ end
+ end
+
+ def find_by_oauth_method
+ self.class.find_by_oauth_method
+ end
+ end
+ end
+end
51 lib/authlogic_oauth/version.rb
@@ -0,0 +1,51 @@
+module AuthlogicOauth
+ # A class for describing the current version of a library. The version
+ # consists of three parts: the +major+ number, the +minor+ number, and the
+ # +tiny+ (or +patch+) number.
+ class Version
+ include Comparable
+
+ # A convenience method for instantiating a new Version instance with the
+ # given +major+, +minor+, and +tiny+ components.
+ def self.[](major, minor, tiny)
+ new(major, minor, tiny)
+ end
+
+ attr_reader :major, :minor, :tiny
+
+ # Create a new Version object with the given components.
+ def initialize(major, minor, tiny)
+ @major, @minor, @tiny = major, minor, tiny
+ end
+
+ # Compare this version to the given +version+ object.
+ def <=>(version)
+ to_i <=> version.to_i
+ end
+
+ # Converts this version object to a string, where each of the three
+ # version components are joined by the '.' character. E.g., 2.0.0.
+ def to_s
+ @to_s ||= [@major, @minor, @tiny].join(".")
+ end
+
+ # Converts this version to a canonical integer that may be compared
+ # against other version objects.
+ def to_i
+ @to_i ||= @major * 1_000_000 + @minor * 1_000 + @tiny
+ end
+
+ def to_a
+ [@major, @minor, @tiny]
+ end
+
+ MAJOR = 1
+ MINOR = 0
+ TINY = 0
+
+ # The current version as a Version instance
+ CURRENT = new(MAJOR, MINOR, TINY)
+ # The current version as a String
+ STRING = CURRENT.to_s
+ end
+end
1 rails/init.rb
@@ -0,0 +1 @@
+require "authlogic_oauth"
100 test/acts_as_authentic_test.rb
@@ -0,0 +1,100 @@
+require File.dirname(__FILE__) + '/test_helper.rb'
+
+class ActsAsAuthenticTest < ActiveSupport::TestCase
+ def test_included
+ assert User.send(:acts_as_authentic_modules).include?(AuthlogicOauth::ActsAsAuthentic::Methods)
+ assert_equal :validate_password_with_oauth?, User.validates_length_of_password_field_options[:if]
+ assert_equal :validate_password_with_oauth?, User.validates_confirmation_of_password_field_options[:if]
+ assert_equal :validate_password_with_oauth?, User.validates_length_of_password_confirmation_field_options[:if]
+ assert_equal :validate_password_with_oauth?, User.validates_length_of_login_field_options[:if]
+ assert_equal :validate_password_with_oauth?, User.validates_format_of_login_field_options[:if]
+ end
+
+ def test_required_fields_on_create
+ user = User.new
+ assert !user.save
+ assert user.errors.on(:login)
+ assert user.errors.on(:password)
+ assert user.errors.on(:password_confirmation)
+ end
+
+ def test_fields_not_required_on_create_if_using_oauth
+ user = User.new
+ user.oauth_token = "SLDKJSD"
+ assert !user.save {} # because we are redirecting, the user was NOT saved
+ #assert redirecting_to_oauth?
+ end
+
+ def test_login_not_required_on_update_if_using_oauth
+ john = users(:john)
+ assert_nil john.login
+ assert john.save
+ end
+
+ def test_password_not_required_on_update_if_using_oauth
+ john = users(:john)
+ assert_nil john.crypted_password
+ assert john.save
+ end
+
+ def test_email_not_required_on_update_if_using_oauth
+ john = users(:john)
+ assert_nil john.email
+ assert john.save
+ end
+
+ def test_fields_required_on_update_if_not_using_oauth
+ john = users(:john)
+ john.oauth_token = nil
+ assert_nil john.login
+ assert_nil john.crypted_password
+ assert_nil john.email
+ assert !john.save
+
+ assert john.errors.on(:login)
+ assert john.errors.on(:password)
+ assert john.errors.on(:password_confirmation)
+ end
+
+ def test_validates_uniqueness_of_oauth_token
+ u = User.new(:oauth_token => "johns_token")
+ assert !u.valid?
+ assert u.errors.on(:oauth_token)
+ end
+
+ def test_blank_oauth_token_gets_set_to_nil
+ u = User.new(:oauth_token => "")
+ assert_nil u.oauth_token
+ end
+
+ def test_blank_oauth_secret_gets_set_to_nil
+ u = User.new(:oauth_secret => "")
+ assert_nil u.oauth_secret
+ end
+
+ def test_updating_without_oauth
+ john = users(:john)
+ john.oauth_token = nil
+ john.login = "john"
+ john.email = "john@example.com"
+ john.password = "test"
+ john.password_confirmation = "test"
+ assert john.save
+ #assert !redirecting_to_yahoo?
+ end
+
+ def test_updating_without_validation
+ john = users(:john)
+ john.oauth_token = "SDSDGSDGSD"
+ assert john.save(false)
+ #assert !redirecting_to_yahoo?
+ end
+
+ def test_updating_without_a_block
+ john = users(:john)
+ john.oauth_token = "SDSDGSDGSD"
+ assert john.save
+ john.reload
+ assert_equal "SDSDGSDGSD", john.oauth_token
+ end
+end
6 test/fixtures/users.yml
@@ -0,0 +1,6 @@
+john:
+ persistence_token: 6cde0674657a8a313ce952df979de2830309aa4c11ca65805dd00bfdc65dbcc2f5e36718660a1d2e68c1a08c276d996763985d2f06fd3d076eb7bc4d97b1e317
+ single_access_token: <%= Authlogic::Random.friendly_token %>
+ perishable_token: <%= Authlogic::Random.friendly_token %>
+ oauth_token: johns_token
+ oauth_secret: johns_token_secret
3 test/lib/user.rb
@@ -0,0 +1,3 @@
+class User < ActiveRecord::Base
+ acts_as_authentic
+end
7 test/lib/user_session.rb
@@ -0,0 +1,7 @@
+class UserSession < Authlogic::Session::Base
+
+ def self.oauth_consumer
+ OAuth::Consumer.new("CONSUMER_TOKEN", "CONSUMER_SECRET", { :site=>"http://example.com" })
+ end
+
+end
27 test/session_test.rb
@@ -0,0 +1,27 @@
+require File.dirname(__FILE__) + '/test_helper.rb'
+
+class SessionTest < ActiveSupport::TestCase
+ def test_authenticate_by_record
+ session = UserSession.new
+ assert session.respond_to?(:record)
+ session.record = users(:john)
+ assert_equal users(:john), session.record
+ end
+
+ def test_validate_by_nil_oauth_token
+ session = UserSession.new
+ assert !session.save
+ #assert !redirecting_to_yahoo?
+ end
+
+ def test_auth_session
+ session = UserSession.new
+ #assert session.is_auth_session?
+ end
+
+ def test_validate_by_oauth
+ session = UserSession.new
+ assert !session.save
+ #assert redirecting_to_yahoo?
+ end
+end
49 test/test_helper.rb
@@ -0,0 +1,49 @@
+require "test/unit"
+require "rubygems"
+require "oauth"
+require "ruby-debug"
+require "active_record"
+require "active_record/fixtures"
+
+ActiveRecord::Schema.verbose = false
+ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
+ActiveRecord::Base.configurations = true
+ActiveRecord::Schema.define(:version => 1) do
+ create_table :users do |t|
+ t.datetime :created_at
+ t.datetime :updated_at
+ t.integer :lock_version, :default => 0
+ t.string :login
+ t.string :crypted_password
+ t.string :password_salt
+ t.string :persistence_token
+ t.string :single_access_token
+ t.string :perishable_token
+ t.string :oauth_token
+ t.string :oauth_secret
+ t.string :email
+ t.integer :login_count, :default => 0, :null => false
+ t.integer :failed_login_count, :default => 0, :null => false
+ t.datetime :last_request_at
+ t.datetime :current_login_at
+ t.datetime :last_login_at
+ t.string :current_login_ip
+ t.string :last_login_ip
+ end
+end
+
+require File.dirname(__FILE__) + '/../../authlogic/lib/authlogic' unless defined?(Authlogic)
+require File.dirname(__FILE__) + '/../../authlogic/lib/authlogic/test_case'
+require File.dirname(__FILE__) + '/../lib/authlogic_oauth' unless defined?(AuthlogicOauth)
+require File.dirname(__FILE__) + '/lib/user'
+require File.dirname(__FILE__) + '/lib/user_session'
+
+class ActiveSupport::TestCase
+ include ActiveRecord::TestFixtures
+ self.fixture_path = File.dirname(__FILE__) + "/fixtures"
+ self.use_transactional_fixtures = false
+ self.use_instantiated_fixtures = false
+ self.pre_loaded_fixtures = false
+ fixtures :all
+ setup :activate_authlogic
+end

0 comments on commit 54c7be7

Please sign in to comment.