authgasm 0.10.1 → 0.10.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +6 -0
- data/README.rdoc +6 -6
- data/authgasm.gemspec +3 -3
- data/lib/authgasm/acts_as_authentic.rb +26 -25
- data/lib/authgasm/session/base.rb +50 -44
- data/lib/authgasm/session/callbacks.rb +8 -7
- data/lib/authgasm/session/config.rb +6 -6
- data/lib/authgasm/sha512_crypto_provider.rb +4 -1
- data/lib/authgasm/version.rb +1 -1
- data/test_app/db/development.sqlite3 +0 -0
- data/test_app/db/test.sqlite3 +0 -0
- metadata +2 -2
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
== 0.10.2 released 2008-10-24
|
2
|
+
|
3
|
+
* Added in stretches to the default Sha512 encryption algorithm.
|
4
|
+
* Use column_names instead of columns when determining if a column is present.
|
5
|
+
* Improved validation callbacks. after_validation should only be run if valid? = true. Also clear errors before the "before_validation" callback.
|
6
|
+
|
1
7
|
== 0.10.1 released 2008-10-24
|
2
8
|
|
3
9
|
* Sessions now store the "remember token" instead of the id. This is much safer and guarantees all "sessions" that are logged in are logged in with a valid password. This way stale sessions can't be persisted.
|
data/README.rdoc
CHANGED
@@ -10,7 +10,7 @@ Wouldn't it be nice to keep your app up to date with the latest and greatest sec
|
|
10
10
|
|
11
11
|
What if creating a user session could be as simple as...
|
12
12
|
|
13
|
-
UserSession.create(params[:
|
13
|
+
UserSession.create(params[:user_session])
|
14
14
|
|
15
15
|
What if your user sessions controller could look just like your other controllers...
|
16
16
|
|
@@ -33,7 +33,7 @@ What if your user sessions controller could look just like your other controller
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
Look familiar? If you didn't know any better, you would think UserSession was an ActiveRecord model. I think that's pretty cool
|
36
|
+
Look familiar? If you didn't know any better, you would think UserSession was an ActiveRecord model. I think that's pretty cool, because it fits nicely into the RESTful development pattern, a style we all know and love. What about the view...
|
37
37
|
|
38
38
|
<%= error_messages_for "user_session" %>
|
39
39
|
<% form_for @user_session do |f| %>
|
@@ -95,7 +95,7 @@ It is important to set your configuration for your session before you set the co
|
|
95
95
|
|
96
96
|
=== Ensure proper database fields
|
97
97
|
|
98
|
-
The user model needs to have the following columns. The names of these columns can be changed with configuration.
|
98
|
+
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.
|
99
99
|
|
100
100
|
t.string :login, :null => false
|
101
101
|
t.string :crypted_password, :null => false
|
@@ -169,7 +169,7 @@ The errors in Authgasm work JUST LIKE ActiveRecord. In fact, it uses the exact s
|
|
169
169
|
|
170
170
|
== Automatic Session Updating
|
171
171
|
|
172
|
-
This is one of my favorite features that I think
|
172
|
+
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.
|
173
173
|
|
174
174
|
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.
|
175
175
|
|
@@ -244,13 +244,13 @@ I don't necessarily think the current solutions are "wrong", nor am I saying Aut
|
|
244
244
|
|
245
245
|
Generators have their place, and it certainly is not to add authentication to a rails app. It doesn't make sense. Generators are meant to be a starting point for repetitive tasks that have no sustainable pattern. Take controllers, the set up is the same thing over and over, but they eventually evolve to a point where there is no clear cut pattern. Trying to extract a pattern out into a library would be extremely hard, messy, and overly complicated. As a result, generators make sense here.
|
246
246
|
|
247
|
-
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 litter your application with code that you get to maintain. You get to make sure it stays up with the latest and greatest security techniques. How fun! Oh, 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! Awesome! The cherry on top is the fact that you get to go through every app you've made and apply this update. You don't really have a choice either, because you can't ignore security updates.
|
247
|
+
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 litter your application with code that you get to maintain. You get to make sure it stays up with the latest and greatest security techniques. How fun! Oh, 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! Awesome! The cherry on top is the fact that you get to go through every app you've made and apply this update. You don't really have a choice either, because you can't ignore security updates.
|
248
248
|
|
249
249
|
Security moves fast, and hackers make sure of this. As a result, it should be easy to update. Doesn't it make sense to leverage a library to handle this functionality for you? This way, when some new security technique is released, or a bug with your authentication system is found, you can fix it with a simple update. Just like everything else in ruby / rails.
|
250
250
|
|
251
251
|
=== Limited to a single authentication
|
252
252
|
|
253
|
-
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
|
253
|
+
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.
|
254
254
|
|
255
255
|
|
256
256
|
Copyright (c) 2008 Ben Johnson of [Binary Logic](http://www.binarylogic.com), released under the MIT license
|
data/authgasm.gemspec
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
|
2
|
-
# Gem::Specification for Authgasm-0.10.
|
2
|
+
# Gem::Specification for Authgasm-0.10.2
|
3
3
|
# Originally generated by Echoe
|
4
4
|
|
5
5
|
--- !ruby/object:Gem::Specification
|
6
6
|
name: authgasm
|
7
7
|
version: !ruby/object:Gem::Version
|
8
|
-
version: 0.10.
|
8
|
+
version: 0.10.2
|
9
9
|
platform: ruby
|
10
10
|
authors:
|
11
11
|
- Ben Johnson of Binary Logic
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
|
15
|
-
date: 2008-10-
|
15
|
+
date: 2008-10-29 00:00:00 -04:00
|
16
16
|
default_executable:
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
@@ -7,32 +7,33 @@ module Authgasm
|
|
7
7
|
# = Acts As Authentic
|
8
8
|
# Provides and "acts_as" method to include in your models to help with authentication. See method below.
|
9
9
|
module ClassMethods
|
10
|
-
# Call this method in your model to add in basic authentication madness
|
10
|
+
# Call this method in your model to add in basic authentication madness that your authgasm session expects.
|
11
11
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
12
|
+
# <b>Please keep in mind</b> that based on your configuration the method names could change. For example, if you pass the option:
|
13
|
+
#
|
14
|
+
# :password_field => :pass
|
15
|
+
#
|
16
|
+
# The method will not be password=, it will be pass=. Same with valid_password?, it will be valid_pass?, etc.
|
16
17
|
#
|
17
18
|
# === Methods
|
18
19
|
# For example purposes lets assume you have a User model.
|
19
20
|
#
|
20
21
|
# Class method name Description
|
21
|
-
# User.unique_token returns unique token generated by your :crypto_provider
|
22
22
|
# User.crypto_provider The class that you set in your :crypto_provider option
|
23
|
-
# User.forget_all!
|
23
|
+
# User.forget_all! Finds all records, loops through them, and calls forget! on each record. This is paginated to save on memory.
|
24
|
+
# User.unique_token returns unique token generated by your :crypto_provider
|
24
25
|
#
|
25
26
|
# Named Scopes
|
26
|
-
# User.logged_in Find all users who are logged in, based on your :logged_in_timeout option
|
27
|
-
# User.logged_out Same as above, but logged out
|
27
|
+
# User.logged_in Find all users who are logged in, based on your :logged_in_timeout option.
|
28
|
+
# User.logged_out Same as above, but logged out.
|
28
29
|
#
|
29
30
|
# Isntace method name
|
30
|
-
# user.password= Method name based on the :password_field option. This is used to set the password. Pass the *raw* password to this
|
31
|
-
# user.confirm_password= Confirms the password, needed to change the password
|
32
|
-
# user.valid_password?(pass)
|
33
|
-
# user.randomize_password! Basically resets the password to a random password using only letters and numbers
|
34
|
-
# user.logged_in? Based on the :logged_in_timeout option. Tells you if the user is logged in or not
|
35
|
-
# user.forget! Changes their remember token, making their cookie invalid.
|
31
|
+
# user.password= Method name based on the :password_field option. This is used to set the password. Pass the *raw* password to this.
|
32
|
+
# user.confirm_password= Confirms the password, needed to change the password.
|
33
|
+
# user.valid_password?(pass) Determines if the password passed is valid. The password could be encrypted or raw.
|
34
|
+
# user.randomize_password! Basically resets the password to a random password using only letters and numbers.
|
35
|
+
# user.logged_in? Based on the :logged_in_timeout option. Tells you if the user is logged in or not.
|
36
|
+
# user.forget! Changes their remember token, making their cookie and session invalid. A way to log the user out withouth changing their password.
|
36
37
|
#
|
37
38
|
# === Options
|
38
39
|
# * <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.
|
@@ -55,15 +56,15 @@ module Authgasm
|
|
55
56
|
options[:login_field_type] ||= options[:login_field] == :email ? :email : :login
|
56
57
|
options[:password_field] ||= options[:session_class].password_field
|
57
58
|
options[:crypted_password_field] ||=
|
58
|
-
(
|
59
|
-
(
|
60
|
-
(
|
61
|
-
(
|
59
|
+
(column_names.include?("crypted_password") && :crypted_password) ||
|
60
|
+
(column_names.include?("encrypted_password") && :encrypted_password) ||
|
61
|
+
(column_names.include?("password_hash") && :password_hash) ||
|
62
|
+
(column_names.include?("pw_hash") && :pw_hash) ||
|
62
63
|
:crypted_password
|
63
64
|
options[:password_salt_field] ||=
|
64
|
-
(
|
65
|
-
(
|
66
|
-
(
|
65
|
+
(column_names.include?("password_salt") && :password_salt) ||
|
66
|
+
(column_names.include?("pw_salt") && :pw_salt) ||
|
67
|
+
(column_names.include?("salt") && :salt) ||
|
67
68
|
:password_salt
|
68
69
|
options[:remember_token_field] ||= options[:session_class].remember_token_field
|
69
70
|
options[:logged_in_timeout] ||= 10.minutes
|
@@ -117,7 +118,7 @@ module Authgasm
|
|
117
118
|
i = 0
|
118
119
|
begin
|
119
120
|
records = find(:all, :limit => 50, :offset => i)
|
120
|
-
records.each { |record|
|
121
|
+
records.each { |record| record.forget! }
|
121
122
|
i += 50
|
122
123
|
end while !records.blank?
|
123
124
|
end
|
@@ -210,7 +211,7 @@ module Authgasm
|
|
210
211
|
end
|
211
212
|
|
212
213
|
def find_my_sessions
|
213
|
-
return if @saving_from_session || !#{options[:session_class]}.activated?
|
214
|
+
return if @saving_from_session || !#{options[:session_class]}.activated? || #{options[:session_ids].inspect}.blank?
|
214
215
|
|
215
216
|
@my_sessions = []
|
216
217
|
#{options[:session_ids].inspect}.each do |session_id|
|
@@ -224,7 +225,7 @@ module Authgasm
|
|
224
225
|
end
|
225
226
|
|
226
227
|
def update_sessions!
|
227
|
-
return if @saving_from_session ||
|
228
|
+
return if @saving_from_session || @my_sessions.blank?
|
228
229
|
|
229
230
|
@my_sessions.each do |stale_session|
|
230
231
|
stale_session.unauthorized_record = self
|
@@ -257,51 +257,12 @@ module Authgasm
|
|
257
257
|
|
258
258
|
def valid?
|
259
259
|
errors.clear
|
260
|
-
temp_record =
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
errors.add(login_field, "can not be blank") if send(login_field).blank?
|
265
|
-
errors.add(password_field, "can not be blank") if send("protected_#{password_field}").blank?
|
266
|
-
return false if errors.count > 0
|
267
|
-
|
268
|
-
temp_record = klass.send(find_by_login_method, send(login_field))
|
269
|
-
|
270
|
-
if temp_record.blank?
|
271
|
-
errors.add(login_field, "was not found")
|
272
|
-
return false
|
273
|
-
end
|
274
|
-
|
275
|
-
unless temp_record.send(verify_password_method, send("protected_#{password_field}"))
|
276
|
-
errors.add(password_field, "is invalid")
|
277
|
-
return false
|
278
|
-
end
|
279
|
-
when :unauthorized_record
|
280
|
-
if temp_record.blank?
|
281
|
-
errors.add_to_base("You can not log in with a blank record.")
|
282
|
-
return false
|
283
|
-
end
|
284
|
-
|
285
|
-
if temp_record.new_record?
|
286
|
-
errors.add_to_base("You can not login with a new record.") if temp_record.new_record?
|
287
|
-
return false
|
288
|
-
end
|
289
|
-
else
|
290
|
-
errors.add_to_base("You must provide some form of credentials before logging in.")
|
291
|
-
return false
|
260
|
+
temp_record = validate_credentials
|
261
|
+
if errors.empty?
|
262
|
+
@record = temp_record
|
263
|
+
return true
|
292
264
|
end
|
293
|
-
|
294
|
-
[:active, :approved, :confirmed].each do |required_status|
|
295
|
-
if temp_record.respond_to?("#{required_status}?") && !temp_record.send("#{required_status}?")
|
296
|
-
errors.add_to_base("Your account has not been marked as #{required_status}")
|
297
|
-
return false
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
# All is good, lets set the record
|
302
|
-
@record = temp_record
|
303
|
-
|
304
|
-
true
|
265
|
+
false
|
305
266
|
end
|
306
267
|
|
307
268
|
def valid_http_auth?
|
@@ -397,6 +358,51 @@ module Authgasm
|
|
397
358
|
def update_session!
|
398
359
|
controller.session[session_key] = record && record.send(remember_token_field)
|
399
360
|
end
|
361
|
+
|
362
|
+
def validate_credentials
|
363
|
+
temp_record = unauthorized_record
|
364
|
+
|
365
|
+
case login_with
|
366
|
+
when :credentials
|
367
|
+
errors.add(login_field, "can not be blank") if send(login_field).blank?
|
368
|
+
errors.add(password_field, "can not be blank") if send("protected_#{password_field}").blank?
|
369
|
+
return if errors.count > 0
|
370
|
+
|
371
|
+
temp_record = klass.send(find_by_login_method, send(login_field))
|
372
|
+
|
373
|
+
if temp_record.blank?
|
374
|
+
errors.add(login_field, "was not found")
|
375
|
+
return
|
376
|
+
end
|
377
|
+
|
378
|
+
unless temp_record.send(verify_password_method, send("protected_#{password_field}"))
|
379
|
+
errors.add(password_field, "is invalid")
|
380
|
+
return
|
381
|
+
end
|
382
|
+
when :unauthorized_record
|
383
|
+
if temp_record.blank?
|
384
|
+
errors.add_to_base("You can not log in with a blank record.")
|
385
|
+
return
|
386
|
+
end
|
387
|
+
|
388
|
+
if temp_record.new_record?
|
389
|
+
errors.add_to_base("You can not login with a new record.") if temp_record.new_record?
|
390
|
+
return
|
391
|
+
end
|
392
|
+
else
|
393
|
+
errors.add_to_base("You must provide some form of credentials before logging in.")
|
394
|
+
return
|
395
|
+
end
|
396
|
+
|
397
|
+
[:active, :approved, :confirmed].each do |required_status|
|
398
|
+
if temp_record.respond_to?("#{required_status}?") && !temp_record.send("#{required_status}?")
|
399
|
+
errors.add_to_base("Your account has not been marked as #{required_status}")
|
400
|
+
return
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
temp_record
|
405
|
+
end
|
400
406
|
end
|
401
407
|
end
|
402
408
|
end
|
@@ -7,7 +7,7 @@ module Authgasm
|
|
7
7
|
CALLBACKS = %w(before_create after_create before_destroy after_destroy before_save after_save before_update after_update before_validation after_validation)
|
8
8
|
|
9
9
|
def self.included(base) #:nodoc:
|
10
|
-
[:destroy, :save, :valid
|
10
|
+
[:destroy, :save, :valid?, :validate_credentials].each do |method|
|
11
11
|
base.send :alias_method_chain, method, :callbacks
|
12
12
|
end
|
13
13
|
|
@@ -41,15 +41,16 @@ module Authgasm
|
|
41
41
|
result
|
42
42
|
end
|
43
43
|
|
44
|
-
def valid_with_callbacks?
|
45
|
-
run_callbacks(:before_validation)
|
44
|
+
def valid_with_callbacks?
|
46
45
|
result = valid_without_callbacks?
|
47
|
-
if result
|
48
|
-
run_callbacks(:after_validation)
|
49
|
-
result = errors.empty?
|
50
|
-
end
|
46
|
+
run_callbacks(:after_validation) if result
|
51
47
|
result
|
52
48
|
end
|
49
|
+
|
50
|
+
def validate_credentials_with_callbacks # :nodoc:
|
51
|
+
run_callbacks(:before_validation)
|
52
|
+
validate_credentials_without_callbacks
|
53
|
+
end
|
53
54
|
end
|
54
55
|
end
|
55
56
|
end
|
@@ -91,7 +91,7 @@ module Authgasm
|
|
91
91
|
# * <tt>Default:</tt> Guesses based on the model columns, tries login, username, and email. If none are present it defaults to login
|
92
92
|
# * <tt>Accepts:</tt> Symbol or String
|
93
93
|
def login_field
|
94
|
-
@login_field ||= (klass.
|
94
|
+
@login_field ||= (klass.column_names.include?("login") && :login) || (klass.column_names.include?("username") && :username) || (klass.column_names.include?("email") && :email) || :login
|
95
95
|
end
|
96
96
|
attr_writer :login_field
|
97
97
|
|
@@ -100,7 +100,7 @@ module Authgasm
|
|
100
100
|
# * <tt>Default:</tt> Guesses based on the model columns, tries password and pass. If none are present it defaults to password
|
101
101
|
# * <tt>Accepts:</tt> Symbol or String
|
102
102
|
def password_field
|
103
|
-
@password_field ||= (klass.
|
103
|
+
@password_field ||= (klass.column_names.include?("password") && :password) || (klass.column_names.include?("pass") && :pass) || :password
|
104
104
|
end
|
105
105
|
attr_writer :password_field
|
106
106
|
|
@@ -126,10 +126,10 @@ module Authgasm
|
|
126
126
|
# * <tt>Accepts:</tt> Symbol or String
|
127
127
|
def remember_token_field
|
128
128
|
@remember_token_field ||=
|
129
|
-
(klass.
|
130
|
-
(klass.
|
131
|
-
(klass.
|
132
|
-
(klass.
|
129
|
+
(klass.column_names.include?("remember_token") && :remember_token) ||
|
130
|
+
(klass.column_names.include?("remember_key") && :remember_key) ||
|
131
|
+
(klass.column_names.include?("cookie_token") && :cookie_token) ||
|
132
|
+
(klass.column_names.include?("cookie_key") && :cookie_key) ||
|
133
133
|
:remember_token
|
134
134
|
end
|
135
135
|
attr_writer :remember_token_field
|
@@ -6,8 +6,11 @@ module Authgasm
|
|
6
6
|
#
|
7
7
|
# 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.
|
8
8
|
class Sha512CryptoProvider
|
9
|
+
STRETCHES = 20
|
9
10
|
def self.encrypt(pass)
|
10
|
-
|
11
|
+
digest = pass
|
12
|
+
STRETCHES.times { digest = Digest::SHA512.hexdigest(digest) }
|
13
|
+
digest
|
11
14
|
end
|
12
15
|
end
|
13
16
|
end
|
data/lib/authgasm/version.rb
CHANGED
Binary file
|
data/test_app/db/test.sqlite3
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: authgasm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.
|
4
|
+
version: 0.10.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Johnson of Binary Logic
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-10-
|
12
|
+
date: 2008-10-29 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|