authgasm 0.9.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,11 @@
1
+ == 0.10.0 released 2008-10-24
2
+
3
+ * Do not allow instantiation if the session has not been activated with a controller object. Just like ActiveRecord won't let you do anything without a DB connection.
4
+ * Abstracted controller implementation to allow for rails, merb, etc adapters. So this is not confined to the rails framework.
5
+ * Removed create and update methods and added save, like ActiveRecord.
6
+ * after_validation should be able to change the result if it adds errors on callbacks.
7
+ * Completed tests.
8
+
1
9
  == 0.9.1 released 2008-10-24
2
10
 
3
11
  * Changed scope to id. Makes more sense to call it an id and fits better with the ActiveRecord model.
data/Manifest CHANGED
@@ -1,7 +1,8 @@
1
1
  CHANGELOG.rdoc
2
2
  init.rb
3
3
  lib/authgasm/acts_as_authentic.rb
4
- lib/authgasm/controller.rb
4
+ lib/authgasm/controller_adapters/abstract_adapter.rb
5
+ lib/authgasm/controller_adapters/rails_adapter.rb
5
6
  lib/authgasm/session/active_record_trickery.rb
6
7
  lib/authgasm/session/base.rb
7
8
  lib/authgasm/session/callbacks.rb
@@ -77,6 +78,7 @@ test_app/script/server
77
78
  test_app/test/fixtures/users.yml
78
79
  test_app/test/functional/user_sessions_controller_test.rb
79
80
  test_app/test/functional/users_controller_test.rb
81
+ test_app/test/integration/user_sesion_stories_test.rb
82
+ test_app/test/integration/user_session_test.rb
80
83
  test_app/test/test_helper.rb
81
- test_app/test/unit/ass_test.rb
82
84
  test_app/test/unit/user_test.rb
data/README.rdoc CHANGED
@@ -19,7 +19,7 @@ What if your user sessions controller could look just like your other controller
19
19
 
20
20
  def create
21
21
  @user_session = UserSession.new(params[:user_session])
22
- if @user_session.create
22
+ if @user_session.save
23
23
  redirect_to account_url
24
24
  else
25
25
  render :action => :new
@@ -134,6 +134,8 @@ Authgasm tries to check the state of the record before creating the session. If
134
134
 
135
135
  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.
136
136
 
137
+ 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.
138
+
137
139
  == Hooks / Callbacks
138
140
 
139
141
  Just like ActiveRecord you can create your own hooks / callbacks so that you can do whatever you want when certain actions are performed. Here they are:
@@ -142,11 +144,27 @@ Just like ActiveRecord you can create your own hooks / callbacks so that you can
142
144
  after_create
143
145
  before_destroy
144
146
  after_destroy
147
+ before_save
148
+ after_save
145
149
  before_update
146
150
  after_update
147
151
  before_validation
148
152
  after_validation
149
153
 
154
+ == Errors
155
+
156
+ The errors in Authgasm work JUST LIKE ActiveRecord. In fact, it uses the exact same ActiveRecord errors class. Use it the same way:
157
+
158
+ class UserSession
159
+ before_validation :check_if_awesome
160
+
161
+ private
162
+ def check_if_awesome
163
+ errors.add(:login, "must contain awesome") if login && !login.include?("awesome")
164
+ errors.add_to_base("You must be awesome to log in") unless record.awesome?
165
+ end
166
+ end
167
+
150
168
  == Automatic Session Updating
151
169
 
152
170
  This is one of my favorite features that I think its pretty cool. It's things like this that make a library great and let you know you are on the right track.
@@ -183,7 +201,7 @@ When things come together like this I think its a sign that you are doing someth
183
201
 
184
202
  You're asking: "why would I want multiple sessions?". Take this example:
185
203
 
186
- You have an app where users login and then need to re-login to view / change their billing information. Similar to how Apples' me.com works, if you've ever used it. 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:
204
+ 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:
187
205
 
