Skip to content

Commit

Permalink
Added last_request_at_threshold cconfig option
Browse files Browse the repository at this point in the history
  • Loading branch information
binarylogic committed Nov 10, 2008
1 parent 93a4787 commit 1d38644
Show file tree
Hide file tree
Showing 14 changed files with 187 additions and 20 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
* The session now gets its configuration from the model, since determining which fields are present is ORM specific.
* Extracted session and cookie logic into their own modules.
* Moved crypto providers into their own module and added a Sha1 provider to help with the restful_authentication transition.
* Allow the unique_token method to use the alternate crypto_provider if it is a hash algorithm, otherwise default to Sha512
* Allow the unique_token method to use the alternate crypto_provider if it is a hash algorithm, otherwise default to Sha512.
* Added last_request_at_threshold configuration option.

== 1.0.0 released 2008-11-05

Expand Down
11 changes: 6 additions & 5 deletions README.rdoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
= Authlogic

Authlogic is a clean and simple ruby authentication solution. Put simply, its the Chuck Norris of authentication solutions for rails, merb, etc.
Authlogic is a clean, simple, and unobtrusive ruby authentication solution. Put simply, its the Chuck Norris of authentication solutions for rails, merb, etc.

The last thing we need is another authentication solution, right? That's what I thought until I tried out some of the current solutions in both rails and merb. None of them felt right. They were either too complicated, bloated, littered my application with tons of code, or were just confusing. This is not the simple / elegant ruby we all fell in love with. We need a "ruby like" authentication solution. Authlogic is my attempt to satisfy that need...

Expand Down Expand Up @@ -75,6 +75,7 @@ Install the gem / plugin (recommended)

$ sudo gem install authlogic


# config/environment.rb
config.gem :authlogic

Expand All @@ -89,14 +90,14 @@ Lets assume you are setting up a session for your User model.
Create your user_session.rb file:

# app/models/user_session.rb
class UserSession < Authgasm::Session::Base
class UserSession < Authlogic::Session::Base
# configuration here, just like ActiveRecord, or in an initializer
# See Authgasm::Session::Config::ClassMethods for more details
# See Authlogic::Session::Config::ClassMethods for more details
end

=== Ensure proper database fields

The user model needs to have the following columns. The names of these columns can be changed with configuration. Better yet, Authgasm tries to guess these names by checking for the existence of common names. See Authgasm::Session::Config::ClassMethods for more details, but chances are you won't have to specify any configuration for your field names, even if they aren't the same names as below.
The user model needs to have the following columns. The names of these columns can be changed with configuration. Better yet, Authlogic tries to guess these names by checking for the existence of common names. See Authlogic::Session::Config::ClassMethods for more details, but chances are you won't have to specify any configuration for your field names, even if they aren't the same names as below.

t.string :login, :null => false
t.string :crypted_password, :null => false
Expand All @@ -109,7 +110,7 @@ The user model needs to have the following columns. The names of these columns c
Make sure you have a model that you will be authenticating with. For this example let's say you have a User model:

class User < ActiveRecord::Base
acts_as_authentic # for options see documentation: Authgasm::ActsAsAuthentic::ClassMethods
acts_as_authentic # for options see documentation: Authlogic::ActsAsAuthentic::ClassMethods
end

Done! Now go use it just like you would with any other ActiveRecord model. Either glance at the code at the beginning of this readme or check out the tutorial (see above in "helpful links") for a more detailed walk through.
Expand Down
2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Echoe.new 'authlogic' do |p|
p.author = "Ben Johnson of Binary Logic"
p.email = 'bjohnson@binarylogic.com'
p.project = 'authlogic'
p.summary = "A clean and simple ruby authentication solution."
p.summary = "A clean, simple, and unobtrusive ruby authentication solution."
p.url = "http://github.com/binarylogic/authlogic"
p.dependencies = %w(activesupport activerecord)
p.include_rakefile = true
Expand Down
4 changes: 4 additions & 0 deletions lib/authlogic/controller_adapters/abstract_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ def cookies
controller.cookies
end

def params
controller.params
end

def request
controller.request
end
Expand Down
15 changes: 9 additions & 6 deletions lib/authlogic/session/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def find_record
if send("valid_#{find_method}?")
self.new_session = false

if record.class.column_names.include?("last_request_at")
if record.class.column_names.include?("last_request_at") && (record.last_request_at.blank? || last_request_at_threshold.ago >= record.last_request_at)
record.last_request_at = Time.now
record.save_without_session_maintenance(false)
end
Expand Down Expand Up @@ -295,6 +295,7 @@ def valid?
errors.clear
if valid_credentials?
validate
valid_record?
return true if errors.empty?
end

