]> projects.mako.cc - selectricity/blob - vendor/plugins/login_engine/lib/login_engine/authenticated_user.rb
e48be196368b8a4be5ee20d7534d3114dd13be17
[selectricity] / vendor / plugins / login_engine / lib / login_engine / authenticated_user.rb
1 require 'digest/sha1'
2
3 # this model expects a certain database layout and its based on the name/login pattern. 
4
5 module LoginEngine
6   module AuthenticatedUser
7
8     def self.included(base)
9       base.class_eval do
10
11         # use the table name given
12         set_table_name LoginEngine.config(:user_table)
13
14         attr_accessor :new_password
15       
16         validates_presence_of :login
17         validates_length_of :login, :within => 3..40
18         validates_uniqueness_of :login
19         validates_uniqueness_of :email
20         validates_format_of :email, :with => /^[^@]+@.+$/
21
22         validates_presence_of :password, :if => :validate_password?
23         validates_confirmation_of :password, :if => :validate_password?
24         validates_length_of :password, { :minimum => 5, :if => :validate_password? }
25         validates_length_of :password, { :maximum => 40, :if => :validate_password? }
26   
27         protected 
28       
29         attr_accessor :password, :password_confirmation
30       
31         after_save :falsify_new_password
32         after_validation :crypt_password
33
34       end
35       base.extend(ClassMethods)
36     end
37
38     module ClassMethods
39     
40       def authenticate(login, pass)
41         u = find(:first, :conditions => ["login = ? AND verified = 1 AND deleted = 0", login])
42         return nil if u.nil?
43         find(:first, :conditions => ["login = ? AND salted_password = ? AND verified = 1", login, AuthenticatedUser.salted_password(u.salt, AuthenticatedUser.hashed(pass))])
44       end
45
46       def authenticate_by_token(id, token)
47         # Allow logins for deleted accounts, but only via this method (and
48         # not the regular authenticate call)
49         u = find(:first, :conditions => ["#{User.primary_key} = ? AND security_token = ?", id, token])
50         return nil if u.nil? or u.token_expired?
51         return nil if false == u.update_expiry
52         u
53       end
54       
55     end
56   
57
58     protected
59     
60       def self.hashed(str)
61         # check if a salt has been set...
62         if LoginEngine.config(:salt) == nil
63           raise "You must define a :salt value in the configuration for the LoginEngine module."
64         end
65   
66         return Digest::SHA1.hexdigest("#{LoginEngine.config(:salt)}--#{str}--}")[0..39]
67       end
68     
69       def self.salted_password(salt, hashed_password)
70         hashed(salt + hashed_password)
71       end
72     
73     public
74   
75     # hmmm, how does this interact with the developer's own User model initialize?
76     # We would have to *insist* that the User.initialize method called 'super'
77     #
78     def initialize(attributes = nil)
79       super
80       @new_password = false
81     end
82
83     def token_expired?
84       self.security_token and self.token_expiry and (Time.now > self.token_expiry)
85     end
86
87     def update_expiry
88       write_attribute('token_expiry', [self.token_expiry, Time.at(Time.now.to_i + 600 * 1000)].min)
89       write_attribute('authenticated_by_token', true)
90       write_attribute("verified", 1)
91       update_without_callbacks
92     end
93
94     def generate_security_token(hours = nil)
95       if not hours.nil? or self.security_token.nil? or self.token_expiry.nil? or 
96           (Time.now.to_i + token_lifetime / 2) >= self.token_expiry.to_i
97         return new_security_token(hours)
98       else
99         return self.security_token
100       end
101     end
102
103     def set_delete_after
104       hours = LoginEngine.config(:delayed_delete_days) * 24
105       write_attribute('deleted', 1)
106       write_attribute('delete_after', Time.at(Time.now.to_i + hours * 60 * 60))
107
108       # Generate and return a token here, so that it expires at
109       # the same time that the account deletion takes effect.
110       return generate_security_token(hours)
111     end
112
113     def change_password(pass, confirm = nil)
114       self.password = pass
115       self.password_confirmation = confirm.nil? ? pass : confirm
116       @new_password = true
117     end
118     
119     protected
120
121     def validate_password?
122       @new_password
123     end
124
125
126     def crypt_password
127       if @new_password
128         write_attribute("salt", AuthenticatedUser.hashed("salt-#{Time.now}"))
129         write_attribute("salted_password", AuthenticatedUser.salted_password(salt, AuthenticatedUser.hashed(@password)))
130       end
131     end
132
133     def falsify_new_password
134       @new_password = false
135       true
136     end
137
138     def new_security_token(hours = nil)
139       write_attribute('security_token', AuthenticatedUser.hashed(self.salted_password + Time.now.to_i.to_s + rand.to_s))
140       write_attribute('token_expiry', Time.at(Time.now.to_i + token_lifetime(hours)))
141       update_without_callbacks
142       return self.security_token
143     end
144
145     def token_lifetime(hours = nil)
146       if hours.nil?
147         LoginEngine.config(:security_token_life_hours) * 60 * 60
148       else
149         hours * 60 * 60
150       end
151     end
152
153   end
154 end
155   

Benjamin Mako Hill || Want to submit a patch?