##// END OF EJS Templates
Remove hard coded LDAP log message....
Eric Davis -
r3629:a1f73c8b2031
parent child
Show More
@@ -1,360 +1,360
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require "digest/sha1"
19 19
20 20 class User < Principal
21 21
22 22 # Account statuses
23 23 STATUS_ANONYMOUS = 0
24 24 STATUS_ACTIVE = 1
25 25 STATUS_REGISTERED = 2
26 26 STATUS_LOCKED = 3
27 27
28 28 USER_FORMATS = {
29 29 :firstname_lastname => '#{firstname} #{lastname}',
30 30 :firstname => '#{firstname}',
31 31 :lastname_firstname => '#{lastname} #{firstname}',
32 32 :lastname_coma_firstname => '#{lastname}, #{firstname}',
33 33 :username => '#{login}'
34 34 }
35 35
36 36 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
37 37 :after_remove => Proc.new {|user, group| group.user_removed(user)}
38 38 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
39 39 has_many :changesets, :dependent => :nullify
40 40 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
41 41 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
42 42 has_one :api_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='api'"
43 43 belongs_to :auth_source
44 44
45 45 # Active non-anonymous users scope
46 46 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
47 47
48 48 acts_as_customizable
49 49
50 50 attr_accessor :password, :password_confirmation
51 51 attr_accessor :last_before_login_on
52 52 # Prevents unauthorized assignments
53 53 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids
54 54
55 55 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
56 56 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }
57 57 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
58 58 # Login must contain lettres, numbers, underscores only
59 59 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
60 60 validates_length_of :login, :maximum => 30
61 61 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
62 62 validates_length_of :firstname, :lastname, :maximum => 30
63 63 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
64 64 validates_length_of :mail, :maximum => 60, :allow_nil => true
65 65 validates_confirmation_of :password, :allow_nil => true
66 66
67 67 def before_create
68 68 self.mail_notification = false
69 69 true
70 70 end
71 71
72 72 def before_save
73 73 # update hashed_password if password was set
74 74 self.hashed_password = User.hash_password(self.password) if self.password
75 75 end
76 76
77 77 def reload(*args)
78 78 @name = nil
79 79 super
80 80 end
81 81
82 82 def identity_url=(url)
83 83 if url.blank?
84 84 write_attribute(:identity_url, '')
85 85 else
86 86 begin
87 87 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
88 88 rescue OpenIdAuthentication::InvalidOpenId
89 89 # Invlaid url, don't save
90 90 end
91 91 end
92 92 self.read_attribute(:identity_url)
93 93 end
94 94
95 95 # Returns the user that matches provided login and password, or nil
96 96 def self.try_to_login(login, password)
97 97 # Make sure no one can sign in with an empty password
98 98 return nil if password.to_s.empty?
99 99 user = find(:first, :conditions => ["login=?", login])
100 100 if user
101 101 # user is already in local database
102 102 return nil if !user.active?
103 103 if user.auth_source
104 104 # user has an external authentication method
105 105 return nil unless user.auth_source.authenticate(login, password)
106 106 else
107 107 # authentication with local password
108 108 return nil unless User.hash_password(password) == user.hashed_password
109 109 end
110 110 else
111 111 # user is not yet registered, try to authenticate with available sources
112 112 attrs = AuthSource.authenticate(login, password)
113 113 if attrs
114 114 user = new(attrs)
115 115 user.login = login
116 116 user.language = Setting.default_language
117 117 if user.save
118 118 user.reload
119 logger.info("User '#{user.login}' created from the LDAP") if logger
119 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger
120 120 end
121 121 end
122 122 end
123 123 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
124 124 user
125 125 rescue => text
126 126 raise text
127 127 end
128 128
129 129 # Returns the user who matches the given autologin +key+ or nil
130 130 def self.try_to_autologin(key)
131 131 tokens = Token.find_all_by_action_and_value('autologin', key)
132 132 # Make sure there's only 1 token that matches the key
133 133 if tokens.size == 1
134 134 token = tokens.first
135 135 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
136 136 token.user.update_attribute(:last_login_on, Time.now)
137 137 token.user
138 138 end
139 139 end
140 140 end
141 141
142 142 # Return user's full name for display
143 143 def name(formatter = nil)
144 144 if formatter
145 145 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
146 146 else
147 147 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
148 148 end
149 149 end
150 150
151 151 def active?
152 152 self.status == STATUS_ACTIVE
153 153 end
154 154
155 155 def registered?
156 156 self.status == STATUS_REGISTERED
157 157 end
158 158
159 159 def locked?
160 160 self.status == STATUS_LOCKED
161 161 end
162 162
163 163 def check_password?(clear_password)
164 164 User.hash_password(clear_password) == self.hashed_password
165 165 end
166 166
167 167 # Generate and set a random password. Useful for automated user creation
168 168 # Based on Token#generate_token_value
169 169 #
170 170 def random_password
171 171 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
172 172 password = ''
173 173 40.times { |i| password << chars[rand(chars.size-1)] }
174 174 self.password = password
175 175 self.password_confirmation = password
176 176 self
177 177 end
178 178
179 179 def pref
180 180 self.preference ||= UserPreference.new(:user => self)
181 181 end
182 182
183 183 def time_zone
184 184 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
185 185 end
186 186
187 187 def wants_comments_in_reverse_order?
188 188 self.pref[:comments_sorting] == 'desc'
189 189 end
190 190
191 191 # Return user's RSS key (a 40 chars long string), used to access feeds
192 192 def rss_key
193 193 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
194 194 token.value
195 195 end
196 196
197 197 # Return user's API key (a 40 chars long string), used to access the API
198 198 def api_key
199 199 token = self.api_token || self.create_api_token(:action => 'api')
200 200 token.value
201 201 end
202 202
203 203 # Return an array of project ids for which the user has explicitly turned mail notifications on
204 204 def notified_projects_ids
205 205 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
206 206 end
207 207
208 208 def notified_project_ids=(ids)
209 209 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
210 210 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
211 211 @notified_projects_ids = nil
212 212 notified_projects_ids
213 213 end
214 214
215 215 def self.find_by_rss_key(key)
216 216 token = Token.find_by_value(key)
217 217 token && token.user.active? ? token.user : nil
218 218 end
219 219
220 220 def self.find_by_api_key(key)
221 221 token = Token.find_by_action_and_value('api', key)
222 222 token && token.user.active? ? token.user : nil
223 223 end
224 224
225 225 # Makes find_by_mail case-insensitive
226 226 def self.find_by_mail(mail)
227 227 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
228 228 end
229 229
230 230 def to_s
231 231 name
232 232 end
233 233
234 234 # Returns the current day according to user's time zone
235 235 def today
236 236 if time_zone.nil?
237 237 Date.today
238 238 else
239 239 Time.now.in_time_zone(time_zone).to_date
240 240 end
241 241 end
242 242
243 243 def logged?
244 244 true
245 245 end
246 246
247 247 def anonymous?
248 248 !logged?
249 249 end
250 250
251 251 # Return user's roles for project
252 252 def roles_for_project(project)
253 253 roles = []
254 254 # No role on archived projects
255 255 return roles unless project && project.active?
256 256 if logged?
257 257 # Find project membership
258 258 membership = memberships.detect {|m| m.project_id == project.id}
259 259 if membership
260 260 roles = membership.roles
261 261 else
262 262 @role_non_member ||= Role.non_member
263 263 roles << @role_non_member
264 264 end
265 265 else
266 266 @role_anonymous ||= Role.anonymous
267 267 roles << @role_anonymous
268 268 end
269 269 roles
270 270 end
271 271
272 272 # Return true if the user is a member of project
273 273 def member_of?(project)
274 274 !roles_for_project(project).detect {|role| role.member?}.nil?
275 275 end
276 276
277 277 # Return true if the user is allowed to do the specified action on project
278 278 # action can be:
279 279 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
280 280 # * a permission Symbol (eg. :edit_project)
281 281 def allowed_to?(action, project, options={})
282 282 if project
283 283 # No action allowed on archived projects
284 284 return false unless project.active?
285 285 # No action allowed on disabled modules
286 286 return false unless project.allows_to?(action)
287 287 # Admin users are authorized for anything else
288 288 return true if admin?
289 289
290 290 roles = roles_for_project(project)
291 291 return false unless roles
292 292 roles.detect {|role| (project.is_public? || role.member?) && role.allowed_to?(action)}
293 293
294 294 elsif options[:global]
295 295 # Admin users are always authorized
296 296 return true if admin?
297 297
298 298 # authorize if user has at least one role that has this permission
299 299 roles = memberships.collect {|m| m.roles}.flatten.uniq
300 300 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
301 301 else
302 302 false
303 303 end
304 304 end
305 305
306 306 def self.current=(user)
307 307 @current_user = user
308 308 end
309 309
310 310 def self.current
311 311 @current_user ||= User.anonymous
312 312 end
313 313
314 314 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
315 315 # one anonymous user per database.
316 316 def self.anonymous
317 317 anonymous_user = AnonymousUser.find(:first)
318 318 if anonymous_user.nil?
319 319 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
320 320 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
321 321 end
322 322 anonymous_user
323 323 end
324 324
325 325 protected
326 326
327 327 def validate
328 328 # Password length validation based on setting
329 329 if !password.nil? && password.size < Setting.password_min_length.to_i
330 330 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
331 331 end
332 332 end
333 333
334 334 private
335 335
336 336 # Return password digest
337 337 def self.hash_password(clear_password)
338 338 Digest::SHA1.hexdigest(clear_password || "")
339 339 end
340 340 end
341 341
342 342 class AnonymousUser < User
343 343
344 344 def validate_on_create
345 345 # There should be only one AnonymousUser in the database
346 346 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
347 347 end
348 348
349 349 def available_custom_fields
350 350 []
351 351 end
352 352
353 353 # Overrides a few properties
354 354 def logged?; false end
355 355 def admin; false end
356 356 def name(*args); I18n.t(:label_user_anonymous) end
357 357 def mail; nil end
358 358 def time_zone; nil end
359 359 def rss_key; nil end
360 360 end
General Comments 0
You need to be logged in to leave comments. Login now