Expand Down Expand Up @@ -407,15 +408,17 @@ def valid_credentials?
return false
end

self.record = unchecked_record
true
end

def valid_record?
[:active, :approved, :confirmed].each do |required_status|
if unchecked_record.respond_to?("#{required_status}?") && !unchecked_record.send("#{required_status}?")
errors.add_to_base("Your account has not been marked as #{required_status}")
if record.respond_to?("#{required_status}?") && !record.send("#{required_status}?")
errors.add_to_base("Your account has not been marked as #{required_status}")
return false
end
end

self.record = unchecked_record
true
end
end
end
Expand Down
72 changes: 69 additions & 3 deletions lib/authlogic/session/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,20 @@ def find_by_login_method(value = nil)
end
alias_method :find_by_login_method=, :find_by_login_method

# Once the user confirms their openid Authlogic tries to find the record with that openod. This is the method it called on the record's
# class to find the record by the openid.
#
# * <tt>Default:</tt> "find_by_#{openid_field}"
# * <tt>Accepts:</tt> Symbol or String
def find_by_openid_method(value = nil)
if value.nil?
read_inheritable_attribute(:find_by_openid_method) || find_by_openid_method("find_by_#{openid_field}")
else
write_inheritable_attribute(:find_by_openid_method, value)
end
end
alias_method :find_by_openid_method=, :find_by_openid_method

# Calling UserSession.find tries to find the user session by session, then cookie, then basic http auth. This option allows you to change the order or remove any of these.
#
# * <tt>Default:</tt> [:session, :cookie, :http_auth]
Expand All @@ -103,9 +117,25 @@ def find_with(*values)
end
alias_method :find_with=, :find_with

# The name of the method you want Authlogic to create for storing the login / username. Keep in mind this is just for your Authlogic::Session, if you want it can be something completely different
# than the field in your model. So if you wanted people to login with a field called "login" and then find users by email this is compeltely doable. See the find_by_login_method configuration option for
# more details.
# Every time a session is found the last_request_at field for that record is updatd with the current time, if that field exists. If you want to limit how frequent that field is updated specify the threshold
# here. For example, if your user is making a request every 5 seconds, and you feel this is too frequent, and feel a minute is a good threashold. Set this to 1.minute. Once a minute has passed in between
# requests the field will be updated.
#
# * <tt>Default:</tt> 0
# * <tt>Accepts:</tt> integer representing time in seconds
def last_request_at_threshold(value = nil)
if value.nil?
read_inheritable_attribute(:last_request_at_threshold) || last_request_at_threshold(0)
else
write_inheritable_attribute(:last_request_at_threshold, value)
end
end
alias_method :last_request_at_threshold=, :last_request_at_threshold

# The name of the method you want Authlogic to create for storing the login / username. Keep in mind this is just for your
# Authlogic::Session, if you want it can be something completely different than the field in your model. So if you wanted people to
# login with a field called "login" and then find users by email this is compeltely doable. See the find_by_login_method configuration
# option for more details.
#
# * <tt>Default:</tt> Guesses based on the model columns, tries login, username, and email. If none are present it defaults to login
# * <tt>Accepts:</tt> Symbol or String
Expand All @@ -118,6 +148,38 @@ def login_field(value = nil)
end
alias_method :login_field=, :login_field

# The name of the method you want Authlogic to create for storing the openid url. Keep in mind this is just for your Authlogic::Session,
# if you want it can be something completely different than the field in your model. So if you wanted people to login with a field called
# "openid_url" and then find users by openid this is compeltely doable. See the find_by_openid_method configuration option for
# more details.
#
# * <tt>Default:</tt> Guesses based on the model columns, tries openid, openid_url, identity_url.
# * <tt>Accepts:</tt> Symbol or String
def openid_field(value = nil)
if value.nil?
read_inheritable_attribute(:openid_field) || openid_field((klass.column_names.include?("openid") && :openid) || (klass.column_names.include?("openid_url") && :openid_url) || (klass.column_names.include?("identity_url") && :identity_url))
else
write_inheritable_attribute(:openid_field, value)
end
end
alias_method :openid_field=, :openid_field

