authgasm 0.10.2 → 0.10.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,11 @@
1
+ == 0.10.3 released 2008-10-31
2
+
3
+ * Instead of raising an error when extra fields are passed in credentials=, just ignore them.
4
+ * Added remember_me config option to set the default value.
5
+ * Only call credential methods if an argument was passed.
6
+ * More unit tests
7
+ * Hardened automatic session updating. Also automatically log the user in if they change their password when logged out.
8
+
1
9
  == 0.10.2 released 2008-10-24
2
10
 
3
11
  * Added in stretches to the default Sha512 encryption algorithm.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Authgasm is "rails authentication done right"
4
4
 
5
- The last thing we need is another authentication solution for rails, right? That's what I thought. It was disappointing to find that all of the current solutions were overly complicated, bloated, poorly written, littered my application with code, or were just plain confusing. They felt very Microsoftish. It's like some Microsoft .NET engineers decided to dabble in ruby / rails for a day and their project was to write an authentication solution. This is not the simple / elegant rails we all fell in love with. It's time someone makes a "rails like" authentication solution. So I give you Authgasm...
5
+ 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...
6
6
 
7
7
  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.
8
8
 
@@ -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. 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.
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, even if they aren't the same names as below.
99
99
 
100
100
  t.string :login, :null => false
101
101
  t.string :crypted_password, :null => false
@@ -238,19 +238,25 @@ From there it is pretty simple. When you try to create a new session the record
238
238
 
239
239
  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.
240
240
 
241
- I don't necessarily think the current solutions are "wrong", nor am I saying Authgasm is the answer to our prayers. But the current solutions were pretty disappointing. Especially when the rails community is full of brilliant programmers, and the best we could come up with was the "restful-authentication" plugin. This was just sad, and frankly kind of irritated me. Here's why...
241
+ 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...
242
242
 
243
243
  === Generators are not the answer
244
244
 
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.
245
+ Generators have their place, and it 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 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.
248
248
 
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.
249
+ 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.
250
+
251
+ Lastly, there is a pattern here, why clutter up all of your applications with the same code? I recently switched over one of my apps to Authgasm and its amazing how much cleaner my app feels.
250
252
 
251
253
  === Limited to a single authentication
252
254
 
253
255
  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
256
 
257
+ === Too presumptuous
258
+
259
+ A lot of them forced me to name my password column as "this", or the key of my cookie had to be "this". They were a little too presumptuous. I am probably overly picky, but little details like that should be configurable. This also made it very hard to implement into an existing app.
260
+
255
261
 
