Class: User
- Includes:
- Redmine::SafeAttributes
- Defined in:
- app/models/user.rb
Direct Known Subclasses
Constant Summary collapse
- STATUS_ANONYMOUS =
Account statuses
0
- STATUS_ACTIVE =
1
- STATUS_REGISTERED =
2
- STATUS_LOCKED =
3
- USER_FORMATS =
{ :firstname_lastname => '#{firstname} #{lastname}', :firstname => '#{firstname}', :lastname_firstname => '#{lastname} #{firstname}', :lastname_coma_firstname => '#{lastname}, #{firstname}', :username => '#{login}' }
- MAIL_NOTIFICATION_OPTIONS =
[ ['all', :label_user_mail_option_all], ['selected', :label_user_mail_option_selected], ['only_my_events', :label_user_mail_option_only_my_events], ['only_assigned', :label_user_mail_option_only_assigned], ['only_owner', :label_user_mail_option_only_owner], ['none', :label_user_mail_option_none] ]
Instance Attribute Summary collapse
-
#last_before_login_on ⇒ Object
Returns the value of attribute last_before_login_on.
-
#password ⇒ Object
Returns the value of attribute password.
-
#password_confirmation ⇒ Object
Returns the value of attribute password_confirmation.
Class Method Summary collapse
-
.anonymous ⇒ Object
Returns the anonymous user.
- .current ⇒ Object
- .current=(user) ⇒ Object
- .find_by_api_key(key) ⇒ Object
-
.find_by_login(login) ⇒ Object
Find a user account by matching the exact login and then a case-insensitive version.
-
.find_by_mail(mail) ⇒ Object
Makes find_by_mail case-insensitive.
- .find_by_rss_key(key) ⇒ Object
-
.salt_unsalted_passwords! ⇒ Object
Salts all existing unsalted passwords It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password)) This method is used in the SaltPasswords migration and is to be kept as is.
-
.try_to_autologin(key) ⇒ Object
Returns the user who matches the given autologin
key
or nil. -
.try_to_login(login, password) ⇒ Object
Returns the user that matches provided login and password, or nil.
-
.valid_notification_options(user = nil) ⇒ Object
Only users that belong to more than 1 project can select projects for which they are notified.
Instance Method Summary collapse
- #activate ⇒ Object
- #activate! ⇒ Object
- #active? ⇒ Boolean
-
#allowed_to?(action, context, options = {}) ⇒ Boolean
Return true if the user is allowed to do the specified action on a specific context Action can be: * a parameter-like Hash (eg. :controller => ‘projects’, :action => ‘edit’) * a permission Symbol (eg. :edit_project) Context can be: * a project : returns true if user is allowed to do the specified action on this project * a group of projects : returns true if user is allowed on every project * nil with options set : check if user has at least one role allowed for this action, or falls back to Non Member / Anonymous permissions depending if the user is logged.
-
#allowed_to_globally?(action, options) ⇒ Boolean
Is the user allowed to do the specified action on any project? See allowed_to? for the actions and valid options.
- #anonymous? ⇒ Boolean
-
#api_key ⇒ Object
Return user’s API key (a 40 chars long string), used to access the API.
- #before_create ⇒ Object
- #before_save ⇒ Object
-
#change_password_allowed? ⇒ Boolean
Does the backend storage allow this user to change their password?.
-
#check_password?(clear_password) ⇒ Boolean
Returns true if
clear_password
is the correct user’s password, otherwise false. - #identity_url=(url) ⇒ Object
- #lock ⇒ Object
- #lock! ⇒ Object
- #locked? ⇒ Boolean
- #logged? ⇒ Boolean
- #mail=(arg) ⇒ Object
-
#member_of?(project) ⇒ Boolean
Return true if the user is a member of project.
-
#name(formatter = nil) ⇒ Object
Return user’s full name for display.
- #notified_project_ids=(ids) ⇒ Object
-
#notified_projects_ids ⇒ Object
Return an array of project ids for which the user has explicitly turned mail notifications on.
-
#notify_about?(object) ⇒ Boolean
Utility method to help check if a user should be notified about an event.
- #pref ⇒ Object
-
#projects_by_role ⇒ Object
Returns a hash of user’s projects grouped by roles.
-
#random_password ⇒ Object
Generate and set a random password.
- #register ⇒ Object
- #register! ⇒ Object
- #registered? ⇒ Boolean
- #reload(*args) ⇒ Object
-
#roles_for_project(project) ⇒ Object
Return user’s roles for project.
-
#rss_key ⇒ Object
Return user’s RSS key (a 40 chars long string), used to access feeds.
-
#salt_password(clear_password) ⇒ Object
Generates a random salt and computes hashed_password for
clear_password
The hashed password is stored in the following form: SHA1(salt + SHA1(password)). - #time_zone ⇒ Object
- #to_s ⇒ Object
-
#today ⇒ Object
Returns the current day according to user’s time zone.
- #valid_notification_options ⇒ Object
- #wants_comments_in_reverse_order? ⇒ Boolean
Methods included from Redmine::SafeAttributes
#delete_unsafe_attributes, included, #safe_attribute_names, #safe_attributes=
Methods inherited from Principal
Instance Attribute Details
#last_before_login_on ⇒ Object
Returns the value of attribute last_before_login_on.
61 62 63 |
# File 'app/models/user.rb', line 61 def last_before_login_on @last_before_login_on end |
#password ⇒ Object
Returns the value of attribute password.
60 61 62 |
# File 'app/models/user.rb', line 60 def password @password end |
#password_confirmation ⇒ Object
Returns the value of attribute password_confirmation.
60 61 62 |
# File 'app/models/user.rb', line 60 def password_confirmation @password_confirmation end |
Class Method Details
.anonymous ⇒ Object
Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only one anonymous user per database.
501 502 503 504 505 506 507 508 |
# File 'app/models/user.rb', line 501 def self.anonymous anonymous_user = AnonymousUser.find(:first) if anonymous_user.nil? anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0) raise 'Unable to create the anonymous user.' if anonymous_user.new_record? end anonymous_user end |
.current ⇒ Object
495 496 497 |
# File 'app/models/user.rb', line 495 def self.current @current_user ||= User.anonymous end |
.current=(user) ⇒ Object
491 492 493 |
# File 'app/models/user.rb', line 491 def self.current=(user) @current_user = user end |
.find_by_api_key(key) ⇒ Object
313 314 315 316 |
# File 'app/models/user.rb', line 313 def self.find_by_api_key(key) token = Token.find_by_action_and_value('api', key) token && token.user.active? ? token.user : nil end |
.find_by_login(login) ⇒ Object
Find a user account by matching the exact login and then a case-insensitive version. Exact matches will be given priority.
298 299 300 301 302 303 304 305 306 |
# File 'app/models/user.rb', line 298 def self.find_by_login(login) # force string comparison to be case sensitive on MySQL type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : '' # First look for an exact match user = first(:conditions => ["#{type_cast} login = ?", login]) # Fail over to case-insensitive if none was found user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase]) end |
.find_by_mail(mail) ⇒ Object
Makes find_by_mail case-insensitive
319 320 321 |
# File 'app/models/user.rb', line 319 def self.find_by_mail(mail) find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase]) end |
.find_by_rss_key(key) ⇒ Object
308 309 310 311 |
# File 'app/models/user.rb', line 308 def self.find_by_rss_key(key) token = Token.find_by_value(key) token && token.user.active? ? token.user : nil end |
.salt_unsalted_passwords! ⇒ Object
Salts all existing unsalted passwords It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password)) This method is used in the SaltPasswords migration and is to be kept as is
513 514 515 516 517 518 519 520 521 522 |
# File 'app/models/user.rb', line 513 def self.salt_unsalted_passwords! transaction do User.find_each(:conditions => "salt IS NULL OR salt = ''") do |user| next if user.hashed_password.blank? salt = User.generate_salt hashed_password = User.hash_password("#{salt}#{user.hashed_password}") User.update_all("salt = '#{salt}', hashed_password = '#{hashed_password}'", ["id = ?", user.id] ) end end end |
.try_to_autologin(key) ⇒ Object
Returns the user who matches the given autologin key
or nil
154 155 156 157 158 159 160 161 162 163 164 |
# File 'app/models/user.rb', line 154 def self.try_to_autologin(key) tokens = Token.find_all_by_action_and_value('autologin', key) # Make sure there's only 1 token that matches the key if tokens.size == 1 token = tokens.first if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active? token.user.update_attribute(:last_login_on, Time.now) token.user end end end |
.try_to_login(login, password) ⇒ Object
Returns the user that matches provided login and password, or nil
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 |
# File 'app/models/user.rb', line 120 def self.try_to_login(login, password) # Make sure no one can sign in with an empty password return nil if password.to_s.empty? user = find_by_login(login) if user # user is already in local database return nil if !user.active? if user.auth_source # user has an external authentication method return nil unless user.auth_source.authenticate(login, password) else # authentication with local password return nil unless user.check_password?(password) end else # user is not yet registered, try to authenticate with available sources attrs = AuthSource.authenticate(login, password) if attrs user = new(attrs) user.login = login user.language = Setting.default_language if user.save user.reload logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source end end end user.update_attribute(:last_login_on, Time.now) if user && !user.new_record? user rescue => text raise text end |
.valid_notification_options(user = nil) ⇒ Object
Only users that belong to more than 1 project can select projects for which they are notified
286 287 288 289 290 291 292 293 294 |
# File 'app/models/user.rb', line 286 def self.(user=nil) # Note that @user.membership.size would fail since AR ignores # :include association option when doing a count if user.nil? || user.memberships.length < 1 MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'} else MAIL_NOTIFICATION_OPTIONS end end |
Instance Method Details
#activate ⇒ Object
187 188 189 |
# File 'app/models/user.rb', line 187 def activate self.status = STATUS_ACTIVE end |
#activate! ⇒ Object
199 200 201 |
# File 'app/models/user.rb', line 199 def activate! update_attribute(:status, STATUS_ACTIVE) end |
#active? ⇒ Boolean
175 176 177 |
# File 'app/models/user.rb', line 175 def active? self.status == STATUS_ACTIVE end |
#allowed_to?(action, context, options = {}) ⇒ Boolean
Return true if the user is allowed to do the specified action on a specific context Action can be:
-
a parameter-like Hash (eg. :controller => ‘projects’, :action => ‘edit’)
-
a permission Symbol (eg. :edit_project)
Context can be:
-
a project : returns true if user is allowed to do the specified action on this project
-
a group of projects : returns true if user is allowed on every project
-
nil with options set : check if user has at least one role allowed for this action, or falls back to Non Member / Anonymous permissions depending if the user is logged
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 |
# File 'app/models/user.rb', line 396 def allowed_to?(action, context, ={}) if context && context.is_a?(Project) # No action allowed on archived projects return false unless context.active? # No action allowed on disabled modules return false unless context.allows_to?(action) # Admin users are authorized for anything else return true if admin? roles = roles_for_project(context) return false unless roles roles.detect {|role| (context.is_public? || role.member?) && role.allowed_to?(action)} elsif context && context.is_a?(Array) # Authorize if user is authorized on every element of the array context.map do |project| allowed_to?(action,project,) end.inject do |memo,allowed| memo && allowed end elsif [:global] # Admin users are always authorized return true if admin? # authorize if user has at least one role that has this permission roles = memberships.collect {|m| m.roles}.flatten.uniq roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action)) else false end end |
#allowed_to_globally?(action, options) ⇒ Boolean
Is the user allowed to do the specified action on any project? See allowed_to? for the actions and valid options.
430 431 432 |
# File 'app/models/user.rb', line 430 def allowed_to_globally?(action, ) allowed_to?(action, nil, .reverse_merge(:global => true)) end |
#anonymous? ⇒ Boolean
340 341 342 |
# File 'app/models/user.rb', line 340 def anonymous? !logged? end |
#api_key ⇒ Object
Return user’s API key (a 40 chars long string), used to access the API
264 265 266 267 |
# File 'app/models/user.rb', line 264 def api_key token = self.api_token || self.create_api_token(:action => 'api') token.value end |
#before_create ⇒ Object
84 85 86 87 |
# File 'app/models/user.rb', line 84 def before_create self.mail_notification = Setting.default_notification_option if self.mail_notification.blank? true end |
#before_save ⇒ Object
89 90 91 92 93 94 |
# File 'app/models/user.rb', line 89 def before_save # update hashed_password if password was set if self.password && self.auth_source_id.blank? salt_password(password) end end |
#change_password_allowed? ⇒ Boolean
Does the backend storage allow this user to change their password?
228 229 230 231 |
# File 'app/models/user.rb', line 228 def change_password_allowed? return true if auth_source_id.blank? return auth_source.allow_password_changes? end |
#check_password?(clear_password) ⇒ Boolean
Returns true if clear_password
is the correct user’s password, otherwise false
212 213 214 215 216 217 218 |
# File 'app/models/user.rb', line 212 def check_password?(clear_password) if auth_source_id.present? auth_source.authenticate(self.login, clear_password) else User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password end end |
#identity_url=(url) ⇒ Object
106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'app/models/user.rb', line 106 def identity_url=(url) if url.blank? write_attribute(:identity_url, '') else begin write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url)) rescue OpenIdAuthentication::InvalidOpenId # Invlaid url, don't save end end self.read_attribute(:identity_url) end |
#lock ⇒ Object
195 196 197 |
# File 'app/models/user.rb', line 195 def lock self.status = STATUS_LOCKED end |
#lock! ⇒ Object
207 208 209 |
# File 'app/models/user.rb', line 207 def lock! update_attribute(:status, STATUS_LOCKED) end |
#locked? ⇒ Boolean
183 184 185 |
# File 'app/models/user.rb', line 183 def locked? self.status == STATUS_LOCKED end |
#logged? ⇒ Boolean
336 337 338 |
# File 'app/models/user.rb', line 336 def logged? true end |
#mail=(arg) ⇒ Object
102 103 104 |
# File 'app/models/user.rb', line 102 def mail=(arg) write_attribute(:mail, arg.to_s.strip) end |
#member_of?(project) ⇒ Boolean
Return true if the user is a member of project
366 367 368 |
# File 'app/models/user.rb', line 366 def member_of?(project) !roles_for_project(project).detect {|role| role.member?}.nil? end |
#name(formatter = nil) ⇒ Object
Return user’s full name for display
167 168 169 170 171 172 173 |
# File 'app/models/user.rb', line 167 def name(formatter = nil) if formatter eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"') else @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"') end end |
#notified_project_ids=(ids) ⇒ Object
274 275 276 277 278 279 |
# File 'app/models/user.rb', line 274 def notified_project_ids=(ids) Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id]) Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty? @notified_projects_ids = nil notified_projects_ids end |
#notified_projects_ids ⇒ Object
Return an array of project ids for which the user has explicitly turned mail notifications on
270 271 272 |
# File 'app/models/user.rb', line 270 def notified_projects_ids @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id) end |
#notify_about?(object) ⇒ Boolean
Utility method to help check if a user should be notified about an event.
TODO: only supports Issue events currently
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 |
# File 'app/models/user.rb', line 455 def notify_about?(object) case mail_notification when 'all' true when 'selected' # user receives notifications for created/assigned issues on unselected projects if object.is_a?(Issue) && (object. == self || object.assigned_to == self) true else false end when 'none' false when 'only_my_events' if object.is_a?(Issue) && (object. == self || object.assigned_to == self) true else false end when 'only_assigned' if object.is_a?(Issue) && object.assigned_to == self true else false end when 'only_owner' if object.is_a?(Issue) && object. == self true else false end else false end end |
#pref ⇒ Object
245 246 247 |
# File 'app/models/user.rb', line 245 def pref self.preference ||= UserPreference.new(:user => self) end |
#projects_by_role ⇒ Object
Returns a hash of user’s projects grouped by roles
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
# File 'app/models/user.rb', line 371 def projects_by_role return @projects_by_role if @projects_by_role @projects_by_role = Hash.new {|h,k| h[k]=[]} memberships.each do |membership| membership.roles.each do |role| @projects_by_role[role] << membership.project if membership.project end end @projects_by_role.each do |role, projects| projects.uniq! end @projects_by_role end |
#random_password ⇒ Object
Generate and set a random password. Useful for automated user creation Based on Token#generate_token_value
236 237 238 239 240 241 242 243 |
# File 'app/models/user.rb', line 236 def random_password chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a password = '' 40.times { |i| password << chars[rand(chars.size-1)] } self.password = password self.password_confirmation = password self end |
#register ⇒ Object
191 192 193 |
# File 'app/models/user.rb', line 191 def register self.status = STATUS_REGISTERED end |
#register! ⇒ Object
203 204 205 |
# File 'app/models/user.rb', line 203 def register! update_attribute(:status, STATUS_REGISTERED) end |
#registered? ⇒ Boolean
179 180 181 |
# File 'app/models/user.rb', line 179 def registered? self.status == STATUS_REGISTERED end |
#reload(*args) ⇒ Object
96 97 98 99 100 |
# File 'app/models/user.rb', line 96 def reload(*args) @name = nil @projects_by_role = nil super end |
#roles_for_project(project) ⇒ Object
Return user’s roles for project
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'app/models/user.rb', line 345 def roles_for_project(project) roles = [] # No role on archived projects return roles unless project && project.active? if logged? # Find project membership membership = memberships.detect {|m| m.project_id == project.id} if membership roles = membership.roles else @role_non_member ||= Role.non_member roles << @role_non_member end else @role_anonymous ||= Role.anonymous roles << @role_anonymous end roles end |
#rss_key ⇒ Object
Return user’s RSS key (a 40 chars long string), used to access feeds
258 259 260 261 |
# File 'app/models/user.rb', line 258 def rss_key token = self.rss_token || Token.create(:user => self, :action => 'feeds') token.value end |
#salt_password(clear_password) ⇒ Object
Generates a random salt and computes hashed_password for clear_password
The hashed password is stored in the following form: SHA1(salt + SHA1(password))
222 223 224 225 |
# File 'app/models/user.rb', line 222 def salt_password(clear_password) self.salt = User.generate_salt self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}") end |
#time_zone ⇒ Object
249 250 251 |
# File 'app/models/user.rb', line 249 def time_zone @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone]) end |
#to_s ⇒ Object
323 324 325 |
# File 'app/models/user.rb', line 323 def to_s name end |
#today ⇒ Object
Returns the current day according to user’s time zone
328 329 330 331 332 333 334 |
# File 'app/models/user.rb', line 328 def today if time_zone.nil? Date.today else Time.now.in_time_zone(time_zone).to_date end end |
#valid_notification_options ⇒ Object
281 282 283 |
# File 'app/models/user.rb', line 281 def self.class.(self) end |
#wants_comments_in_reverse_order? ⇒ Boolean
253 254 255 |
# File 'app/models/user.rb', line 253 def wants_comments_in_reverse_order? self.pref[:comments_sorting] == 'desc' end |