Module: Authlogic::ActiveRecord::ActsAsAuthentic
- Defined in:
- lib/authlogic/active_record/acts_as_authentic.rb
Overview
Acts As Authentic
Provides the acts_as_authentic method to include in your models to help with authentication. See method below.
Instance Method Summary collapse
-
#acts_as_authentic(options = {}) ⇒ Object
Call this method in your model to add in basic authentication madness that your authlogic session expects.
Instance Method Details
#acts_as_authentic(options = {}) ⇒ Object
Call this method in your model to add in basic authentication madness that your authlogic session expects.
Methods
For example purposes lets assume you have a User model.
Class method name Description
User.crypto_provider The class that you set in your :crypto_provider option
User.forget_all! Finds all records, loops through them, and calls forget! on each record. This is paginated to save on memory.
User.unique_token returns unique token generated by your :crypto_provider
Named Scopes
User.logged_in Find all users who are logged in, based on your :logged_in_timeout option.
User.logged_out Same as above, but logged out.
Isntace method name
user.password= Method name based on the :password_field option. This is used to set the password. Pass the *raw* password to this.
user.confirm_password= Confirms the password, needed to change the password.
user.valid_password?(pass) Determines if the password passed is valid. The password could be encrypted or raw.
user.reset_password! Basically resets the password to a random password using only letters and numbers.
user.logged_in? Based on the :logged_in_timeout option. Tells you if the user is logged in or not.
user.forget! Changes their remember token, making their cookie and session invalid. A way to log the user out withouth changing their password.
Options
-
session_class:
default: “#nameSession”, This is the related session class. A lot of the configuration will be based off of the configuration values of this class. -
crypto_provider:
default: Authlogic::Sha512CryptoProvider, This is the class that provides your encryption. By default Authlogic provides its own crypto provider that uses Sha512 encrypton. -
login_field:
default: options.login_field, The name of the field used for logging in, this is guess based on what columns are in your db. Only specify if you aren’t using: login, username, or email -
login_field_type:
default: options == :email ? :email : :login, Tells authlogic how to validation the field, what regex to use, etc. If the field name is email it will automatically use email, otherwise it uses login. -
login_field_regex:
default: if email then typical email regex, otherwise typical login regex. This is used in validates_format_of for the login_field. -
login_field_regex_message:
the message to use when the validates_format_of for the login field fails. -
password_field:
default: options.password_field, This is the name of the field to set the password, NOT the field the encrypted password is stored. -
crypted_password_field:
default: depends on which columns are present, The name of the database field where your encrypted password is stored. If the name of the field is different from any of the following you need to specify it with this option: crypted_password, encrypted_password, password_hash, pw_hash -
password_salt_field:
default: depends on which columns are present, This is the name of the field in your database that stores your password salt. If the name of the field is different from any of the following then you need to specify it with this option: password_salt, pw_salt, salt -
remember_token_field:
default: options.remember_token_field, This is the name of the field your remember_token is stored. The remember token is a unique token that is stored in the users cookie and session. This way you have complete control of when session expire and you don’t have to change passwords to expire sessions. This also ensures that stale sessions can not be persisted. By stale, I mean sessions that are logged in using an outdated password. If the name of the field is anything other than the following you need to specify it with this option: remember_token, remember_key, cookie_token, cookie_key -
scope:
default: nil, This scopes validations. If all of your users belong to an account you might want to scope everything to the account. Just pass :account_id -
logged_in_timeout:
default: 10.minutes, This is really just a nifty feature to tell if a user is logged in or not. It’s based on activity. So if the user in inactive longer than the value you pass here they are assumed “logged out”. -
session_ids:
default: [nil], The sessions that we want to automatically reset when a user is created or updated so you don’t have to worry about this. Set to [] to disable. Should be an array of ids. See the Authlogic::Session documentation for information on ids. The order is important. The first id should be your main session, the session they need to log into first. This is generally nil. When you don’t specify an id in your session you are really just inexplicitly saying you want to use the id of nil.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/authlogic/active_record/acts_as_authentic.rb', line 79 def acts_as_authentic( = {}) # If we don't have a database, skip all of this, solves initial setup errors begin column_names rescue Exception return end # Setup default options begin [:session_class] ||= "#{name}Session".constantize rescue NameError raise NameError.new("You must create a #{name}Session class before a model can act_as_authentic. If that is not the name of the class pass the class constant via the :session_class option.") end [:crypto_provider] ||= Sha512CryptoProvider [:crypto_provider_type] ||= [:crypto_provider].respond_to?(:decrypt) ? :encryption : :hash [:login_field] ||= [:session_class].login_field [:login_field_type] ||= [:login_field] == :email ? :email : :login [:password_field] ||= [:session_class].password_field [:crypted_password_field] ||= (column_names.include?("crypted_password") && :crypted_password) || (column_names.include?("encrypted_password") && :encrypted_password) || (column_names.include?("password_hash") && :password_hash) || (column_names.include?("pw_hash") && :pw_hash) || :crypted_password [:password_salt_field] ||= (column_names.include?("password_salt") && :password_salt) || (column_names.include?("pw_salt") && :pw_salt) || (column_names.include?("salt") && :salt) || :password_salt [:remember_token_field] ||= [:session_class].remember_token_field [:logged_in_timeout] ||= 10.minutes [:session_ids] ||= [nil] # Validations case [:login_field_type] when :email validates_length_of [:login_field], :within => 6..100 email_name_regex = '[\w\.%\+\-]+' domain_head_regex = '(?:[A-Z0-9\-]+\.)+' domain_tld_regex = '(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|jobs|museum)' [:login_field_regex] ||= /\A#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}\z/i [:login_field_regex_message] ||= "should look like an email address." validates_format_of [:login_field], :with => [:login_field_regex], :message => [:login_field_regex_message] else validates_length_of [:login_field], :within => 2..100 [:login_field_regex] ||= /\A\w[\w\.\-_@ ]+\z/ [:login_field_regex_message] ||= "use only letters, numbers, spaces, and .-_@ please." validates_format_of [:login_field], :with => [:login_field_regex], :message => [:login_field_regex_message] end validates_uniqueness_of [:login_field], :scope => [:scope] validates_uniqueness_of [:remember_token_field] validate :validate_password validates_numericality_of :login_count, :only_integer => :true, :greater_than_or_equal_to => 0, :allow_nil => true if column_names.include?("login_count") if column_names.include?("last_request_at") named_scope :logged_in, lambda { {:conditions => ["last_request_at > ?", [:logged_in_timeout].ago]} } named_scope :logged_out, lambda { {:conditions => ["last_request_at is NULL or last_request_at <= ?", [:logged_in_timeout].ago]} } end before_save :get_session_information, :if => :update_sessions? after_save :maintain_sessions!, :if => :update_sessions? # Attributes attr_writer "confirm_#{[:password_field]}" attr_accessor "tried_to_set_#{[:password_field]}" # Class methods class_eval <<-"end_eval", __FILE__, __LINE__ def self.unique_token # Force using the Sha512 because all that we are doing is creating a unique token, a hash is perfect for this Authlogic::Sha512CryptoProvider.encrypt(Time.now.to_s + (1..10).collect{ rand.to_s }.join) end def self.crypto_provider #{[:crypto_provider]} end def self.forget_all! # Paginate these to save on memory records = nil i = 0 begin records = find(:all, :limit => 50, :offset => i) records.each { |record| record.forget! } i += 50 end while !records.blank? end end_eval # Instance methods if column_names.include?("last_request_at") class_eval <<-"end_eval", __FILE__, __LINE__ def logged_in? !last_request_at.nil? && last_request_at > #{[:logged_in_timeout].to_i}.seconds.ago end end_eval end class_eval <<-"end_eval", __FILE__, __LINE__ def #{[:password_field]}=(pass) return if pass.blank? self.tried_to_set_#{[:password_field]} = true @#{[:password_field]} = pass self.#{[:remember_token_field]} = self.class.unique_token self.#{[:password_salt_field]} = self.class.unique_token self.#{[:crypted_password_field]} = crypto_provider.encrypt(@#{[:password_field]} + #{[:password_salt_field]}) end def valid_#{[:password_field]}?(attempted_password) return false if attempted_password.blank? || #{[:crypted_password_field]}.blank? || #{[:password_salt_field]}.blank? attempted_password == #{[:crypted_password_field]} || (crypto_provider.respond_to?(:decrypt) && crypto_provider.decrypt(#{[:crypted_password_field]}) == attempted_password + #{[:password_salt_field]}) || (!crypto_provider.respond_to?(:decrypt) && crypto_provider.encrypt(attempted_password + #{[:password_salt_field]}) == #{[:crypted_password_field]}) end end_eval class_eval <<-"end_eval", __FILE__, __LINE__ def #{[:password_field]}; end def confirm_#{[:password_field]}; end def crypto_provider self.class.crypto_provider end def forget! self.#{[:remember_token_field]} = self.class.unique_token save_without_session_maintenance(false) end def reset_#{[:password_field]}! chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a newpass = "" 1.upto(10) { |i| newpass << chars[rand(chars.size-1)] } self.#{[:password_field]} = newpass self.confirm_#{[:password_field]} = newpass save_without_session_maintenance(false) end alias_method :randomize_password!, :reset_password! def save_without_session_maintenance(*args) @skip_session_maintenance = true result = save(*args) @skip_session_maintenance = false result end protected def update_sessions? !@skip_session_maintenance && #{[:session_class]}.activated? && !#{[:session_ids].inspect}.blank? && #{[:remember_token_field]}_changed? end def get_session_information # Need to determine if we are completely logged out, or logged in as another user @_sessions = [] @_logged_out = true #{[:session_ids].inspect}.each do |session_id| session = #{[:session_class]}.find(*[session_id].compact) if session if !session.record.blank? @_logged_out = false @_sessions << session if session.record == self end end end end def maintain_sessions! if @_logged_out create_session! elsif !@_sessions.blank? update_sessions! end end def create_session! # We only want to automatically login into the first session, since this is the main session. The other sessions are sessions # that need to be created after logging into the main session. session_id = #{[:session_ids].inspect}.first # If we are already logged in, ignore this completely. All that we care about is updating ourself. next if #{[:session_class]}.find(*[session_id].compact) # Log me in args = [self, session_id].compact #{[:session_class]}.create(*args) end def update_sessions! # We found sessions above, let's update them with the new info @_sessions.each do |stale_session| stale_session.unauthorized_record = self stale_session.save end end def tried_to_set_password? tried_to_set_password == true end def validate_password if new_record? || tried_to_set_#{[:password_field]}? if @#{[:password_field]}.blank? errors.add(:#{[:password_field]}, "can not be blank") else errors.add(:confirm_#{[:password_field]}, "did not match") if @confirm_#{[:password_field]} != @#{[:password_field]} end end end end_eval end |