188
206
  # regular user session
189
207
  @user_session = UserSession.new
@@ -202,9 +220,13 @@ This will keep everything separate. The :secure session will store its info in a
202
220
 
203
221
  For more information on ids checkout Authgasm::Session::Base#initialize
204
222
 
223
+ == What about [insert framework here]?
224
+
225
+ 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.
226
+
205
227
  == How it works
206
228
 
207
- 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 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 is thread safe.
229
+ 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 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.
208
230
 
209
231
  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.
210
232
 
data/authgasm.gemspec CHANGED
@@ -1,18 +1,18 @@
1
1
 
2
- # Gem::Specification for Authgasm-0.9.1
2
+ # Gem::Specification for Authgasm-0.10.0
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.9.1
8
+ version: 0.10.0
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-26 00:00:00 -04:00
15
+ date: 2008-10-27 00:00:00 -04:00
16
16
  default_executable:
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
@@ -54,7 +54,8 @@ extensions: []
54
54
  extra_rdoc_files:
55
55
  - CHANGELOG.rdoc
56
56
  - lib/authgasm/acts_as_authentic.rb
57
- - lib/authgasm/controller.rb
57
+ - lib/authgasm/controller_adapters/abstract_adapter.rb
58
+ - lib/authgasm/controller_adapters/rails_adapter.rb
58
59
  - lib/authgasm/session/active_record_trickery.rb
59
60
  - lib/authgasm/session/base.rb
60
61
  - lib/authgasm/session/callbacks.rb
@@ -68,7 +69,8 @@ files:
68
69
  - CHANGELOG.rdoc
69
70
  - init.rb
70
71
  - lib/authgasm/acts_as_authentic.rb
71
- - lib/authgasm/controller.rb
72
+ - lib/authgasm/controller_adapters/abstract_adapter.rb
73
+ - lib/authgasm/controller_adapters/rails_adapter.rb
72
74
  - lib/authgasm/session/active_record_trickery.rb
73
75
  - lib/authgasm/session/base.rb
74
76
  - lib/authgasm/session/callbacks.rb
@@ -144,8 +146,9 @@ files:
144
146
  - test_app/test/fixtures/users.yml
145
147
  - test_app/test/functional/user_sessions_controller_test.rb
146
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
147
151
  - test_app/test/test_helper.rb
148
- - test_app/test/unit/ass_test.rb
149
152
  - test_app/test/unit/user_test.rb
150
153
  - authgasm.gemspec
151
154
  has_rdoc: true
data/init.rb CHANGED
@@ -1,2 +1 @@
1
- require "digest/sha2"
2
1
  require "authgasm"
data/lib/authgasm.rb CHANGED
@@ -1,5 +1,8 @@
1
+ require "digest/sha2"
1
2
  require File.dirname(__FILE__) + "/authgasm/version"
2
- require File.dirname(__FILE__) + "/authgasm/controller"
3
+
4
+ require File.dirname(__FILE__) + "/authgasm/controller_adapters/rails_adapter" if defined?(Rails)
5
+
3
6
  require File.dirname(__FILE__) + "/authgasm/sha256_crypto_provider"
4
7
  require File.dirname(__FILE__) + "/authgasm/acts_as_authentic"
5
8
  require File.dirname(__FILE__) + "/authgasm/session/active_record_trickery"
@@ -1,5 +1,5 @@
1
1
  module Authgasm
2
- module ActsAsAuthenticated # :nodoc:
2
+ module ActsAsAuthentic # :nodoc:
3
3
  def self.included(base)
4
4
  base.extend(ClassMethods)
5
5
  end
@@ -20,6 +20,7 @@ module Authgasm
20
20
  # Class method name Description
21
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! Resets all records so they will not be remembered on their next visit. Basically makes their cookies invalid
23
24
  #
24
25
  # Named Scopes
25
26
  # User.logged_in Find all users who are logged in, based on your :logged_in_timeout option
