Skip to content

Commit

Permalink
Release v1.3.5
Browse files Browse the repository at this point in the history
  • Loading branch information
binarylogic committed Nov 30, 2008
1 parent 72f3a21 commit 6ddadfb
Show file tree
Hide file tree
Showing 11 changed files with 62 additions and 40 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.rdoc
@@ -1,7 +1,11 @@
== 1.3.5 released 2008-11-24
== 1.3.5 released 2008-11-30

* :transition_from_crypto_provider for acts_as_authentic now accepts an array to transition from multiple providers. Which solves the problem of a double transition.
* Added AES256 as a crypto_provider option, for those that want to use a reversible encryption method by supplying a key.
* Fixed typo for using validates_format_of_options instead of validates_length_of_options
* Fixed bug when accessing the dynamic method for accessing the session record in a namespace, since it uses class_name.underscore which replaces :: with a /
* Added minimum length requirement of 4 for the password, and removed validates_presence_of for password since validates_length_of enforces this
* Set before_validation to reset the persistence token if it is blank, since a password is not required for open id authentication

== 1.3.4 released 2008-11-24

Expand Down
32 changes: 23 additions & 9 deletions README.rdoc
Expand Up @@ -2,7 +2,7 @@

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 solutions felt right to me, RESTful development and authentication just didn't seem to go well together. It was like trying to fit a square peg in a round hole. All of the current solutions, for both rails and merb, just seemed to force that square peg in the round hole for me. Just because they did it for me doesn't make it 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...
So what is Authlogic, and why would I create a solution to a problem that already has plenty of solutions? Because none of the solutions felt right to me, RESTful development and authentication just didn't seem to go well together. It was like trying to fit a square peg in a round hole. All of the current solutions, for both rails and merb, just seemed to force that square peg in the round hole for me. Just because they did it for me doesn't make it right. They were either too complicated, bloated, littered my application with tons of code, had no platform for reasonable updating, used an inferior encryption algorithm, 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...

Let's take a rails application...

Expand Down Expand Up @@ -130,9 +130,7 @@ One thing to keep in mind here is that the default :crypto_provider for Authlogi

You are all set, 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 tutorials (see above in "helpful links") for a more detailed walk through.

== Migrating from restful_authentication

This is for migrating an existing application from restful_authentication to Authlogic. If you are starting a new application, please ignore this section.
== Migrating an existing app from restful_authentication and upgrading your encryption

For those that are switching existing apps over, I made an option especially for you. Just do the following and everything will be taken care of, your users won't even know anything changed:

Expand All @@ -141,13 +139,25 @@ For those that are switching existing apps over, I made an option especially for
acts_as_authentic :act_like_restful_authentication => true
end

Or you can transition your users to the Authlogic password system:
The above will not change a thing, from your database's perspective it will be as if you are still using restful_authentication.

Or you can upgrade from Sha1 and transition your users to a much more secure encryption algorithm:

# app/models/user.rb
class User < ActiveRecord::Base
acts_as_authentic :transition_from_restful_authentication => true
end

By default this will switch your users to Authlogic's Sha512 implementation. You do *NOT* have to use this. Check out the encryption methods section below for a list of encryption methods Authlogic provides you. If you want to use something besides Sha512 just specify it by doing:

# app/models/user.rb
class User < ActiveRecord::Base
acts_as_authentic :transition_from_restful_authentication => true,
:crypto_provider => Authlogic::CryptoProviders::BCrypt
end

Every time a user logs in their password will be upgraded and every time a new account is created it will use the new algorithm all while allowing users to login with the old algorithm.

For more information checkout my blog post on this: http://www.binarylogic.com/2008/11/23/tutorial-easily-migrate-from-restful_authentication-to-authlogic

== Magic Columns
Expand Down Expand Up @@ -272,11 +282,11 @@ There could be many more depending on your application. What's great about Authl

Here are the 3 tokens in more detail:

=== 1. Persistence token
=== 1. Persistence token (stored in cookie / session)

This token is used to persist the user's session. This is the token that is stored in the session and the cookie, so that during each request the user stays logged in. What's unique about this token is that the first time it is used the value is stored in the session, thus persisting the session. This field is required and must be in your database.

=== 2. Single access token
=== 2. Single access token (private feed access, etc.)

This token is used for single access only, it is not persisted. Meaning the user provides it, Authlogic grants them access, and that's it. If they want access again they need to provide the token again. Authlogic will *NEVER* store this value in the session or a cookie. For added security, by default this token is *ONLY* allowed for RSS and ATOM requests. Also, this token does *NOT* change with the password. Meaning if the user changes their password, this token will remain the same. Lastly, this token uses a "friendly" toke (see the URL example below) so that it is easier to email / copy and paste. You can change all of this with configuration (see Authlogic::Session::config), so if you don't like how this works by default, just set some simple configuration in your session.

