Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Released v0.10.4

  • Loading branch information...
commit 43e849c6cadf9e0fa93fe2295f86ec16c935f374 1 parent 38b6e9b
@binarylogic binarylogic authored
Showing with 611 additions and 270 deletions.
  1. +1 −0  CHANGELOG.rdoc
  2. +27 −11 Manifest
  3. +113 −82 README.rdoc
  4. +5 −5 Rakefile
  5. +1 −1  init.rb
  6. +0 −21 lib/authgasm.rb
  7. +25 −0 lib/authlogic.rb
  8. +36 −40 lib/{authgasm → authlogic/active_record}/acts_as_authentic.rb
  9. +19 −0 lib/authlogic/active_record/authenticates_many.rb
  10. +28 −0 lib/authlogic/active_record/scoped_session.rb
  11. +2 −2 lib/{authgasm → authlogic}/controller_adapters/abstract_adapter.rb
  12. +5 −5 lib/{authgasm → authlogic}/controller_adapters/rails_adapter.rb
  13. +3 −3 lib/{authgasm → authlogic}/session/active_record_trickery.rb
  14. +107 −21 lib/{authgasm → authlogic}/session/base.rb
  15. +2 −2 lib/{authgasm → authlogic}/session/callbacks.rb
  16. +10 −6 lib/{authgasm → authlogic}/session/config.rb
  17. +2 −2 lib/{authgasm → authlogic}/session/errors.rb
  18. +2 −2 lib/{authgasm → authlogic}/sha512_crypto_provider.rb
  19. +2 −2 lib/{authgasm → authlogic}/version.rb
  20. +29 −3 test_app/app/controllers/application.rb
  21. +2 −0  test_app/app/controllers/companies_controller.rb
  22. +4 −4 test_app/app/controllers/user_sessions_controller.rb
  23. +7 −7 test_app/app/controllers/users_controller.rb
  24. +2 −0  test_app/app/helpers/companies_helper.rb
  25. +0 −3  test_app/app/models/account.rb
  26. +4 −0 test_app/app/models/company.rb
  27. +3 −0  test_app/app/models/project.rb
  28. +2 −1  test_app/app/models/user.rb
  29. +1 −1  test_app/app/models/user_session.rb
  30. +7 −5 test_app/app/views/layouts/application.html.erb
  31. +1 −1  test_app/app/views/user_sessions/new.html.erb
  32. +1 −1  test_app/app/views/users/edit.html.erb
  33. +1 −1  test_app/app/views/users/new.html.erb
  34. +1 −1  test_app/app/views/users/show.html.erb
  35. +1 −1  test_app/config/environment.rb
  36. +5 −0 test_app/config/routes.rb
  37. +0 −1  test_app/db/migrate/20081023040052_create_users.rb
  38. +0 −12 test_app/db/migrate/20081101190907_create_accounts.rb
  39. +14 −0 test_app/db/migrate/20081103003828_create_companies.rb
  40. +18 −0 test_app/db/migrate/20081103003834_create_projects.rb
  41. 0  test_app/test/fixtures/{accounts.yml → companies.yml}
  42. +4 −0 test_app/test/fixtures/projects.yml
  43. +6 −4 test_app/test/fixtures/users.yml
  44. +8 −0 test_app/test/functional/companies_controller_test.rb
  45. +46 −0 test_app/test/integration/company_user_session_stories_test.rb
  46. +1 −1  test_app/test/integration/user_sesion_stories_test.rb
  47. +1 −1  test_app/test/integration/user_session_test.rb
  48. +35 −16 test_app/test/test_helper.rb
  49. +8 −0 test_app/test/unit/company_test.rb
  50. +8 −0 test_app/test/unit/project_test.rb
  51. +1 −1  test_app/test/unit/user_test.rb
