Skip to content

Commit

Permalink
Session method definitions now check for already defined methods, all…
Browse files Browse the repository at this point in the history
…owing you to write custom "credential" methods
  • Loading branch information
binarylogic committed Jan 2, 2009
1 parent 74823b5 commit 91f86dd
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rdoc
Expand Up @@ -2,6 +2,7 @@

* Added the disable_perishable_token_maintenance option to disable the automatic resetting of the perishable_token, meaning you will have to maintain this yourself.
* Changed shoulda macro to conform to standards so model is not required to be passed
* Modified method definitions for the Session class to check for already defined methods, allowing you to write your own "credential" methods, and Authlogic will not overwrite your custom methods.

== 1.3.8 released 2008-12-24

Expand Down
10 changes: 5 additions & 5 deletions README.rdoc
Expand Up @@ -2,16 +2,16 @@

Authlogic is a clean, simple, and unobtrusive ruby authentication solution. Put simply, its the Chuck Norris of authentication solutions for your framework of choice.

So what is Authlogic, and why would I create a solution to a problem that already has plenty of solutions? Because none of the current solutions feel right. The feel wrong because they don't properly follow the MVC design pattern. Somewhere along the line people forgot that the "M" in MVC is not *ONLY* for data access, its where you place your domain logic. This is why the RESTful design pattern and the current authentication solutions don't play nice. It's like fitting a square peg in a round hole, which is a red flag that something is wrong. Authlogic solves this by placing the session maintenance logic into its own domain (aka "model"), where it belongs. Moving session maintenance into its own domain has many benefits:
So what is Authlogic, and why would I create a solution to a problem that already has plenty of solutions? Because none of the current solutions feel right. The feel wrong because logic is not organized properly in the MVC structure. A common misconception with the MVC design pattern is the the model "M" is only for data access logic, which is wrong. A model is a place for domain logic. This is why the RESTful design pattern and the current authentication solutions don't play nice, because the logic is not in the right spot. It's like fitting a square peg in a round hole, which is a red flag that something is wrong. Authlogic solves this by placing the session maintenance logic into its own domain (aka "model"), where it belongs. Moving session maintenance into its own domain has many benefits:

1. It's easier to update and stay current with the latest security practices. Since authlogic sits in between you and your session it can assist in keeping your security top notch. Such as upgrading your hashing algorithm, helping you transition to a new algorithm, etc. Since Authlogic is a gem, you get all of these benefits just like you do with everything else in ruby, through rubygems.
2. It ties everything together on the domain level. Such as when a new user registers. No reason to manually log the user in, authlogic handles this for you via callbacks. The same goes for when a user changes their password. Authlogic handles maintaining the session for you.
1. It's easier to update and stay current with the latest security practices. Since authlogic sits in between you and your session it can assist in keeping your security top notch. Such as upgrading your hashing algorithm, helping you transition to a new algorithm, etc. Also, Authlogic is a gem, which means you get all of these benefits easily, through a rubygems update.
2. It ties everything together on the domain level. Take a new user registration for example, no reason to manually log the user in, authlogic handles this for you via callbacks. The same applies to a user changing their password. Authlogic handles maintaining the session for you.
3. Your application can stay clean and focused and free of redundant authentication code from app to app. Meaning generators are *NOT* necessary at all.
4. A by product of #3 is that you don't have to test the same code over and over in each of your apps. You don't test the internals of ActiveRecord in each of your apps, so why would you test the internals of Authlogic? It's already been thoroughly tested for you. Focus on your application, and get rid of the noise by testing your application specific code.
4. A byproduct of #3 is that you don't have to test the same code over and over in each of your apps. You don't test the internals of ActiveRecord in each of your apps, so why would you test the internals of Authlogic? It's already been thoroughly tested for you. Focus on your application, and get rid of the noise by testing your application specific code and not generated code that you didn't write.
5. You get to write your own code, just like you do for any other model. Meaning the code you write is specific to your application, the way you want it, and more importantly you understand it.
6. You are not restricted to a single session. Think about Apple's me.com, where they need you to authenticate a second time before changing your billing information. Why not just create a second session for this? It works just like your initial session. Then your billing controller can require an "ultra secure" session.

Authlogic can do all of this, keep reading to find out how...
Authlogic can do all of this and much more, keep reading to see...

== Quick example

Expand Down
36 changes: 25 additions & 11 deletions lib/authlogic/session/base.rb
Expand Up @@ -7,6 +7,8 @@ class Base
include Config

class << self
attr_accessor :methods_configured

# Returns true if a controller have been set and can be used properly. This MUST be set before anything can be done. Similar to how ActiveRecord won't allow you to do anything
# without establishing a DB connection. In your framework environment this is done for you, but if you are using Authlogic outside of your frameword, you need to assign a controller
# object to Authlogic via Authlogic::Session::Base.controller = obj.
Expand Down Expand Up @@ -353,32 +355,44 @@ def controller
self.class.controller
end

# The goal with Authlogic is to feel as natural as possible. As a result, this method creates methods on the fly
# based on the configuration set. By default the configuration is based off of the columns names in the authenticating
# model. Thus allowing you to call user_session.username instead of user_session.login if you have a username column
# instead of a login column. Since class configuration can change during initialization it makes the most sense to enforce
# this configuration during the first initialization. At this point, all configuration should be set.
#
# Lastly, each method is defined individually to allow the user to provide their own "custom" method and this makes sure
# we don't replace their method.
def create_configurable_methods!
return if respond_to?(login_field) # already created these methods
return if self.class.methods_configured == true

self.class.send(:alias_method, klass_name.demodulize.underscore.to_sym, :record)
self.class.send(:attr_writer, login_field) if !respond_to?("#{login_field}=")
self.class.send(:attr_reader, login_field) if !respond_to?(login_field)
self.class.send(:attr_writer, password_field) if !respond_to?("#{password_field}=")
self.class.send(:define_method, password_field) {} if !respond_to?(password_field)

self.class.class_eval <<-"end_eval", __FILE__, __LINE__
alias_method :#{klass_name.demodulize.underscore}, :record
attr_reader :#{login_field}
def #{login_field}=(value)
def #{login_field}_with_authentication_flag=(value)
self.authenticating_with = :password
@#{login_field} = value
self.#{login_field}_without_authentication_flag = value
end
alias_method_chain :#{login_field}=, :authentication_flag
def #{password_field}=(value)
def #{password_field}_with_authentication_flag=(value)
self.authenticating_with = :password
@#{password_field} = value
self.#{password_field}_without_authentication_flag = value
end
def #{password_field}; end
alias_method_chain :#{password_field}=, :authentication_flag
private
# The password should not be accessible publicly. This way forms using form_for don't fill the password with the attempted password. The prevent this we just create this method that is private.
def protected_#{password_field}
@#{password_field}
end
end_eval

self.class.methods_configured = true
end

def klass
Expand Down
2 changes: 2 additions & 0 deletions test/session_tests/config_test.rb
Expand Up @@ -102,6 +102,7 @@ def test_login_not_found_message
end

def test_login_field
UserSession.methods_configured = false
UserSession.login_field = :saweet
assert_equal :saweet, UserSession.login_field
session = UserSession.new
Expand Down Expand Up @@ -176,6 +177,7 @@ def test_password_blank_message
end

def test_password_field
UserSession.methods_configured = false
UserSession.password_field = :saweet
assert_equal :saweet, UserSession.password_field
session = UserSession.new
Expand Down

0 comments on commit 91f86dd

Please sign in to comment.