@@ -31,6 +32,7 @@ module Authgasm
31
32
  # user.valid_password?(pass) Based on the valid of :password_field. Determines if the password passed is valid. The password could be encrypted or raw.
32
33
  # user.randomize_password! Basically resets the password to a random password using only letters and numbers
33
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.
34
36
  #
35
37
  # === Options
36
38
  # * <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.
@@ -107,6 +109,17 @@ module Authgasm
107
109
  def self.crypto_provider
108
110
  #{options[:crypto_provider]}
109
111
  end
112
+
113
+ def self.forget_all!
114
+ # Paginate these to save on memory
115
+ records = nil
116
+ i = 0
117
+ begin
118
+ records = find(:all, :limit => 50, :offset => i)
119
+ records.each { |record| records.update_attribute(:#{options[:remember_token_field]}, unique_token) }
120
+ i += 50
121
+ end while !records.blank?
122
+ end
110
123
  end_eval
111
124
 
112
125
  # Instance methods
@@ -125,12 +138,13 @@ module Authgasm
125
138
  return if pass.blank?
126
139
  self.tried_to_set_#{options[:password_field]} = true
127
140
  @#{options[:password_field]} = pass
128
- salt = [Array.new(6) {rand(256).chr}.join].pack("m").chomp
129
141
  self.#{options[:remember_token_field]} = self.class.unique_token
130
- self.#{options[:password_salt_field]}, self.#{options[:crypted_password_field]} = salt, crypto_provider.encrypt(@#{options[:password_field]} + salt)
142
+ self.#{options[:password_salt_field]} = self.class.unique_token
143
+ self.#{options[:crypted_password_field]} = crypto_provider.encrypt(@#{options[:password_field]} + #{options[:password_salt_field]})
131
144
  end
132
145
 
133
146
  def valid_#{options[:password_field]}?(attempted_password)
147
+ return false if attempted_password.blank?
134
148
  attempted_password == #{options[:crypted_password_field]} || #{options[:crypted_password_field]} == crypto_provider.encrypt(attempted_password + #{options[:password_salt_field]})
135
149
  end
136
150
  end_eval
@@ -145,6 +159,7 @@ module Authgasm
145
159
  end
146
160
 
147
161
  def valid_#{options[:password_field]}?(attemtped_password)
162
+ return false if attempted_password.blank?
148
163
  attempted_password == #{options[:crypted_password_field]} || #{options[:crypted_password_field]} = crypto_provider.decrypt(attempted_password)
149
164
  end
150
165
  end_eval
@@ -158,6 +173,10 @@ module Authgasm
158
173
  self.class.crypto_provider
159
174
  end
160
175
 
176
+ def forget!
177
+ update_attribute(:#{options[:remember_token_field]}, self.class.unique_token)
178
+ end
179
+
161
180
  def randomize_#{options[:password_field]}!
162
181
  chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
163
182
  newpass = ""
@@ -166,6 +185,13 @@ module Authgasm
166
185
  self.confirm_#{options[:password_field]} = newpass
167
186
  end
168
187
 
188
+ def save_from_session(*args)
189
+ @saving_from_session = true
190
+ result = save(*args)
191
+ @saving_from_session = false
192
+ result
193
+ end
194
+
169
195
  protected
170
196
  def create_sessions!
171
197
  return if !#{options[:session_class]}.activated? || #{options[:session_ids].inspect}.blank?
@@ -183,7 +209,7 @@ module Authgasm
183
209
  end
184
210
 
185
211
  def update_sessions!
186
- return if !#{options[:session_class]}.activated?
212
+ return if @saving_from_session || !#{options[:session_class]}.activated?
187
213
 
188
214
  #{options[:session_ids].inspect}.each do |session_id|
189
215
  session = #{options[:session_class]}.find(*[session_id].compact)
@@ -192,7 +218,7 @@ module Authgasm
192
218
  next if !session || session.record != self
193
219
 
194
220
  # We know we are logged in and this is our record, update the session
195
- session.update
221
+ session.save
196
222
  end
197
223
  end
198
224
 
@@ -215,4 +241,4 @@ module Authgasm
215
241
  end
216
242
  end
217
243
 
218
- ActiveRecord::Base.send(:include, Authgasm::ActsAsAuthenticated)
244
+ ActiveRecord::Base.send(:include, Authgasm::ActsAsAuthentic)
@@ -0,0 +1,25 @@
1
+ module Authgasm
2
+ module ControllerAdapters # :nodoc:
3
+ # = Abstract Adapter
4
+ # 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.
5
+ class AbstractAdapter
6
+ attr_accessor :controller
7
+
8
+ def initialize(controller)
9
+ self.controller = controller
10
+ end
11
+
12
+ def authenticate_with_http_basic(*args, &block)
13
+ end
14
+
15
+ def cookies
16
+ end
17
+
18
+ def request
19
+ end
20
+
21
+ def session
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,39 @@
1
+ module Authgasm
2
+ module ControllerAdapters
3
+ # = Rails Adapter
4
+ # Adapts authgasm to work with rails. The point is to close the gap between what authgasm expects and what the rails controller object
5
+ # provides. Similar to how ActiveRecord has an adapter for MySQL, PostgreSQL, SQLite, etc.
6
+ class RailsAdapter < AbstractAdapter
7
+ def authenticate_with_http_basic(*args, &block)
8
+ controller.authenticate_with_http_basic(*args, &block)
9
+ end
10
+
11
+ def cookies
12
+ controller.send(:cookies)
13
+ end
14
+
15
+ def request
16
+ controller.request
17
+ end
18
+
19
+ def session
20
+ controller.session
21
+ end
22
+ end
23
+
24
+ # = Rails Implementation
25
+ # Lets Authgasm know about the controller object, AKA "activates" authgasm.
26
+ module RailsImplementation
27
+ def self.included(klass) # :nodoc:
28
+ klass.prepend_before_filter :set_controller
29
+ end
30
+
31
+ private
32
+ def set_controller
33
+ Authgasm::Session::Base.controller = RailsAdapter.new(self)
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ ActionController::Base.send(:include, Authgasm::ControllerAdapters::RailsImplementation)
@@ -18,7 +18,7 @@ module Authgasm
18
18
 
19
19
  module InstanceMethods # :nodoc:
20
20
  def new_record?
21
- true
21
+ new_session?
22
22
  end
23
23
  end
24
24
  end
@@ -7,7 +7,9 @@ module Authgasm
7
7
  include Config
8
8
 
9
9
  class << self
10
- # Returns true if a controller have been set and can be used properly.
10
+ # 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
11
+ # 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
12
+ # object to Authgasm via Authgasm::Session::Base.controller = obj.
11
13
  def activated?
12
14
  !controller.blank?
13
15
  end
@@ -26,13 +28,13 @@ module Authgasm
26
28
  # session.create
27
29
  def create(*args)
28
30
  session = new(*args)
29
- session.create
31
+ session.save
30
32
  end
31
33
 
32
34
  # Same as create but calls create!, which raises an exception when authentication fails
33
35
  def create!(*args)
34
36
  session = new(*args)
35
- session.create!
37
+ session.save!
36
38
  end
37
39
 
38
40
  # Finds your session by session, then cookie, and finally basic http auth. Perfect for that global before_filter to find your logged in user:
@@ -49,9 +51,13 @@ module Authgasm
49
51
  args = [id].compact
50
52
  session = new(*args)
51
53
  find_with.each do |find_method|
52
- args = []
53
- args << true unless find_method == :session
54
- return session if session.send("valid_#{find_method}?", *args)
54
+ if session.send("valid_#{find_method}?")
55
+ if session.record.class.column_names.include?("last_request_at")
56
+ session.record.last_request_at = Time.now
57
+ session.record.save_from_session(false)
58
+ end
59
+ return session
60
+ end
55
61
  end
56
62
  nil
57
63
  end
@@ -72,29 +78,15 @@ module Authgasm
72
78
  end
73
79
  end
74
80
 
75
- # Convenience method. The same as:
76
- #
77
- # session = UserSession.new
78
- # session.update
79
- def update(*args)
80
- session = new(*args)
81
- session.update
82
- end
83
-
84
- # The same as update but calls update!, which raises an exception when authentication fails
85
- def update!(*args)
86
- session = new(*args)
87
- session.update!
88
- end
89
-
90
81
  private
91
82
  def controllers
92
83
  @@controllers ||= {}
93
84
  end
94
85
  end
95
86
 
96
- attr_accessor :login_with, :remember_me, :id
87
+ attr_accessor :login_with, :new_session, :remember_me
97
88
  attr_reader :record, :unauthorized_record
89
+ attr_writer :id
98
90
 
99
91
  # You can initialize a session by doing any of the following:
100
92
  #
@@ -111,6 +103,8 @@ module Authgasm
111
103
  # 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.
112
104
  # To solve this just pass a id called :proxy, or whatever you want. Authgasm will separate everything out.
113
105
  def initialize(*args)
106
+ raise NotActivated.new(self) unless self.class.activated?
107
+
114
108
  create_configurable_methods!
115
109
 
116
110
  self.id = args.pop if args.last.is_a?(Symbol)
@@ -131,45 +125,6 @@ module Authgasm
131
125
  end
132
126
  end
133
127
 
134
- # Creates a new user session for you. It does all of the magic:
135
- #
136
- # 1. validates
137
- # 2. sets session
138
- # 3. sets cookie
139
- # 4. updates magic fields
140
- def create(updating = false)
141
- if valid?(true)
142
- cookies[cookie_key] = {
143
- :value => record.send(remember_token_field),
144
- :expires => remember_me? ? remember_me_for.from_now : nil
145
- }
146
-
147
- if !updating
148
- record.login_count = record.login_count + 1 if record.respond_to?(:login_count)
149
-
150
- if record.respond_to?(:current_login_at)
151
- record.last_login_at = record.current_login_at if record.respond_to?(:last_login_at)
152
- record.current_login_at = Time.now
153
- end
154
-
155
- if record.respond_to?(:current_login_ip)
156
- record.last_login_ip = record.current_login_ip if record.respond_to?(:last_login_ip)
157
- record.current_login_ip = controller.request.remote_ip
158
- end
159
-
160
- record.save(false)
161
- end
162
-
163
- self
164
- end
165
- end
166
-
167
- # Same as create but raises an exception when authentication fails
168
- def create!(updating = false)
169
- raise SessionInvalid.new(self) unless create(updating)
170
- end
171
- alias_method :start!, :create!
172
-
173
128
  # Your login credentials in hash format. Usually {:login => "my login", :password => "<protected>"} depending on your configuration.
174
129
  # Password is protected as a security measure. The raw password should never be publicly accessible.
175
130
  def credentials
@@ -178,8 +133,8 @@ module Authgasm
178
133
 
179
134
  # Lets you set your loging and password via a hash format.
180
135
  def credentials=(values)
181
- values.symbolize_keys!
182
- raise(ArgumentError, "Only 2 credentials are allowed: #{login_field} and #{password_field}") if !values.is_a?(Hash) || values.keys.size > 2 || !values.key?(login_field) || !values.key?(password_field)
136
+ 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
183
138
  values.each { |field, value| send("#{field}=", value) }
184
139
  end
185
140
 
@@ -187,21 +142,49 @@ module Authgasm
187
142
  def destroy
188
143
  errors.clear
189
144
  @record = nil
190
- cookies.delete cookie_key
191
- session[session_key] = nil
145
+ controller.cookies.delete cookie_key
146
+ controller.session[session_key] = nil
192
147
  true
193
148
  end
194
149
 
195
- # Errors when authentication fails, just like ActiveRecord errors. In fact it uses the same exact class.
150
+ # The errors in Authgasm work JUST LIKE ActiveRecord. In fact, it uses the exact same ActiveRecord errors class. Use it the same way:
151
+ #
152
+ # === Example
153
+ #
154
+ # class UserSession
155
+ # before_validation :check_if_awesome
156
+ #
157
+ # private
158
+ # def check_if_awesome
159
+ # errors.add(:login, "must contain awesome") if login && !login.include?("awesome")
160
+ # errors.add_to_base("You must be awesome to log in") unless record.awesome?
161
+ # end
162
+ # end
196
163
  def errors
197
164
  @errors ||= Errors.new(self)
198
165
  end
199
166
 
167
+ # 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
168
+ # 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
169
+ # user sessions. Just use an id for the "secure" session and you should be good.
170
+ #
171
+ # You can set the id a number of ways:
172
+ #
173
+ # session = Session.new(:secure)
174
+ # session = Session.new("username", "password", :secure)
175
+ # session = Session.new({:username => "username", :password => "password"}, :secure)
176
+ # session.id = :secure
177
+ #
178
+ # Just be sure and set your id before you validate / create / update your session.
179
+ def id
180
+ @id
181
+ end
182
+
200
183
  def inspect # :nodoc:
201
184
  details = {}
202
185
  case login_with
203
186
  when :unauthorized_record
204
- details[:unauthorized_record] = unauthorized_record
187
+ details[:unauthorized_record] = "<protected>"
205
188
  else
206
189
  details[login_field.to_sym] = send(login_field)
207
190
  details[password_field.to_sym] = "<protected>"
@@ -209,16 +192,62 @@ module Authgasm
209
192
  "#<#{self.class.name} #{details.inspect}>"
210
193
  end
211
194
 
195
+
196
+ def new_session?
197
+ new_session != false
198
+ end
199
+
212
200
  # Allows users to be remembered via a cookie.
213
201
  def remember_me?
214
- remember_me == true || remember_me = "true" || remember_me == "1"
202
+ remember_me == true || remember_me == "true" || remember_me == "1"
215
203
  end
216
204
 
217
205
  # When to expire the cookie. See remember_me_for configuration option to change this.
218
206
  def remember_me_until
207
+ return unless remember_me?
219
208
  remember_me_for.from_now
220
209
  end
221
210
 
211
+ # Creates / updates a new user session for you. It does all of the magic:
212
+ #
213
+ # 1. validates
214
+ # 2. sets session
215
+ # 3. sets cookie
216
+ # 4. updates magic fields
217
+ def save
218
+ if valid?
219
+ update_session!
220
+ controller.cookies[cookie_key] = {
221
+ :value => record.send(remember_token_field),
222
+ :expires => remember_me_until
223
+ }
224
+
225
+ record.login_count = record.login_count + 1 if record.respond_to?(:login_count)
226
+
227
+ if record.respond_to?(:current_login_at)
228
+ record.last_login_at = record.current_login_at if record.respond_to?(:last_login_at)
229
+ record.current_login_at = Time.now
230
+ end
231
+
232
+ if record.respond_to?(:current_login_ip)
233
+ record.last_login_ip = record.current_login_ip if record.respond_to?(:last_login_ip)
234
+ record.current_login_ip = controller.request.remote_ip
235
+ end
236
+
237
+ record.save_from_session(false)
238
+
239
+ self.new_session = false
240
+ self
241
+ end
242
+ end
243
+
244
+ # Same as save but raises an exception when authentication fails
245
+ def save!
246
+ result = save
247
+ raise SessionInvalid.new(self) unless result
248
+ result
249
+ end
250
+
222
251
  # Sometimes you don't want to create a session via credentials (login and password). Maybe you already have the record. Just set this record to this and it will be authenticated when you try to validate
223
252
  # the session. Basically this is another form of credentials, you are just skipping username and password validation.
224
253
  def unauthorized_record=(value)
@@ -226,21 +255,12 @@ module Authgasm
226
255
  @unauthorized_record = value
227
256
  end
228
257
 
229
- # Updates the session with any new information. Resets the session and cookie.
230
- def update
231
- create(true)
232
- end
233
-
234
- # Same as update but raises an exception if validation is failed
235
- def update!
236
- create!(true)
237
- end
238
-
239
- def valid?(set_session = false)
258
+ def valid?
240
259
  errors.clear
241
260
  temp_record = unauthorized_record
242
261
 
243
- if login_with == :credentials
262
+ case login_with
263
+ when :credentials
244
264
  errors.add(login_field, "can not be blank") if login.blank?
245
265
  errors.add(password_field, "can not be blank") if protected_password.blank?
246
266
  return false if errors.count > 0
@@ -256,6 +276,19 @@ module Authgasm
256
276
  errors.add(password_field, "is invalid")
257
277
  return false
258
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
259
292
  end
260
293
 
261
294
  [:approved, :confirmed, :inactive].each do |required_status|
@@ -268,34 +301,34 @@ module Authgasm
268
301
  # All is good, lets set the record
269
302
  @record = temp_record
270
303
 
271
- # Now lets set the session to make things easier on successive requests. This is nice when logging in from a cookie, the next requests will be right from the session, which is quicker.
272
- if set_session
273
- session[session_key] = record.id
274
- if record.class.column_names.include?("last_request_at")
275
- record.last_request_at = Time.now
276
- record.save(false)
277
- end
278
- end
279
-
280
304
  true
281
305
  end
282
306
 
283
- def valid_http_auth?(set_session = false)
307
+ def valid_http_auth?
284
308
  controller.authenticate_with_http_basic do |login, password|
285
309
  if !login.blank? && !password.blank?
286
310
  send("#{login_method}=", login)
287
311
  send("#{password_method}=", password)
288
- return valid?(set_session)
312
+ result = valid?
313
+ if result
314
+ update_session!
315
+ return result
316
+ end
289
317
  end
290
318
  end
291
319
 
292
320
  false
293
321
  end
294
322
 
295
- def valid_cookie?(set_session = false)
323
+ def valid_cookie?
296
324
  if cookie_credentials
297
325
  self.unauthorized_record = klass.send("find_by_#{remember_token_field}", cookie_credentials)
298
- valid?(set_session)
326
+ result = valid?
327
+ if result
328
+ update_session!
329
+ self.new_session = false
330
+ return result
331
+ end
299
332
  end
300
333
 
301
334
  false
@@ -304,7 +337,11 @@ module Authgasm
304
337
  def valid_session?
305
338
  if session_credentials
306
339
  self.unauthorized_record = klass.find_by_id(session_credentials)
307
- return valid?
340
+ result = valid?
341
+ if result
342
+ self.new_session = false
343
+ return result
344
+ end
308
345
  end
309
346
 
310
347
  false
@@ -315,12 +352,8 @@ module Authgasm
315
352
  self.class.controller
316
353
  end
317
354
 
318
- def cookies
319
- controller.send(:cookies)
320
- end
321
-
322
355
  def cookie_credentials
323
- cookies[cookie_key]
356
+ controller.cookies[cookie_key]
324
357
  end
325
358
 
326
359
  def create_configurable_methods!
@@ -356,12 +389,12 @@ module Authgasm
356
389
  @password
357
390
  end
358
391
 
359
- def session
360
- controller.session
392
+ def session_credentials
393
+ controller.session[session_key]
361
394
  end
362
395
 
363
- def session_credentials
364
- session[session_key]
396
+ def update_session!
397
+ controller.session[session_key] = record && record.id
365
398
  end
366
399
  end
367
400
  end