Expand All @@ -293,7 +303,7 @@ The single_access_token parameter name is configurable (see Authlogic::Session::

For more information see: Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::SingleAccess

=== 3. Perishable token
=== 3. Perishable token (resetting passwords, confirming accounts, etc)

This token is used for temporary account access, hence the term "perishable". This token is constantly changing, it changes...

Expand Down Expand Up @@ -445,7 +455,7 @@ From there it is pretty simple. When you try to create a new session the record

== What's wrong with the current solutions?

You probably don't care, but I think releasing the millionth authentication solution for a framework that has been around for over 4 years requires a little explanation.
You probably don't care, but I think releasing the millionth ruby authentication solution requires a little explanation.

I don't necessarily think the current solutions are "wrong", nor am I saying Authlogic is the answer to your prayers. But, to me, the current solutions were lacking something. Here's what I came up with...

Expand All @@ -459,6 +469,10 @@ Using a library that hundreds of other people use has it advantages. Probably on

Lastly, there is a pattern here, why clutter up all of your applications with the same code over and over?

=== Security gets outdated

Just as I stated in the above section, you can't stay up to date with your security since the code is generated and updating the plugin does nothing. If there is one thing you should stay up to date with, it's security. But it's not just the fact that there is no reasonable method for receiving updates. It's the fact that they tie you down to an encryption algorithm *AND* they use a bad one at that. Every single solution I've seen uses Sha1, which is joining the party with MD5. Sha1 is not as secure as it used to be. But that's the nature of algorithms, they eventually get phased out, which is fine. Everyone knows this, why not accommodate for this? Authlogic does this with the :transition_from_crypto_provider option. It takes care of transitioning all of your users to a new algorithm. Even better, it provides BCrypt as an option which should, in theory, never require you to switch since you can adjust the cost and make the encryption stronger. At the same time, still compatible with older passwords using the lower cost.

=== Why test the same code over and over?

I've noticed my apps get cluttered with authentication tests, and they are the same exact tests! This irritates me. When you have identical tests across your apps thats a red flag that code can be extracted into a library. What's great about Authlogic is that I tested it for you. You don't write tests that test the internals of ActiveRecord do you? The same applies for Authlogic. Only test code that you've written. Essentially testing authentication is similar to testing any another RESTful controller. This makes your tests focused and easier to understand.
Expand Down
2 changes: 2 additions & 0 deletions lib/authlogic/crypto_providers/aes256.rb
Expand Up @@ -30,6 +30,8 @@ def matches?(crypted, *tokens)
aes.decrypt
aes.key = @key
(aes.update(crypted.unpack("m").first) + aes.final) == tokens.join
rescue OpenSSL::CipherError
false
end

private
Expand Down
Expand Up @@ -124,8 +124,8 @@ module ActsAsAuthentic
# * <tt>password_field_validation_options</tt> - default: {},
# The same as :validation_options but these are only applied to validations that pertain to the :password_field
#
# * <tt>password_field_validates_presence_of_options</tt> - default: {:on => :create},
# These options are applied to the validates_presence_of call for the :password_field
# * <tt>password_field_validates_length_of_options</tt> - default: {:minimum => 4},
# These options are applied to the validates_length_of call for the :password_field
#
# * <tt>login_field_validates_confirmation_of_options</tt> - default: {},
# These options are applied to the validates_confirmation_of call for the :password_field
Expand Down Expand Up @@ -187,7 +187,7 @@ def acts_as_authentic_with_config(options = {})
field_key = "#{field_name}_field_validation_options".to_sym
options[field_key] = options[:validation_options].merge(options[field_key] || {})

validation_types = field_name == :password ? [:presence, :confirmation] : [:length, :format, :uniqueness]
validation_types = field_name == :password ? [:length, :confirmation] : [:length, :format, :uniqueness]
validation_types.each do |validation_type|
validation_key = "#{field_name}_field_validates_#{validation_type}_of_options".to_sym
options[validation_key] = options[field_key].merge(options[validation_key] || {})
Expand Down
Expand Up @@ -30,7 +30,7 @@ def acts_as_authentic_with_credentials(options = {})
case options[:login_field_type]
when :email
validates_length_of options[:login_field], {:within => 6..100}.merge(options[:login_field_validates_length_of_options])
validates_format_of options[:login_field], {:with => email_field_regex, :message => "should look like an email address."}.merge(options[:login_field_validates_length_of_options])
validates_format_of options[:login_field], {:with => email_field_regex, :message => "should look like an email address."}.merge(options[:login_field_validates_format_of_options])
else
validates_length_of options[:login_field], {:within => 2..100}.merge(options[:login_field_validates_length_of_options])
validates_format_of options[:login_field], {:with => /\A\w[\w\.\-_@ ]+\z/, :message => "should use only letters, numbers, spaces, and .-_@ please."}.merge(options[:login_field_validates_format_of_options])
Expand All @@ -40,9 +40,9 @@ def acts_as_authentic_with_credentials(options = {})
end

if options[:validate_password_field]
validates_presence_of options[:password_field], {:on => :create}.merge(options[:password_field_validates_presence_of_options])
validates_confirmation_of options[:password_field], options[:password_field_validates_confirmation_of_options].merge(:if => "#{options[:crypted_password_field]}_changed?".to_sym)
validates_presence_of "#{options[:password_field]}_confirmation", :if => "#{options[:crypted_password_field]}_changed?"
validates_length_of options[:password_field], {:minimum => 4}.merge(options[:password_field_validates_length_of_options].merge(:if => "validate_#{options[:password_field]}?".to_sym))
validates_confirmation_of options[:password_field], options[:password_field_validates_confirmation_of_options].merge(:if => "validate_#{options[:password_field]}?".to_sym)
validates_presence_of "#{options[:password_field]}_confirmation", :if => "validate_#{options[:password_field]}?".to_sym
end

if options[:validate_email_field] && options[:email_field]
Expand Down Expand Up @@ -113,6 +113,10 @@ def reset_#{options[:password_field]}!
end
alias_method :randomize_password!, :reset_password!
def validate_#{options[:password_field]}?
new_record? || #{options[:crypted_password_field]}_changed?
end
private
def encrypt_arguments(raw_password, arguments_type = nil)
case arguments_type
Expand Down
Expand Up @@ -22,7 +22,10 @@ module Persistence
def acts_as_authentic_with_persistence(options = {})
acts_as_authentic_without_persistence(options)

validates_presence_of options[:persistence_token_field]
validates_uniqueness_of options[:persistence_token_field], :if => "#{options[:persistence_token_field]}_changed?".to_sym

before_validation "reset_#{options[:persistence_token_field]}".to_sym, :if => "reset_#{options[:persistence_token_field]}?".to_sym

def forget_all!
# Paginate these to save on memory
Expand All @@ -46,10 +49,23 @@ def forget!
end
def #{options[:password_field]}_with_persistence=(value)
self.#{options[:persistence_token_field]} = self.class.unique_token
reset_#{options[:persistence_token_field]}
self.#{options[:password_field]}_without_persistence = value
end
alias_method_chain :#{options[:password_field]}=, :persistence
def reset_#{options[:persistence_token_field]}
self.#{options[:persistence_token_field]} = self.class.unique_token
end
def reset_#{options[:persistence_token_field]}!
reset_#{options[:persistence_token_field]}
save_without_session_maintenance(false)
end
def reset_#{options[:persistence_token_field]}?
#{options[:persistence_token_field]}.blank?
end
end_eval
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/authlogic/session/base.rb
Expand Up @@ -203,7 +203,7 @@ def find_record
end

# Allows you to set a unique identifier for your session, so that you can have more than 1 session at a time. A good example when this might be needed is when you want to have a normal user session
# and a "secure" user session. The secure user session would be created only when they want to modify their billing information, or other sensative information. Similar to me.com. This requires 2
# and a "secure" user session. The secure user session would be created only when they want to modify their billing information, or other sensitive information. Similar to me.com. This requires 2
# user sessions. Just use an id for the "secure" session and you should be good.
#
# You can set the id during initialization (see initialize for more information), or as an attribute:
Expand Down Expand Up @@ -357,7 +357,7 @@ def create_configurable_methods!
return if respond_to?(login_field) # already created these methods

self.class.class_eval <<-"end_eval", __FILE__, __LINE__
alias_method :#{klass_name.underscore}, :record
alias_method :#{klass_name.underscore.split("/").last}, :record
attr_reader :#{login_field}
Expand Down
2 changes: 1 addition & 1 deletion lib/authlogic/version.rb
Expand Up @@ -44,7 +44,7 @@ def to_a

MAJOR = 1
MINOR = 3
TINY = 4
TINY = 5

# The current version as a Version instance
CURRENT = new(MAJOR, MINOR, TINY)
Expand Down
17 changes: 0 additions & 17 deletions test/libs/aes128_crypto_provider.rb

This file was deleted.

Expand Up @@ -36,7 +36,7 @@ def test_acts_as_authentic_config
:validate_fields => true,
:login_field => :login,
:perishable_token_valid_for => 600,
:password_field_validates_presence_of_options => {},
:password_field_validates_length_of_options => {},
:password_field => :password,
:validate_login_field => true,
:email_field => :email,
Expand Down
1 change: 0 additions & 1 deletion test/test_helper.rb
Expand Up @@ -4,7 +4,6 @@
require "active_record"
require 'active_record/fixtures'
require File.dirname(__FILE__) + '/../lib/authlogic' unless defined?(Authlogic)
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'
Expand Down

0 comments on commit 6ddadfb

Please sign in to comment.