2009-09-12 12:36:46 +04:00
# Redmine - project management software
2012-05-05 16:56:53 +04:00
# Copyright (C) 2006-2012 Jean-Philippe Lang
2007-03-12 20:59:02 +03:00
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
2011-08-21 05:57:25 +04:00
#
2007-03-12 20:59:02 +03:00
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
2011-08-21 05:57:25 +04:00
#
2007-03-12 20:59:02 +03:00
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require " digest/sha1 "
2009-09-12 12:36:46 +04:00
class User < Principal
2010-12-12 16:19:07 +03:00
include Redmine :: SafeAttributes
2011-08-21 05:57:25 +04:00
2007-05-23 21:18:21 +04:00
# Account statuses
2007-11-20 18:40:16 +03:00
STATUS_ANONYMOUS = 0
2007-05-23 21:18:21 +04:00
STATUS_ACTIVE = 1
STATUS_REGISTERED = 2
STATUS_LOCKED = 3
2011-08-21 05:57:25 +04:00
2011-11-26 21:37:20 +04:00
# Different ways of displaying/sorting users
2008-01-25 13:31:06 +03:00
USER_FORMATS = {
2012-10-01 11:07:49 +04:00
:firstname_lastname = > {
:string = > '#{firstname} #{lastname}' ,
:order = > %w( firstname lastname id ) ,
:setting_order = > 1
} ,
:firstname = > {
:string = > '#{firstname}' ,
:order = > %w( firstname id ) ,
:setting_order = > 2
} ,
:lastname_firstname = > {
:string = > '#{lastname} #{firstname}' ,
:order = > %w( lastname firstname id ) ,
:setting_order = > 3
} ,
:lastname_coma_firstname = > {
:string = > '#{lastname}, #{firstname}' ,
:order = > %w( lastname firstname id ) ,
:setting_order = > 4
} ,
2012-10-01 11:08:04 +04:00
:lastname = > {
:string = > '#{lastname}' ,
:order = > %w( lastname id ) ,
:setting_order = > 5
} ,
2012-10-01 11:07:49 +04:00
:username = > {
:string = > '#{login}' ,
:order = > %w( login id ) ,
2012-10-01 11:08:04 +04:00
:setting_order = > 6
2012-10-01 11:07:49 +04:00
} ,
2008-01-25 13:31:06 +03:00
}
2007-05-23 21:18:21 +04:00
2010-09-28 22:22:00 +04:00
MAIL_NOTIFICATION_OPTIONS = [
2010-12-12 17:02:39 +03:00
[ '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 ]
]
2010-09-28 22:22:00 +04:00
2009-09-12 12:36:46 +04:00
has_and_belongs_to_many :groups , :after_add = > Proc . new { | user , group | group . user_added ( user ) } ,
:after_remove = > Proc . new { | user , group | group . user_removed ( user ) }
2008-11-10 21:59:06 +03:00
has_many :changesets , :dependent = > :nullify
2007-03-12 20:59:02 +03:00
has_one :preference , :dependent = > :destroy , :class_name = > 'UserPreference'
2011-01-16 17:27:02 +03:00
has_one :rss_token , :class_name = > 'Token' , :conditions = > " action='feeds' "
has_one :api_token , :class_name = > 'Token' , :conditions = > " action='api' "
2007-03-12 20:59:02 +03:00
belongs_to :auth_source
2011-08-21 05:57:25 +04:00
2012-04-27 03:51:10 +04:00
scope :logged , :conditions = > " #{ User . table_name } .status <> #{ STATUS_ANONYMOUS } "
scope :status , lambda { | arg | arg . blank? ? { } : { :conditions = > { :status = > arg . to_i } } }
2011-08-21 05:57:25 +04:00
2008-06-28 00:13:56 +04:00
acts_as_customizable
2011-08-21 05:57:25 +04:00
2007-03-12 20:59:02 +03:00
attr_accessor :password , :password_confirmation
attr_accessor :last_before_login_on
# Prevents unauthorized assignments
2010-12-12 18:13:34 +03:00
attr_protected :login , :admin , :password , :password_confirmation , :hashed_password
2012-02-05 15:50:53 +04:00
LOGIN_LENGTH_LIMIT = 60
MAIL_LENGTH_LIMIT = 60
2007-11-20 18:40:16 +03:00
validates_presence_of :login , :firstname , :lastname , :mail , :if = > Proc . new { | user | ! user . is_a? ( AnonymousUser ) }
2012-04-19 12:42:07 +04:00
validates_uniqueness_of :login , :if = > Proc . new { | user | user . login_changed? && user . login . present? } , :case_sensitive = > false
2009-01-10 14:29:35 +03:00
validates_uniqueness_of :mail , :if = > Proc . new { | user | ! user . mail . blank? } , :case_sensitive = > false
2007-03-12 20:59:02 +03:00
# Login must contain lettres, numbers, underscores only
2007-11-20 18:40:16 +03:00
validates_format_of :login , :with = > / ^[a-z0-9_ \ -@ \ .]*$ /i
2012-02-05 15:50:53 +04:00
validates_length_of :login , :maximum = > LOGIN_LENGTH_LIMIT
2007-04-02 22:44:35 +04:00
validates_length_of :firstname , :lastname , :maximum = > 30
2011-07-03 16:37:57 +04:00
validates_format_of :mail , :with = > / ^([^@ \ s]+)@((?:[-a-z0-9]+ \ .)+[a-z]{2,})$ /i , :allow_blank = > true
2012-02-05 15:50:53 +04:00
validates_length_of :mail , :maximum = > MAIL_LENGTH_LIMIT , :allow_nil = > true
2007-03-12 20:59:02 +03:00
validates_confirmation_of :password , :allow_nil = > true
2010-12-12 17:02:39 +03:00
validates_inclusion_of :mail_notification , :in = > MAIL_NOTIFICATION_OPTIONS . collect ( & :first ) , :allow_blank = > true
2011-09-21 10:14:11 +04:00
validate :validate_password_length
2007-03-12 20:59:02 +03:00
2011-08-31 10:06:43 +04:00
before_create :set_mail_notification
2011-08-31 20:06:36 +04:00
before_save :update_hashed_password
2011-01-16 17:27:02 +03:00
before_destroy :remove_references_before_destroy
2011-08-21 05:57:25 +04:00
2012-04-27 03:51:10 +04:00
scope :in_group , lambda { | group |
2011-03-16 21:20:08 +03:00
group_id = group . is_a? ( Group ) ? group . id : group . to_i
{ :conditions = > [ " #{ User . table_name } .id IN (SELECT gu.user_id FROM #{ table_name_prefix } groups_users #{ table_name_suffix } gu WHERE gu.group_id = ?) " , group_id ] }
}
2012-04-27 03:51:10 +04:00
scope :not_in_group , lambda { | group |
2011-04-01 20:47:30 +04:00
group_id = group . is_a? ( Group ) ? group . id : group . to_i
{ :conditions = > [ " #{ User . table_name } .id NOT IN (SELECT gu.user_id FROM #{ table_name_prefix } groups_users #{ table_name_suffix } gu WHERE gu.group_id = ?) " , group_id ] }
}
2011-08-21 05:57:25 +04:00
2011-08-31 10:06:43 +04:00
def set_mail_notification
2010-09-29 02:13:06 +04:00
self . mail_notification = Setting . default_notification_option if self . mail_notification . blank?
2007-10-20 16:47:05 +04:00
true
end
2011-08-21 05:57:25 +04:00
2011-08-31 20:06:36 +04:00
def update_hashed_password
2007-03-12 20:59:02 +03:00
# update hashed_password if password was set
2011-02-23 20:27:31 +03:00
if self . password && self . auth_source_id . blank?
salt_password ( password )
end
2007-03-12 20:59:02 +03:00
end
2011-08-21 05:57:25 +04:00
2008-11-11 15:59:28 +03:00
def reload ( * args )
@name = nil
2011-03-15 18:39:59 +03:00
@projects_by_role = nil
2008-11-11 15:59:28 +03:00
super
end
2011-08-21 05:57:25 +04:00
2010-07-25 13:50:41 +04:00
def mail = ( arg )
write_attribute ( :mail , arg . to_s . strip )
end
2011-08-21 05:57:25 +04:00
2009-02-12 07:31:28 +03:00
def identity_url = ( url )
2009-02-15 21:46:50 +03:00
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
2009-02-12 07:31:28 +03:00
end
self . read_attribute ( :identity_url )
end
2011-08-21 05:57:25 +04:00
2007-03-12 20:59:02 +03:00
# Returns the user that matches provided login and password, or nil
def self . try_to_login ( login , password )
2012-06-10 17:24:28 +04:00
login = login . to_s
password = password . to_s
2008-03-12 20:56:19 +03:00
# Make sure no one can sign in with an empty password
2012-06-10 17:24:28 +04:00
return nil if password . empty?
2012-02-28 10:27:23 +04:00
user = find_by_login ( login )
2007-03-12 20:59:02 +03:00
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
2011-02-23 20:27:31 +03:00
return nil unless user . check_password? ( password )
2007-03-12 20:59:02 +03:00
end
else
# user is not yet registered, try to authenticate with available sources
attrs = AuthSource . authenticate ( login , password )
if attrs
2010-02-26 12:13:12 +03:00
user = new ( attrs )
2008-07-19 14:47:19 +04:00
user . login = login
user . language = Setting . default_language
if user . save
user . reload
2010-05-23 07:16:37 +04:00
logger . info ( " User ' #{ user . login } ' created from external auth source: #{ user . auth_source . type } - #{ user . auth_source . name } " ) if logger && user . auth_source
2007-03-12 20:59:02 +03:00
end
end
2011-08-21 05:57:25 +04:00
end
2008-07-19 14:47:19 +04:00
user . update_attribute ( :last_login_on , Time . now ) if user && ! user . new_record?
2007-03-12 20:59:02 +03:00
user
2008-04-13 13:12:43 +04:00
rescue = > text
raise text
2007-03-12 20:59:02 +03:00
end
2011-08-21 05:57:25 +04:00
2009-02-25 17:59:33 +03:00
# Returns the user who matches the given autologin +key+ or nil
def self . try_to_autologin ( key )
2012-06-10 17:27:51 +04:00
tokens = Token . find_all_by_action_and_value ( 'autologin' , key . to_s )
2009-05-13 20:56:31 +04:00
# 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
2009-02-25 17:59:33 +03:00
end
end
2011-11-26 21:37:20 +04:00
def self . name_formatter ( formatter = nil )
USER_FORMATS [ formatter || Setting . user_format ] || USER_FORMATS [ :firstname_lastname ]
end
# Returns an array of fields names than can be used to make an order statement for users
# according to how user names are displayed
# Examples:
#
# User.fields_for_order_statement => ['users.login', 'users.id']
# User.fields_for_order_statement('authors') => ['authors.login', 'authors.id']
def self . fields_for_order_statement ( table = nil )
table || = table_name
name_formatter [ :order ] . map { | field | " #{ table } . #{ field } " }
end
2007-03-12 20:59:02 +03:00
# Return user's full name for display
2008-01-25 13:31:06 +03:00
def name ( formatter = nil )
2011-11-26 21:37:20 +04:00
f = self . class . name_formatter ( formatter )
2008-11-13 19:43:39 +03:00
if formatter
2011-11-26 21:37:20 +04:00
eval ( '"' + f [ :string ] + '"' )
2008-11-13 19:43:39 +03:00
else
2011-11-26 21:37:20 +04:00
@name || = eval ( '"' + f [ :string ] + '"' )
2008-11-13 19:43:39 +03:00
end
2007-03-12 20:59:02 +03:00
end
2011-08-21 05:57:25 +04:00
2007-03-12 20:59:02 +03:00
def active?
self . status == STATUS_ACTIVE
end
def registered?
self . status == STATUS_REGISTERED
end
2011-08-21 05:57:25 +04:00
2007-03-12 20:59:02 +03:00
def locked?
self . status == STATUS_LOCKED
end
2010-08-03 19:26:50 +04:00
def activate
self . status = STATUS_ACTIVE
end
def register
self . status = STATUS_REGISTERED
end
def lock
self . status = STATUS_LOCKED
end
def activate!
update_attribute ( :status , STATUS_ACTIVE )
end
def register!
update_attribute ( :status , STATUS_REGISTERED )
end
def lock!
update_attribute ( :status , STATUS_LOCKED )
end
2011-02-23 20:27:31 +03:00
# Returns true if +clear_password+ is the correct user's password, otherwise false
2007-03-12 20:59:02 +03:00
def check_password? ( clear_password )
2010-05-23 07:16:37 +04:00
if auth_source_id . present?
auth_source . authenticate ( self . login , clear_password )
else
2011-02-23 20:27:31 +03:00
User . hash_password ( " #{ salt } #{ User . hash_password clear_password } " ) == hashed_password
2010-05-23 07:16:37 +04:00
end
end
2011-08-21 05:57:25 +04:00
2011-02-23 20:27:31 +03:00
# Generates a random salt and computes hashed_password for +clear_password+
# The hashed password is stored in the following form: SHA1(salt + SHA1(password))
def salt_password ( clear_password )
self . salt = User . generate_salt
self . hashed_password = User . hash_password ( " #{ salt } #{ User . hash_password clear_password } " )
end
2010-05-23 07:16:37 +04:00
# Does the backend storage allow this user to change their password?
def change_password_allowed?
2011-12-18 19:48:06 +04:00
return true if auth_source . nil?
2010-05-23 07:16:37 +04:00
return auth_source . allow_password_changes?
2007-03-12 20:59:02 +03:00
end
2009-02-11 22:07:12 +03:00
# Generate and set a random password. Useful for automated user creation
# Based on Token#generate_token_value
#
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
2011-08-21 05:57:25 +04:00
2007-03-12 20:59:02 +03:00
def pref
self . preference || = UserPreference . new ( :user = > self )
end
2011-08-21 05:57:25 +04:00
2007-11-20 01:28:43 +03:00
def time_zone
2008-12-14 18:36:59 +03:00
@time_zone || = ( self . pref . time_zone . blank? ? nil : ActiveSupport :: TimeZone [ self . pref . time_zone ] )
2007-11-20 01:28:43 +03:00
end
2011-08-21 05:57:25 +04:00
2008-03-05 18:41:54 +03:00
def wants_comments_in_reverse_order?
self . pref [ :comments_sorting ] == 'desc'
end
2011-08-21 05:57:25 +04:00
2007-08-29 20:52:35 +04:00
# Return user's RSS key (a 40 chars long string), used to access feeds
def rss_key
2012-04-15 19:34:14 +04:00
if rss_token . nil?
create_rss_token ( :action = > 'feeds' )
end
rss_token . value
2007-03-17 18:18:50 +03:00
end
2009-12-23 09:27:28 +03:00
# Return user's API key (a 40 chars long string), used to access the API
def api_key
2012-04-15 19:34:14 +04:00
if api_token . nil?
create_api_token ( :action = > 'api' )
end
api_token . value
2009-12-23 09:27:28 +03:00
end
2011-08-21 05:57:25 +04:00
2007-10-20 16:47:05 +04:00
# Return an array of project ids for which the user has explicitly turned mail notifications on
def notified_projects_ids
@notified_projects_ids || = memberships . select { | m | m . mail_notification? } . collect ( & :project_id )
end
2011-08-21 05:57:25 +04:00
2007-10-20 16:47:05 +04:00
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
2010-06-21 01:40:55 +04:00
2010-09-29 02:13:11 +04:00
def valid_notification_options
2011-01-16 18:36:42 +03:00
self . class . valid_notification_options ( self )
end
# Only users that belong to more than 1 project can select projects for which they are notified
def self . valid_notification_options ( user = nil )
2010-09-29 02:13:11 +04:00
# Note that @user.membership.size would fail since AR ignores
# :include association option when doing a count
2011-01-16 18:36:42 +03:00
if user . nil? || user . memberships . length < 1
2011-01-16 17:40:38 +03:00
MAIL_NOTIFICATION_OPTIONS . reject { | option | option . first == 'selected' }
2010-09-29 02:13:11 +04:00
else
MAIL_NOTIFICATION_OPTIONS
end
end
2010-06-21 01:40:55 +04:00
# Find a user account by matching the exact login and then a case-insensitive
# version. Exact matches will be given priority.
2010-06-21 01:40:50 +04:00
def self . find_by_login ( login )
# First look for an exact match
2012-02-28 10:27:23 +04:00
user = all ( :conditions = > { :login = > login } ) . detect { | u | u . login == login }
unless user
# Fail over to case-insensitive if none was found
user = first ( :conditions = > [ " LOWER(login) = ? " , login . to_s . downcase ] )
end
user
2010-06-21 01:40:50 +04:00
end
2007-03-17 18:18:50 +03:00
def self . find_by_rss_key ( key )
2012-06-10 17:29:47 +04:00
token = Token . find_by_action_and_value ( 'feeds' , key . to_s )
2007-03-17 18:18:50 +03:00
token && token . user . active? ? token . user : nil
end
2011-08-21 05:57:25 +04:00
2009-12-23 09:27:28 +03:00
def self . find_by_api_key ( key )
2012-06-10 17:27:51 +04:00
token = Token . find_by_action_and_value ( 'api' , key . to_s )
2009-12-23 09:27:28 +03:00
token && token . user . active? ? token . user : nil
end
2011-08-21 05:57:25 +04:00
2008-12-12 15:07:09 +03:00
# Makes find_by_mail case-insensitive
def self . find_by_mail ( mail )
find ( :first , :conditions = > [ " LOWER(mail) = ? " , mail . to_s . downcase ] )
end
2011-08-21 05:57:25 +04:00
2012-04-09 19:53:48 +04:00
# Returns true if the default admin account can no longer be used
def self . default_admin_account_changed?
! User . active . find_by_login ( " admin " ) . try ( :check_password? , " admin " )
end
2007-08-29 20:52:35 +04:00
def to_s
name
end
2011-08-21 05:57:25 +04:00
2012-10-18 21:08:42 +04:00
CSS_CLASS_BY_STATUS = {
STATUS_ANONYMOUS = > 'anon' ,
STATUS_ACTIVE = > 'active' ,
STATUS_REGISTERED = > 'registered' ,
STATUS_LOCKED = > 'locked'
}
def css_classes
" user #{ CSS_CLASS_BY_STATUS [ status ] } "
end
2009-11-06 22:41:03 +03:00
# Returns the current day according to user's time zone
def today
if time_zone . nil?
Date . today
else
Time . now . in_time_zone ( time_zone ) . to_date
end
end
2011-08-21 05:57:25 +04:00
2012-05-26 16:07:56 +04:00
# Returns the day of +time+ according to user's time zone
def time_to_date ( time )
if time_zone . nil?
time . to_date
else
time . in_time_zone ( time_zone ) . to_date
end
end
2007-08-29 20:52:35 +04:00
def logged?
true
end
2011-08-21 05:57:25 +04:00
2008-07-16 23:33:15 +04:00
def anonymous?
! logged?
end
2011-08-21 05:57:25 +04:00
2009-05-10 14:54:31 +04:00
# Return user's roles for project
def roles_for_project ( project )
roles = [ ]
2007-08-29 20:52:35 +04:00
# No role on archived projects
2012-06-25 21:49:35 +04:00
return roles if project . nil? || project . archived?
2007-11-24 18:14:32 +03:00
if logged?
# Find project membership
membership = memberships . detect { | m | m . project_id == project . id }
if membership
2009-05-10 14:54:31 +04:00
roles = membership . roles
2007-11-24 18:14:32 +03:00
else
@role_non_member || = Role . non_member
2009-05-10 14:54:31 +04:00
roles << @role_non_member
2007-11-24 18:14:32 +03:00
end
2007-08-29 20:52:35 +04:00
else
2007-11-24 18:14:32 +03:00
@role_anonymous || = Role . anonymous
2009-05-10 14:54:31 +04:00
roles << @role_anonymous
2007-08-29 20:52:35 +04:00
end
2009-05-10 14:54:31 +04:00
roles
2007-08-29 20:52:35 +04:00
end
2011-08-21 05:57:25 +04:00
2007-08-29 20:52:35 +04:00
# Return true if the user is a member of project
def member_of? ( project )
2009-05-10 14:54:31 +04:00
! roles_for_project ( project ) . detect { | role | role . member? } . nil?
2007-08-29 20:52:35 +04:00
end
2011-08-21 05:57:25 +04:00
2011-03-15 18:39:59 +03:00
# Returns a hash of user's projects grouped by roles
def projects_by_role
return @projects_by_role if @projects_by_role
2011-08-21 05:57:25 +04:00
2012-08-27 14:11:20 +04:00
@projects_by_role = Hash . new ( [ ] )
2011-03-15 18:39:59 +03:00
memberships . each do | membership |
2012-08-27 14:11:20 +04:00
if membership . project
membership . roles . each do | role |
@projects_by_role [ role ] = [ ] unless @projects_by_role . key? ( role )
@projects_by_role [ role ] << membership . project
end
2011-03-15 18:39:59 +03:00
end
end
@projects_by_role . each do | role , projects |
projects . uniq!
end
2011-08-21 05:57:25 +04:00
2011-03-15 18:39:59 +03:00
@projects_by_role
end
2011-08-21 05:57:25 +04:00
2011-07-23 22:18:13 +04:00
# Returns true if user is arg or belongs to arg
def is_or_belongs_to? ( arg )
if arg . is_a? ( User )
self == arg
elsif arg . is_a? ( Group )
arg . users . include? ( self )
else
false
end
end
2011-08-21 05:57:25 +04:00
2010-09-29 09:22:45 +04:00
# Return true if the user is allowed to do the specified action on a specific context
# Action can be:
2007-08-29 20:52:35 +04:00
# * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
# * a permission Symbol (eg. :edit_project)
2010-09-29 09:22:45 +04:00
# Context can be:
# * a project : returns true if user is allowed to do the specified action on this project
2011-04-11 21:53:15 +04:00
# * an array of projects : returns true if user is allowed on every project
2011-08-21 05:57:25 +04:00
# * nil with options[:global] set : check if user has at least one role allowed for this action,
2010-09-29 09:22:45 +04:00
# or falls back to Non Member / Anonymous permissions depending if the user is logged
2011-04-11 21:53:15 +04:00
def allowed_to? ( action , context , options = { } , & block )
2010-10-06 09:08:38 +04:00
if context && context . is_a? ( Project )
return false unless context . allows_to? ( action )
2008-03-30 18:20:07 +04:00
# Admin users are authorized for anything else
return true if admin?
2011-08-21 05:57:25 +04:00
2010-10-06 09:08:38 +04:00
roles = roles_for_project ( context )
2009-05-10 14:54:31 +04:00
return false unless roles
2012-10-12 13:12:03 +04:00
roles . any? { | role |
2011-04-11 21:53:15 +04:00
( context . is_public? || role . member? ) &&
role . allowed_to? ( action ) &&
( block_given? ? yield ( role , self ) : true )
}
2010-10-06 09:08:38 +04:00
elsif context && context . is_a? ( Array )
2012-10-12 13:12:03 +04:00
if context . empty?
false
else
# Authorize if user is authorized on every element of the array
context . map { | project | allowed_to? ( action , project , options , & block ) } . reduce ( :& )
2010-09-29 09:22:45 +04:00
end
2008-03-30 18:20:07 +04:00
elsif options [ :global ]
2009-05-17 16:59:14 +04:00
# Admin users are always authorized
return true if admin?
2011-08-21 05:57:25 +04:00
2008-03-30 18:20:07 +04:00
# authorize if user has at least one role that has this permission
2009-05-10 14:54:31 +04:00
roles = memberships . collect { | m | m . roles } . flatten . uniq
2011-04-11 21:53:15 +04:00
roles << ( self . logged? ? Role . non_member : Role . anonymous )
2012-10-12 13:12:03 +04:00
roles . any? { | role |
2011-04-11 21:53:15 +04:00
role . allowed_to? ( action ) &&
( block_given? ? yield ( role , self ) : true )
}
2008-03-30 18:20:07 +04:00
else
false
end
2007-08-29 20:52:35 +04:00
end
2010-09-20 20:38:00 +04:00
# Is the user allowed to do the specified action on any project?
# See allowed_to? for the actions and valid options.
2011-04-11 21:53:15 +04:00
def allowed_to_globally? ( action , options , & block )
allowed_to? ( action , nil , options . reverse_merge ( :global = > true ) , & block )
2010-09-20 20:38:00 +04:00
end
2010-12-12 16:19:07 +03:00
2012-04-15 18:31:54 +04:00
# Returns true if the user is allowed to delete his own account
def own_account_deletable?
Setting . unsubscribe? &&
( ! admin? || User . active . first ( :conditions = > [ " admin = ? AND id <> ? " , true , id ] ) . present? )
end
2010-12-12 16:19:07 +03:00
safe_attributes 'login' ,
'firstname' ,
'lastname' ,
'mail' ,
'mail_notification' ,
'language' ,
'custom_field_values' ,
'custom_fields' ,
'identity_url'
2011-08-21 05:57:25 +04:00
2010-12-12 16:19:07 +03:00
safe_attributes 'status' ,
'auth_source_id' ,
:if = > lambda { | user , current_user | current_user . admin? }
2011-08-21 05:57:25 +04:00
2010-12-12 18:13:34 +03:00
safe_attributes 'group_ids' ,
:if = > lambda { | user , current_user | current_user . admin? && ! user . new_record? }
2011-08-21 05:57:25 +04:00
2010-09-28 22:22:10 +04:00
# Utility method to help check if a user should be notified about an
# event.
#
# TODO: only supports Issue events currently
def notify_about? ( object )
2010-12-12 17:02:39 +03:00
case mail_notification
when 'all'
2010-09-28 22:22:10 +04:00
true
2010-12-12 17:02:39 +03:00
when 'selected'
2011-01-24 20:28:59 +03:00
# user receives notifications for created/assigned issues on unselected projects
2012-01-23 21:55:29 +04:00
if object . is_a? ( Issue ) && ( object . author == self || is_or_belongs_to? ( object . assigned_to ) || is_or_belongs_to? ( object . assigned_to_was ) )
2011-01-24 20:28:59 +03:00
true
else
false
end
2010-12-12 17:02:39 +03:00
when 'none'
2010-09-28 22:22:10 +04:00
false
2010-12-12 17:02:39 +03:00
when 'only_my_events'
2012-01-23 21:55:29 +04:00
if object . is_a? ( Issue ) && ( object . author == self || is_or_belongs_to? ( object . assigned_to ) || is_or_belongs_to? ( object . assigned_to_was ) )
2010-09-28 22:22:10 +04:00
true
else
false
end
2010-12-12 17:02:39 +03:00
when 'only_assigned'
2012-01-23 21:55:29 +04:00
if object . is_a? ( Issue ) && ( is_or_belongs_to? ( object . assigned_to ) || is_or_belongs_to? ( object . assigned_to_was ) )
2010-09-28 22:22:10 +04:00
true
else
false
end
2010-12-12 17:02:39 +03:00
when 'only_owner'
2010-09-28 22:22:10 +04:00
if object . is_a? ( Issue ) && object . author == self
true
else
false
end
else
false
end
end
2011-08-21 05:57:25 +04:00
2007-08-29 20:52:35 +04:00
def self . current = ( user )
@current_user = user
end
2011-08-21 05:57:25 +04:00
2007-08-29 20:52:35 +04:00
def self . current
2007-11-20 18:40:16 +03:00
@current_user || = User . anonymous
2007-08-29 20:52:35 +04:00
end
2011-08-21 05:57:25 +04:00
2009-03-21 03:39:53 +03:00
# Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
# one anonymous user per database.
2007-08-29 20:52:35 +04:00
def self . anonymous
2007-11-20 18:40:16 +03:00
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
2008-05-22 22:08:54 +04:00
anonymous_user
2007-08-29 20:52:35 +04:00
end
2011-02-23 20:27:31 +03:00
# 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
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
2011-08-21 05:57:25 +04:00
2009-04-21 17:43:57 +04:00
protected
2011-08-21 05:57:25 +04:00
2011-09-21 10:14:11 +04:00
def validate_password_length
2009-04-21 17:43:57 +04:00
# Password length validation based on setting
if ! password . nil? && password . size < Setting . password_min_length . to_i
errors . add ( :password , :too_short , :count = > Setting . password_min_length . to_i )
end
end
2011-08-21 05:57:25 +04:00
2009-04-21 17:43:57 +04:00
private
2011-08-21 05:57:25 +04:00
2011-01-16 17:27:02 +03:00
# Removes references that are not handled by associations
# Things that are not deleted are reassociated with the anonymous user
def remove_references_before_destroy
return if self . id . nil?
2011-08-21 05:57:25 +04:00
2011-01-16 17:27:02 +03:00
substitute = User . anonymous
Attachment . update_all [ 'author_id = ?' , substitute . id ] , [ 'author_id = ?' , id ]
Comment . update_all [ 'author_id = ?' , substitute . id ] , [ 'author_id = ?' , id ]
Issue . update_all [ 'author_id = ?' , substitute . id ] , [ 'author_id = ?' , id ]
Issue . update_all 'assigned_to_id = NULL' , [ 'assigned_to_id = ?' , id ]
Journal . update_all [ 'user_id = ?' , substitute . id ] , [ 'user_id = ?' , id ]
JournalDetail . update_all [ 'old_value = ?' , substitute . id . to_s ] , [ " property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ? " , id . to_s ]
JournalDetail . update_all [ 'value = ?' , substitute . id . to_s ] , [ " property = 'attr' AND prop_key = 'assigned_to_id' AND value = ? " , id . to_s ]
Message . update_all [ 'author_id = ?' , substitute . id ] , [ 'author_id = ?' , id ]
News . update_all [ 'author_id = ?' , substitute . id ] , [ 'author_id = ?' , id ]
# Remove private queries and keep public ones
2011-12-09 16:08:58 +04:00
:: Query . delete_all [ 'user_id = ? AND is_public = ?' , id , false ]
:: Query . update_all [ 'user_id = ?' , substitute . id ] , [ 'user_id = ?' , id ]
2011-01-16 17:27:02 +03:00
TimeEntry . update_all [ 'user_id = ?' , substitute . id ] , [ 'user_id = ?' , id ]
Token . delete_all [ 'user_id = ?' , id ]
Watcher . delete_all [ 'user_id = ?' , id ]
WikiContent . update_all [ 'author_id = ?' , substitute . id ] , [ 'author_id = ?' , id ]
WikiContent :: Version . update_all [ 'author_id = ?' , substitute . id ] , [ 'author_id = ?' , id ]
end
2011-08-21 05:57:25 +04:00
2007-03-12 20:59:02 +03:00
# Return password digest
def self . hash_password ( clear_password )
Digest :: SHA1 . hexdigest ( clear_password || " " )
2006-07-09 20:30:01 +04:00
end
2011-08-21 05:57:25 +04:00
2011-02-23 20:27:31 +03:00
# Returns a 128bits random salt as a hex string (32 chars long)
def self . generate_salt
2012-03-04 15:05:02 +04:00
Redmine :: Utils . random_hex ( 16 )
2011-02-23 20:27:31 +03:00
end
2011-08-21 05:57:25 +04:00
2006-06-28 22:11:03 +04:00
end
2007-08-29 20:52:35 +04:00
class AnonymousUser < User
2011-12-18 19:48:06 +04:00
validate :validate_anonymous_uniqueness , :on = > :create
2011-12-19 08:41:13 +04:00
2011-12-18 19:48:06 +04:00
def validate_anonymous_uniqueness
2007-11-20 18:40:16 +03:00
# There should be only one AnonymousUser in the database
2011-10-07 16:25:20 +04:00
errors . add :base , 'An anonymous user already exists.' if AnonymousUser . find ( :first )
2007-11-20 01:28:43 +03:00
end
2011-08-21 05:57:25 +04:00
2008-06-28 00:13:56 +04:00
def available_custom_fields
[ ]
end
2011-08-21 05:57:25 +04:00
2007-11-20 18:40:16 +03:00
# Overrides a few properties
def logged? ; false end
def admin ; false end
2009-11-11 13:48:54 +03:00
def name ( * args ) ; I18n . t ( :label_user_anonymous ) end
2007-11-20 18:40:16 +03:00
def mail ; nil end
def time_zone ; nil end
def rss_key ; nil end
2011-08-21 05:57:25 +04:00
2012-07-05 17:05:11 +04:00
def pref
UserPreference . new ( :user = > self )
end
2011-01-16 17:27:02 +03:00
# Anonymous user can not be destroyed
def destroy
false
end
2007-08-29 20:52:35 +04:00
end