# The name of the method you want Authlogic to create for storing the openid url. Keep in mind this is just for your Authlogic::Session,
# if you want it can be something completely different than the field in your model. So if you wanted people to login with a field called
# "openid_url" and then find users by openid this is compeltely doable. See the find_by_openid_method configuration option for
# more details.
#
# * <tt>Default:</tt> Guesses based on the model columns, tries openid, openid_url, identity_url.
# * <tt>Accepts:</tt> Symbol or String
def openid_file_store_path(value = nil)
if value.nil?
read_inheritable_attribute(:openid_file_store_path) || openid_file_store_path((defined?(RAILS_ROOT) && RAILS_ROOT + "/tmp/openids") || (defined?(Merb) && Merb.root + "/tmp/openids"))
else
write_inheritable_attribute(:openid_file_store_path, value)
end
end
alias_method :openid_file_store_path=, :openid_file_store_path

# Works exactly like login_field, but for the password instead.
#
# * <tt>Default:</tt> Guesses based on the model columns, tries password and pass. If none are present it defaults to password
Expand Down Expand Up @@ -212,6 +274,10 @@ def find_by_login_method
def find_with
self.class.find_with
end

def last_request_at_threshold
self.class.last_request_at_threshold
end

def login_field
self.class.login_field
Expand Down
73 changes: 73 additions & 0 deletions lib/authlogic/session/openid.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
module Authlogic
module Session
module OpenID
def self.included(klass)
klass.alias_method_chain :create_configurable_methods!, :openid
klass.before_validation :valid_openid?
klass.attr_accessor :openid_response
end

# Returns true if logging in with openid. Credentials mean username and password.
def logging_in_with_openid?
login_with == :openid
end

def valid_openid?
if controller.params[:openid_complete].blank?
if send(openid_field).blank?
errors.add(openid_field, "can not be blank")
return false
end

begin
self.openid_response = openid_consumer.begin(send(openid_field))
rescue OpenID::OpenIDError => e
errors.add("The OpenID identifier #{send(openid_field)} could not be found: #{e}")
return false
end

sregreq = OpenID::SReg::Request.new
# required fields
#sregreq.request_fields(['email','nickname'], true)
# optional fields
#sregreq.request_fields(['dob', 'fullname'], false)
oidreq.add_extension(sregreq)
oidreq.return_to_args["openid_complete"] = 1
else
case openid_response.status
when OpenID::Consumer::SUCCESS

when OpenID::Consumer::CANCEL
errors.add_to_base("OpenID authentication was cancelled.")
when OpenID::Consumer::FAILURE
errors.add_to_base("OpenID authentication failed.")
when OpenID::Consumer::SETUP_NEEDED
errors.add_to_Base("OpenID authentication needs setup.")
end
end
end

private
def create_configurable_methods_with_openid!
create_configurable_methods_without_openid!

return if respond_to?(openid_field)

if openid_field
self.class.class_eval <<-"end_eval", __FILE__, __LINE__
attr_reader :#{openid_field}
def #{openid_field}=(value)
self.login_with = :openid
@#{openid_field} = value
end
end_eval
end
end

def openid_consumer
@openid_consumer ||= OpenID::Consumer.new(controller.session, OpenID::FilesystemStore.new(openid_file_store_path))
end
end
end
end
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
19 changes: 19 additions & 0 deletions test/session_tests/config_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,25 @@ def test_find_with

assert UserSession.find
end

def test_last_request_at_threshold
ben = users(:ben)
set_session_for(ben)
UserSession.last_request_at_threshold = 2.seconds
assert_equal 2.seconds, UserSession.last_request_at_threshold

assert UserSession.find
last_request_at = ben.reload.last_request_at
sleep(1)
assert UserSession.find
assert_equal last_request_at, ben.reload.last_request_at
sleep(1)
assert UserSession.find
assert_not_equal last_request_at, ben.reload.last_request_at

UserSession.last_request_at_threshold 0
assert_equal 0, UserSession.last_request_at_threshold
end

def test_login_field
UserSession.login_field = :saweet
Expand Down
8 changes: 4 additions & 4 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
require "active_record"
require 'active_record/fixtures'
require File.dirname(__FILE__) + '/../lib/authlogic' unless defined?(Authlogic)
require File.dirname(__FILE__) + '/../test_libs/aes128_crypto_provider'
require File.dirname(__FILE__) + '/../test_libs/mock_request'
require File.dirname(__FILE__) + '/../test_libs/mock_cookie_jar'
require File.dirname(__FILE__) + '/../test_libs/mock_controller'
require File.dirname(__FILE__) + '/libs/aes128_crypto_provider'
require File.dirname(__FILE__) + '/libs/mock_request'
require File.dirname(__FILE__) + '/libs/mock_cookie_jar'
require File.dirname(__FILE__) + '/libs/mock_controller'

ActiveRecord::Schema.verbose = false
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
Expand Down

0 comments on commit 1d38644

Please sign in to comment.