View
1  CHANGELOG.rdoc
@@ -2,6 +2,7 @@
* Changed configuration to use inheritable attributes
* Cleaned up requires to be in their proper files
+* Added in scope support.
== 0.10.3 released 2008-10-31
View
38 Manifest
@@ -1,26 +1,32 @@
CHANGELOG.rdoc
init.rb
-lib/authgasm/acts_as_authentic.rb
-lib/authgasm/controller_adapters/abstract_adapter.rb
-lib/authgasm/controller_adapters/rails_adapter.rb
-lib/authgasm/session/active_record_trickery.rb
-lib/authgasm/session/base.rb
-lib/authgasm/session/callbacks.rb
-lib/authgasm/session/config.rb
-lib/authgasm/session/errors.rb
-lib/authgasm/sha512_crypto_provider.rb
-lib/authgasm/version.rb
-lib/authgasm.rb
+lib/authlogic/active_record/acts_as_authentic.rb
+lib/authlogic/active_record/authenticates_many.rb
+lib/authlogic/active_record/scoped_session.rb
+lib/authlogic/controller_adapters/abstract_adapter.rb
+lib/authlogic/controller_adapters/rails_adapter.rb
+lib/authlogic/session/active_record_trickery.rb
+lib/authlogic/session/base.rb
+lib/authlogic/session/callbacks.rb
+lib/authlogic/session/config.rb
+lib/authlogic/session/errors.rb
+lib/authlogic/sha512_crypto_provider.rb
+lib/authlogic/version.rb
+lib/authlogic.rb
Manifest
MIT-LICENSE
Rakefile
README.rdoc
test_app/app/controllers/application.rb
+test_app/app/controllers/companies_controller.rb
test_app/app/controllers/user_sessions_controller.rb
test_app/app/controllers/users_controller.rb
test_app/app/helpers/application_helper.rb
+test_app/app/helpers/companies_helper.rb
test_app/app/helpers/user_sessions_helper.rb
test_app/app/helpers/users_helper.rb
+test_app/app/models/company.rb
+test_app/app/models/project.rb
test_app/app/models/user.rb
test_app/app/models/user_session.rb
test_app/app/views/layouts/application.html.erb
@@ -41,6 +47,8 @@ test_app/config/initializers/new_rails_defaults.rb
test_app/config/routes.rb
test_app/db/development.sqlite3
test_app/db/migrate/20081023040052_create_users.rb
+test_app/db/migrate/20081103003828_create_companies.rb
+test_app/db/migrate/20081103003834_create_projects.rb
test_app/db/schema.rb
test_app/db/test.sqlite3
test_app/doc/README_FOR_APP
@@ -75,10 +83,18 @@ test_app/script/process/reaper
test_app/script/process/spawner
test_app/script/runner
test_app/script/server
+test_app/test/fixtures/companies.yml
+test_app/test/fixtures/projects.yml
test_app/test/fixtures/users.yml
+test_app/test/functional/companies_controller_test.rb
test_app/test/functional/user_sessions_controller_test.rb
test_app/test/functional/users_controller_test.rb
+test_app/test/integration/company_user_session_stories_test.rb
test_app/test/integration/user_sesion_stories_test.rb
+test_app/test/integration/user_session_config_test.rb
test_app/test/integration/user_session_test.rb
test_app/test/test_helper.rb
+test_app/test/unit/account_test.rb
+test_app/test/unit/company_test.rb
+test_app/test/unit/project_test.rb
test_app/test/unit/user_test.rb
View
195 README.rdoc
@@ -1,13 +1,13 @@
-= Authgasm
+= Authlogic
-Authgasm is "rails authentication done right"
+Authlogic is "rails authentication done right". Put simply, its the Chuck Norris of authentication solutions.
-The last thing we need is another authentication solution for rails, right? That's what I thought until I tried out some of the current solutions. 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 rails we all fell in love with. We need a "rails like" authentication solution. Authgasm is my attempt to satisfy that need...
-
-What if you could have authentication up and running in minutes without having to run a generator? All because it's simple, like everything else in rails.
+The last thing we need is another authentication solution for rails, right? That's what I thought until I tried out some of the current solutions. 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 rails we all fell in love with. We need a "rails like" authentication solution. Authlogic is my attempt to satisfy that need...
Wouldn't it be nice to keep your app up to date with the latest and greatest security techniques with a simple update of a plugin?
+What if you could have authentication up and running in minutes without having to run a generator? All because it's simple, like everything else in rails.
+
What if creating a user session could be as simple as...
UserSession.create(params[:user_session])
@@ -58,64 +58,32 @@ Or how about persisting the session...
end
end
-Authgasm makes this a reality. This is just the tip of the ice berg. Keep reading to find out everything Authgasm can do.
+Authlogic makes this a reality. This is just the tip of the ice berg. Keep reading to find out everything Authlogic can do.
== Helpful links
-* <b>Documentation:</b> http://authgasm.rubyforge.org
-* <b>Authgasm tutorial:</b> coming soon...
-* <b>Live example of the tutorial above (with source):</b> coming soon....
-* <b>Bugs / feature suggestions:</b> http://binarylogic.lighthouseapp.com/projects/18752-authgasm
+* <b>Documentation:</b> http://authlogic.rubyforge.org
+* <b>Authlogic setup tutorial:</b> coming soon...
+* <b>Live example of the setup tutorial above (with source):</b> coming soon....
+* <b>Bugs / feature suggestions:</b> http://binarylogic.lighthouseapp.com/projects/18752-authlogic
== Install and use
-=== Install the gem / plugin
+Install the gem / plugin
- $ sudo gem install authgasm
+ $ sudo gem install authlogic
$ cd vendor/plugins
- $ sudo gem unpack authgasm
+ $ sudo gem unpack authlogic
Or as a plugin
- script/plugin install git://github.com/binarylogic/authgasm.git
-
-=== Create your session
-
-For this walk through 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
- # configuration here, just like ActiveRecord, or in an initializer
- # See Authgasm::Session::Config::ClassMethods for more details
- end
-
-It is important to set your configuration for your session before you set the configuration for your model. This will save you some time. Your model will try to guess its own configuration based on what you set in the session. These are completely separate, making Authgasm as flexible as it needs to be, but the majority of the time they will be the same and no one likes to repeat their self.
-
-=== 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.
+ script/plugin install git://github.com/binarylogic/authlogic.git
- t.string :login, :null => false
- t.string :crypted_password, :null => false
- t.string :password_salt, :null => false # not needed if you are encrypting your pw instead of using a hash algorithm
- t.string :remember_token, :null => false
- t.integer :login_count # This is optional, it is a "magic" column, just like "created_at". See below for a list of all magic columns.
-
-=== Set up your model
-
-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
- end
-
-Done! Now go use it just like you would with any other ActiveRecord model (see above).
+In an effort to keep this readme a reference instead of a step by step guide, I moved the setup tutorial to my blog. See "helpful links" above. If this is your first time using Authlogic checkout the tutorial.
== Magic Columns
-Just like ActiveRecord has "magic" columns, such as: created_at and updated_at. Authgasm has its own "magic" columns too:
+Just like ActiveRecord has "magic" columns, such as: created_at and updated_at. Authlogic has its own "magic" columns too:
Column name Description
login_count Increased every time an explicit login is made. This will *NOT* increase if logging in by a session, cookie, or basic http auth
@@ -127,7 +95,7 @@ Just like ActiveRecord has "magic" columns, such as: created_at and updated_at.
== Magic States
-Authgasm tries to check the state of the record before creating the session. If your record responds to the following methods and any of them return false, validation will fail:
+Authlogic tries to check the state of the record before creating the session. If your record responds to the following methods and any of them return false, validation will fail:
Method name Description
active? Is the record marked as active?
@@ -136,7 +104,7 @@ Authgasm tries to check the state of the record before creating the session. If
What's neat about this is that these are checked upon any type of login. When logging in explicitly, by cookie, session, or basic http auth. So if you mark a user inactive in the middle of their session they wont be logged back in next time they refresh the page. Giving you complete control.
-Need Authgasm to check your own "state"? No problem, check out the hooks section below. Add in a before_validation or after_validation to do your own checking.
+Need Authlogic to check your own "state"? No problem, check out the hooks section below. Add in a before_validation or after_validation to do your own checking.
== Hooks / Callbacks
@@ -152,10 +120,96 @@ Just like ActiveRecord you can create your own hooks / callbacks so that you can
after_update
before_validation
after_validation
+
+== Multiple Sessions / Session Identifiers
+
+You're asking: "why would I want multiple sessions?". Take this example:
+
+You have an app where users login and then need to re-login to view / change their billing information. Similar to how Apple's me.com works. What you could do is have the user login with their normal session, then have an entirely new session that represents their "secure" session. But wait, this is 2 users sessions. No problem:
+
+ # regular user session
+ @user_session = UserSession.new
+ @user_session.id
+ # => nil
+
+ # secure user session
+ @secure_user_session = UserSession.new(:secure)
+ @secure_user_session.id
+ # => :secure
+
+This will keep everything separate. The :secure session will store its info in a separate cookie, separate session, etc. Just set the id and you are good to go. Need to retrieve the session?
+
+ @user_session = UserSession.find
+ @secure_user_session = UserSession.find(:secure)
+
+For more information on ids checkout Authlogic::Session::Base#initialize
+
+== Scoping
+
+Scoping with authentication is a little tricky because it can come in many different flavors:
+
+1. Accounts have many users, meaning users can only belong to one account at a time.
+2. Accounts have and belong to many users, meaning a user can belong to more than one account.
+3. Users access their accounts via subdomains.
+4. Users access their accounts by selecting their account and storing their selection, *NOT* using subdomains. Maybe you store their selection in a session, cookie, or the database. It doesn't matter.
+
+Now mix and match the above, it can get pretty hairy. Fear not, because Authlogic is designed in a manner where it doesn't care how you do it, all that you have to do is break it down. When scoping a session there are 3 parts you might want to scope:
+
+1. The model (the validations, etc)
+2. The session (finding the record)
+3. The cookies (the names of the session key and cookie)
+
+I will describe each below, in order.
+
+=== Scoping your model
+
+This scopes your login field validation, so that users are allowed to have the same login, just not in the same account.
+
+ # app/models/user.rb
+ class User < ActiveRecord::Base
+ acts_as_authentic :scope => :account_id
+ end
+
+=== Scoping your session
+
+When you session tries to validate it searches for a record. You want to scope that search. No problem...
+
+The goal of Authlogic was to not try and introduce anything new. As a result I came up with:
+
+ @account.user_sessions.find
+ @account.user_sessions.create
+ @account.user_sessions.build
+ # ... etc
+
+This works just like ActiveRecord, so it should come natural. Here is how you get this functionality:
+
+ class Account < ActiveRecord::Base
+ authenticates_many :user_sessions
+ end
+
+=== Scoping cookies
+
+What's neat about cookies is that if you use sub domains they automatically scope their self. Meaning if you create a cookie in whatever.yourdomain.com it will not exist in another.yourdomain.com. So if you have the example used above, where you can access accounts via a sub domain, you don't have to do anything.
+
+But what if you don't want to separate your cookies by subdomains? You can accomplish this by doing:
+
+ ActionController::Base.session_options[:session_domain] = ‘.mydomain.com’
+
+If for some reason the above doesn't work for you, do some simple Google searches. There are a million blog posts on this.
+
+Now let's look at this from the other angle. What if you are *NOT* using subdomains, but still want to separate cookies for each account. Simple, set the :scope_cookies option for authenticate_many:
+
+ class Account < ActiveRecord::Base
+ authenticates_many :user_sessions, :scope_cookies => true
+ end
+
+Done, Authlogic will give each cookie a unique name depending on the account.
+
+With the above information you should be able to scope your sessions any way you want. Just mix and match the tools above to accomplish what you want. Also check out the documentation on Authlogic::ActiveRecord::AuthenticatesMany.
== Errors
-The errors in Authgasm work JUST LIKE ActiveRecord. In fact, it uses the exact same ActiveRecord errors class. Use it the same way:
+The errors in Authlogic work JUST LIKE ActiveRecord. In fact, it uses the exact same ActiveRecord errors class. Use it the same way:
class UserSession
before_validation :check_if_awesome
@@ -171,11 +225,11 @@ The errors in Authgasm work JUST LIKE ActiveRecord. In fact, it uses the exact s
This is one of my favorite features that I think is pretty cool. It's things like this that make a library great and let you know you are on the right track.
-Just to clear up any confusion, Authgasm does not store the plain id in the session. It stores a token. This token changes with the password, this way stale sessions can not be persisted.
+Just to clear up any confusion, Authlogic does not store the plain id in the session. It stores a token. This token changes with the password, this way stale sessions can not be persisted.
That being said...What if a user changes their password? You have to re-log them in with the new password, recreate the session, etc, pain in the ass. Or what if a user creates a new user account? You have to do the same thing. Here's an even better one: what if a user is in the admin area and changes his own password? There might even be another place passwords can change. It shouldn't matter, your code should be written in a way where you don't have to remember to do this.
-Instead of updating sessions all over the place, doesn't it make sense to do this at a lower level? Like the User model? You're saying "but Ben, models can't mess around with sessions and cookies". True...but Authgasm can, and you can access Authgasm just like a model. I know in most situations it's not good practice to do this but I view this in the same class as sweepers, and feel like it actually is good practice here. User sessions are directly tied to users, they should be connected on the model level.
+Instead of updating sessions all over the place, doesn't it make sense to do this at a lower level? Like the User model? You're saying "but Ben, models can't mess around with sessions and cookies". True...but Authlogic can, and you can access Authlogic just like a model. I know in most situations it's not good practice to do this but I view this in the same class as sweepers, and feel like it actually is good practice here. User sessions are directly tied to users, they should be connected on the model level.
Fear not, because the acts_as_authentic method you call in your model takes care of this for you, by adding an after_save callback to automatically keep the session up to date. You don't have to worry about it anymore. Don't even think about it. Let your UsersController deal with users, not users *AND* sessions. *ANYTIME* the user changes his password in *ANY* way, his session will be updated.
@@ -196,36 +250,13 @@ Obviously there is a little more to it than this, but hopefully this clarifies a
When things come together like this I think its a sign that you are doing something right. Put that in your pipe and smoke it!
-== Multiple Sessions / Session Identifiers
-
-You're asking: "why would I want multiple sessions?". Take this example:
-
-You have an app where users login and then need to re-login to view / change their billing information. Similar to how Apple's me.com works. What you could do is have the user login with their normal session, then have an entirely new session that represents their "secure" session. But wait, this is 2 users sessions. No problem:
-
- # regular user session
- @user_session = UserSession.new
- @user_session.id
- # => nil
-
- # secure user session
- @secure_user_session = UserSession.new(:secure)
- @secure_user_session.id
- # => :secure
-
-This will keep everything separate. The :secure session will store its info in a separate cookie, separate session, etc. Just set the id and you are good to go. Need to retrieve the session?
-
- @user_session = UserSession.find
- @secure_user_session = UserSession.find(:secure)
-
-For more information on ids checkout Authgasm::Session::Base#initialize
-
== What about [insert framework here]?
-As of now, authgasm supports rails right out of the box. But I designed authgasm to be framework agnostic. The only thing stopping Authgasm from being implemented in merb, or any other framework, is a simple adapter. I have not had the opportunity to use Authgasm in anything other than rails. If you want to use this in merb or any other framework take a look at authgasm/controller/rails_adapter.rb.
+As of now, authlogic supports rails right out of the box. But I designed authlogic to be framework agnostic. The only thing stopping Authlogic from being implemented in merb, or any other framework, is a simple adapter. I have not had the opportunity to use Authlogic in anything other than rails. If you want to use this in merb or any other framework take a look at authlogic/controller/rails_adapter.rb.
== How it works
-Interested in how all of this all works? Basically a before_filter is automatically set in your controller which lets Authgasm know about the current controller object. This "activates" Authgasm and allows Authgasm to set sessions, cookies, login via basic http auth, etc. If you are using rails in a multiple thread environment, don't worry. I kept that in mind and made this thread safe.
+Interested in how all of this all works? Basically a before_filter is automatically set in your controller which lets Authlogic know about the current controller object. This "activates" Authlogic and allows Authlogic to set sessions, cookies, login via basic http auth, etc. If you are using rails in a multiple thread environment, don't worry. I kept that in mind and made this thread safe.
From there it is pretty simple. When you try to create a new session the record is authenticated and then all of the session / cookie magic is done for you. The sky is the limit.
@@ -233,7 +264,7 @@ From there it is pretty simple. When you try to create a new session the record
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.
-I don't necessarily think the current solutions are "wrong", nor am I saying Authgasm is the answer to your prayers. But, to me, the current solutions were lacking something. Here's what I came up with...
+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...
=== Generators are messy
@@ -241,13 +272,13 @@ Generators have their place, and it is not to add authentication to a rails app.
Authentication is a one time set up process for your app. It's the same thing over and over and the pattern never really changes. The only time it changes is to conform with newer / stricter security techniques. This is exactly why generators should not be an authentication solution. Generators add code to your application, once code crosses that line, you are responsible for maintaining it. You get to make sure it stays up with the latest and greatest security techniques. And when the plugin you used releases some major update, you can't just re-run the generator, you get to sift through the code to see what changed. You don't really have a choice either, because you can't ignore security updates.
-Using a library that hundreds of other people use has it advantages. Probably one of the biggest advantages if that you get to benefit from other people using the same code. When Bob in California figures out a new awesome security technique and adds it into Authgasm, you get to benefit from that with a single update. The catch is that this benefit is limited to code that is not "generated" or added into your app. As I said above, once code is "generated" and added into your app, it's your responsibility.
+Using a library that hundreds of other people use has it advantages. Probably one of the biggest advantages if that you get to benefit from other people using the same code. When Bob in California figures out a new awesome security technique and adds it into Authlogic, you get to benefit from that with a single update. The catch is that this benefit is limited to code that is not "generated" or added into your app. As I said above, once code is "generated" and added into your app, it's your responsibility.
Lastly, there is a pattern here, why clutter up all of your applications with the same code over and over?
=== Limited to a single authentication
-I recently had an app where you could log in as a user and also log in as an employee. I won't go into the specifics of the app, but it made the most sense to do it this way. So I had two sessions in one app. None of the current solutions I found easily supported this. They all assumed a single session. One session was messy enough, adding another just put me over the edge and eventually forced me to write Authgasm. Authgasm can support 100 different sessions easily and in a clean format. Just like an app can support 100 different models and 100 different records of each model.
+I recently had an app where you could log in as a user and also log in as an employee. I won't go into the specifics of the app, but it made the most sense to do it this way. So I had two sessions in one app. None of the current solutions I found easily supported this. They all assumed a single session. One session was messy enough, adding another just put me over the edge and eventually forced me to write Authlogic. Authlogic can support 100 different sessions easily and in a clean format. Just like an app can support 100 different models and 100 different records of each model.
=== Too presumptuous
@@ -255,7 +286,7 @@ A lot of them forced me to name my password column as "this", or the key of my c
=== Disclaimer
-I am not trying to "bash" any other authentication solutions. These are just my opinions, formulate your own opinion. I released Authgasm because it has made my life easier and I enjoy using it, hopefully it does the same for you.
+I am not trying to "bash" any other authentication solutions. These are just my opinions, formulate your own opinion. I released Authlogic because it has made my life easier and I enjoy using it, hopefully it does the same for you.
Copyright (c) 2008 Ben Johnson of [Binary Logic](http://www.binarylogic.com), released under the MIT license
View
10 Rakefile
@@ -1,15 +1,15 @@
require 'rubygems'
require 'echoe'
-require File.dirname(__FILE__) << "/lib/authgasm/version"
+require File.dirname(__FILE__) << "/lib/authlogic/version"
-Echoe.new 'authgasm' do |p|
- p.version = Authgasm::Version::STRING
+Echoe.new 'authlogic' do |p|
+ p.version = Authlogic::Version::STRING
p.author = "Ben Johnson of Binary Logic"
p.email = 'bjohnson@binarylogic.com'
- p.project = 'authgasm'
+ p.project = 'authlogic'
p.summary = "Rails authentication done right"
- p.url = "http://github.com/binarylogic/authgasm"
+ p.url = "http://github.com/binarylogic/authlogic"
p.dependencies = %w(activesupport activerecord)
p.include_rakefile = true
end
View
2  init.rb
@@ -1 +1 @@
-require "authgasm"
+require "authlogic"
View
21 lib/authgasm.rb
@@ -1,21 +0,0 @@
-require File.dirname(__FILE__) + "/authgasm/version"
-
-require File.dirname(__FILE__) + "/authgasm/controller_adapters/abstract_adapter"
-require File.dirname(__FILE__) + "/authgasm/controller_adapters/rails_adapter" if defined?(Rails)
-
-require File.dirname(__FILE__) + "/authgasm/sha512_crypto_provider"
-require File.dirname(__FILE__) + "/authgasm/acts_as_authentic"
-require File.dirname(__FILE__) + "/authgasm/session/active_record_trickery"
-require File.dirname(__FILE__) + "/authgasm/session/callbacks"
-require File.dirname(__FILE__) + "/authgasm/session/config"
-require File.dirname(__FILE__) + "/authgasm/session/errors"
-require File.dirname(__FILE__) + "/authgasm/session/base"
-
-module Authgasm
- module Session
- class Base
- include ActiveRecordTrickery
- include Callbacks
- end
- end
-end
View
25 lib/authlogic.rb
@@ -0,0 +1,25 @@
+require File.dirname(__FILE__) + "/authlogic/version"
+
+require File.dirname(__FILE__) + "/authlogic/controller_adapters/abstract_adapter"
+require File.dirname(__FILE__) + "/authlogic/controller_adapters/rails_adapter" if defined?(Rails)
+
+require File.dirname(__FILE__) + "/authlogic/sha512_crypto_provider"
+
+require File.dirname(__FILE__) + "/authlogic/active_record/acts_as_authentic"
+require File.dirname(__FILE__) + "/authlogic/active_record/authenticates_many"
+require File.dirname(__FILE__) + "/authlogic/active_record/scoped_session"
+
+require File.dirname(__FILE__) + "/authlogic/session/active_record_trickery"
+require File.dirname(__FILE__) + "/authlogic/session/callbacks"
+require File.dirname(__FILE__) + "/authlogic/session/config"
+require File.dirname(__FILE__) + "/authlogic/session/errors"
+require File.dirname(__FILE__) + "/authlogic/session/base"
+
+module Authlogic
+ module Session
+ class Base
+ include ActiveRecordTrickery
+ include Callbacks
+ end
+ end
+end
View
76 lib/authgasm/acts_as_authentic.rb → ...hlogic/active_record/acts_as_authentic.rb
@@ -1,13 +1,9 @@
-module Authgasm
- module ActsAsAuthentic # :nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- end
-
+module Authlogic
+ module ActiveRecord # :nodoc:
# = Acts As Authentic
# Provides and "acts_as" method to include in your models to help with authentication. See method below.
- module ClassMethods
- # Call this method in your model to add in basic authentication madness that your authgasm session expects.
+ module ActsAsAuthentic
+ # Call this method in your model to add in basic authentication madness that your authlogic session expects.
#
# <b>Please keep in mind</b> that based on your configuration the method names could change. For example, if you pass the option:
#
@@ -37,17 +33,17 @@ module ClassMethods
#
# === Options
# * <tt>session_class:</tt> default: "#{name}Session", the related session class. Used so that you don't have to repeat yourself here. A lot of the configuration will be based off of the configuration values of this class.
- # * <tt>crypto_provider:</tt> default: Authgasm::Sha256CryptoProvider, class that provides Sha256 encryption. What ultimately encrypts your password.
- # * <tt>crypto_provider_type:</tt> default: options[:crypto_provider].respond_to?(:decrypt) ? :encryption : :hash. You can explicitly set this if you wish. Since encryptions and hashes are handled different this is the flag Authgasm uses.
+ # * <tt>crypto_provider:</tt> default: Authlogic::Sha512CryptoProvider, class that provides Sha512 encryption. What ultimately encrypts your password.
+ # * <tt>crypto_provider_type:</tt> default: options[:crypto_provider].respond_to?(:decrypt) ? :encryption : :hash. You can explicitly set this if you wish. Since encryptions and hashes are handled different this is the flag Authlogic uses.
# * <tt>login_field:</tt> default: options[:session_class].login_field, the name of the field used for logging in
- # * <tt>login_field_type:</tt> default: options[:login_field] == :email ? :email : :login, tells authgasm how to validation the field, what regex to use, etc.
+ # * <tt>login_field_type:</tt> default: options[:login_field] == :email ? :email : :login, tells authlogic how to validation the field, what regex to use, etc.
# * <tt>password_field:</tt> default: options[:session_class].password_field, the name of the field to set the password, *NOT* the field the encrypted password is stored
# * <tt>crypted_password_field:</tt> default: depends on which columns are present, checks: crypted_password, encrypted_password, password_hash, pw_hash, if none are present defaults to crypted_password. This is the name of column that your encrypted password is stored.
# * <tt>password_salt_field:</tt> default: depends on which columns are present, checks: password_salt, pw_salt, salt, if none are present defaults to password_salt. This is the name of the field your salt is stored, only relevant for a hash crypto provider.
# * <tt>remember_token_field:</tt> default: options[:session_class].remember_token_field, the name of the field your remember token is stored. What the cookie stores so the session can be "remembered"
# * <tt>scope:</tt> default: nil, if all of your users belong to an account you might want to scope everything to the account. Just pass :account_id
# * <tt>logged_in_timeout:</tt> default: 10.minutes, this allows you to specify a time the determines if a user is logged in or out. Useful if you want to count how many users are currently logged in.
- # * <tt>session_ids:</tt> default: [nil], the sessions that we want to automatically reset when a user is created or updated so you don't have to worry about this. Set to [] to disable. Should be an array of ids. See Authgasm::Session::Base#initialize for information on ids. The order is important. The first id should be your main session, the session they need to log into first. This is generally nil, meaning so explicitly set id.
+ # * <tt>session_ids:</tt> default: [nil], the sessions that we want to automatically reset when a user is created or updated so you don't have to worry about this. Set to [] to disable. Should be an array of ids. See Authlogic::Session::Base#initialize for information on ids. The order is important. The first id should be your main session, the session they need to log into first. This is generally nil, meaning so explicitly set id.
def acts_as_authentic(options = {})
# Setup default options
options[:session_class] ||= "#{name}Session".constantize
@@ -70,7 +66,7 @@ def acts_as_authentic(options = {})
options[:remember_token_field] ||= options[:session_class].remember_token_field
options[:logged_in_timeout] ||= 10.minutes
options[:session_ids] ||= [nil]
-
+
# Validations
case options[:login_field_type]
when :email
@@ -84,34 +80,34 @@ def acts_as_authentic(options = {})
validates_length_of options[:login_field], :within => 2..100
validates_format_of options[:login_field], :with => /\A\w[\w\.\-_@]+\z/, :message => "use only letters, numbers, and .-_@ please."
end
-
+
validates_uniqueness_of options[:login_field], :scope => options[:scope]
validates_uniqueness_of options[:remember_token_field]
validate :validate_password
validates_numericality_of :login_count, :only_integer => :true, :greater_than_or_equal_to => 0, :allow_nil => true if column_names.include?("login_count")
-
+
if column_names.include?("last_request_at")
named_scope :logged_in, lambda { {:conditions => ["last_request_at > ?", options[:logged_in_timeout].ago]} }
named_scope :logged_out, lambda { {:conditions => ["last_request_at <= ?", options[:logged_in_timeout].ago]} }
end
-
+
before_save :get_session_information, :if => :update_sessions?
after_save :maintain_sessions!, :if => :update_sessions?
-
+
# Attributes
attr_writer "confirm_#{options[:password_field]}"
attr_accessor "tried_to_set_#{options[:password_field]}"
-
+
# Class methods
class_eval <<-"end_eval", __FILE__, __LINE__
def self.unique_token
crypto_provider.encrypt(Time.now.to_s + (1..10).collect{ rand.to_s }.join)
end
-
+
def self.crypto_provider
#{options[:crypto_provider]}
end
-
+
def self.forget_all!
# Paginate these to save on memory
records = nil
@@ -123,7 +119,7 @@ def self.forget_all!
end while !records.blank?
end
end_eval
-
+
# Instance methods
if column_names.include?("last_request_at")
class_eval <<-"end_eval", __FILE__, __LINE__
@@ -132,7 +128,7 @@ def logged_in?
end
end_eval
end
-
+
case options[:crypto_provider_type]
when :hash
class_eval <<-"end_eval", __FILE__, __LINE__
@@ -144,7 +140,7 @@ def #{options[:password_field]}=(pass)
self.#{options[:password_salt_field]} = self.class.unique_token
self.#{options[:crypted_password_field]} = crypto_provider.encrypt(@#{options[:password_field]} + #{options[:password_salt_field]})
end
-
+
def valid_#{options[:password_field]}?(attempted_password)
return false if attempted_password.blank?
attempted_password == #{options[:crypted_password_field]} || #{options[:crypted_password_field]} == crypto_provider.encrypt(attempted_password + #{options[:password_salt_field]})
@@ -159,27 +155,27 @@ def #{options[:password_field]}=(pass)
self.#{options[:remember_token_field]} = self.class.unique_token
self.#{options[:crypted_password_field]} = crypto_provider.encrypt(@#{options[:password_field]})
end
-
+
def valid_#{options[:password_field]}?(attemtped_password)
return false if attempted_password.blank?
attempted_password == #{options[:crypted_password_field]} || #{options[:crypted_password_field]} = crypto_provider.decrypt(attempted_password)
end
end_eval
end
-
+
class_eval <<-"end_eval", __FILE__, __LINE__
def #{options[:password_field]}; end
def confirm_#{options[:password_field]}; end
-
+
def crypto_provider
self.class.crypto_provider
end
-
+
def forget!
self.#{options[:remember_token_field]} = self.class.unique_token
save_without_session_maintenance(false)
end
-
+
def reset_#{options[:password_field]}!
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
newpass = ""
@@ -189,24 +185,24 @@ def reset_#{options[:password_field]}!
save_without_session_maintenance(false)
end
alias_method :randomize_password!, :reset_password!
-
+
def save_without_session_maintenance(*args)
@skip_session_maintenance = true
result = save(*args)
@skip_session_maintenance = false
result
end
-
+
protected
def update_sessions?
!@skip_session_maintenance && #{options[:session_class]}.activated? && !#{options[:session_ids].inspect}.blank? && #{options[:remember_token_field]}_changed?
end
-
+
def get_session_information
# Need to determine if we are completely logged out, or logged in as another user
@_sessions = []
@_logged_out = true
-
+
#{options[:session_ids].inspect}.each do |session_id|
session = #{options[:session_class]}.find(*[session_id].compact)
if session
@@ -217,7 +213,7 @@ def get_session_information
end
end
end
-
+
def maintain_sessions!
if @_logged_out
create_session!
@@ -225,20 +221,20 @@ def maintain_sessions!
update_sessions!
end
end
-
+
def create_session!
# We only want to automatically login into the first session, since this is the main session. The other sessions are sessions
# that need to be created after logging into the main session.
session_id = #{options[:session_ids].inspect}.first
-
+
# If we are already logged in, ignore this completely. All that we care about is updating ourself.
next if #{options[:session_class]}.find(*[session_id].compact)
-
+
# Log me in
args = [self, session_id].compact
#{options[:session_class]}.create(*args)
end
-
+
def update_sessions!
# We found sessions above, let's update them with the new info
@_sessions.each do |stale_session|
@@ -246,11 +242,11 @@ def update_sessions!
stale_session.save
end
end
-
+
def tried_to_set_password?
tried_to_set_password == true
end
-
+
def validate_password
if new_record? || tried_to_set_#{options[:password_field]}?
if @#{options[:password_field]}.blank?
@@ -266,4 +262,4 @@ def validate_password
end
end
-ActiveRecord::Base.send(:include, Authgasm::ActsAsAuthentic)
+ActiveRecord::Base.extend Authlogic::ActiveRecord::ActsAsAuthentic
View
19 lib/authlogic/active_record/authenticates_many.rb
@@ -0,0 +1,19 @@
+module Authlogic
+ module ActiveRecord
+ module AuthenticatesMany
+ def authenticates_many(name, options = {})
+ options[:session_class] ||= name.to_s.classify.constantize
+ options[:relationship_name] ||= options[:session_class].klass_name.underscore.pluralize
+ class_eval <<-"end_eval", __FILE__, __LINE__
+ def #{name}
+ find_options = #{options[:find_options].inspect} || #{options[:relationship_name]}.scope(:find)
+ find_options.delete_if { |key, value| ![:conditions, :include, :joins].include?(key.to_sym) || value.nil? }
+ @#{name} ||= Authlogic::ActiveRecord::ScopedSession.new(#{options[:session_class]}, find_options, #{options[:scope_cookies] ? "self.class.model_name.underscore + '_' + self.send(self.class.primary_key).to_s" : "nil"})
+ end
+ end_eval
+ end
+ end
+ end
+end
+
+ActiveRecord::Base.extend Authlogic::ActiveRecord::AuthenticatesMany
View
28 lib/authlogic/active_record/scoped_session.rb
@@ -0,0 +1,28 @@
+module Authlogic
+ module ActiveRecord
+ class ScopedSession # :nodoc:
+ attr_accessor :klass, :find_options, :id
+
+ def initialize(klass, find_options, id)
+ self.klass = klass
+ self.find_options = find_options
+ self.id = id
+ end
+
+ [:create, :create!, :find, :new].each do |method|
+ class_eval <<-"end_eval", __FILE__, __LINE__
+ def #{method}(*args)
+ klass.with_scope(scope_options) do
+ klass.#{method}(*args)
+ end
+ end
+ end_eval
+ end
+
+ private
+ def scope_options
+ {:find_options => find_options, :id => id}
+ end
+ end
+ end
+end
View
4 ...m/controller_adapters/abstract_adapter.rb → ...c/controller_adapters/abstract_adapter.rb
@@ -1,7 +1,7 @@
-module Authgasm
+module Authlogic
module ControllerAdapters # :nodoc:
# = Abstract Adapter
- # Allows you to use Authgasm in any framework you want, not just rails. See tha RailsAdapter for an example of how to adapter Authgasm to work with your framework.
+ # Allows you to use Authlogic in any framework you want, not just rails. See tha RailsAdapter for an example of how to adapter Authlogic to work with your framework.
class AbstractAdapter
attr_accessor :controller
View
10 ...gasm/controller_adapters/rails_adapter.rb → ...ogic/controller_adapters/rails_adapter.rb
@@ -1,7 +1,7 @@
-module Authgasm
+module Authlogic
module ControllerAdapters
# = Rails Adapter
- # Adapts authgasm to work with rails. The point is to close the gap between what authgasm expects and what the rails controller object
+ # Adapts authlogic to work with rails. The point is to close the gap between what authlogic expects and what the rails controller object
# provides. Similar to how ActiveRecord has an adapter for MySQL, PostgreSQL, SQLite, etc.
class RailsAdapter < AbstractAdapter
def authenticate_with_http_basic(*args, &block)
@@ -22,7 +22,7 @@ def session
end
# = Rails Implementation
- # Lets Authgasm know about the controller object, AKA "activates" authgasm.
+ # Lets Authlogic know about the controller object, AKA "activates" authlogic.
module RailsImplementation
def self.included(klass) # :nodoc:
klass.prepend_before_filter :set_controller
@@ -30,10 +30,10 @@ def self.included(klass) # :nodoc:
private
def set_controller
- Authgasm::Session::Base.controller = RailsAdapter.new(self)
+ Authlogic::Session::Base.controller = RailsAdapter.new(self)
end
end
end
end
-ActionController::Base.send(:include, Authgasm::ControllerAdapters::RailsImplementation)
+ActionController::Base.send(:include, Authlogic::ControllerAdapters::RailsImplementation)
View
6 ...uthgasm/session/active_record_trickery.rb → ...thlogic/session/active_record_trickery.rb
@@ -1,9 +1,9 @@
-module Authgasm
+module Authlogic
module Session
# = ActiveRecord Trickery
#
- # Authgasm looks like ActiveRecord, sounds like ActiveRecord, but its not ActiveRecord. That's the goal here. This is useful for the various rails helper methods such as form_for, error_messages_for, etc.
- # These helpers exptect various methods to be present. This adds in those methods into Authgasm.
+ # Authlogic looks like ActiveRecord, sounds like ActiveRecord, but its not ActiveRecord. That's the goal here. This is useful for the various rails helper methods such as form_for, error_messages_for, etc.
+ # These helpers exptect various methods to be present. This adds in those methods into Authlogic.
module ActiveRecordTrickery
def self.included(klass) # :nodoc:
klass.extend ClassMethods
View
128 lib/authgasm/session/base.rb → lib/authlogic/session/base.rb
@@ -1,15 +1,15 @@
-module Authgasm
+module Authlogic
module Session # :nodoc:
# = Base
#
- # This is the muscle behind Authgasm. For detailed information on how to use this please refer to the README. For detailed method explanations see below.
+ # This is the muscle behind Authlogic. For detailed information on how to use this please refer to the README. For detailed method explanations see below.
class Base
include Config
class << self
# 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. By default this is done for you automatically, but if you are using Authgasm in a unique way outside of rails, you need to assign a controller
- # object to Authgasm via Authgasm::Session::Base.controller = obj.
+ # without establishing a DB connection. By default this is done for you automatically, but if you are using Authlogic in a unique way outside of rails, you need to assign a controller
+ # object to Authlogic via Authlogic::Session::Base.controller = obj.
def activated?
!controller.blank?
end
@@ -37,7 +37,7 @@ def create!(*args)
session.save!
end
- # Finds your session by session, then cookie, and finally basic http auth. Perfect for that global before_filter to find your logged in user:
+ # A convenience method for session.find_record. Finds your session by session, then cookie, and finally basic http auth. Perfect for that global before_filter to find your logged in user:
#
# before_filter :load_user
#
@@ -50,15 +50,7 @@ def create!(*args)
def find(id = nil)
args = [id].compact
session = new(*args)
- find_with.each do |find_method|
- if session.send("valid_#{find_method}?")
- if session.record.class.column_names.include?("last_request_at")
- session.record.last_request_at = Time.now
- session.record.save_without_session_maintenance(false)
- end
- return session
- end
- end
+ return session if session.find_record
nil
end
@@ -78,15 +70,77 @@ def klass_name # :nodoc:
end
end
+ # The current scope set, should be used in the block passed to with_scope.
+ def scope
+ scopes[Thread.current]
+ end
+
+ # Authentication can be scoped, but scoping authentication can get a little tricky. Checkout the section "Scoping" in the readme for more details.
+ #
+ # What with_scopes focuses on is scoping the query when finding the object and the name of the cookies.
+ #
+ # with_scope accepts a hash with any of the following options:
+ #
+ # * <tt>find_options:</tt> any options you can pass into ActiveRecord::Base.find. This is used when trying to find the record.
+ # * <tt>id:</tt> see the id method above
+ #
+ # So you use it just like an ActiveRecord scope, essentially:
+ #
+ # UserSession.with_scope(:find_options => {:conditions => "account_id = 2"}, :id => "account_2") do
+ # UserSession.find
+ # end
+ #
+ # Eseentially what the above does is scope the searching of the object with the sql you provided. So instead of:
+ #
+ # User.find(:first, :conditions => "login = 'ben'")
+ #
+ # it would be:
+ #
+ # User.find(:first, :conditions => "login = 'ben' and account_id = 2")
+ #
+ # You will also notice the :id option. This works just like the id method. It scopes your cookies. So the name of your cookie will be:
+ #
+ # account_2_user_credentials
+ #
+ # instead of:
+ #
+ # user_credentials
+ #
+ # What is also nifty about scoping with an :id is that it merges your id's. So if you do:
+ #
+ # UserSession.with_scope(:find_options => {:conditions => "account_id = 2"}, :id => "account_2") do
+ # session = UserSession.new
+ # session.id = :secure
+ # end
+ #
+ # The name of your cookies will be:
+ #
+ # secure_account_2_user_credentials
+ def with_scope(options = {}, &block)
+ raise ArgumentError.new("You must provide a block") unless block_given?
+ self.scope = options
+ result = yield
+ self.scope = nil
+ result
+ end
+
private
def controllers
@@controllers ||= {}
end
+
+ def scope=(value)
+ scopes[Thread.current] = value
+ end
+
+ def scopes
+ @scopes ||= {}
+ end
end
attr_accessor :login_with, :new_session
attr_reader :record, :unauthorized_record
- attr_writer :id
+ attr_writer :id, :scope
# You can initialize a session by doing any of the following:
#
@@ -95,7 +149,7 @@ def controllers
# UserSession.new(:login => login, :password => password)
# UserSession.new(User.first)
#
- # If a user has more than one session you need to pass an id so that Authgasm knows how to differentiate the sessions. The id MUST be a Symbol.
+ # If a user has more than one session you need to pass an id so that Authlogic knows how to differentiate the sessions. The id MUST be a Symbol.
#
# UserSession.new(:my_id)
# UserSession.new(login, password, :my_id)
@@ -103,12 +157,13 @@ def controllers
# UserSession.new(User.first, :my_id)
#
# Ids are rarely used, but they can be useful. For example, what if users allow other users to login into their account via proxy? Now that user can "technically" be logged into 2 accounts at once.
- # To solve this just pass a id called :proxy, or whatever you want. Authgasm will separate everything out.
+ # To solve this just pass a id called :proxy, or whatever you want. Authlogic will separate everything out.
def initialize(*args)
raise NotActivated.new(self) unless self.class.activated?
create_configurable_methods!
+ self.scope = self.class.scope
self.id = args.pop if args.last.is_a?(Symbol)
case args.size
@@ -152,7 +207,7 @@ def destroy
true
end
- # The errors in Authgasm work JUST LIKE ActiveRecord. In fact, it uses the exact same ActiveRecord errors class. Use it the same way:
+ # The errors in Authlogic work JUST LIKE ActiveRecord. In fact, it uses the exact same ActiveRecord errors class. Use it the same way:
#
# === Example
#
@@ -169,6 +224,21 @@ def errors
@errors ||= Errors.new(self)
end
+ # Attempts to find the record by session, then cookie, and finally basic http auth. See the class level find method if you are wanting to use this in a before_filter to persist your session.
+ def find_record
+ return record if record
+ find_with.each do |find_method|
+ if send("valid_#{find_method}?")
+ if record.class.column_names.include?("last_request_at")
+ record.last_request_at = Time.now
+ record.save_without_session_maintenance(false)
+ end
+ return record
+ end
+ end
+ nil
+ 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
# user sessions. Just use an id for the "secure" session and you should be good.
@@ -224,6 +294,11 @@ def remember_me_until
remember_me_for.from_now
end
+ # See the class level with_scope method on information on scopes. with_scope essentialls sets this scope with the options passed and unsets it after the block executes.
+ def scope
+ @scope ||= {}
+ end
+
# Creates / updates a new user session for you. It does all of the magic:
#
# 1. validates
@@ -271,6 +346,8 @@ def unauthorized_record=(value)
@unauthorized_record = value
end
+ # Returns if the session is valid or not. Basically it means that a record could or could not be found. If the session is valid you will have a result when calling the "record" method. If it was unsuccessful
+ # you will not have a record.
def valid?
errors.clear
temp_record = validate_credentials
@@ -281,6 +358,7 @@ def valid?
false
end
+ # Tries to validate the session from information from a basic http auth, if it was provided.
def valid_http_auth?
controller.authenticate_with_http_basic do |login, password|
if !login.blank? && !password.blank?
@@ -297,9 +375,10 @@ def valid_http_auth?
false
end
+ # Tries to validate the session from information in the cookie
def valid_cookie?
if cookie_credentials
- self.unauthorized_record = klass.send("find_by_#{remember_token_field}", cookie_credentials)
+ self.unauthorized_record = search_for_record("find_by_#{remember_token_field}", cookie_credentials)
result = valid?
if result
update_session!
@@ -311,9 +390,10 @@ def valid_cookie?
false
end
+ # Tries to validate the session from information in the session
def valid_session?
if session_credentials
- self.unauthorized_record = klass.send("find_by_#{remember_token_field}", cookie_credentials)
+ self.unauthorized_record = search_for_record("find_by_#{remember_token_field}", session_credentials)
result = valid?
if result
self.new_session = false
@@ -359,6 +439,12 @@ def protected_#{password_field}
end_eval
end
+ def search_for_record(method, value)
+ klass.send(:with_scope, :find => (scope[:find_options] || {})) do
+ klass.send(method, value)
+ end
+ end
+
def klass
self.class.klass
end
@@ -384,7 +470,7 @@ def validate_credentials
errors.add(password_field, "can not be blank") if send("protected_#{password_field}").blank?
return if errors.count > 0
- temp_record = klass.send(find_by_login_method, send(login_field))
+ temp_record = search_for_record(find_by_login_method, send(login_field))
if temp_record.blank?
errors.add(login_field, "was not found")
View
4 lib/authgasm/session/callbacks.rb → lib/authlogic/session/callbacks.rb
@@ -1,8 +1,8 @@
-module Authgasm
+module Authlogic
module Session
# = Callbacks
#
- # Just like in ActiveRecord you have before_save, before_validation, etc. You have similar callbacks with Authgasm, see all callbacks below.
+ # Just like in ActiveRecord you have before_save, before_validation, etc. You have similar callbacks with Authlogic, see all callbacks below.
module Callbacks
CALLBACKS = %w(before_create after_create before_destroy after_destroy before_save after_save before_update after_update before_validation after_validation)
View
16 lib/authgasm/session/config.rb → lib/authlogic/session/config.rb
@@ -1,4 +1,4 @@
-module Authgasm
+module Authlogic
module Session
module Config # :nodoc:
def self.included(klass)
@@ -17,7 +17,7 @@ def self.included(klass)
#
# or you can set your configuration in the session class directly:
#
- # class UserSession < Authgasm::Session::Base
+ # class UserSession < Authlogic::Session::Base
# authenticate_with User
# # ... more configuration
# end
@@ -56,7 +56,7 @@ def cookie_key(value = nil)
end
alias_method :cookie_key=, :cookie_key
- # The name of the method used to find the record by the login. What's nifty about this is that you can do anything in your method, Authgasm will just pass you the login.
+ # The name of the method used to find the record by the login. What's nifty about this is that you can do anything in your method, Authlogic will just pass you the login.
#
# Let's say you allow users to login by username or email. Set this to "find_login", or whatever method you want. Then in your model create a class method like:
#
@@ -89,7 +89,7 @@ def find_with(*values)
end
alias_method :find_with=, :find_with
- # The name of the method you want Authgasm to create for storing the login / username. Keep in mind this is just for your Authgasm::Session, if you want it can be something completely different
+ # 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.
#
@@ -194,13 +194,17 @@ def verify_password_method(value = nil)
module InstanceMethods # :nodoc:
def cookie_key
- key_parts = [id, self.class.cookie_key].compact
+ key_parts = [id, scope[:id], self.class.cookie_key].compact
key_parts.join("_")
end
def find_by_login_method
self.class.find_by_login_method
end
+
+ def find_with
+ self.class.find_with
+ end
def login_field
self.class.login_field
@@ -220,7 +224,7 @@ def remember_token_field
end
def session_key
- key_parts = [id, self.class.session_key].compact
+ key_parts = [id, scope[:id], self.class.session_key].compact
key_parts.join("_")
end
View
4 lib/authgasm/session/errors.rb → lib/authlogic/session/errors.rb
@@ -1,11 +1,11 @@
-module Authgasm
+module Authlogic
module Session
class Errors < ::ActiveRecord::Errors # :nodoc:
end
class NotActivated < ::StandardError # :nodoc:
def initialize(session)
- super("You must activate the Authgasm::Session::Base.controller with a controller object before creating objects")
+ super("You must activate the Authlogic::Session::Base.controller with a controller object before creating objects")
end
end
View
4 lib/authgasm/sha512_crypto_provider.rb → lib/authlogic/sha512_crypto_provider.rb
@@ -1,12 +1,12 @@
require "digest/sha2"
-module Authgasm
+module Authlogic
# = Sha512 Crypto Provider
#
# The acts_as_authentic method allows you to pass a :crypto_provider option. This allows you to use any type of encryption you like. Just create a class with a class level encrypt and decrypt method.
# The password will be passed as the single parameter to each of these methods so you can do your magic.
#
- # If you are encrypting via a hash just don't include a decrypt method, since hashes can't be decrypted. Authgasm will notice this adjust accordingly.
+ # If you are encrypting via a hash just don't include a decrypt method, since hashes can't be decrypted. Authlogic will notice this adjust accordingly.
class Sha512CryptoProvider
STRETCHES = 20
def self.encrypt(pass)
View
4 lib/authgasm/version.rb → lib/authlogic/version.rb
@@ -1,4 +1,4 @@
-module Authgasm # :nodoc:
+module Authlogic # :nodoc:
# = Version
#
# A class for describing the current version of a library. The version
@@ -44,7 +44,7 @@ def to_a
MAJOR = 0
MINOR = 10
- TINY = 3
+ TINY = 4
# The current version as a Version instance
CURRENT = new(MAJOR, MINOR, TINY)
View
32 test_app/app/controllers/application.rb
@@ -3,11 +3,27 @@ class ApplicationController < ActionController::Base
protect_from_forgery # :secret => '3e944977657f54e55cb20d83a418ff65'
filter_parameter_logging :password, :confirm_password
+ helper_method :scoped_url
+
+ before_filter :load_company
before_filter :load_current_user
private
+ def load_company
+ if params[:company_id]
+ @current_company = Company.find_by_id(params[:company_id])
+ if @current_company.blank?
+ flash[:notice] = "The company specified could not be found"
+ redirect_to default_url
+ return false
+ end
+ end
+ end
+
def load_current_user
- @user_session = UserSession.find
+ @session_owner = (@current_company && @current_company.user_sessions) || UserSession
+ @user_owner = (@current_company && @current_company.users) || User
+ @user_session = @session_owner.find
@current_user = @user_session && @user_session.record
end
@@ -15,7 +31,7 @@ def require_user
unless @current_user
store_location
flash[:notice] = "You must be logged in to access this page"
- redirect_to new_user_session_url
+ redirect_to scoped_url("new_user_session_url")
return false
end
end
@@ -24,7 +40,7 @@ def require_no_user
if @current_user
store_location
flash[:notice] = "You must be logged out to access this page"
- redirect_to account_url
+ redirect_to scoped_url("account_url")
return false
end
end
@@ -33,6 +49,16 @@ def prevent_store_location
@prevent_store_location = true
end
+ def scoped_url(unscoped_url, *args)
+ if @current_company
+ regex = /^(new|edit)_/
+ prefix = unscoped_url =~ regex ? "#{$1}_" : ""
+ send("#{prefix}company_#{unscoped_url.gsub(regex, "")}", @current_company.id, *args)
+ else
+ send(unscoped_url, *args)
+ end
+ end
+
def store_location
return if @prevent_store_location == true
session[:return_to] = request.request_uri
View
2  test_app/app/controllers/companies_controller.rb
@@ -0,0 +1,2 @@
+class CompaniesController < ApplicationController
+end
View
8 test_app/app/controllers/user_sessions_controller.rb
@@ -4,14 +4,14 @@ class UserSessionsController < ApplicationController
before_filter :require_user, :only => :destroy
def new
- @user_session = UserSession.new
+ @user_session = @session_owner.new
end
def create
- @user_session = UserSession.new(params[:user_session])
+ @user_session = @session_owner.new(params[:user_session])
if @user_session.save
flash[:notice] = "Login successful!"
- redirect_back_or_default(account_url)
+ redirect_back_or_default(scoped_url("account_url"))
else
render :action => :new
end
@@ -20,6 +20,6 @@ def create
def destroy
@user_session.destroy
flash[:notice] = "Logout successful!"
- redirect_back_or_default(new_user_session_url)
+ redirect_back_or_default(scoped_url("new_user_session_url"))
end
end
View
14 test_app/app/controllers/users_controller.rb
@@ -4,14 +4,14 @@ class UsersController < ApplicationController
before_filter :load_user, :except => [:new, :create]
def new
- @user = User.new
+ @user = @user_owner.new
end
def create
- @user = User.new(params[:user])
+ @user = @user_owner.new(params[:user])
if @user.save
flash[:notice] = "Account registered!"
- redirect_to account_path
+ redirect_to scoped_url("account_path")
else
render :action => :new
end
@@ -22,7 +22,7 @@ def show
@user.update_attribute(:profile_views, @user.profile_views + 1) if @user && params[:id]
else
flash[:notice] = "We're sorry, but no user was found"
- redirect_to new_user_session_url
+ redirect_to scoped_url("new_user_session_url")
end
end
@@ -34,7 +34,7 @@ def reset_password
@user.save
else
flash[:notice] = "We're sorry, but no user was found"
- redirect_to new_user_session_url
+ redirect_to scoped_url("new_user_session_url")
end
end
@@ -43,7 +43,7 @@ def update
@user.attributes = params[:user]
if @user.save
flash[:notice] = "Account updated!"
- redirect_to account_path
+ redirect_to scoped_url("account_path")
else
render :action => :edit
end
@@ -52,7 +52,7 @@ def update
private
def load_user
if params[:id]
- @user = User.find_by_id(params[:id])
+ @user = @user_owner.find_by_id(params[:id])
@user.update_attribute(:profile_views, @user.profile_views + 1) if @user
else
@user = @current_user
View
2  test_app/app/helpers/companies_helper.rb
@@ -0,0 +1,2 @@
+module CompaniesHelper
+end
View
3  test_app/app/models/account.rb
@@ -1,3 +0,0 @@
-class Account < ActiveRecord::Base
- has_many :users, :dependent => :destroy
-end
View
4 test_app/app/models/company.rb
@@ -0,0 +1,4 @@
+class Company < ActiveRecord::Base
+ has_many :users, :dependent => :destroy
+ authenticates_many :user_sessions, :scope_cookies => true
+end
View
3  test_app/app/models/project.rb
@@ -0,0 +1,3 @@
+class Project < ActiveRecord::Base
+ has_and_belongs_to_many :users
+end
View
3  test_app/app/models/user.rb
@@ -1,4 +1,5 @@
class User < ActiveRecord::Base
acts_as_authentic
- belongs_to :account
+ has_and_belongs_to_many :projects
+ belongs_to :company
end
View
2  test_app/app/models/user_session.rb
@@ -1,3 +1,3 @@
-class UserSession < Authgasm::Session::Base
+class UserSession < Authlogic::Session::Base
remember_me true
end
View
12 test_app/app/views/layouts/application.html.erb
@@ -8,13 +8,15 @@
<%= stylesheet_link_tag 'scaffold' %>
</head>
<body>
-
+
+<% if @company %><h1><%= @company.name %></h1><% end %>
+
<% if !@current_user %>
- <%= link_to "Register", new_account_path %> |
- <%= link_to "Log In", new_user_session_path %>
+ <%= link_to "Register", scoped_url("new_account_path") %> |
+ <%= link_to "Log In", scoped_url("new_user_session_path") %>
<% else %>
- <%= link_to "My Account", account_path %> |
- <%= link_to "Logout", user_session_path, :method => :delete, :confirm => "Are you sure you want to logout?" %>
+ <%= link_to "My Account", scoped_url("account_path") %> |
+ <%= link_to "Logout", scoped_url("user_session_path"), :method => :delete, :confirm => "Are you sure you want to logout?" %>
<% end %>
<p style="color: green"><%= flash[:notice] %></p>
View
2  test_app/app/views/user_sessions/new.html.erb
@@ -2,7 +2,7 @@
<%= error_messages_for "user_session", :header_message => nil %>
-<% form_for @user_session, :url => user_session_path do |f| %>
+<% form_for @user_session, :url => scoped_url("user_session_path") do |f| %>
<%= f.label :login %><br />
<%= f.text_field :login %><br />
<br />
View
2  test_app/app/views/users/edit.html.erb
@@ -2,7 +2,7 @@
<%= error_messages_for "user" %>
-<% form_for @user, :url => account_path do |f| %>
+<% form_for @user, :url => scoped_url("account_path") do |f| %>
<%= render :partial => "form", :object => f %>
<%= f.submit "Update" %>
<% end %>
View
2  test_app/app/views/users/new.html.erb
@@ -2,7 +2,7 @@
<%= error_messages_for "user" %>
-<% form_for @user, :url => account_path do |f| %>
+<% form_for @user, :url => scoped_url("account_path") do |f| %>
<%= render :partial => "form", :object => f %>
<%= f.submit "Register" %>
<% end %>
View
2  test_app/app/views/users/show.html.erb
@@ -25,5 +25,5 @@
<br />
<% if @user == @current_user %>
- <%= link_to "Edit", edit_account_path %><br />
+ <%= link_to "Edit", scoped_url("edit_account_path") %><br />
<% end %>
View
2  test_app/config/environment.rb
@@ -31,7 +31,7 @@
# :all can be used as a placeholder for all plugins not explicitly named
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
config.plugin_paths += ["#{RAILS_ROOT}/../.."]
- config.plugins = [:authgasm]
+ config.plugins = [:authlogic]
# Add additional load paths for your own custom dirs
# config.load_paths += %W( #{RAILS_ROOT}/extras )
View
5 test_app/config/routes.rb
@@ -1,6 +1,11 @@
ActionController::Routing::Routes.draw do |map|
map.resource :user_session
map.resource :account, :controller => "users"
+ map.resources :companies do |company|
+ company.resource :account, :controller => "users"
+ company.resource :user_session
+ company.resources :users
+ end
map.resources :users, :member => {:reset_password => :get}
map.default "/", :controller => "user_sessions", :action => "new"
end
View
1  test_app/db/migrate/20081023040052_create_users.rb
@@ -2,7 +2,6 @@ class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.timestamps
- t.integer :account_id, :null => false
t.string :login, :null => false
t.string :crypted_password
t.string :password_salt
View
12 test_app/db/migrate/20081101190907_create_accounts.rb
@@ -1,12 +0,0 @@
-class CreateAccounts < ActiveRecord::Migration
- def self.up
- create_table :accounts do |t|
- t.timestamps
- t.string :name
- end
- end
-
- def self.down
- drop_table :accounts
- end
-end
View
14 test_app/db/migrate/20081103003828_create_companies.rb
@@ -0,0 +1,14 @@
+class CreateCompanies < ActiveRecord::Migration
+ def self.up
+ create_table :companies do |t|
+ t.timestamps
+ t.string :name
+ end
+ add_column :users, :company_id, :integer
+ end
+
+ def self.down
+ drop_table :companies
+ remove_column :users, :company_id
+ end
+end
View
18 test_app/db/migrate/20081103003834_create_projects.rb
@@ -0,0 +1,18 @@
+class CreateProjects < ActiveRecord::Migration
+ def self.up
+ create_table :projects do |t|
+ t.timestamps
+ t.string :name
+ end
+
+ create_table :projects_users, :id => false, :force => true do |t|
+ t.integer :project_id, :null => false
+ t.integer :user_id, :null => false
+ end
+ end
+
+ def self.down
+ drop_table :projects
+ drop_table :projects_users
+ end
+end
View
0  test_app/test/fixtures/accounts.yml → test_app/test/fixtures/companies.yml
File renamed without changes
View
4 test_app/test/fixtures/projects.yml
@@ -0,0 +1,4 @@
+web_services:
+ id: 1
+ name: web services
+
View
10 test_app/test/fixtures/users.yml
@@ -1,19 +1,21 @@
ben:
id: 1
- account_id: 1
+ company_id: 1
+ projects: web_services
login: bjohnson
password_salt: <%= salt = User.unique_token %>
- crypted_password: <%= Authgasm::Sha512CryptoProvider.encrypt("benrocks" + salt) %>
+ crypted_password: <%= Authlogic::Sha512CryptoProvider.encrypt("benrocks" + salt) %>
remember_token: 6cde0674657a8a313ce952df979de2830309aa4c11ca65805dd00bfdc65dbcc2f5e36718660a1d2e68c1a08c276d996763985d2f06fd3d076eb7bc4d97b1e317
first_name: Ben
last_name: Johnson
zack:
id: 2
- account_id: 2
+ company_id: 2
+ projects: web_services
login: zham
password_salt: <%= salt = User.unique_token %>
- crypted_password: <%= Authgasm::Sha512CryptoProvider.encrypt("zackrocks" + salt) %>
+ crypted_password: <%= Authlogic::Sha512CryptoProvider.encrypt("zackrocks" + salt) %>
remember_token: fd3c2d5ce09ab98e7547d21f1b3dcf9158a9a19b5d3022c0402f32ae197019fce3fdbc6614d7ee57d719bae53bb089e30edc9e5d6153e5bc3afca0ac1d320342
first_name: Zack
last_name: Ham
View
8 test_app/test/functional/companies_controller_test.rb
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class CompaniesControllerTest < ActionController::TestCase
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
View
46 test_app/test/integration/company_user_session_stories_test.rb
@@ -0,0 +1,46 @@
+require 'test_helper'
+
+class CompanyUserSessionStoriesTest < ActionController::IntegrationTest
+ def setup
+ super
+ self.scope = Company.first
+ end
+
+ def test_login_process
+ # Try to access the company account area without being logged in
+ get scoped_url("account_url")
+ assert_redirected_to scoped_url("new_user_session_url")
+ follow_redirect!
+ assert flash.key?(:notice)
+ assert_template "user_sessions/new"
+
+ # Try to login unsuccessfully
+ assert_unsuccessful_login
+ assert_unsuccessful_login("bjohnson", "badpassword")
+ assert_unsuccessful_login("zham", "zackrocks") # this is correct, but zack does not belong to this company
+
+ assert_successful_login("bjohnson", "benrocks")
+
+ # Try to log in again after a successful login
+ get scoped_url("new_user_session_url")
+ assert_redirected_to scoped_url("account_url")
+ follow_redirect!
+ assert flash.key?(:notice)
+ assert_template "users/show"
+
+ # Try to register after a successful login
+ get scoped_url("new_account_url")
+ assert_redirected_to scoped_url("account_url")
+ follow_redirect!
+ assert flash.key?(:notice)
+ assert_template "users/show"
+
+ assert_account_access
+ assert_successful_logout(scoped_url("new_account_url")) # before I tried to register, it stored my location
+
+ # Try to access my account again
+ get scoped_url("account_url")
+ assert_redirected_to scoped_url("new_user_session_url")
+ assert flash.key?(:notice)
+ end
+end
View
2  test_app/test/integration/user_sesion_stories_test.rb
@@ -14,7 +14,7 @@ def test_registration
assert_template "users/new"
# Register successfully
- post account_url, {:user => {:account_id => 1, :login => "binarylogic", :password => "pass", :confirm_password => "pass", :first_name => "Ben", :last_name => "Johnson"}}
+ post account_url, {:user => {:login => "binarylogic", :password => "pass", :confirm_password => "pass", :first_name => "Ben", :last_name => "Johnson"}}
assert_redirected_to account_url
assert flash.key?(:notice)
View
2  test_app/test/integration/user_session_test.rb
@@ -12,7 +12,7 @@ def test_activated
def test_create
assert !UserSession.create("unknown", "bad")
assert UserSession.create("bjohnson", "benrocks")
- assert_raise(Authgasm::Session::SessionInvalid) { assert !UserSession.create!("unknown", "bad") }
+ assert_raise(Authlogic::Session::SessionInvalid) { assert !UserSession.create!("unknown", "bad") }
assert_nothing_raised { UserSession.create!("bjohnson", "benrocks") }
end
View
51 test_app/test/test_helper.rb
@@ -9,54 +9,73 @@ class Test::Unit::TestCase
end
class ActionController::IntegrationTest
+ attr_accessor :scope
+
def setup
- #UserSession.reset_inheritable_attributes
- get new_user_session_url # to active authgasm
+ get new_user_session_url # to active authlogic
end
def teardown
- Authgasm::Session::Base.controller = nil
+ Authlogic::Session::Base.controller = nil
end
private
def assert_successful_login(login, password)
- post user_session_url, :user_session => {:login => login, :password => password}
- assert_redirected_to account_url
+ post scoped_url("user_session_url"), :user_session => {:login => login, :password => password}
+ assert_redirected_to scoped_url("account_url")
follow_redirect!
assert_template "users/show"
end
def assert_unsuccessful_login(login = nil, password = nil)
params = (login || password) ? {:user_session => {:login => login, :password => password}} : nil
- post user_session_url, params
+ post scoped_url("user_session_url"), params
assert_template "user_sessions/new"
end
def assert_successful_logout(alt_redirect = nil)
- redirecting_to = alt_redirect || new_user_session_url
- delete user_session_url
+ redirecting_to = alt_redirect || scoped_url("new_user_session_url")
+ delete scoped_url("user_session_url")
assert_redirected_to redirecting_to # because I tried to access registration above, and it stored it
follow_redirect!
assert flash.key?(:notice)
- assert_equal nil, session["user_credentials"]
- assert_equal "", cookies["user_credentials"]
- assert_template redirecting_to.gsub("http://www.example.com/", "").gsub("user_session", "user_sessions").gsub("account", "users")
+ assert_equal nil, session[scoped_key]
+ assert_equal "", cookies[scoped_key]
+ assert_template redirecting_to.gsub("http://www.example.com/", "").gsub("user_session", "user_sessions").gsub("account", "users").gsub(/^companies\/[1-9]*\//, "")
end
def assert_account_access(user = nil)
user ||= users(:ben).reload
# Perform multiple requests to make sure the session is persisting properly, just being anal here
3.times do
- get account_url
- assert_equal user.remember_token, session["user_credentials"]
- assert_equal user.remember_token, cookies["user_credentials"]
+ get scoped_url("account_url")
+ assert_equal user.remember_token, session[scoped_key]
+ assert_equal user.remember_token, cookies[scoped_key]
assert_response :success
assert_template "users/show"
end
end
def assert_no_account_access(alt_redirect = nil)
- get account_url
- assert_redirected_to alt_redirect || new_user_session_url
+ get scoped_url("account_url")
+ assert_redirected_to alt_redirect || scoped_url("new_user_session_url")
+ end
+
+ def scoped_url(unscoped_url, *args)
+ case scope
+ when Company
+ regex = /^(new|edit)_/
+ prefix = unscoped_url =~ regex ? "#{$1}_" : ""
+ send("#{prefix}company_#{unscoped_url.gsub(regex, "")}", scope.id, *args)
+ else
+ send(unscoped_url, *args)
+ end
+ end
+
+ def scoped_key
+ parts = []
+ parts << "#{scope.class.model_name.underscore}_#{scope.id}" if scope
+ parts << "user_credentials"
+ parts.join("_")
end
end
View
8 test_app/test/unit/company_test.rb
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class CompanyTest < ActiveSupport::TestCase
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
View
8 test_app/test/unit/project_test.rb
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class ProjectTest < ActiveSupport::TestCase
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
View
2  test_app/test/unit/user_test.rb
@@ -28,7 +28,7 @@ def test_unique_token
end
def test_crypto_provider
- assert_equal Authgasm::Sha512CryptoProvider, User.crypto_provider
+ assert_equal Authlogic::Sha512CryptoProvider, User.crypto_provider
end
def test_forget_all
Please sign in to comment.
Something went wrong with that request. Please try again.