256
262
  Copyright (c) 2008 Ben Johnson of [Binary Logic](http://www.binarylogic.com), released under the MIT license
@@ -1,184 +1,38 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{authgasm}
3
+ s.version = "0.10.3"
1
4
 
2
- # Gem::Specification for Authgasm-0.10.2
3
- # Originally generated by Echoe
5
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
6
+ s.authors = ["Ben Johnson of Binary Logic"]
7
+ s.date = %q{2008-10-31}
8
+ s.description = %q{Rails authentication done right}
9
+ s.email = %q{bjohnson@binarylogic.com}
10
+ s.extra_rdoc_files = ["CHANGELOG.rdoc", "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", "README.rdoc"]
11
+ s.files = ["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", "Manifest", "MIT-LICENSE", "Rakefile", "README.rdoc", "test_app/app/controllers/application.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/user_sessions_helper.rb", "test_app/app/helpers/users_helper.rb", "test_app/app/models/user.rb", "test_app/app/models/user_session.rb", "test_app/app/views/layouts/application.html.erb", "test_app/app/views/user_sessions/new.html.erb", "test_app/app/views/users/_form.erb", "test_app/app/views/users/edit.html.erb", "test_app/app/views/users/new.html.erb", "test_app/app/views/users/show.html.erb", "test_app/config/boot.rb", "test_app/config/database.yml", "test_app/config/environment.rb", "test_app/config/environments/development.rb", "test_app/config/environments/production.rb", "test_app/config/environments/test.rb", "test_app/config/initializers/inflections.rb", "test_app/config/initializers/mime_types.rb", "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/schema.rb", "test_app/db/test.sqlite3", "test_app/doc/README_FOR_APP", "test_app/public/404.html", "test_app/public/422.html", "test_app/public/500.html", "test_app/public/dispatch.cgi", "test_app/public/dispatch.fcgi", "test_app/public/dispatch.rb", "test_app/public/favicon.ico", "test_app/public/images/rails.png", "test_app/public/javascripts/application.js", "test_app/public/javascripts/controls.js", "test_app/public/javascripts/dragdrop.js", "test_app/public/javascripts/effects.js", "test_app/public/javascripts/prototype.js", "test_app/public/robots.txt", "test_app/public/stylesheets/scaffold.css", "test_app/Rakefile", "test_app/README", "test_app/script/about", "test_app/script/console", "test_app/script/dbconsole", "test_app/script/destroy", "test_app/script/generate", "test_app/script/performance/benchmarker", "test_app/script/performance/profiler", "test_app/script/performance/request", "test_app/script/plugin", "test_app/script/process/inspector", "test_app/script/process/reaper", "test_app/script/process/spawner", "test_app/script/runner", "test_app/script/server", "test_app/test/fixtures/users.yml", "test_app/test/functional/user_sessions_controller_test.rb", "test_app/test/functional/users_controller_test.rb", "test_app/test/integration/user_sesion_stories_test.rb", "test_app/test/integration/user_session_test.rb", "test_app/test/test_helper.rb", "test_app/test/unit/user_test.rb", "authgasm.gemspec"]
12
+ s.has_rdoc = true
13
+ s.homepage = %q{http://github.com/binarylogic/authgasm}
14
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Authgasm", "--main", "README.rdoc"]
15
+ s.require_paths = ["lib"]
16
+ s.rubyforge_project = %q{authgasm}
17
+ s.rubygems_version = %q{1.2.0}
18
+ s.summary = %q{Rails authentication done right}
4
19
 
5
- --- !ruby/object:Gem::Specification
6
- name: authgasm
7
- version: !ruby/object:Gem::Version
8
- version: 0.10.2
9
- platform: ruby
10
- authors:
11
- - Ben Johnson of Binary Logic
12
- autorequire:
13
- bindir: bin
20
+ if s.respond_to? :specification_version then
21
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
22
+ s.specification_version = 2
14
23
 
15
- date: 2008-10-29 00:00:00 -04:00
16
- default_executable:
17
- dependencies:
18
- - !ruby/object:Gem::Dependency
19
- name: activesupport
20
- type: :runtime
21
- version_requirement:
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: "0"
27
- version:
28
- - !ruby/object:Gem::Dependency
29
- name: activerecord
30
- type: :runtime
31
- version_requirement:
32
- version_requirements: !ruby/object:Gem::Requirement
33
- requirements:
34
- - - ">="
35
- - !ruby/object:Gem::Version
36
- version: "0"
37
- version:
38
- - !ruby/object:Gem::Dependency
39
- name: echoe
40
- type: :development
41
- version_requirement:
42
- version_requirements: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: "0"
47
- version:
48
- description: Rails authentication done right
49
- email: bjohnson@binarylogic.com
50
- executables: []
51
-
52
- extensions: []
53
-
54
- extra_rdoc_files:
55
- - CHANGELOG.rdoc
56
- - lib/authgasm/acts_as_authentic.rb
57
- - lib/authgasm/controller_adapters/abstract_adapter.rb
58
- - lib/authgasm/controller_adapters/rails_adapter.rb
59
- - lib/authgasm/session/active_record_trickery.rb
60
- - lib/authgasm/session/base.rb
61
- - lib/authgasm/session/callbacks.rb
62
- - lib/authgasm/session/config.rb
63
- - lib/authgasm/session/errors.rb
64
- - lib/authgasm/sha512_crypto_provider.rb
65
- - lib/authgasm/version.rb
66
- - lib/authgasm.rb
67
- - README.rdoc
68
- files:
69
- - CHANGELOG.rdoc
70
- - init.rb
71
- - lib/authgasm/acts_as_authentic.rb
72
- - lib/authgasm/controller_adapters/abstract_adapter.rb
73
- - lib/authgasm/controller_adapters/rails_adapter.rb
74
- - lib/authgasm/session/active_record_trickery.rb
75
- - lib/authgasm/session/base.rb
76
- - lib/authgasm/session/callbacks.rb
77
- - lib/authgasm/session/config.rb
78
- - lib/authgasm/session/errors.rb
79
- - lib/authgasm/sha512_crypto_provider.rb
80
- - lib/authgasm/version.rb
81
- - lib/authgasm.rb
82
- - Manifest
83
- - MIT-LICENSE
84
- - Rakefile
85
- - README.rdoc
86
- - test_app/app/controllers/application.rb
87
- - test_app/app/controllers/user_sessions_controller.rb
88
- - test_app/app/controllers/users_controller.rb
89
- - test_app/app/helpers/application_helper.rb
90
- - test_app/app/helpers/user_sessions_helper.rb
91
- - test_app/app/helpers/users_helper.rb
92
- - test_app/app/models/user.rb
93
- - test_app/app/models/user_session.rb
94
- - test_app/app/views/layouts/application.html.erb
95
- - test_app/app/views/user_sessions/new.html.erb
96
- - test_app/app/views/users/_form.erb
97
- - test_app/app/views/users/edit.html.erb
98
- - test_app/app/views/users/new.html.erb
99
- - test_app/app/views/users/show.html.erb
100
- - test_app/config/boot.rb
101
- - test_app/config/database.yml
102
- - test_app/config/environment.rb
103
- - test_app/config/environments/development.rb
104
- - test_app/config/environments/production.rb
105
- - test_app/config/environments/test.rb
106
- - test_app/config/initializers/inflections.rb
107
- - test_app/config/initializers/mime_types.rb
108
- - test_app/config/initializers/new_rails_defaults.rb
109
- - test_app/config/routes.rb
110
- - test_app/db/development.sqlite3
111
- - test_app/db/migrate/20081023040052_create_users.rb
112
- - test_app/db/schema.rb
113
- - test_app/db/test.sqlite3
114
- - test_app/doc/README_FOR_APP
115
- - test_app/public/404.html
116
- - test_app/public/422.html
117
- - test_app/public/500.html
118
- - test_app/public/dispatch.cgi
119
- - test_app/public/dispatch.fcgi
120
- - test_app/public/dispatch.rb
121
- - test_app/public/favicon.ico
122
- - test_app/public/images/rails.png
123
- - test_app/public/javascripts/application.js
124
- - test_app/public/javascripts/controls.js
125
- - test_app/public/javascripts/dragdrop.js
126
- - test_app/public/javascripts/effects.js
127
- - test_app/public/javascripts/prototype.js
128
- - test_app/public/robots.txt
129
- - test_app/public/stylesheets/scaffold.css
130
- - test_app/Rakefile
131
- - test_app/README
132
- - test_app/script/about
133
- - test_app/script/console
134
- - test_app/script/dbconsole
135
- - test_app/script/destroy
136
- - test_app/script/generate
137
- - test_app/script/performance/benchmarker
138
- - test_app/script/performance/profiler
139
- - test_app/script/performance/request
140
- - test_app/script/plugin
141
- - test_app/script/process/inspector
142
- - test_app/script/process/reaper
143
- - test_app/script/process/spawner
144
- - test_app/script/runner
145
- - test_app/script/server
146
- - test_app/test/fixtures/users.yml
147
- - test_app/test/functional/user_sessions_controller_test.rb
148
- - test_app/test/functional/users_controller_test.rb
149
- - test_app/test/integration/user_sesion_stories_test.rb
150
- - test_app/test/integration/user_session_test.rb
151
- - test_app/test/test_helper.rb
152
- - test_app/test/unit/user_test.rb
153
- - authgasm.gemspec
154
- has_rdoc: true
155
- homepage: http://github.com/binarylogic/authgasm
156
- post_install_message:
157
- rdoc_options:
158
- - --line-numbers
159
- - --inline-source
160
- - --title
161
- - Authgasm
162
- - --main
163
- - README.rdoc
164
- require_paths:
165
- - lib
166
- required_ruby_version: !ruby/object:Gem::Requirement
167
- requirements:
168
- - - ">="
169
- - !ruby/object:Gem::Version
170
- version: "0"
171
- version:
172
- required_rubygems_version: !ruby/object:Gem::Requirement
173
- requirements:
174
- - - ">="
175
- - !ruby/object:Gem::Version
176
- version: "1.2"
177
- version:
178
- requirements: []
179
-
180
- rubyforge_project: authgasm
181
- rubygems_version: 1.2.0
182
- specification_version: 2
183
- summary: Rails authentication done right
184
- test_files: []
24
+ if current_version >= 3 then
25
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
26
+ s.add_runtime_dependency(%q<activerecord>, [">= 0"])
27
+ s.add_development_dependency(%q<echoe>, [">= 0"])
28
+ else
29
+ s.add_dependency(%q<activesupport>, [">= 0"])
30
+ s.add_dependency(%q<activerecord>, [">= 0"])
31
+ s.add_dependency(%q<echoe>, [">= 0"])
32
+ end
33
+ else
34
+ s.add_dependency(%q<activesupport>, [">= 0"])
35
+ s.add_dependency(%q<activerecord>, [">= 0"])
36
+ s.add_dependency(%q<echoe>, [">= 0"])
37
+ end
38
+ end
@@ -31,7 +31,7 @@ module Authgasm
31
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
32
  # user.confirm_password= Confirms the password, needed to change the password.
33
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.
34
+ # user.reset_password! Basically resets the password to a random password using only letters and numbers.
35
35
  # user.logged_in? Based on the :logged_in_timeout option. Tells you if the user is logged in or not.
36
36
  # user.forget! Changes their remember token, making their cookie and session invalid. A way to log the user out withouth changing their password.
37
37
  #
@@ -89,14 +89,13 @@ module Authgasm
89
89
  validate :validate_password
90
90
  validates_numericality_of :login_count, :only_integer => :true, :greater_than_or_equal_to => 0, :allow_nil => true if column_names.include?("login_count")
91
91
 
92
- if column_names.include?("last_click_at")
93
- named_scope :logged_in, lambda { {:conditions => ["last_click_at > ?", options[:logged_in_timeout].ago]} }
94
- named_scope :logged_out, lambda { {:conditions => ["last_click_at <= ?", options[:logged_in_timeout].ago]} }
92
+ if column_names.include?("last_request_at")
93
+ named_scope :logged_in, lambda { {:conditions => ["last_request_at > ?", options[:logged_in_timeout].ago]} }
94
+ named_scope :logged_out, lambda { {:conditions => ["last_request_at <= ?", options[:logged_in_timeout].ago]} }
95
95
  end
96
96
 
97
- after_create :create_sessions!
98
- before_update :find_my_sessions
99
- after_update :update_sessions!
97
+ before_save :get_session_information, :if => :update_sessions?
98
+ after_save :maintain_sessions!, :if => :update_sessions?
100
99
 
101
100
  # Attributes
102
101
  attr_writer "confirm_#{options[:password_field]}"
@@ -125,10 +124,10 @@ module Authgasm
125
124
  end_eval
126
125
 
127
126
  # Instance methods
128
- if column_names.include?("last_click_at")
127
+ if column_names.include?("last_request_at")
129
128
  class_eval <<-"end_eval", __FILE__, __LINE__
130
129
  def logged_in?
131
- !last_click_at.nil? && last_click_at > #{options[:logged_in_timeout].to_i}.seconds.ago
130
+ !last_request_at.nil? && last_request_at > #{options[:logged_in_timeout].to_i}.seconds.ago
132
131
  end
133
132
  end_eval
134
133
  end
@@ -176,62 +175,75 @@ module Authgasm
176
175
  end
177
176
 
178
177
  def forget!
179
- update_attribute(:#{options[:remember_token_field]}, self.class.unique_token)
178
+ self.#{options[:remember_token_field]} = self.class.unique_token
179
+ save_without_session_maintenance(false)
180
180
  end
181
181
 
182
- def randomize_#{options[:password_field]}!
182
+ def reset_#{options[:password_field]}!
183
183
  chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
184
184
  newpass = ""
185
185
  1.upto(10) { |i| newpass << chars[rand(chars.size-1)] }
186
186
  self.#{options[:password_field]} = newpass
187
187
  self.confirm_#{options[:password_field]} = newpass
188
+ save_without_session_maintenance(false)
188
189
  end
190
+ alias_method :randomize_password!, :reset_password!
189
191
 
190
- def save_from_session(*args)
191
- @saving_from_session = true
192
+ def save_without_session_maintenance(*args)
193
+ @skip_session_maintenance = true
192
194
  result = save(*args)
193
- @saving_from_session = false
195
+ @skip_session_maintenance = false
194
196
  result
195
197
  end
196
198
 
197
199
  protected
198
- def create_sessions!
199
- return if !#{options[:session_class]}.activated? || #{options[:session_ids].inspect}.blank?
200
+ def update_sessions?
201
+ !@skip_session_maintenance && #{options[:session_class]}.activated? && !#{options[:session_ids].inspect}.blank? && #{options[:remember_token_field]}_changed?
202
+ end
203
+
204
+ def get_session_information
205
+ # Need to determine if we are completely logged out, or logged in as another user
206
+ @_sessions = []
207
+ @_logged_out = true
200
208
 
209
+ #{options[:session_ids].inspect}.each do |session_id|
210
+ session = #{options[:session_class]}.find(*[session_id].compact)
211
+ if session
212
+ if !session.record.blank?
213
+ @_logged_out = false
214
+ @_sessions << session if session.record == self
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ def maintain_sessions!
221
+ if @_logged_out
222
+ create_session!
223
+ elsif !@_sessions.blank?
224
+ update_sessions!
225
+ end
226
+ end
227
+
228
+ def create_session!
201
229
  # We only want to automatically login into the first session, since this is the main session. The other sessions are sessions
202
230
  # that need to be created after logging into the main session.
203
231
  session_id = #{options[:session_ids].inspect}.first
204
232
 
205
233
  # If we are already logged in, ignore this completely. All that we care about is updating ourself.
206
234
  next if #{options[:session_class]}.find(*[session_id].compact)
207
-
235
+
208
236
  # Log me in
209
237
  args = [self, session_id].compact
210
238
  #{options[:session_class]}.create(*args)
211
239
  end
212
240
 
213
- def find_my_sessions
214
- return if @saving_from_session || !#{options[:session_class]}.activated? || #{options[:session_ids].inspect}.blank?
215
-
216
- @my_sessions = []
217
- #{options[:session_ids].inspect}.each do |session_id|
218
- session = #{options[:session_class]}.find(*[session_id].compact)
219
-
220
- # Ignore if we can't find the session or the session isn't this record
221
- next if !session || session.record != self
222
-
223
- @my_sessions << session
224
- end
225
- end
226
-
227
241
  def update_sessions!
228
- return if @saving_from_session || @my_sessions.blank?
229
-
230
- @my_sessions.each do |stale_session|
242
+ # We found sessions above, let's update them with the new info
243
+ @_sessions.each do |stale_session|
231
244
  stale_session.unauthorized_record = self
232
245
  stale_session.save
233
246
  end
234
- @my_sessions = nil
235
247
  end
236
248
 
237
249
  def tried_to_set_password?
@@ -54,7 +54,7 @@ module Authgasm
54
54
  if session.send("valid_#{find_method}?")
55
55
  if session.record.class.column_names.include?("last_request_at")
56
56
  session.record.last_request_at = Time.now
57
- session.record.save_from_session(false)
57
+ session.record.save_without_session_maintenance(false)
58
58
  end
59
59
  return session
60
60
  end
@@ -84,7 +84,7 @@ module Authgasm
84
84
  end
85
85
  end
86
86
 
87
- attr_accessor :login_with, :new_session, :remember_me
87
+ attr_accessor :login_with, :new_session
88
88
  attr_reader :record, :unauthorized_record
89
89
  attr_writer :id
90
90
 
@@ -93,12 +93,14 @@ module Authgasm
93
93
  # UserSession.new
94
94
  # UserSession.new(login, password)
95
95
  # UserSession.new(:login => login, :password => password)
96
+ # UserSession.new(User.first)
96
97
  #
97
98
  # 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.
98
99
  #
99
100
  # UserSession.new(:my_id)
100
101
  # UserSession.new(login, password, :my_id)
101
102
  # UserSession.new({:login => loing, :password => password}, :my_id)
103
+ # UserSession.new(User.first, :my_id)
102
104
  #
103
105
  # 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.
104
106
  # To solve this just pass a id called :proxy, or whatever you want. Authgasm will separate everything out.
@@ -119,9 +121,9 @@ module Authgasm
119
121
  self.unauthorized_record = credentials_or_record
120
122
  end
121
123
  else
122
- send("#{login_field}=", args[0])
123
- send("#{password_field}=", args[1])
124
- self.remember_me = args[2]
124
+ send("#{login_field}=", args[0]) if args.size > 0
125
+ send("#{password_field}=", args[1]) if args.size > 1
126
+ self.remember_me = args[2] if args.size > 2
125
127
  end
126
128
  end
127
129
 
@@ -131,11 +133,14 @@ module Authgasm
131
133
  {login_field => send(login_field), password_field => "<Protected>"}
132
134
  end
133
135
 
134
- # Lets you set your loging and password via a hash format.
136
+ # Lets you set your loging and password via a hash format. This is "params" safe. It only allows for 3 keys: your login field name, password field name, and remember me.
135
137
  def credentials=(values)
136
138
  return if values.blank? || !values.is_a?(Hash)
137
- raise(ArgumentError, "Only 2 credentials are allowed: #{login_field} and #{password_field}") if (values.keys - [login_field.to_sym, login_field.to_s, password_field.to_sym, password_field.to_s]).size > 0
138
- values.each { |field, value| send("#{field}=", value) }
139
+ values.symbolize_keys!
140
+ [login_field.to_sym, password_field.to_sym, :remember_me].each do |field|
141
+ next if !values.key?(field)
142
+ send("#{field}=", values[field])
143
+ end
139
144
  end
140
145
 
141
146
  # Resets everything, your errors, record, cookies, and session. Basically "logs out" a user.
@@ -192,11 +197,22 @@ module Authgasm
192
197
  "#<#{self.class.name} #{details.inspect}>"
193
198
  end
194
199
 
195
-
200
+ # Similar to ActiveRecord's new_record? Returns true if the session has not been saved yet.
196
201
  def new_session?
197
202
  new_session != false
198
203
  end
199
204
 
205
+ def remember_me # :nodoc:
206
+ return @remember_me if @set_remember_me
207
+ @remember_me ||= self.class.remember_me
208
+ end
209
+
210
+ # Accepts a boolean as a flag to remember the session or not. Basically to expire the cookie at the end of the session or keep it for "remember_me_until".
211
+ def remember_me=(value)
212
+ @set_remember_me = true
213
+ @remember_me = value
214
+ end
215
+
200
216
  # Allows users to be remembered via a cookie.
201
217
  def remember_me?
202
218
  remember_me == true || remember_me == "true" || remember_me == "1"
@@ -234,7 +250,7 @@ module Authgasm
234
250
  record.current_login_ip = controller.request.remote_ip
235
251
  end
236
252
 
237
- record.save_from_session(false)
253
+ record.save_without_session_maintenance(false)
238
254
 
239
255
  self.new_session = false
240
256
  self
@@ -104,6 +104,15 @@ module Authgasm
104
104
  end
105
105
  attr_writer :password_field
106
106
 
107
+ # If sessions should be remembered by default or not.
108
+ #
109
+ # * <tt>Default:</tt> false
110
+ # * <tt>Accepts:</tt> Boolean
111
+ def remember_me
112
+ @remember_me
113
+ end
114
+ attr_writer :remember_me
115
+
107
116
  # The length of time until the cookie expires.
108
117
  #
109
118
  # * <tt>Default:</tt> 3.months
@@ -44,7 +44,7 @@ module Authgasm # :nodoc:
44
44
 
45
45
  MAJOR = 0
46
46
  MINOR = 10
47
- TINY = 2
47
+ TINY = 3
48
48
 
49
49
  # The current version as a Version instance
50
50
  CURRENT = new(MAJOR, MINOR, TINY)
@@ -1,6 +1,7 @@
1
1
  class UsersController < ApplicationController
2
2
  before_filter :require_no_user, :only => [:new, :create]
3
- before_filter :require_user, :only => [:show, :edit, :update]
3
+ before_filter :require_user, :only => [:edit, :update]
4
+ before_filter :load_user, :except => [:new, :create]
4
5
 
5
6
  def new
6
7
  @user = User.new
@@ -17,11 +18,24 @@ class UsersController < ApplicationController
17
18
  end
18
19
 
19
20
  def show
20
- @user = @current_user
21
+ if @user
22
+ @user.update_attribute(:profile_views, @user.profile_views + 1) if @user && params[:id]
23
+ else
24
+ flash[:notice] = "We're sorry, but no user was found"
25
+ redirect_to new_user_session_url
26
+ end
21
27
  end
22
28
 
23
- def edit
24
- @user = @current_user
29
+ # This is a method created for tests only, to make sure users logged out get logged in when changing passwords
30
+ def reset_password
31
+ if @user
32
+ @user.password = "saweet"
33
+ @user.confirm_password = "saweet"
34
+ @user.save
35
+ else
36
+ flash[:notice] = "We're sorry, but no user was found"
37
+ redirect_to new_user_session_url
38
+ end
25
39
  end
26
40
 
27
41
  def update
@@ -34,4 +48,14 @@ class UsersController < ApplicationController
34
48
  render :action => :edit
35
49
  end
36
50
  end
51
+
52
+ private
53
+ def load_user
54
+ if params[:id]
55
+ @user = User.find_by_id(params[:id])
56
+ @user.update_attribute(:profile_views, @user.profile_views + 1) if @user
57
+ else
58
+ @user = @current_user
59
+ end
60
+ end
37
61
  end
@@ -1,3 +1,3 @@
1
1
  class UserSession < Authgasm::Session::Base
2
-
2
+ self.remember_me = true
3
3
  end
@@ -9,5 +9,7 @@
9
9
  <%= f.label :password %><br />
10
10
  <%= f.password_field :password %><br />
11
11
  <br />
12
+ <%= f.check_box :remember_me %><%= f.label :remember_me %><br />
13
+ <br />
12
14
  <%= f.submit "Login" %>
13
15
  <% end %>
@@ -1,23 +1,29 @@
1
- <h1><%= @current_user.login %></h1>
1
+ <h1><%= @user.login %></h1>
2
2
 
3
3
  <table>
4
4
  <tr>
5
5
  <td>Login:</td>
6
- <td><%= @current_user.login %></td>
6
+ <td><%= @user.login %></td>
7
7
  </tr>
8
8
  <tr>
9
9
  <td>Login count:</td>
10
- <td><%= @current_user.login_count %></td>
10
+ <td><%= @user.login_count %></td>
11
+ </tr>
12
+ <tr>
13
+ <td>Profile views:</td>
14
+ <td><%= @user.profile_views %></td>
11
15
  </tr>
12
16
  <tr>
13
17
  <td>First name:</td>
14
- <td><%= @current_user.first_name %></td>
18
+ <td><%= @user.first_name %></td>
15
19
  </tr>
16
20
  <tr>
17
21
  <td>Last name:</td>
18
- <td><%= @current_user.last_name %></td>
22
+ <td><%= @user.last_name %></td>
19
23
  </tr>
20
24
  </table>
21
25
  <br />
22
26
 
23
- <%= link_to "Edit", edit_account_path %><br />
27
+ <% if @user == @current_user %>
28
+ <%= link_to "Edit", edit_account_path %><br />
29
+ <% end %>
@@ -1,5 +1,6 @@
1
1
  ActionController::Routing::Routes.draw do |map|
2
2
  map.resource :user_session
3
3
  map.resource :account, :controller => "users"
4
+ map.resources :users, :member => {:reset_password => :get}
4
5
  map.default "/", :controller => "user_sessions", :action => "new"
5
6
  end
@@ -2,13 +2,15 @@ class CreateUsers < ActiveRecord::Migration
2
2
  def self.up
3
3
  create_table :users do |t|
4
4
  t.timestamps
5
- t.integer :login_count, :null => false, :default => 0
6
5
  t.string :login, :null => false
7
6
  t.string :crypted_password
8
7
  t.string :password_salt
9
8
  t.string :remember_token
10
9
  t.string :first_name
11
10
  t.string :last_name
11
+ t.integer :login_count, :null => false, :default => 0
12
+ t.datetime :last_request_at
13
+ t.integer :profile_views, :null => false, :default => 0
12
14
  end
13
15
  end
14
16
 
@@ -14,13 +14,15 @@ ActiveRecord::Schema.define(:version => 20081023040052) do
14
14
  create_table "users", :force => true do |t|
15
15
  t.datetime "created_at"
16
16
  t.datetime "updated_at"
17
- t.integer "login_count", :default => 0, :null => false
18
17
  t.string "login", :null => false
19
18
  t.string "crypted_password"
20
19
  t.string "password_salt"
21
20
  t.string "remember_token"
22
21
  t.string "first_name"
23
22
  t.string "last_name"
23
+ t.integer "login_count", :default => 0, :null => false
24
+ t.datetime "last_request_at"
25
+ t.integer "profile_views", :default => 0, :null => false
24
26
  end
25
27
 
26
28
  end
Binary file
@@ -6,3 +6,12 @@ ben:
6
6
  remember_token: 6cde0674657a8a313ce952df979de2830309aa4c11ca65805dd00bfdc65dbcc2f5e36718660a1d2e68c1a08c276d996763985d2f06fd3d076eb7bc4d97b1e317
7
7
  first_name: Ben
8
8
  last_name: Johnson
9
+
10
+ zack:
11
+ id: 2
12
+ login: zham
13
+ password_salt: <%= salt = User.unique_token %>
14
+ crypted_password: <%= Authgasm::Sha512CryptoProvider.encrypt("zackrocks" + salt) %>
15
+ remember_token: fd3c2d5ce09ab98e7547d21f1b3dcf9158a9a19b5d3022c0402f32ae197019fce3fdbc6614d7ee57d719bae53bb089e30edc9e5d6153e5bc3afca0ac1d320342
16
+ first_name: Zack
17
+ last_name: Ham
@@ -17,8 +17,8 @@ class UserSessionStoriesTest < ActionController::IntegrationTest
17
17
  post account_url, {:user => {:login => "binarylogic", :password => "pass", :confirm_password => "pass", :first_name => "Ben", :last_name => "Johnson"}}
18
18
  assert_redirected_to account_url
19
19
  assert flash.key?(:notice)
20
-
21
- access_account(User.find(2))
20
+
21
+ assert_account_access(User.last)
22
22
  end
23
23
 
24
24
  def test_login_process
@@ -29,9 +29,9 @@ class UserSessionStoriesTest < ActionController::IntegrationTest
29
29
  assert flash.key?(:notice)
30
30
  assert_template "user_sessions/new"
31
31
 
32
- login_unsuccessfully
33
- login_unsuccessfully("bjohnson", "badpassword")
34
- login_successfully("bjohnson", "benrocks")
32
+ assert_unsuccessful_login
33
+ assert_unsuccessful_login("bjohnson", "badpassword")
34
+ assert_successful_login("bjohnson", "benrocks")
35
35
 
36
36
  # Try to log in again after a successful login
37
37
  get new_user_session_url
@@ -47,8 +47,8 @@ class UserSessionStoriesTest < ActionController::IntegrationTest
47
47
  assert flash.key?(:notice)
48
48
  assert_template "users/show"
49
49
 
50
- access_account
51
- logout(new_account_url) # before I tried to register, it stored my location
50
+ assert_account_access
51
+ assert_successful_logout(new_account_url) # before I tried to register, it stored my location
52
52
 
53
53
  # Try to access my account again
54
54
  get account_url
@@ -58,7 +58,7 @@ class UserSessionStoriesTest < ActionController::IntegrationTest
58
58
 
59
59
  def test_changing_password
60
60
  # Try logging in with correct credentials
61
- login_successfully("bjohnson", "benrocks")
61
+ assert_successful_login("bjohnson", "benrocks")
62
62
 
63
63
  # Go to edit form
64
64
  get edit_account_path
@@ -71,15 +71,35 @@ class UserSessionStoriesTest < ActionController::IntegrationTest
71
71
  assert flash.key?(:notice)
72
72
  assert_template "users/show"
73
73
 
74
- access_account
75
- logout
74
+ assert_account_access
75
+ assert_successful_logout
76
76
 
77
77
  # Try to access my account again
78
78
  get account_url
79
79
  assert_redirected_to new_user_session_url
80
80
  assert flash.key?(:notice)
81
81
 
82
- login_successfully("bjohnson", "sillywilly")
83
- access_account
82
+ assert_successful_login("bjohnson", "sillywilly")
83
+ assert_account_access
84
+ end
85
+
86
+ def test_updating_user_with_no_password_change
87
+ ben = users(:ben)
88
+ profile_views = ben.profile_views
89
+ assert_no_account_access
90
+ get user_url(ben)
91
+ ben.reload
92
+ assert ben.profile_views > profile_views
93
+ assert_no_account_access
94
+ end
95
+
96
+ def test_updating_user_with_password_change
97
+ ben = users(:ben)
98
+ crypted_password = ben.crypted_password
99
+ assert_no_account_access
100
+ get reset_password_user_url(ben)
101
+ ben.reload
102
+ assert_not_equal crypted_password, ben.crypted_password
103
+ assert_account_access
84
104
  end
85
105
  end
@@ -26,7 +26,7 @@ class UserSessionTest < ActionController::IntegrationTest
26
26
 
27
27
  def test_find
28
28
  assert_equal nil, UserSession.find
29
- login_successfully("bjohnson", "benrocks")
29
+ assert_successful_login("bjohnson", "benrocks")
30
30
  assert UserSession.find
31
31
  end
32
32
 
@@ -89,7 +89,7 @@ class UserSessionTest < ActionController::IntegrationTest
89
89
  assert_equal({:login => "ben", :password => "<Protected>"}, session.credentials)
90
90
 
91
91
  session = UserSession.new
92
- assert_raise(ArgumentError) { session.credentials = {:login => "ben", :random_field => "test"} }
92
+ assert_nothing_raised { session.credentials = {:login => "ben", :random_field => "test"} }
93
93
 
94
94
  session = UserSession.new
95
95
  session.credentials = {:login => "ben", :password => "awesome"}
@@ -105,6 +105,9 @@ class UserSessionTest < ActionController::IntegrationTest
105
105
  # don't need to go crazy here since we are using ActiveRecord's error class, which has been thorough tested there
106
106
  session = UserSession.new
107
107
  assert !session.valid?
108
+ session.login = ""
109
+ session.password = ""
110
+ assert !session.valid?
108
111
  assert session.errors.on(:login)
109
112
  assert session.errors.on(:password)
110
113
  end
@@ -136,7 +139,7 @@ class UserSessionTest < ActionController::IntegrationTest
136
139
  session.save
137
140
  assert !session.new_session?
138
141
 
139
- login_successfully("bjohnson", "benrocks")
142
+ assert_successful_login("bjohnson", "benrocks")
140
143
  session = UserSession.find
141
144
  assert !session.new_session?
142
145
  end
@@ -45,21 +45,32 @@ class ActionController::IntegrationTest
45
45
  end
46
46
 
47
47
  private
48
- def login_successfully(login, password)
48
+ def assert_successful_login(login, password)
49
49
  post user_session_url, :user_session => {:login => login, :password => password}
50
50
  assert_redirected_to account_url
51
51
  follow_redirect!
52
52
  assert_template "users/show"
53
53
  end
54
54
 
55
- def login_unsuccessfully(login = nil, password = nil)
55
+ def assert_unsuccessful_login(login = nil, password = nil)
56
56
  params = (login || password) ? {:user_session => {:login => login, :password => password}} : nil
57
57
  post user_session_url, params
58
58
  assert_template "user_sessions/new"
59
59
  end
60
+
61
+ def assert_successful_logout(alt_redirect = nil)
62
+ redirecting_to = alt_redirect || new_user_session_url
63
+ delete user_session_url
64
+ assert_redirected_to redirecting_to # because I tried to access registration above, and it stored it
65
+ follow_redirect!
66
+ assert flash.key?(:notice)
67
+ assert_equal nil, session["user_credentials"]
68
+ assert_equal "", cookies["user_credentials"]
69
+ assert_template redirecting_to.gsub("http://www.example.com/", "").gsub("user_session", "user_sessions").gsub("account", "users")
70
+ end
60
71
 
61
- def access_account(user = nil)
62
- user ||= users(:ben)
72
+ def assert_account_access(user = nil)
73
+ user ||= users(:ben).reload
63
74
  # Perform multiple requests to make sure the session is persisting properly, just being anal here
64
75
  3.times do
65
76
  get account_url
@@ -69,15 +80,9 @@ class ActionController::IntegrationTest
69
80
  assert_template "users/show"
70
81
  end
71
82
  end
72
-
73
- def logout(alt_redirect = nil)
74
- redirecting_to = alt_redirect || new_user_session_url
75
- delete user_session_url
76
- assert_redirected_to redirecting_to # because I tried to access registration above, and it stored it
77
- follow_redirect!
78
- assert flash.key?(:notice)
79
- assert_equal nil, session["user_credentials"]
80
- assert_equal "", cookies["user_credentials"]
81
- assert_template redirecting_to.gsub("http://www.example.com/", "").gsub("user_session", "user_sessions").gsub("account", "users")
83
+
84
+ def assert_no_account_access(alt_redirect = nil)
85
+ get account_url
86
+ assert_redirected_to alt_redirect || new_user_session_url
82
87
  end
83
88
  end
@@ -1,8 +1,80 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class UserTest < ActiveSupport::TestCase
4
- # Replace this with your real tests.
5
- def test_truth
6
- assert true
4
+ def test_validations
5
+ user = User.new
6
+ assert !user.valid?
7
+ assert user.errors.on(:login)
8
+ user.login = "^fds#%"
9
+ assert !user.valid?
10
+ assert user.errors.on(:login)
11
+ user.login = "bjohnson"
12
+ assert !user.valid?
13
+ assert user.errors.on(:login)
14
+ user.login = "unique"
15
+ assert !user.valid?
16
+ assert user.errors.on(:password)
17
+ user.password = "awesome"
18
+ assert !user.valid?
19
+ assert user.errors.on(:confirm_password)
20
+ user.confirm_password = "awesome"
21
+ assert user.valid?
22
+ end
23
+
24
+ def test_unique_token
25
+ tokens = []
26
+ 100.times { tokens << User.unique_token }
27
+ assert_equal 100, tokens.uniq.size
28
+ end
29
+
30
+ def test_crypto_provider
31
+ assert_equal Authgasm::Sha512CryptoProvider, User.crypto_provider
32
+ end
33
+
34
+ def test_forget_all
35
+ bens_token = users(:ben).remember_token
36
+ zacks_token = users(:zack).remember_token
37
+ User.forget_all!
38
+ assert_not_equal bens_token, users(:ben).reload.remember_token
39
+ assert_not_equal zacks_token, users(:zack).reload.remember_token
40
+ end
41
+
42
+ def test_logged_in
43
+ ben = users(:ben)
44
+ assert !ben.logged_in?
45
+ ben.update_attribute(:last_request_at, Time.now)
46
+ assert ben.logged_in?
47
+ end
48
+
49
+ def test_password
50
+ user = User.new
51
+ user.password = "test"
52
+ assert user.password_salt
53
+ assert_equal User.crypto_provider.encrypt("test" + user.password_salt), user.crypted_password
54
+ assert user.remember_token
55
+ end
56
+
57
+ def test_valid_password
58
+ ben = users(:ben)
59
+ assert ben.valid_password?("benrocks")
60
+ assert ben.valid_password?(User.crypto_provider.encrypt("benrocks" + ben.password_salt))
61
+ end
62
+
63
+ def test_forget
64
+ ben = users(:ben)
65
+ token = ben.remember_token
66
+ ben.forget!
67
+ ben.reload
68
+ assert_not_equal token, ben.remember_token
69
+ end
70
+
71
+ def test_randomize_password
72
+ ben = users(:ben)
73
+ crypted_password = ben.crypted_password
74
+ password_salt = ben.password_salt
75
+ ben.randomize_password!
76
+ ben.reload
77
+ assert_not_equal crypted_password, ben.crypted_password
78
+ assert_not_equal password_salt, ben.password_salt
7
79
  end
8
80
  end
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.2
4
+ version: 0.10.3
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-29 00:00:00 -04:00
12
+ date: 2008-10-31 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency