##// END OF EJS Templates
Removed unused exception definition (r1678)....
Jean-Philippe Lang -
r1671:590a829a06d4
parent child
Show More
@@ -1,295 +1,293
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require "digest/sha1"
18 require "digest/sha1"
19
19
20 class User < ActiveRecord::Base
20 class User < ActiveRecord::Base
21
21
22 class OnTheFlyCreationFailure < Exception; end
23
24 # Account statuses
22 # Account statuses
25 STATUS_ANONYMOUS = 0
23 STATUS_ANONYMOUS = 0
26 STATUS_ACTIVE = 1
24 STATUS_ACTIVE = 1
27 STATUS_REGISTERED = 2
25 STATUS_REGISTERED = 2
28 STATUS_LOCKED = 3
26 STATUS_LOCKED = 3
29
27
30 USER_FORMATS = {
28 USER_FORMATS = {
31 :firstname_lastname => '#{firstname} #{lastname}',
29 :firstname_lastname => '#{firstname} #{lastname}',
32 :firstname => '#{firstname}',
30 :firstname => '#{firstname}',
33 :lastname_firstname => '#{lastname} #{firstname}',
31 :lastname_firstname => '#{lastname} #{firstname}',
34 :lastname_coma_firstname => '#{lastname}, #{firstname}',
32 :lastname_coma_firstname => '#{lastname}, #{firstname}',
35 :username => '#{login}'
33 :username => '#{login}'
36 }
34 }
37
35
38 has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name", :dependent => :delete_all
36 has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name", :dependent => :delete_all
39 has_many :projects, :through => :memberships
37 has_many :projects, :through => :memberships
40 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
38 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
41 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
39 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
42 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
40 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
43 belongs_to :auth_source
41 belongs_to :auth_source
44
42
45 acts_as_customizable
43 acts_as_customizable
46
44
47 attr_accessor :password, :password_confirmation
45 attr_accessor :password, :password_confirmation
48 attr_accessor :last_before_login_on
46 attr_accessor :last_before_login_on
49 # Prevents unauthorized assignments
47 # Prevents unauthorized assignments
50 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
48 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
51
49
52 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
50 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
53 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }
51 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }
54 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }
52 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }
55 # Login must contain lettres, numbers, underscores only
53 # Login must contain lettres, numbers, underscores only
56 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
54 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
57 validates_length_of :login, :maximum => 30
55 validates_length_of :login, :maximum => 30
58 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
56 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
59 validates_length_of :firstname, :lastname, :maximum => 30
57 validates_length_of :firstname, :lastname, :maximum => 30
60 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
58 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
61 validates_length_of :mail, :maximum => 60, :allow_nil => true
59 validates_length_of :mail, :maximum => 60, :allow_nil => true
62 validates_length_of :password, :minimum => 4, :allow_nil => true
60 validates_length_of :password, :minimum => 4, :allow_nil => true
63 validates_confirmation_of :password, :allow_nil => true
61 validates_confirmation_of :password, :allow_nil => true
64
62
65 def before_create
63 def before_create
66 self.mail_notification = false
64 self.mail_notification = false
67 true
65 true
68 end
66 end
69
67
70 def before_save
68 def before_save
71 # update hashed_password if password was set
69 # update hashed_password if password was set
72 self.hashed_password = User.hash_password(self.password) if self.password
70 self.hashed_password = User.hash_password(self.password) if self.password
73 end
71 end
74
72
75 def self.active
73 def self.active
76 with_scope :find => { :conditions => [ "status = ?", STATUS_ACTIVE ] } do
74 with_scope :find => { :conditions => [ "status = ?", STATUS_ACTIVE ] } do
77 yield
75 yield
78 end
76 end
79 end
77 end
80
78
81 def self.find_active(*args)
79 def self.find_active(*args)
82 active do
80 active do
83 find(*args)
81 find(*args)
84 end
82 end
85 end
83 end
86
84
87 # Returns the user that matches provided login and password, or nil
85 # Returns the user that matches provided login and password, or nil
88 def self.try_to_login(login, password)
86 def self.try_to_login(login, password)
89 # Make sure no one can sign in with an empty password
87 # Make sure no one can sign in with an empty password
90 return nil if password.to_s.empty?
88 return nil if password.to_s.empty?
91 user = find(:first, :conditions => ["login=?", login])
89 user = find(:first, :conditions => ["login=?", login])
92 if user
90 if user
93 # user is already in local database
91 # user is already in local database
94 return nil if !user.active?
92 return nil if !user.active?
95 if user.auth_source
93 if user.auth_source
96 # user has an external authentication method
94 # user has an external authentication method
97 return nil unless user.auth_source.authenticate(login, password)
95 return nil unless user.auth_source.authenticate(login, password)
98 else
96 else
99 # authentication with local password
97 # authentication with local password
100 return nil unless User.hash_password(password) == user.hashed_password
98 return nil unless User.hash_password(password) == user.hashed_password
101 end
99 end
102 else
100 else
103 # user is not yet registered, try to authenticate with available sources
101 # user is not yet registered, try to authenticate with available sources
104 attrs = AuthSource.authenticate(login, password)
102 attrs = AuthSource.authenticate(login, password)
105 if attrs
103 if attrs
106 user = new(*attrs)
104 user = new(*attrs)
107 user.login = login
105 user.login = login
108 user.language = Setting.default_language
106 user.language = Setting.default_language
109 if user.save
107 if user.save
110 user.reload
108 user.reload
111 logger.info("User '#{user.login}' created from the LDAP") if logger
109 logger.info("User '#{user.login}' created from the LDAP") if logger
112 end
110 end
113 end
111 end
114 end
112 end
115 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
113 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
116 user
114 user
117 rescue => text
115 rescue => text
118 raise text
116 raise text
119 end
117 end
120
118
121 # Return user's full name for display
119 # Return user's full name for display
122 def name(formatter = nil)
120 def name(formatter = nil)
123 f = USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
121 f = USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
124 eval '"' + f + '"'
122 eval '"' + f + '"'
125 end
123 end
126
124
127 def active?
125 def active?
128 self.status == STATUS_ACTIVE
126 self.status == STATUS_ACTIVE
129 end
127 end
130
128
131 def registered?
129 def registered?
132 self.status == STATUS_REGISTERED
130 self.status == STATUS_REGISTERED
133 end
131 end
134
132
135 def locked?
133 def locked?
136 self.status == STATUS_LOCKED
134 self.status == STATUS_LOCKED
137 end
135 end
138
136
139 def check_password?(clear_password)
137 def check_password?(clear_password)
140 User.hash_password(clear_password) == self.hashed_password
138 User.hash_password(clear_password) == self.hashed_password
141 end
139 end
142
140
143 def pref
141 def pref
144 self.preference ||= UserPreference.new(:user => self)
142 self.preference ||= UserPreference.new(:user => self)
145 end
143 end
146
144
147 def time_zone
145 def time_zone
148 self.pref.time_zone.nil? ? nil : TimeZone[self.pref.time_zone]
146 self.pref.time_zone.nil? ? nil : TimeZone[self.pref.time_zone]
149 end
147 end
150
148
151 def wants_comments_in_reverse_order?
149 def wants_comments_in_reverse_order?
152 self.pref[:comments_sorting] == 'desc'
150 self.pref[:comments_sorting] == 'desc'
153 end
151 end
154
152
155 # Return user's RSS key (a 40 chars long string), used to access feeds
153 # Return user's RSS key (a 40 chars long string), used to access feeds
156 def rss_key
154 def rss_key
157 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
155 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
158 token.value
156 token.value
159 end
157 end
160
158
161 # Return an array of project ids for which the user has explicitly turned mail notifications on
159 # Return an array of project ids for which the user has explicitly turned mail notifications on
162 def notified_projects_ids
160 def notified_projects_ids
163 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
161 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
164 end
162 end
165
163
166 def notified_project_ids=(ids)
164 def notified_project_ids=(ids)
167 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
165 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
168 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
166 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
169 @notified_projects_ids = nil
167 @notified_projects_ids = nil
170 notified_projects_ids
168 notified_projects_ids
171 end
169 end
172
170
173 def self.find_by_rss_key(key)
171 def self.find_by_rss_key(key)
174 token = Token.find_by_value(key)
172 token = Token.find_by_value(key)
175 token && token.user.active? ? token.user : nil
173 token && token.user.active? ? token.user : nil
176 end
174 end
177
175
178 def self.find_by_autologin_key(key)
176 def self.find_by_autologin_key(key)
179 token = Token.find_by_action_and_value('autologin', key)
177 token = Token.find_by_action_and_value('autologin', key)
180 token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
178 token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
181 end
179 end
182
180
183 def <=>(user)
181 def <=>(user)
184 if user.nil?
182 if user.nil?
185 -1
183 -1
186 elsif lastname.to_s.downcase == user.lastname.to_s.downcase
184 elsif lastname.to_s.downcase == user.lastname.to_s.downcase
187 firstname.to_s.downcase <=> user.firstname.to_s.downcase
185 firstname.to_s.downcase <=> user.firstname.to_s.downcase
188 else
186 else
189 lastname.to_s.downcase <=> user.lastname.to_s.downcase
187 lastname.to_s.downcase <=> user.lastname.to_s.downcase
190 end
188 end
191 end
189 end
192
190
193 def to_s
191 def to_s
194 name
192 name
195 end
193 end
196
194
197 def logged?
195 def logged?
198 true
196 true
199 end
197 end
200
198
201 def anonymous?
199 def anonymous?
202 !logged?
200 !logged?
203 end
201 end
204
202
205 # Return user's role for project
203 # Return user's role for project
206 def role_for_project(project)
204 def role_for_project(project)
207 # No role on archived projects
205 # No role on archived projects
208 return nil unless project && project.active?
206 return nil unless project && project.active?
209 if logged?
207 if logged?
210 # Find project membership
208 # Find project membership
211 membership = memberships.detect {|m| m.project_id == project.id}
209 membership = memberships.detect {|m| m.project_id == project.id}
212 if membership
210 if membership
213 membership.role
211 membership.role
214 else
212 else
215 @role_non_member ||= Role.non_member
213 @role_non_member ||= Role.non_member
216 end
214 end
217 else
215 else
218 @role_anonymous ||= Role.anonymous
216 @role_anonymous ||= Role.anonymous
219 end
217 end
220 end
218 end
221
219
222 # Return true if the user is a member of project
220 # Return true if the user is a member of project
223 def member_of?(project)
221 def member_of?(project)
224 role_for_project(project).member?
222 role_for_project(project).member?
225 end
223 end
226
224
227 # Return true if the user is allowed to do the specified action on project
225 # Return true if the user is allowed to do the specified action on project
228 # action can be:
226 # action can be:
229 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
227 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
230 # * a permission Symbol (eg. :edit_project)
228 # * a permission Symbol (eg. :edit_project)
231 def allowed_to?(action, project, options={})
229 def allowed_to?(action, project, options={})
232 if project
230 if project
233 # No action allowed on archived projects
231 # No action allowed on archived projects
234 return false unless project.active?
232 return false unless project.active?
235 # No action allowed on disabled modules
233 # No action allowed on disabled modules
236 return false unless project.allows_to?(action)
234 return false unless project.allows_to?(action)
237 # Admin users are authorized for anything else
235 # Admin users are authorized for anything else
238 return true if admin?
236 return true if admin?
239
237
240 role = role_for_project(project)
238 role = role_for_project(project)
241 return false unless role
239 return false unless role
242 role.allowed_to?(action) && (project.is_public? || role.member?)
240 role.allowed_to?(action) && (project.is_public? || role.member?)
243
241
244 elsif options[:global]
242 elsif options[:global]
245 # authorize if user has at least one role that has this permission
243 # authorize if user has at least one role that has this permission
246 roles = memberships.collect {|m| m.role}.uniq
244 roles = memberships.collect {|m| m.role}.uniq
247 roles.detect {|r| r.allowed_to?(action)}
245 roles.detect {|r| r.allowed_to?(action)}
248 else
246 else
249 false
247 false
250 end
248 end
251 end
249 end
252
250
253 def self.current=(user)
251 def self.current=(user)
254 @current_user = user
252 @current_user = user
255 end
253 end
256
254
257 def self.current
255 def self.current
258 @current_user ||= User.anonymous
256 @current_user ||= User.anonymous
259 end
257 end
260
258
261 def self.anonymous
259 def self.anonymous
262 anonymous_user = AnonymousUser.find(:first)
260 anonymous_user = AnonymousUser.find(:first)
263 if anonymous_user.nil?
261 if anonymous_user.nil?
264 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
262 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
265 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
263 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
266 end
264 end
267 anonymous_user
265 anonymous_user
268 end
266 end
269
267
270 private
268 private
271 # Return password digest
269 # Return password digest
272 def self.hash_password(clear_password)
270 def self.hash_password(clear_password)
273 Digest::SHA1.hexdigest(clear_password || "")
271 Digest::SHA1.hexdigest(clear_password || "")
274 end
272 end
275 end
273 end
276
274
277 class AnonymousUser < User
275 class AnonymousUser < User
278
276
279 def validate_on_create
277 def validate_on_create
280 # There should be only one AnonymousUser in the database
278 # There should be only one AnonymousUser in the database
281 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
279 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
282 end
280 end
283
281
284 def available_custom_fields
282 def available_custom_fields
285 []
283 []
286 end
284 end
287
285
288 # Overrides a few properties
286 # Overrides a few properties
289 def logged?; false end
287 def logged?; false end
290 def admin; false end
288 def admin; false end
291 def name; 'Anonymous' end
289 def name; 'Anonymous' end
292 def mail; nil end
290 def mail; nil end
293 def time_zone; nil end
291 def time_zone; nil end
294 def rss_key; nil end
292 def rss_key; nil end
295 end
293 end
General Comments 0
You need to be logged in to leave comments. Login now