##// END OF EJS Templates
Validate status of users and groups....
Jean-Philippe Lang -
r14938:97a647c1e5d4
parent child
Show More
@@ -1,117 +1,119
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 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 class Group < Principal
18 class Group < Principal
19 include Redmine::SafeAttributes
19 include Redmine::SafeAttributes
20
20
21 has_and_belongs_to_many :users,
21 has_and_belongs_to_many :users,
22 :join_table => "#{table_name_prefix}groups_users#{table_name_suffix}",
22 :join_table => "#{table_name_prefix}groups_users#{table_name_suffix}",
23 :after_add => :user_added,
23 :after_add => :user_added,
24 :after_remove => :user_removed
24 :after_remove => :user_removed
25
25
26 acts_as_customizable
26 acts_as_customizable
27
27
28 validates_presence_of :lastname
28 validates_presence_of :lastname
29 validates_uniqueness_of :lastname, :case_sensitive => false
29 validates_uniqueness_of :lastname, :case_sensitive => false
30 validates_length_of :lastname, :maximum => 255
30 validates_length_of :lastname, :maximum => 255
31 attr_protected :id
31 attr_protected :id
32
32
33 self.valid_statuses = [STATUS_ACTIVE]
34
33 before_destroy :remove_references_before_destroy
35 before_destroy :remove_references_before_destroy
34
36
35 scope :sorted, lambda { order(:type => :asc, :lastname => :asc) }
37 scope :sorted, lambda { order(:type => :asc, :lastname => :asc) }
36 scope :named, lambda {|arg| where("LOWER(#{table_name}.lastname) = LOWER(?)", arg.to_s.strip)}
38 scope :named, lambda {|arg| where("LOWER(#{table_name}.lastname) = LOWER(?)", arg.to_s.strip)}
37 scope :givable, lambda {where(:type => 'Group')}
39 scope :givable, lambda {where(:type => 'Group')}
38
40
39 safe_attributes 'name',
41 safe_attributes 'name',
40 'user_ids',
42 'user_ids',
41 'custom_field_values',
43 'custom_field_values',
42 'custom_fields',
44 'custom_fields',
43 :if => lambda {|group, user| user.admin? && !group.builtin?}
45 :if => lambda {|group, user| user.admin? && !group.builtin?}
44
46
45 def to_s
47 def to_s
46 name.to_s
48 name.to_s
47 end
49 end
48
50
49 def name
51 def name
50 lastname
52 lastname
51 end
53 end
52
54
53 def name=(arg)
55 def name=(arg)
54 self.lastname = arg
56 self.lastname = arg
55 end
57 end
56
58
57 def builtin_type
59 def builtin_type
58 nil
60 nil
59 end
61 end
60
62
61 # Return true if the group is a builtin group
63 # Return true if the group is a builtin group
62 def builtin?
64 def builtin?
63 false
65 false
64 end
66 end
65
67
66 # Returns true if the group can be given to a user
68 # Returns true if the group can be given to a user
67 def givable?
69 def givable?
68 !builtin?
70 !builtin?
69 end
71 end
70
72
71 def user_added(user)
73 def user_added(user)
72 members.each do |member|
74 members.each do |member|
73 next if member.project.nil?
75 next if member.project.nil?
74 user_member = Member.find_by_project_id_and_user_id(member.project_id, user.id) || Member.new(:project_id => member.project_id, :user_id => user.id)
76 user_member = Member.find_by_project_id_and_user_id(member.project_id, user.id) || Member.new(:project_id => member.project_id, :user_id => user.id)
75 member.member_roles.each do |member_role|
77 member.member_roles.each do |member_role|
76 user_member.member_roles << MemberRole.new(:role => member_role.role, :inherited_from => member_role.id)
78 user_member.member_roles << MemberRole.new(:role => member_role.role, :inherited_from => member_role.id)
77 end
79 end
78 user_member.save!
80 user_member.save!
79 end
81 end
80 end
82 end
81
83
82 def user_removed(user)
84 def user_removed(user)
83 members.each do |member|
85 members.each do |member|
84 MemberRole.
86 MemberRole.
85 joins(:member).
87 joins(:member).
86 where("#{Member.table_name}.user_id = ? AND #{MemberRole.table_name}.inherited_from IN (?)", user.id, member.member_role_ids).
88 where("#{Member.table_name}.user_id = ? AND #{MemberRole.table_name}.inherited_from IN (?)", user.id, member.member_role_ids).
87 each(&:destroy)
89 each(&:destroy)
88 end
90 end
89 end
91 end
90
92
91 def self.human_attribute_name(attribute_key_name, *args)
93 def self.human_attribute_name(attribute_key_name, *args)
92 attr_name = attribute_key_name.to_s
94 attr_name = attribute_key_name.to_s
93 if attr_name == 'lastname'
95 if attr_name == 'lastname'
94 attr_name = "name"
96 attr_name = "name"
95 end
97 end
96 super(attr_name, *args)
98 super(attr_name, *args)
97 end
99 end
98
100
99 def self.anonymous
101 def self.anonymous
100 GroupAnonymous.load_instance
102 GroupAnonymous.load_instance
101 end
103 end
102
104
103 def self.non_member
105 def self.non_member
104 GroupNonMember.load_instance
106 GroupNonMember.load_instance
105 end
107 end
106
108
107 private
109 private
108
110
109 # Removes references that are not handled by associations
111 # Removes references that are not handled by associations
110 def remove_references_before_destroy
112 def remove_references_before_destroy
111 return if self.id.nil?
113 return if self.id.nil?
112
114
113 Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL')
115 Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL')
114 end
116 end
115 end
117 end
116
118
117 require_dependency "group_builtin"
119 require_dependency "group_builtin"
@@ -1,184 +1,196
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 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 class Principal < ActiveRecord::Base
18 class Principal < ActiveRecord::Base
19 self.table_name = "#{table_name_prefix}users#{table_name_suffix}"
19 self.table_name = "#{table_name_prefix}users#{table_name_suffix}"
20
20
21 # Account statuses
21 # Account statuses
22 STATUS_ANONYMOUS = 0
22 STATUS_ANONYMOUS = 0
23 STATUS_ACTIVE = 1
23 STATUS_ACTIVE = 1
24 STATUS_REGISTERED = 2
24 STATUS_REGISTERED = 2
25 STATUS_LOCKED = 3
25 STATUS_LOCKED = 3
26
26
27 class_attribute :valid_statuses
28
27 has_many :members, :foreign_key => 'user_id', :dependent => :destroy
29 has_many :members, :foreign_key => 'user_id', :dependent => :destroy
28 has_many :memberships,
30 has_many :memberships,
29 lambda {preload(:project, :roles).
31 lambda {preload(:project, :roles).
30 joins(:project).
32 joins(:project).
31 where("#{Project.table_name}.status<>#{Project::STATUS_ARCHIVED}")},
33 where("#{Project.table_name}.status<>#{Project::STATUS_ARCHIVED}")},
32 :class_name => 'Member',
34 :class_name => 'Member',
33 :foreign_key => 'user_id'
35 :foreign_key => 'user_id'
34 has_many :projects, :through => :memberships
36 has_many :projects, :through => :memberships
35 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
37 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
36
38
39 validate :validate_status
40
37 # Groups and active users
41 # Groups and active users
38 scope :active, lambda { where(:status => STATUS_ACTIVE) }
42 scope :active, lambda { where(:status => STATUS_ACTIVE) }
39
43
40 scope :visible, lambda {|*args|
44 scope :visible, lambda {|*args|
41 user = args.first || User.current
45 user = args.first || User.current
42
46
43 if user.admin?
47 if user.admin?
44 all
48 all
45 else
49 else
46 view_all_active = false
50 view_all_active = false
47 if user.memberships.to_a.any?
51 if user.memberships.to_a.any?
48 view_all_active = user.memberships.any? {|m| m.roles.any? {|r| r.users_visibility == 'all'}}
52 view_all_active = user.memberships.any? {|m| m.roles.any? {|r| r.users_visibility == 'all'}}
49 else
53 else
50 view_all_active = user.builtin_role.users_visibility == 'all'
54 view_all_active = user.builtin_role.users_visibility == 'all'
51 end
55 end
52
56
53 if view_all_active
57 if view_all_active
54 active
58 active
55 else
59 else
56 # self and members of visible projects
60 # self and members of visible projects
57 active.where("#{table_name}.id = ? OR #{table_name}.id IN (SELECT user_id FROM #{Member.table_name} WHERE project_id IN (?))",
61 active.where("#{table_name}.id = ? OR #{table_name}.id IN (SELECT user_id FROM #{Member.table_name} WHERE project_id IN (?))",
58 user.id, user.visible_project_ids
62 user.id, user.visible_project_ids
59 )
63 )
60 end
64 end
61 end
65 end
62 }
66 }
63
67
64 scope :like, lambda {|q|
68 scope :like, lambda {|q|
65 q = q.to_s
69 q = q.to_s
66 if q.blank?
70 if q.blank?
67 where({})
71 where({})
68 else
72 else
69 pattern = "%#{q}%"
73 pattern = "%#{q}%"
70 sql = %w(login firstname lastname).map {|column| "LOWER(#{table_name}.#{column}) LIKE LOWER(:p)"}.join(" OR ")
74 sql = %w(login firstname lastname).map {|column| "LOWER(#{table_name}.#{column}) LIKE LOWER(:p)"}.join(" OR ")
71 sql << " OR #{table_name}.id IN (SELECT user_id FROM #{EmailAddress.table_name} WHERE LOWER(address) LIKE LOWER(:p))"
75 sql << " OR #{table_name}.id IN (SELECT user_id FROM #{EmailAddress.table_name} WHERE LOWER(address) LIKE LOWER(:p))"
72 params = {:p => pattern}
76 params = {:p => pattern}
73 if q =~ /^(.+)\s+(.+)$/
77 if q =~ /^(.+)\s+(.+)$/
74 a, b = "#{$1}%", "#{$2}%"
78 a, b = "#{$1}%", "#{$2}%"
75 sql << " OR (LOWER(#{table_name}.firstname) LIKE LOWER(:a) AND LOWER(#{table_name}.lastname) LIKE LOWER(:b))"
79 sql << " OR (LOWER(#{table_name}.firstname) LIKE LOWER(:a) AND LOWER(#{table_name}.lastname) LIKE LOWER(:b))"
76 sql << " OR (LOWER(#{table_name}.firstname) LIKE LOWER(:b) AND LOWER(#{table_name}.lastname) LIKE LOWER(:a))"
80 sql << " OR (LOWER(#{table_name}.firstname) LIKE LOWER(:b) AND LOWER(#{table_name}.lastname) LIKE LOWER(:a))"
77 params.merge!(:a => a, :b => b)
81 params.merge!(:a => a, :b => b)
78 end
82 end
79 where(sql, params)
83 where(sql, params)
80 end
84 end
81 }
85 }
82
86
83 # Principals that are members of a collection of projects
87 # Principals that are members of a collection of projects
84 scope :member_of, lambda {|projects|
88 scope :member_of, lambda {|projects|
85 projects = [projects] if projects.is_a?(Project)
89 projects = [projects] if projects.is_a?(Project)
86 if projects.blank?
90 if projects.blank?
87 where("1=0")
91 where("1=0")
88 else
92 else
89 ids = projects.map(&:id)
93 ids = projects.map(&:id)
90 active.where("#{Principal.table_name}.id IN (SELECT DISTINCT user_id FROM #{Member.table_name} WHERE project_id IN (?))", ids)
94 active.where("#{Principal.table_name}.id IN (SELECT DISTINCT user_id FROM #{Member.table_name} WHERE project_id IN (?))", ids)
91 end
95 end
92 }
96 }
93 # Principals that are not members of projects
97 # Principals that are not members of projects
94 scope :not_member_of, lambda {|projects|
98 scope :not_member_of, lambda {|projects|
95 projects = [projects] unless projects.is_a?(Array)
99 projects = [projects] unless projects.is_a?(Array)
96 if projects.empty?
100 if projects.empty?
97 where("1=0")
101 where("1=0")
98 else
102 else
99 ids = projects.map(&:id)
103 ids = projects.map(&:id)
100 where("#{Principal.table_name}.id NOT IN (SELECT DISTINCT user_id FROM #{Member.table_name} WHERE project_id IN (?))", ids)
104 where("#{Principal.table_name}.id NOT IN (SELECT DISTINCT user_id FROM #{Member.table_name} WHERE project_id IN (?))", ids)
101 end
105 end
102 }
106 }
103 scope :sorted, lambda { order(*Principal.fields_for_order_statement)}
107 scope :sorted, lambda { order(*Principal.fields_for_order_statement)}
104
108
105 before_create :set_default_empty_values
109 before_create :set_default_empty_values
106
110
107 def name(formatter = nil)
111 def name(formatter = nil)
108 to_s
112 to_s
109 end
113 end
110
114
111 def mail=(*args)
115 def mail=(*args)
112 nil
116 nil
113 end
117 end
114
118
115 def mail
119 def mail
116 nil
120 nil
117 end
121 end
118
122
119 def visible?(user=User.current)
123 def visible?(user=User.current)
120 Principal.visible(user).where(:id => id).first == self
124 Principal.visible(user).where(:id => id).first == self
121 end
125 end
122
126
123 # Return true if the principal is a member of project
127 # Return true if the principal is a member of project
124 def member_of?(project)
128 def member_of?(project)
125 projects.to_a.include?(project)
129 projects.to_a.include?(project)
126 end
130 end
127
131
128 def <=>(principal)
132 def <=>(principal)
129 if principal.nil?
133 if principal.nil?
130 -1
134 -1
131 elsif self.class.name == principal.class.name
135 elsif self.class.name == principal.class.name
132 self.to_s.casecmp(principal.to_s)
136 self.to_s.casecmp(principal.to_s)
133 else
137 else
134 # groups after users
138 # groups after users
135 principal.class.name <=> self.class.name
139 principal.class.name <=> self.class.name
136 end
140 end
137 end
141 end
138
142
139 # Returns an array of fields names than can be used to make an order statement for principals.
143 # Returns an array of fields names than can be used to make an order statement for principals.
140 # Users are sorted before Groups.
144 # Users are sorted before Groups.
141 # Examples:
145 # Examples:
142 def self.fields_for_order_statement(table=nil)
146 def self.fields_for_order_statement(table=nil)
143 table ||= table_name
147 table ||= table_name
144 columns = ['type DESC'] + (User.name_formatter[:order] - ['id']) + ['lastname', 'id']
148 columns = ['type DESC'] + (User.name_formatter[:order] - ['id']) + ['lastname', 'id']
145 columns.uniq.map {|field| "#{table}.#{field}"}
149 columns.uniq.map {|field| "#{table}.#{field}"}
146 end
150 end
147
151
148 # Returns the principal that matches the keyword among principals
152 # Returns the principal that matches the keyword among principals
149 def self.detect_by_keyword(principals, keyword)
153 def self.detect_by_keyword(principals, keyword)
150 keyword = keyword.to_s
154 keyword = keyword.to_s
151 return nil if keyword.blank?
155 return nil if keyword.blank?
152
156
153 principal = nil
157 principal = nil
154 principal ||= principals.detect {|a| keyword.casecmp(a.login.to_s) == 0}
158 principal ||= principals.detect {|a| keyword.casecmp(a.login.to_s) == 0}
155 principal ||= principals.detect {|a| keyword.casecmp(a.mail.to_s) == 0}
159 principal ||= principals.detect {|a| keyword.casecmp(a.mail.to_s) == 0}
156
160
157 if principal.nil? && keyword.match(/ /)
161 if principal.nil? && keyword.match(/ /)
158 firstname, lastname = *(keyword.split) # "First Last Throwaway"
162 firstname, lastname = *(keyword.split) # "First Last Throwaway"
159 principal ||= principals.detect {|a|
163 principal ||= principals.detect {|a|
160 a.is_a?(User) &&
164 a.is_a?(User) &&
161 firstname.casecmp(a.firstname.to_s) == 0 &&
165 firstname.casecmp(a.firstname.to_s) == 0 &&
162 lastname.casecmp(a.lastname.to_s) == 0
166 lastname.casecmp(a.lastname.to_s) == 0
163 }
167 }
164 end
168 end
165 if principal.nil?
169 if principal.nil?
166 principal ||= principals.detect {|a| keyword.casecmp(a.name) == 0}
170 principal ||= principals.detect {|a| keyword.casecmp(a.name) == 0}
167 end
171 end
168 principal
172 principal
169 end
173 end
170
174
171 protected
175 protected
172
176
173 # Make sure we don't try to insert NULL values (see #4632)
177 # Make sure we don't try to insert NULL values (see #4632)
174 def set_default_empty_values
178 def set_default_empty_values
175 self.login ||= ''
179 self.login ||= ''
176 self.hashed_password ||= ''
180 self.hashed_password ||= ''
177 self.firstname ||= ''
181 self.firstname ||= ''
178 self.lastname ||= ''
182 self.lastname ||= ''
179 true
183 true
180 end
184 end
185
186 def validate_status
187 if status_changed? && self.class.valid_statuses.present?
188 unless self.class.valid_statuses.include?(status)
189 errors.add :status, :invalid
190 end
191 end
192 end
181 end
193 end
182
194
183 require_dependency "user"
195 require_dependency "user"
184 require_dependency "group"
196 require_dependency "group"
@@ -1,919 +1,923
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 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 < Principal
20 class User < Principal
21 include Redmine::SafeAttributes
21 include Redmine::SafeAttributes
22
22
23 # Different ways of displaying/sorting users
23 # Different ways of displaying/sorting users
24 USER_FORMATS = {
24 USER_FORMATS = {
25 :firstname_lastname => {
25 :firstname_lastname => {
26 :string => '#{firstname} #{lastname}',
26 :string => '#{firstname} #{lastname}',
27 :order => %w(firstname lastname id),
27 :order => %w(firstname lastname id),
28 :setting_order => 1
28 :setting_order => 1
29 },
29 },
30 :firstname_lastinitial => {
30 :firstname_lastinitial => {
31 :string => '#{firstname} #{lastname.to_s.chars.first}.',
31 :string => '#{firstname} #{lastname.to_s.chars.first}.',
32 :order => %w(firstname lastname id),
32 :order => %w(firstname lastname id),
33 :setting_order => 2
33 :setting_order => 2
34 },
34 },
35 :firstinitial_lastname => {
35 :firstinitial_lastname => {
36 :string => '#{firstname.to_s.gsub(/(([[:alpha:]])[[:alpha:]]*\.?)/, \'\2.\')} #{lastname}',
36 :string => '#{firstname.to_s.gsub(/(([[:alpha:]])[[:alpha:]]*\.?)/, \'\2.\')} #{lastname}',
37 :order => %w(firstname lastname id),
37 :order => %w(firstname lastname id),
38 :setting_order => 2
38 :setting_order => 2
39 },
39 },
40 :firstname => {
40 :firstname => {
41 :string => '#{firstname}',
41 :string => '#{firstname}',
42 :order => %w(firstname id),
42 :order => %w(firstname id),
43 :setting_order => 3
43 :setting_order => 3
44 },
44 },
45 :lastname_firstname => {
45 :lastname_firstname => {
46 :string => '#{lastname} #{firstname}',
46 :string => '#{lastname} #{firstname}',
47 :order => %w(lastname firstname id),
47 :order => %w(lastname firstname id),
48 :setting_order => 4
48 :setting_order => 4
49 },
49 },
50 :lastnamefirstname => {
50 :lastnamefirstname => {
51 :string => '#{lastname}#{firstname}',
51 :string => '#{lastname}#{firstname}',
52 :order => %w(lastname firstname id),
52 :order => %w(lastname firstname id),
53 :setting_order => 5
53 :setting_order => 5
54 },
54 },
55 :lastname_comma_firstname => {
55 :lastname_comma_firstname => {
56 :string => '#{lastname}, #{firstname}',
56 :string => '#{lastname}, #{firstname}',
57 :order => %w(lastname firstname id),
57 :order => %w(lastname firstname id),
58 :setting_order => 6
58 :setting_order => 6
59 },
59 },
60 :lastname => {
60 :lastname => {
61 :string => '#{lastname}',
61 :string => '#{lastname}',
62 :order => %w(lastname id),
62 :order => %w(lastname id),
63 :setting_order => 7
63 :setting_order => 7
64 },
64 },
65 :username => {
65 :username => {
66 :string => '#{login}',
66 :string => '#{login}',
67 :order => %w(login id),
67 :order => %w(login id),
68 :setting_order => 8
68 :setting_order => 8
69 },
69 },
70 }
70 }
71
71
72 MAIL_NOTIFICATION_OPTIONS = [
72 MAIL_NOTIFICATION_OPTIONS = [
73 ['all', :label_user_mail_option_all],
73 ['all', :label_user_mail_option_all],
74 ['selected', :label_user_mail_option_selected],
74 ['selected', :label_user_mail_option_selected],
75 ['only_my_events', :label_user_mail_option_only_my_events],
75 ['only_my_events', :label_user_mail_option_only_my_events],
76 ['only_assigned', :label_user_mail_option_only_assigned],
76 ['only_assigned', :label_user_mail_option_only_assigned],
77 ['only_owner', :label_user_mail_option_only_owner],
77 ['only_owner', :label_user_mail_option_only_owner],
78 ['none', :label_user_mail_option_none]
78 ['none', :label_user_mail_option_none]
79 ]
79 ]
80
80
81 has_and_belongs_to_many :groups,
81 has_and_belongs_to_many :groups,
82 :join_table => "#{table_name_prefix}groups_users#{table_name_suffix}",
82 :join_table => "#{table_name_prefix}groups_users#{table_name_suffix}",
83 :after_add => Proc.new {|user, group| group.user_added(user)},
83 :after_add => Proc.new {|user, group| group.user_added(user)},
84 :after_remove => Proc.new {|user, group| group.user_removed(user)}
84 :after_remove => Proc.new {|user, group| group.user_removed(user)}
85 has_many :changesets, :dependent => :nullify
85 has_many :changesets, :dependent => :nullify
86 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
86 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
87 has_one :rss_token, lambda {where "action='feeds'"}, :class_name => 'Token'
87 has_one :rss_token, lambda {where "action='feeds'"}, :class_name => 'Token'
88 has_one :api_token, lambda {where "action='api'"}, :class_name => 'Token'
88 has_one :api_token, lambda {where "action='api'"}, :class_name => 'Token'
89 has_one :email_address, lambda {where :is_default => true}, :autosave => true
89 has_one :email_address, lambda {where :is_default => true}, :autosave => true
90 has_many :email_addresses, :dependent => :delete_all
90 has_many :email_addresses, :dependent => :delete_all
91 belongs_to :auth_source
91 belongs_to :auth_source
92
92
93 scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") }
93 scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") }
94 scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) }
94 scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) }
95
95
96 acts_as_customizable
96 acts_as_customizable
97
97
98 attr_accessor :password, :password_confirmation, :generate_password
98 attr_accessor :password, :password_confirmation, :generate_password
99 attr_accessor :last_before_login_on
99 attr_accessor :last_before_login_on
100 attr_accessor :remote_ip
100 attr_accessor :remote_ip
101
101
102 # Prevents unauthorized assignments
102 # Prevents unauthorized assignments
103 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
103 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
104
104
105 LOGIN_LENGTH_LIMIT = 60
105 LOGIN_LENGTH_LIMIT = 60
106 MAIL_LENGTH_LIMIT = 60
106 MAIL_LENGTH_LIMIT = 60
107
107
108 validates_presence_of :login, :firstname, :lastname, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
108 validates_presence_of :login, :firstname, :lastname, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
109 validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false
109 validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false
110 # Login must contain letters, numbers, underscores only
110 # Login must contain letters, numbers, underscores only
111 validates_format_of :login, :with => /\A[a-z0-9_\-@\.]*\z/i
111 validates_format_of :login, :with => /\A[a-z0-9_\-@\.]*\z/i
112 validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT
112 validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT
113 validates_length_of :firstname, :lastname, :maximum => 30
113 validates_length_of :firstname, :lastname, :maximum => 30
114 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
114 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
115 validate :validate_password_length
115 validate :validate_password_length
116 validate do
116 validate do
117 if password_confirmation && password != password_confirmation
117 if password_confirmation && password != password_confirmation
118 errors.add(:password, :confirmation)
118 errors.add(:password, :confirmation)
119 end
119 end
120 end
120 end
121
121
122 self.valid_statuses = [STATUS_ACTIVE, STATUS_REGISTERED, STATUS_LOCKED]
123
122 before_validation :instantiate_email_address
124 before_validation :instantiate_email_address
123 before_create :set_mail_notification
125 before_create :set_mail_notification
124 before_save :generate_password_if_needed, :update_hashed_password
126 before_save :generate_password_if_needed, :update_hashed_password
125 before_destroy :remove_references_before_destroy
127 before_destroy :remove_references_before_destroy
126 after_save :update_notified_project_ids, :destroy_tokens, :deliver_security_notification
128 after_save :update_notified_project_ids, :destroy_tokens, :deliver_security_notification
127 after_destroy :deliver_security_notification
129 after_destroy :deliver_security_notification
128
130
129 scope :in_group, lambda {|group|
131 scope :in_group, lambda {|group|
130 group_id = group.is_a?(Group) ? group.id : group.to_i
132 group_id = group.is_a?(Group) ? group.id : group.to_i
131 where("#{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)
133 where("#{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)
132 }
134 }
133 scope :not_in_group, lambda {|group|
135 scope :not_in_group, lambda {|group|
134 group_id = group.is_a?(Group) ? group.id : group.to_i
136 group_id = group.is_a?(Group) ? group.id : group.to_i
135 where("#{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)
137 where("#{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)
136 }
138 }
137 scope :sorted, lambda { order(*User.fields_for_order_statement)}
139 scope :sorted, lambda { order(*User.fields_for_order_statement)}
138 scope :having_mail, lambda {|arg|
140 scope :having_mail, lambda {|arg|
139 addresses = Array.wrap(arg).map {|a| a.to_s.downcase}
141 addresses = Array.wrap(arg).map {|a| a.to_s.downcase}
140 if addresses.any?
142 if addresses.any?
141 joins(:email_addresses).where("LOWER(#{EmailAddress.table_name}.address) IN (?)", addresses).uniq
143 joins(:email_addresses).where("LOWER(#{EmailAddress.table_name}.address) IN (?)", addresses).uniq
142 else
144 else
143 none
145 none
144 end
146 end
145 }
147 }
146
148
147 def set_mail_notification
149 def set_mail_notification
148 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
150 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
149 true
151 true
150 end
152 end
151
153
152 def update_hashed_password
154 def update_hashed_password
153 # update hashed_password if password was set
155 # update hashed_password if password was set
154 if self.password && self.auth_source_id.blank?
156 if self.password && self.auth_source_id.blank?
155 salt_password(password)
157 salt_password(password)
156 end
158 end
157 end
159 end
158
160
159 alias :base_reload :reload
161 alias :base_reload :reload
160 def reload(*args)
162 def reload(*args)
161 @name = nil
163 @name = nil
162 @projects_by_role = nil
164 @projects_by_role = nil
163 @membership_by_project_id = nil
165 @membership_by_project_id = nil
164 @notified_projects_ids = nil
166 @notified_projects_ids = nil
165 @notified_projects_ids_changed = false
167 @notified_projects_ids_changed = false
166 @builtin_role = nil
168 @builtin_role = nil
167 @visible_project_ids = nil
169 @visible_project_ids = nil
168 @managed_roles = nil
170 @managed_roles = nil
169 base_reload(*args)
171 base_reload(*args)
170 end
172 end
171
173
172 def mail
174 def mail
173 email_address.try(:address)
175 email_address.try(:address)
174 end
176 end
175
177
176 def mail=(arg)
178 def mail=(arg)
177 email = email_address || build_email_address
179 email = email_address || build_email_address
178 email.address = arg
180 email.address = arg
179 end
181 end
180
182
181 def mail_changed?
183 def mail_changed?
182 email_address.try(:address_changed?)
184 email_address.try(:address_changed?)
183 end
185 end
184
186
185 def mails
187 def mails
186 email_addresses.pluck(:address)
188 email_addresses.pluck(:address)
187 end
189 end
188
190
189 def self.find_or_initialize_by_identity_url(url)
191 def self.find_or_initialize_by_identity_url(url)
190 user = where(:identity_url => url).first
192 user = where(:identity_url => url).first
191 unless user
193 unless user
192 user = User.new
194 user = User.new
193 user.identity_url = url
195 user.identity_url = url
194 end
196 end
195 user
197 user
196 end
198 end
197
199
198 def identity_url=(url)
200 def identity_url=(url)
199 if url.blank?
201 if url.blank?
200 write_attribute(:identity_url, '')
202 write_attribute(:identity_url, '')
201 else
203 else
202 begin
204 begin
203 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
205 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
204 rescue OpenIdAuthentication::InvalidOpenId
206 rescue OpenIdAuthentication::InvalidOpenId
205 # Invalid url, don't save
207 # Invalid url, don't save
206 end
208 end
207 end
209 end
208 self.read_attribute(:identity_url)
210 self.read_attribute(:identity_url)
209 end
211 end
210
212
211 # Returns the user that matches provided login and password, or nil
213 # Returns the user that matches provided login and password, or nil
212 def self.try_to_login(login, password, active_only=true)
214 def self.try_to_login(login, password, active_only=true)
213 login = login.to_s
215 login = login.to_s
214 password = password.to_s
216 password = password.to_s
215
217
216 # Make sure no one can sign in with an empty login or password
218 # Make sure no one can sign in with an empty login or password
217 return nil if login.empty? || password.empty?
219 return nil if login.empty? || password.empty?
218 user = find_by_login(login)
220 user = find_by_login(login)
219 if user
221 if user
220 # user is already in local database
222 # user is already in local database
221 return nil unless user.check_password?(password)
223 return nil unless user.check_password?(password)
222 return nil if !user.active? && active_only
224 return nil if !user.active? && active_only
223 else
225 else
224 # user is not yet registered, try to authenticate with available sources
226 # user is not yet registered, try to authenticate with available sources
225 attrs = AuthSource.authenticate(login, password)
227 attrs = AuthSource.authenticate(login, password)
226 if attrs
228 if attrs
227 user = new(attrs)
229 user = new(attrs)
228 user.login = login
230 user.login = login
229 user.language = Setting.default_language
231 user.language = Setting.default_language
230 if user.save
232 if user.save
231 user.reload
233 user.reload
232 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
234 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
233 end
235 end
234 end
236 end
235 end
237 end
236 user.update_column(:last_login_on, Time.now) if user && !user.new_record? && user.active?
238 user.update_column(:last_login_on, Time.now) if user && !user.new_record? && user.active?
237 user
239 user
238 rescue => text
240 rescue => text
239 raise text
241 raise text
240 end
242 end
241
243
242 # Returns the user who matches the given autologin +key+ or nil
244 # Returns the user who matches the given autologin +key+ or nil
243 def self.try_to_autologin(key)
245 def self.try_to_autologin(key)
244 user = Token.find_active_user('autologin', key, Setting.autologin.to_i)
246 user = Token.find_active_user('autologin', key, Setting.autologin.to_i)
245 if user
247 if user
246 user.update_column(:last_login_on, Time.now)
248 user.update_column(:last_login_on, Time.now)
247 user
249 user
248 end
250 end
249 end
251 end
250
252
251 def self.name_formatter(formatter = nil)
253 def self.name_formatter(formatter = nil)
252 USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
254 USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
253 end
255 end
254
256
255 # Returns an array of fields names than can be used to make an order statement for users
257 # Returns an array of fields names than can be used to make an order statement for users
256 # according to how user names are displayed
258 # according to how user names are displayed
257 # Examples:
259 # Examples:
258 #
260 #
259 # User.fields_for_order_statement => ['users.login', 'users.id']
261 # User.fields_for_order_statement => ['users.login', 'users.id']
260 # User.fields_for_order_statement('authors') => ['authors.login', 'authors.id']
262 # User.fields_for_order_statement('authors') => ['authors.login', 'authors.id']
261 def self.fields_for_order_statement(table=nil)
263 def self.fields_for_order_statement(table=nil)
262 table ||= table_name
264 table ||= table_name
263 name_formatter[:order].map {|field| "#{table}.#{field}"}
265 name_formatter[:order].map {|field| "#{table}.#{field}"}
264 end
266 end
265
267
266 # Return user's full name for display
268 # Return user's full name for display
267 def name(formatter = nil)
269 def name(formatter = nil)
268 f = self.class.name_formatter(formatter)
270 f = self.class.name_formatter(formatter)
269 if formatter
271 if formatter
270 eval('"' + f[:string] + '"')
272 eval('"' + f[:string] + '"')
271 else
273 else
272 @name ||= eval('"' + f[:string] + '"')
274 @name ||= eval('"' + f[:string] + '"')
273 end
275 end
274 end
276 end
275
277
276 def active?
278 def active?
277 self.status == STATUS_ACTIVE
279 self.status == STATUS_ACTIVE
278 end
280 end
279
281
280 def registered?
282 def registered?
281 self.status == STATUS_REGISTERED
283 self.status == STATUS_REGISTERED
282 end
284 end
283
285
284 def locked?
286 def locked?
285 self.status == STATUS_LOCKED
287 self.status == STATUS_LOCKED
286 end
288 end
287
289
288 def activate
290 def activate
289 self.status = STATUS_ACTIVE
291 self.status = STATUS_ACTIVE
290 end
292 end
291
293
292 def register
294 def register
293 self.status = STATUS_REGISTERED
295 self.status = STATUS_REGISTERED
294 end
296 end
295
297
296 def lock
298 def lock
297 self.status = STATUS_LOCKED
299 self.status = STATUS_LOCKED
298 end
300 end
299
301
300 def activate!
302 def activate!
301 update_attribute(:status, STATUS_ACTIVE)
303 update_attribute(:status, STATUS_ACTIVE)
302 end
304 end
303
305
304 def register!
306 def register!
305 update_attribute(:status, STATUS_REGISTERED)
307 update_attribute(:status, STATUS_REGISTERED)
306 end
308 end
307
309
308 def lock!
310 def lock!
309 update_attribute(:status, STATUS_LOCKED)
311 update_attribute(:status, STATUS_LOCKED)
310 end
312 end
311
313
312 # Returns true if +clear_password+ is the correct user's password, otherwise false
314 # Returns true if +clear_password+ is the correct user's password, otherwise false
313 def check_password?(clear_password)
315 def check_password?(clear_password)
314 if auth_source_id.present?
316 if auth_source_id.present?
315 auth_source.authenticate(self.login, clear_password)
317 auth_source.authenticate(self.login, clear_password)
316 else
318 else
317 User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password
319 User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password
318 end
320 end
319 end
321 end
320
322
321 # Generates a random salt and computes hashed_password for +clear_password+
323 # Generates a random salt and computes hashed_password for +clear_password+
322 # The hashed password is stored in the following form: SHA1(salt + SHA1(password))
324 # The hashed password is stored in the following form: SHA1(salt + SHA1(password))
323 def salt_password(clear_password)
325 def salt_password(clear_password)
324 self.salt = User.generate_salt
326 self.salt = User.generate_salt
325 self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
327 self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
326 self.passwd_changed_on = Time.now.change(:usec => 0)
328 self.passwd_changed_on = Time.now.change(:usec => 0)
327 end
329 end
328
330
329 # Does the backend storage allow this user to change their password?
331 # Does the backend storage allow this user to change their password?
330 def change_password_allowed?
332 def change_password_allowed?
331 return true if auth_source.nil?
333 return true if auth_source.nil?
332 return auth_source.allow_password_changes?
334 return auth_source.allow_password_changes?
333 end
335 end
334
336
335 # Returns true if the user password has expired
337 # Returns true if the user password has expired
336 def password_expired?
338 def password_expired?
337 period = Setting.password_max_age.to_i
339 period = Setting.password_max_age.to_i
338 if period.zero?
340 if period.zero?
339 false
341 false
340 else
342 else
341 changed_on = self.passwd_changed_on || Time.at(0)
343 changed_on = self.passwd_changed_on || Time.at(0)
342 changed_on < period.days.ago
344 changed_on < period.days.ago
343 end
345 end
344 end
346 end
345
347
346 def must_change_password?
348 def must_change_password?
347 (must_change_passwd? || password_expired?) && change_password_allowed?
349 (must_change_passwd? || password_expired?) && change_password_allowed?
348 end
350 end
349
351
350 def generate_password?
352 def generate_password?
351 generate_password == '1' || generate_password == true
353 generate_password == '1' || generate_password == true
352 end
354 end
353
355
354 # Generate and set a random password on given length
356 # Generate and set a random password on given length
355 def random_password(length=40)
357 def random_password(length=40)
356 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
358 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
357 chars -= %w(0 O 1 l)
359 chars -= %w(0 O 1 l)
358 password = ''
360 password = ''
359 length.times {|i| password << chars[SecureRandom.random_number(chars.size)] }
361 length.times {|i| password << chars[SecureRandom.random_number(chars.size)] }
360 self.password = password
362 self.password = password
361 self.password_confirmation = password
363 self.password_confirmation = password
362 self
364 self
363 end
365 end
364
366
365 def pref
367 def pref
366 self.preference ||= UserPreference.new(:user => self)
368 self.preference ||= UserPreference.new(:user => self)
367 end
369 end
368
370
369 def time_zone
371 def time_zone
370 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
372 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
371 end
373 end
372
374
373 def force_default_language?
375 def force_default_language?
374 Setting.force_default_language_for_loggedin?
376 Setting.force_default_language_for_loggedin?
375 end
377 end
376
378
377 def language
379 def language
378 if force_default_language?
380 if force_default_language?
379 Setting.default_language
381 Setting.default_language
380 else
382 else
381 super
383 super
382 end
384 end
383 end
385 end
384
386
385 def wants_comments_in_reverse_order?
387 def wants_comments_in_reverse_order?
386 self.pref[:comments_sorting] == 'desc'
388 self.pref[:comments_sorting] == 'desc'
387 end
389 end
388
390
389 # Return user's RSS key (a 40 chars long string), used to access feeds
391 # Return user's RSS key (a 40 chars long string), used to access feeds
390 def rss_key
392 def rss_key
391 if rss_token.nil?
393 if rss_token.nil?
392 create_rss_token(:action => 'feeds')
394 create_rss_token(:action => 'feeds')
393 end
395 end
394 rss_token.value
396 rss_token.value
395 end
397 end
396
398
397 # Return user's API key (a 40 chars long string), used to access the API
399 # Return user's API key (a 40 chars long string), used to access the API
398 def api_key
400 def api_key
399 if api_token.nil?
401 if api_token.nil?
400 create_api_token(:action => 'api')
402 create_api_token(:action => 'api')
401 end
403 end
402 api_token.value
404 api_token.value
403 end
405 end
404
406
405 # Generates a new session token and returns its value
407 # Generates a new session token and returns its value
406 def generate_session_token
408 def generate_session_token
407 token = Token.create!(:user_id => id, :action => 'session')
409 token = Token.create!(:user_id => id, :action => 'session')
408 token.value
410 token.value
409 end
411 end
410
412
411 # Returns true if token is a valid session token for the user whose id is user_id
413 # Returns true if token is a valid session token for the user whose id is user_id
412 def self.verify_session_token(user_id, token)
414 def self.verify_session_token(user_id, token)
413 return false if user_id.blank? || token.blank?
415 return false if user_id.blank? || token.blank?
414
416
415 scope = Token.where(:user_id => user_id, :value => token.to_s, :action => 'session')
417 scope = Token.where(:user_id => user_id, :value => token.to_s, :action => 'session')
416 if Setting.session_lifetime?
418 if Setting.session_lifetime?
417 scope = scope.where("created_on > ?", Setting.session_lifetime.to_i.minutes.ago)
419 scope = scope.where("created_on > ?", Setting.session_lifetime.to_i.minutes.ago)
418 end
420 end
419 if Setting.session_timeout?
421 if Setting.session_timeout?
420 scope = scope.where("updated_on > ?", Setting.session_timeout.to_i.minutes.ago)
422 scope = scope.where("updated_on > ?", Setting.session_timeout.to_i.minutes.ago)
421 end
423 end
422 scope.update_all(:updated_on => Time.now) == 1
424 scope.update_all(:updated_on => Time.now) == 1
423 end
425 end
424
426
425 # Return an array of project ids for which the user has explicitly turned mail notifications on
427 # Return an array of project ids for which the user has explicitly turned mail notifications on
426 def notified_projects_ids
428 def notified_projects_ids
427 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
429 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
428 end
430 end
429
431
430 def notified_project_ids=(ids)
432 def notified_project_ids=(ids)
431 @notified_projects_ids_changed = true
433 @notified_projects_ids_changed = true
432 @notified_projects_ids = ids.map(&:to_i).uniq.select {|n| n > 0}
434 @notified_projects_ids = ids.map(&:to_i).uniq.select {|n| n > 0}
433 end
435 end
434
436
435 # Updates per project notifications (after_save callback)
437 # Updates per project notifications (after_save callback)
436 def update_notified_project_ids
438 def update_notified_project_ids
437 if @notified_projects_ids_changed
439 if @notified_projects_ids_changed
438 ids = (mail_notification == 'selected' ? Array.wrap(notified_projects_ids).reject(&:blank?) : [])
440 ids = (mail_notification == 'selected' ? Array.wrap(notified_projects_ids).reject(&:blank?) : [])
439 members.update_all(:mail_notification => false)
441 members.update_all(:mail_notification => false)
440 members.where(:project_id => ids).update_all(:mail_notification => true) if ids.any?
442 members.where(:project_id => ids).update_all(:mail_notification => true) if ids.any?
441 end
443 end
442 end
444 end
443 private :update_notified_project_ids
445 private :update_notified_project_ids
444
446
445 def valid_notification_options
447 def valid_notification_options
446 self.class.valid_notification_options(self)
448 self.class.valid_notification_options(self)
447 end
449 end
448
450
449 # Only users that belong to more than 1 project can select projects for which they are notified
451 # Only users that belong to more than 1 project can select projects for which they are notified
450 def self.valid_notification_options(user=nil)
452 def self.valid_notification_options(user=nil)
451 # Note that @user.membership.size would fail since AR ignores
453 # Note that @user.membership.size would fail since AR ignores
452 # :include association option when doing a count
454 # :include association option when doing a count
453 if user.nil? || user.memberships.length < 1
455 if user.nil? || user.memberships.length < 1
454 MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
456 MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
455 else
457 else
456 MAIL_NOTIFICATION_OPTIONS
458 MAIL_NOTIFICATION_OPTIONS
457 end
459 end
458 end
460 end
459
461
460 # Find a user account by matching the exact login and then a case-insensitive
462 # Find a user account by matching the exact login and then a case-insensitive
461 # version. Exact matches will be given priority.
463 # version. Exact matches will be given priority.
462 def self.find_by_login(login)
464 def self.find_by_login(login)
463 login = Redmine::CodesetUtil.replace_invalid_utf8(login.to_s)
465 login = Redmine::CodesetUtil.replace_invalid_utf8(login.to_s)
464 if login.present?
466 if login.present?
465 # First look for an exact match
467 # First look for an exact match
466 user = where(:login => login).detect {|u| u.login == login}
468 user = where(:login => login).detect {|u| u.login == login}
467 unless user
469 unless user
468 # Fail over to case-insensitive if none was found
470 # Fail over to case-insensitive if none was found
469 user = where("LOWER(login) = ?", login.downcase).first
471 user = where("LOWER(login) = ?", login.downcase).first
470 end
472 end
471 user
473 user
472 end
474 end
473 end
475 end
474
476
475 def self.find_by_rss_key(key)
477 def self.find_by_rss_key(key)
476 Token.find_active_user('feeds', key)
478 Token.find_active_user('feeds', key)
477 end
479 end
478
480
479 def self.find_by_api_key(key)
481 def self.find_by_api_key(key)
480 Token.find_active_user('api', key)
482 Token.find_active_user('api', key)
481 end
483 end
482
484
483 # Makes find_by_mail case-insensitive
485 # Makes find_by_mail case-insensitive
484 def self.find_by_mail(mail)
486 def self.find_by_mail(mail)
485 having_mail(mail).first
487 having_mail(mail).first
486 end
488 end
487
489
488 # Returns true if the default admin account can no longer be used
490 # Returns true if the default admin account can no longer be used
489 def self.default_admin_account_changed?
491 def self.default_admin_account_changed?
490 !User.active.find_by_login("admin").try(:check_password?, "admin")
492 !User.active.find_by_login("admin").try(:check_password?, "admin")
491 end
493 end
492
494
493 def to_s
495 def to_s
494 name
496 name
495 end
497 end
496
498
497 CSS_CLASS_BY_STATUS = {
499 CSS_CLASS_BY_STATUS = {
498 STATUS_ANONYMOUS => 'anon',
500 STATUS_ANONYMOUS => 'anon',
499 STATUS_ACTIVE => 'active',
501 STATUS_ACTIVE => 'active',
500 STATUS_REGISTERED => 'registered',
502 STATUS_REGISTERED => 'registered',
501 STATUS_LOCKED => 'locked'
503 STATUS_LOCKED => 'locked'
502 }
504 }
503
505
504 def css_classes
506 def css_classes
505 "user #{CSS_CLASS_BY_STATUS[status]}"
507 "user #{CSS_CLASS_BY_STATUS[status]}"
506 end
508 end
507
509
508 # Returns the current day according to user's time zone
510 # Returns the current day according to user's time zone
509 def today
511 def today
510 if time_zone.nil?
512 if time_zone.nil?
511 Date.today
513 Date.today
512 else
514 else
513 Time.now.in_time_zone(time_zone).to_date
515 Time.now.in_time_zone(time_zone).to_date
514 end
516 end
515 end
517 end
516
518
517 # Returns the day of +time+ according to user's time zone
519 # Returns the day of +time+ according to user's time zone
518 def time_to_date(time)
520 def time_to_date(time)
519 if time_zone.nil?
521 if time_zone.nil?
520 time.to_date
522 time.to_date
521 else
523 else
522 time.in_time_zone(time_zone).to_date
524 time.in_time_zone(time_zone).to_date
523 end
525 end
524 end
526 end
525
527
526 def logged?
528 def logged?
527 true
529 true
528 end
530 end
529
531
530 def anonymous?
532 def anonymous?
531 !logged?
533 !logged?
532 end
534 end
533
535
534 # Returns user's membership for the given project
536 # Returns user's membership for the given project
535 # or nil if the user is not a member of project
537 # or nil if the user is not a member of project
536 def membership(project)
538 def membership(project)
537 project_id = project.is_a?(Project) ? project.id : project
539 project_id = project.is_a?(Project) ? project.id : project
538
540
539 @membership_by_project_id ||= Hash.new {|h, project_id|
541 @membership_by_project_id ||= Hash.new {|h, project_id|
540 h[project_id] = memberships.where(:project_id => project_id).first
542 h[project_id] = memberships.where(:project_id => project_id).first
541 }
543 }
542 @membership_by_project_id[project_id]
544 @membership_by_project_id[project_id]
543 end
545 end
544
546
545 # Returns the user's bult-in role
547 # Returns the user's bult-in role
546 def builtin_role
548 def builtin_role
547 @builtin_role ||= Role.non_member
549 @builtin_role ||= Role.non_member
548 end
550 end
549
551
550 # Return user's roles for project
552 # Return user's roles for project
551 def roles_for_project(project)
553 def roles_for_project(project)
552 # No role on archived projects
554 # No role on archived projects
553 return [] if project.nil? || project.archived?
555 return [] if project.nil? || project.archived?
554 if membership = membership(project)
556 if membership = membership(project)
555 membership.roles.to_a
557 membership.roles.to_a
556 elsif project.is_public?
558 elsif project.is_public?
557 project.override_roles(builtin_role)
559 project.override_roles(builtin_role)
558 else
560 else
559 []
561 []
560 end
562 end
561 end
563 end
562
564
563 # Returns a hash of user's projects grouped by roles
565 # Returns a hash of user's projects grouped by roles
564 def projects_by_role
566 def projects_by_role
565 return @projects_by_role if @projects_by_role
567 return @projects_by_role if @projects_by_role
566
568
567 hash = Hash.new([])
569 hash = Hash.new([])
568
570
569 group_class = anonymous? ? GroupAnonymous : GroupNonMember
571 group_class = anonymous? ? GroupAnonymous : GroupNonMember
570 members = Member.joins(:project, :principal).
572 members = Member.joins(:project, :principal).
571 where("#{Project.table_name}.status <> 9").
573 where("#{Project.table_name}.status <> 9").
572 where("#{Member.table_name}.user_id = ? OR (#{Project.table_name}.is_public = ? AND #{Principal.table_name}.type = ?)", self.id, true, group_class.name).
574 where("#{Member.table_name}.user_id = ? OR (#{Project.table_name}.is_public = ? AND #{Principal.table_name}.type = ?)", self.id, true, group_class.name).
573 preload(:project, :roles).
575 preload(:project, :roles).
574 to_a
576 to_a
575
577
576 members.reject! {|member| member.user_id != id && project_ids.include?(member.project_id)}
578 members.reject! {|member| member.user_id != id && project_ids.include?(member.project_id)}
577 members.each do |member|
579 members.each do |member|
578 if member.project
580 if member.project
579 member.roles.each do |role|
581 member.roles.each do |role|
580 hash[role] = [] unless hash.key?(role)
582 hash[role] = [] unless hash.key?(role)
581 hash[role] << member.project
583 hash[role] << member.project
582 end
584 end
583 end
585 end
584 end
586 end
585
587
586 hash.each do |role, projects|
588 hash.each do |role, projects|
587 projects.uniq!
589 projects.uniq!
588 end
590 end
589
591
590 @projects_by_role = hash
592 @projects_by_role = hash
591 end
593 end
592
594
593 # Returns the ids of visible projects
595 # Returns the ids of visible projects
594 def visible_project_ids
596 def visible_project_ids
595 @visible_project_ids ||= Project.visible(self).pluck(:id)
597 @visible_project_ids ||= Project.visible(self).pluck(:id)
596 end
598 end
597
599
598 # Returns the roles that the user is allowed to manage for the given project
600 # Returns the roles that the user is allowed to manage for the given project
599 def managed_roles(project)
601 def managed_roles(project)
600 if admin?
602 if admin?
601 @managed_roles ||= Role.givable.to_a
603 @managed_roles ||= Role.givable.to_a
602 else
604 else
603 membership(project).try(:managed_roles) || []
605 membership(project).try(:managed_roles) || []
604 end
606 end
605 end
607 end
606
608
607 # Returns true if user is arg or belongs to arg
609 # Returns true if user is arg or belongs to arg
608 def is_or_belongs_to?(arg)
610 def is_or_belongs_to?(arg)
609 if arg.is_a?(User)
611 if arg.is_a?(User)
610 self == arg
612 self == arg
611 elsif arg.is_a?(Group)
613 elsif arg.is_a?(Group)
612 arg.users.include?(self)
614 arg.users.include?(self)
613 else
615 else
614 false
616 false
615 end
617 end
616 end
618 end
617
619
618 # Return true if the user is allowed to do the specified action on a specific context
620 # Return true if the user is allowed to do the specified action on a specific context
619 # Action can be:
621 # Action can be:
620 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
622 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
621 # * a permission Symbol (eg. :edit_project)
623 # * a permission Symbol (eg. :edit_project)
622 # Context can be:
624 # Context can be:
623 # * a project : returns true if user is allowed to do the specified action on this project
625 # * a project : returns true if user is allowed to do the specified action on this project
624 # * an array of projects : returns true if user is allowed on every project
626 # * an array of projects : returns true if user is allowed on every project
625 # * nil with options[:global] set : check if user has at least one role allowed for this action,
627 # * nil with options[:global] set : check if user has at least one role allowed for this action,
626 # or falls back to Non Member / Anonymous permissions depending if the user is logged
628 # or falls back to Non Member / Anonymous permissions depending if the user is logged
627 def allowed_to?(action, context, options={}, &block)
629 def allowed_to?(action, context, options={}, &block)
628 if context && context.is_a?(Project)
630 if context && context.is_a?(Project)
629 return false unless context.allows_to?(action)
631 return false unless context.allows_to?(action)
630 # Admin users are authorized for anything else
632 # Admin users are authorized for anything else
631 return true if admin?
633 return true if admin?
632
634
633 roles = roles_for_project(context)
635 roles = roles_for_project(context)
634 return false unless roles
636 return false unless roles
635 roles.any? {|role|
637 roles.any? {|role|
636 (context.is_public? || role.member?) &&
638 (context.is_public? || role.member?) &&
637 role.allowed_to?(action) &&
639 role.allowed_to?(action) &&
638 (block_given? ? yield(role, self) : true)
640 (block_given? ? yield(role, self) : true)
639 }
641 }
640 elsif context && context.is_a?(Array)
642 elsif context && context.is_a?(Array)
641 if context.empty?
643 if context.empty?
642 false
644 false
643 else
645 else
644 # Authorize if user is authorized on every element of the array
646 # Authorize if user is authorized on every element of the array
645 context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&)
647 context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&)
646 end
648 end
647 elsif context
649 elsif context
648 raise ArgumentError.new("#allowed_to? context argument must be a Project, an Array of projects or nil")
650 raise ArgumentError.new("#allowed_to? context argument must be a Project, an Array of projects or nil")
649 elsif options[:global]
651 elsif options[:global]
650 # Admin users are always authorized
652 # Admin users are always authorized
651 return true if admin?
653 return true if admin?
652
654
653 # authorize if user has at least one role that has this permission
655 # authorize if user has at least one role that has this permission
654 roles = memberships.collect {|m| m.roles}.flatten.uniq
656 roles = memberships.collect {|m| m.roles}.flatten.uniq
655 roles << (self.logged? ? Role.non_member : Role.anonymous)
657 roles << (self.logged? ? Role.non_member : Role.anonymous)
656 roles.any? {|role|
658 roles.any? {|role|
657 role.allowed_to?(action) &&
659 role.allowed_to?(action) &&
658 (block_given? ? yield(role, self) : true)
660 (block_given? ? yield(role, self) : true)
659 }
661 }
660 else
662 else
661 false
663 false
662 end
664 end
663 end
665 end
664
666
665 # Is the user allowed to do the specified action on any project?
667 # Is the user allowed to do the specified action on any project?
666 # See allowed_to? for the actions and valid options.
668 # See allowed_to? for the actions and valid options.
667 #
669 #
668 # NB: this method is not used anywhere in the core codebase as of
670 # NB: this method is not used anywhere in the core codebase as of
669 # 2.5.2, but it's used by many plugins so if we ever want to remove
671 # 2.5.2, but it's used by many plugins so if we ever want to remove
670 # it it has to be carefully deprecated for a version or two.
672 # it it has to be carefully deprecated for a version or two.
671 def allowed_to_globally?(action, options={}, &block)
673 def allowed_to_globally?(action, options={}, &block)
672 allowed_to?(action, nil, options.reverse_merge(:global => true), &block)
674 allowed_to?(action, nil, options.reverse_merge(:global => true), &block)
673 end
675 end
674
676
675 def allowed_to_view_all_time_entries?(context)
677 def allowed_to_view_all_time_entries?(context)
676 allowed_to?(:view_time_entries, context) do |role, user|
678 allowed_to?(:view_time_entries, context) do |role, user|
677 role.time_entries_visibility == 'all'
679 role.time_entries_visibility == 'all'
678 end
680 end
679 end
681 end
680
682
681 # Returns true if the user is allowed to delete the user's own account
683 # Returns true if the user is allowed to delete the user's own account
682 def own_account_deletable?
684 def own_account_deletable?
683 Setting.unsubscribe? &&
685 Setting.unsubscribe? &&
684 (!admin? || User.active.where("admin = ? AND id <> ?", true, id).exists?)
686 (!admin? || User.active.where("admin = ? AND id <> ?", true, id).exists?)
685 end
687 end
686
688
687 safe_attributes 'firstname',
689 safe_attributes 'firstname',
688 'lastname',
690 'lastname',
689 'mail',
691 'mail',
690 'mail_notification',
692 'mail_notification',
691 'notified_project_ids',
693 'notified_project_ids',
692 'language',
694 'language',
693 'custom_field_values',
695 'custom_field_values',
694 'custom_fields',
696 'custom_fields',
695 'identity_url'
697 'identity_url'
696
698
697 safe_attributes 'status',
699 safe_attributes 'status',
698 'auth_source_id',
700 'auth_source_id',
699 'generate_password',
701 'generate_password',
700 'must_change_passwd',
702 'must_change_passwd',
701 :if => lambda {|user, current_user| current_user.admin?}
703 :if => lambda {|user, current_user| current_user.admin?}
702
704
703 safe_attributes 'group_ids',
705 safe_attributes 'group_ids',
704 :if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
706 :if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
705
707
706 # Utility method to help check if a user should be notified about an
708 # Utility method to help check if a user should be notified about an
707 # event.
709 # event.
708 #
710 #
709 # TODO: only supports Issue events currently
711 # TODO: only supports Issue events currently
710 def notify_about?(object)
712 def notify_about?(object)
711 if mail_notification == 'all'
713 if mail_notification == 'all'
712 true
714 true
713 elsif mail_notification.blank? || mail_notification == 'none'
715 elsif mail_notification.blank? || mail_notification == 'none'
714 false
716 false
715 else
717 else
716 case object
718 case object
717 when Issue
719 when Issue
718 case mail_notification
720 case mail_notification
719 when 'selected', 'only_my_events'
721 when 'selected', 'only_my_events'
720 # user receives notifications for created/assigned issues on unselected projects
722 # user receives notifications for created/assigned issues on unselected projects
721 object.author == self || is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
723 object.author == self || is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
722 when 'only_assigned'
724 when 'only_assigned'
723 is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
725 is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
724 when 'only_owner'
726 when 'only_owner'
725 object.author == self
727 object.author == self
726 end
728 end
727 when News
729 when News
728 # always send to project members except when mail_notification is set to 'none'
730 # always send to project members except when mail_notification is set to 'none'
729 true
731 true
730 end
732 end
731 end
733 end
732 end
734 end
733
735
734 def self.current=(user)
736 def self.current=(user)
735 RequestStore.store[:current_user] = user
737 RequestStore.store[:current_user] = user
736 end
738 end
737
739
738 def self.current
740 def self.current
739 RequestStore.store[:current_user] ||= User.anonymous
741 RequestStore.store[:current_user] ||= User.anonymous
740 end
742 end
741
743
742 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
744 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
743 # one anonymous user per database.
745 # one anonymous user per database.
744 def self.anonymous
746 def self.anonymous
745 anonymous_user = AnonymousUser.first
747 anonymous_user = AnonymousUser.first
746 if anonymous_user.nil?
748 if anonymous_user.nil?
747 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :login => '', :status => 0)
749 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :login => '', :status => 0)
748 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
750 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
749 end
751 end
750 anonymous_user
752 anonymous_user
751 end
753 end
752
754
753 # Salts all existing unsalted passwords
755 # Salts all existing unsalted passwords
754 # It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password))
756 # It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password))
755 # This method is used in the SaltPasswords migration and is to be kept as is
757 # This method is used in the SaltPasswords migration and is to be kept as is
756 def self.salt_unsalted_passwords!
758 def self.salt_unsalted_passwords!
757 transaction do
759 transaction do
758 User.where("salt IS NULL OR salt = ''").find_each do |user|
760 User.where("salt IS NULL OR salt = ''").find_each do |user|
759 next if user.hashed_password.blank?
761 next if user.hashed_password.blank?
760 salt = User.generate_salt
762 salt = User.generate_salt
761 hashed_password = User.hash_password("#{salt}#{user.hashed_password}")
763 hashed_password = User.hash_password("#{salt}#{user.hashed_password}")
762 User.where(:id => user.id).update_all(:salt => salt, :hashed_password => hashed_password)
764 User.where(:id => user.id).update_all(:salt => salt, :hashed_password => hashed_password)
763 end
765 end
764 end
766 end
765 end
767 end
766
768
767 protected
769 protected
768
770
769 def validate_password_length
771 def validate_password_length
770 return if password.blank? && generate_password?
772 return if password.blank? && generate_password?
771 # Password length validation based on setting
773 # Password length validation based on setting
772 if !password.nil? && password.size < Setting.password_min_length.to_i
774 if !password.nil? && password.size < Setting.password_min_length.to_i
773 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
775 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
774 end
776 end
775 end
777 end
776
778
777 def instantiate_email_address
779 def instantiate_email_address
778 email_address || build_email_address
780 email_address || build_email_address
779 end
781 end
780
782
781 private
783 private
782
784
783 def generate_password_if_needed
785 def generate_password_if_needed
784 if generate_password? && auth_source.nil?
786 if generate_password? && auth_source.nil?
785 length = [Setting.password_min_length.to_i + 2, 10].max
787 length = [Setting.password_min_length.to_i + 2, 10].max
786 random_password(length)
788 random_password(length)
787 end
789 end
788 end
790 end
789
791
790 # Delete all outstanding password reset tokens on password change.
792 # Delete all outstanding password reset tokens on password change.
791 # Delete the autologin tokens on password change to prohibit session leakage.
793 # Delete the autologin tokens on password change to prohibit session leakage.
792 # This helps to keep the account secure in case the associated email account
794 # This helps to keep the account secure in case the associated email account
793 # was compromised.
795 # was compromised.
794 def destroy_tokens
796 def destroy_tokens
795 if hashed_password_changed? || (status_changed? && !active?)
797 if hashed_password_changed? || (status_changed? && !active?)
796 tokens = ['recovery', 'autologin', 'session']
798 tokens = ['recovery', 'autologin', 'session']
797 Token.where(:user_id => id, :action => tokens).delete_all
799 Token.where(:user_id => id, :action => tokens).delete_all
798 end
800 end
799 end
801 end
800
802
801 # Removes references that are not handled by associations
803 # Removes references that are not handled by associations
802 # Things that are not deleted are reassociated with the anonymous user
804 # Things that are not deleted are reassociated with the anonymous user
803 def remove_references_before_destroy
805 def remove_references_before_destroy
804 return if self.id.nil?
806 return if self.id.nil?
805
807
806 substitute = User.anonymous
808 substitute = User.anonymous
807 Attachment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
809 Attachment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
808 Comment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
810 Comment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
809 Issue.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
811 Issue.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
810 Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL')
812 Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL')
811 Journal.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
813 Journal.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
812 JournalDetail.
814 JournalDetail.
813 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]).
815 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]).
814 update_all(['old_value = ?', substitute.id.to_s])
816 update_all(['old_value = ?', substitute.id.to_s])
815 JournalDetail.
817 JournalDetail.
816 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]).
818 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]).
817 update_all(['value = ?', substitute.id.to_s])
819 update_all(['value = ?', substitute.id.to_s])
818 Message.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
820 Message.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
819 News.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
821 News.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
820 # Remove private queries and keep public ones
822 # Remove private queries and keep public ones
821 ::Query.delete_all ['user_id = ? AND visibility = ?', id, ::Query::VISIBILITY_PRIVATE]
823 ::Query.delete_all ['user_id = ? AND visibility = ?', id, ::Query::VISIBILITY_PRIVATE]
822 ::Query.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
824 ::Query.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
823 TimeEntry.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
825 TimeEntry.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
824 Token.delete_all ['user_id = ?', id]
826 Token.delete_all ['user_id = ?', id]
825 Watcher.delete_all ['user_id = ?', id]
827 Watcher.delete_all ['user_id = ?', id]
826 WikiContent.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
828 WikiContent.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
827 WikiContent::Version.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
829 WikiContent::Version.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
828 end
830 end
829
831
830 # Return password digest
832 # Return password digest
831 def self.hash_password(clear_password)
833 def self.hash_password(clear_password)
832 Digest::SHA1.hexdigest(clear_password || "")
834 Digest::SHA1.hexdigest(clear_password || "")
833 end
835 end
834
836
835 # Returns a 128bits random salt as a hex string (32 chars long)
837 # Returns a 128bits random salt as a hex string (32 chars long)
836 def self.generate_salt
838 def self.generate_salt
837 Redmine::Utils.random_hex(16)
839 Redmine::Utils.random_hex(16)
838 end
840 end
839
841
840 # Send a security notification to all admins if the user has gained/lost admin privileges
842 # Send a security notification to all admins if the user has gained/lost admin privileges
841 def deliver_security_notification
843 def deliver_security_notification
842 options = {
844 options = {
843 field: :field_admin,
845 field: :field_admin,
844 value: login,
846 value: login,
845 title: :label_user_plural,
847 title: :label_user_plural,
846 url: {controller: 'users', action: 'index'}
848 url: {controller: 'users', action: 'index'}
847 }
849 }
848
850
849 deliver = false
851 deliver = false
850 if (admin? && id_changed? && active?) || # newly created admin
852 if (admin? && id_changed? && active?) || # newly created admin
851 (admin? && admin_changed? && active?) || # regular user became admin
853 (admin? && admin_changed? && active?) || # regular user became admin
852 (admin? && status_changed? && active?) # locked admin became active again
854 (admin? && status_changed? && active?) # locked admin became active again
853
855
854 deliver = true
856 deliver = true
855 options[:message] = :mail_body_security_notification_add
857 options[:message] = :mail_body_security_notification_add
856
858
857 elsif (admin? && destroyed? && active?) || # active admin user was deleted
859 elsif (admin? && destroyed? && active?) || # active admin user was deleted
858 (!admin? && admin_changed? && active?) || # admin is no longer admin
860 (!admin? && admin_changed? && active?) || # admin is no longer admin
859 (admin? && status_changed? && !active?) # admin was locked
861 (admin? && status_changed? && !active?) # admin was locked
860
862
861 deliver = true
863 deliver = true
862 options[:message] = :mail_body_security_notification_remove
864 options[:message] = :mail_body_security_notification_remove
863 end
865 end
864
866
865 if deliver
867 if deliver
866 users = User.active.where(admin: true).to_a
868 users = User.active.where(admin: true).to_a
867 Mailer.security_notification(users, options).deliver
869 Mailer.security_notification(users, options).deliver
868 end
870 end
869 end
871 end
870 end
872 end
871
873
872 class AnonymousUser < User
874 class AnonymousUser < User
873 validate :validate_anonymous_uniqueness, :on => :create
875 validate :validate_anonymous_uniqueness, :on => :create
874
876
877 self.valid_statuses = [STATUS_ANONYMOUS]
878
875 def validate_anonymous_uniqueness
879 def validate_anonymous_uniqueness
876 # There should be only one AnonymousUser in the database
880 # There should be only one AnonymousUser in the database
877 errors.add :base, 'An anonymous user already exists.' if AnonymousUser.exists?
881 errors.add :base, 'An anonymous user already exists.' if AnonymousUser.exists?
878 end
882 end
879
883
880 def available_custom_fields
884 def available_custom_fields
881 []
885 []
882 end
886 end
883
887
884 # Overrides a few properties
888 # Overrides a few properties
885 def logged?; false end
889 def logged?; false end
886 def admin; false end
890 def admin; false end
887 def name(*args); I18n.t(:label_user_anonymous) end
891 def name(*args); I18n.t(:label_user_anonymous) end
888 def mail=(*args); nil end
892 def mail=(*args); nil end
889 def mail; nil end
893 def mail; nil end
890 def time_zone; nil end
894 def time_zone; nil end
891 def rss_key; nil end
895 def rss_key; nil end
892
896
893 def pref
897 def pref
894 UserPreference.new(:user => self)
898 UserPreference.new(:user => self)
895 end
899 end
896
900
897 # Returns the user's bult-in role
901 # Returns the user's bult-in role
898 def builtin_role
902 def builtin_role
899 @builtin_role ||= Role.anonymous
903 @builtin_role ||= Role.anonymous
900 end
904 end
901
905
902 def membership(*args)
906 def membership(*args)
903 nil
907 nil
904 end
908 end
905
909
906 def member_of?(*args)
910 def member_of?(*args)
907 false
911 false
908 end
912 end
909
913
910 # Anonymous user can not be destroyed
914 # Anonymous user can not be destroyed
911 def destroy
915 def destroy
912 false
916 false
913 end
917 end
914
918
915 protected
919 protected
916
920
917 def instantiate_email_address
921 def instantiate_email_address
918 end
922 end
919 end
923 end
@@ -1,1229 +1,1237
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 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 File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class UserTest < ActiveSupport::TestCase
20 class UserTest < ActiveSupport::TestCase
21 fixtures :users, :email_addresses, :members, :projects, :roles, :member_roles, :auth_sources,
21 fixtures :users, :email_addresses, :members, :projects, :roles, :member_roles, :auth_sources,
22 :trackers, :issue_statuses,
22 :trackers, :issue_statuses,
23 :projects_trackers,
23 :projects_trackers,
24 :watchers,
24 :watchers,
25 :issue_categories, :enumerations, :issues,
25 :issue_categories, :enumerations, :issues,
26 :journals, :journal_details,
26 :journals, :journal_details,
27 :groups_users,
27 :groups_users,
28 :enabled_modules,
28 :enabled_modules,
29 :tokens
29 :tokens
30
30
31 include Redmine::I18n
31 include Redmine::I18n
32
32
33 def setup
33 def setup
34 @admin = User.find(1)
34 @admin = User.find(1)
35 @jsmith = User.find(2)
35 @jsmith = User.find(2)
36 @dlopper = User.find(3)
36 @dlopper = User.find(3)
37 end
37 end
38
38
39 def test_sorted_scope_should_sort_user_by_display_name
39 def test_sorted_scope_should_sort_user_by_display_name
40 # Use .active to ignore anonymous with localized display name
40 # Use .active to ignore anonymous with localized display name
41 assert_equal User.active.map(&:name).map(&:downcase).sort,
41 assert_equal User.active.map(&:name).map(&:downcase).sort,
42 User.active.sorted.map(&:name).map(&:downcase)
42 User.active.sorted.map(&:name).map(&:downcase)
43 end
43 end
44
44
45 def test_generate
45 def test_generate
46 User.generate!(:firstname => 'Testing connection')
46 User.generate!(:firstname => 'Testing connection')
47 User.generate!(:firstname => 'Testing connection')
47 User.generate!(:firstname => 'Testing connection')
48 assert_equal 2, User.where(:firstname => 'Testing connection').count
48 assert_equal 2, User.where(:firstname => 'Testing connection').count
49 end
49 end
50
50
51 def test_truth
51 def test_truth
52 assert_kind_of User, @jsmith
52 assert_kind_of User, @jsmith
53 end
53 end
54
54
55 def test_should_validate_status
56 user = User.new
57 user.status = 0
58
59 assert !user.save
60 assert_include I18n.translate('activerecord.errors.messages.invalid'), user.errors[:status]
61 end
62
55 def test_mail_should_be_stripped
63 def test_mail_should_be_stripped
56 u = User.new
64 u = User.new
57 u.mail = " foo@bar.com "
65 u.mail = " foo@bar.com "
58 assert_equal "foo@bar.com", u.mail
66 assert_equal "foo@bar.com", u.mail
59 end
67 end
60
68
61 def test_should_create_email_address
69 def test_should_create_email_address
62 u = User.new(:firstname => "new", :lastname => "user")
70 u = User.new(:firstname => "new", :lastname => "user")
63 u.login = "create_email_address"
71 u.login = "create_email_address"
64 u.mail = "defaultemail@somenet.foo"
72 u.mail = "defaultemail@somenet.foo"
65 assert u.save
73 assert u.save
66 u.reload
74 u.reload
67 assert u.email_address
75 assert u.email_address
68 assert_equal "defaultemail@somenet.foo", u.email_address.address
76 assert_equal "defaultemail@somenet.foo", u.email_address.address
69 assert_equal true, u.email_address.is_default
77 assert_equal true, u.email_address.is_default
70 assert_equal true, u.email_address.notify
78 assert_equal true, u.email_address.notify
71 end
79 end
72
80
73 def test_should_not_create_user_without_mail
81 def test_should_not_create_user_without_mail
74 set_language_if_valid 'en'
82 set_language_if_valid 'en'
75 u = User.new(:firstname => "new", :lastname => "user")
83 u = User.new(:firstname => "new", :lastname => "user")
76 u.login = "user_without_mail"
84 u.login = "user_without_mail"
77 assert !u.save
85 assert !u.save
78 assert_equal ["Email #{I18n.translate('activerecord.errors.messages.blank')}"], u.errors.full_messages
86 assert_equal ["Email #{I18n.translate('activerecord.errors.messages.blank')}"], u.errors.full_messages
79 end
87 end
80
88
81 def test_should_not_create_user_with_blank_mail
89 def test_should_not_create_user_with_blank_mail
82 set_language_if_valid 'en'
90 set_language_if_valid 'en'
83 u = User.new(:firstname => "new", :lastname => "user")
91 u = User.new(:firstname => "new", :lastname => "user")
84 u.login = "user_with_blank_mail"
92 u.login = "user_with_blank_mail"
85 u.mail = ''
93 u.mail = ''
86 assert !u.save
94 assert !u.save
87 assert_equal ["Email #{I18n.translate('activerecord.errors.messages.blank')}"], u.errors.full_messages
95 assert_equal ["Email #{I18n.translate('activerecord.errors.messages.blank')}"], u.errors.full_messages
88 end
96 end
89
97
90 def test_should_not_update_user_with_blank_mail
98 def test_should_not_update_user_with_blank_mail
91 set_language_if_valid 'en'
99 set_language_if_valid 'en'
92 u = User.find(2)
100 u = User.find(2)
93 u.mail = ''
101 u.mail = ''
94 assert !u.save
102 assert !u.save
95 assert_equal ["Email #{I18n.translate('activerecord.errors.messages.blank')}"], u.errors.full_messages
103 assert_equal ["Email #{I18n.translate('activerecord.errors.messages.blank')}"], u.errors.full_messages
96 end
104 end
97
105
98 def test_login_length_validation
106 def test_login_length_validation
99 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
107 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
100 user.login = "x" * (User::LOGIN_LENGTH_LIMIT+1)
108 user.login = "x" * (User::LOGIN_LENGTH_LIMIT+1)
101 assert !user.valid?
109 assert !user.valid?
102
110
103 user.login = "x" * (User::LOGIN_LENGTH_LIMIT)
111 user.login = "x" * (User::LOGIN_LENGTH_LIMIT)
104 assert user.valid?
112 assert user.valid?
105 assert user.save
113 assert user.save
106 end
114 end
107
115
108 def test_generate_password_should_respect_minimum_password_length
116 def test_generate_password_should_respect_minimum_password_length
109 with_settings :password_min_length => 15 do
117 with_settings :password_min_length => 15 do
110 user = User.generate!(:generate_password => true)
118 user = User.generate!(:generate_password => true)
111 assert user.password.length >= 15
119 assert user.password.length >= 15
112 end
120 end
113 end
121 end
114
122
115 def test_generate_password_should_not_generate_password_with_less_than_10_characters
123 def test_generate_password_should_not_generate_password_with_less_than_10_characters
116 with_settings :password_min_length => 4 do
124 with_settings :password_min_length => 4 do
117 user = User.generate!(:generate_password => true)
125 user = User.generate!(:generate_password => true)
118 assert user.password.length >= 10
126 assert user.password.length >= 10
119 end
127 end
120 end
128 end
121
129
122 def test_generate_password_on_create_should_set_password
130 def test_generate_password_on_create_should_set_password
123 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
131 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
124 user.login = "newuser"
132 user.login = "newuser"
125 user.generate_password = true
133 user.generate_password = true
126 assert user.save
134 assert user.save
127
135
128 password = user.password
136 password = user.password
129 assert user.check_password?(password)
137 assert user.check_password?(password)
130 end
138 end
131
139
132 def test_generate_password_on_update_should_update_password
140 def test_generate_password_on_update_should_update_password
133 user = User.find(2)
141 user = User.find(2)
134 hash = user.hashed_password
142 hash = user.hashed_password
135 user.generate_password = true
143 user.generate_password = true
136 assert user.save
144 assert user.save
137
145
138 password = user.password
146 password = user.password
139 assert user.check_password?(password)
147 assert user.check_password?(password)
140 assert_not_equal hash, user.reload.hashed_password
148 assert_not_equal hash, user.reload.hashed_password
141 end
149 end
142
150
143 def test_create
151 def test_create
144 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
152 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
145
153
146 user.login = "jsmith"
154 user.login = "jsmith"
147 user.password, user.password_confirmation = "password", "password"
155 user.password, user.password_confirmation = "password", "password"
148 # login uniqueness
156 # login uniqueness
149 assert !user.save
157 assert !user.save
150 assert_equal 1, user.errors.count
158 assert_equal 1, user.errors.count
151
159
152 user.login = "newuser"
160 user.login = "newuser"
153 user.password, user.password_confirmation = "password", "pass"
161 user.password, user.password_confirmation = "password", "pass"
154 # password confirmation
162 # password confirmation
155 assert !user.save
163 assert !user.save
156 assert_equal 1, user.errors.count
164 assert_equal 1, user.errors.count
157
165
158 user.password, user.password_confirmation = "password", "password"
166 user.password, user.password_confirmation = "password", "password"
159 assert user.save
167 assert user.save
160 end
168 end
161
169
162 def test_user_before_create_should_set_the_mail_notification_to_the_default_setting
170 def test_user_before_create_should_set_the_mail_notification_to_the_default_setting
163 @user1 = User.generate!
171 @user1 = User.generate!
164 assert_equal 'only_my_events', @user1.mail_notification
172 assert_equal 'only_my_events', @user1.mail_notification
165 with_settings :default_notification_option => 'all' do
173 with_settings :default_notification_option => 'all' do
166 @user2 = User.generate!
174 @user2 = User.generate!
167 assert_equal 'all', @user2.mail_notification
175 assert_equal 'all', @user2.mail_notification
168 end
176 end
169 end
177 end
170
178
171 def test_user_login_should_be_case_insensitive
179 def test_user_login_should_be_case_insensitive
172 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
180 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
173 u.login = 'newuser'
181 u.login = 'newuser'
174 u.password, u.password_confirmation = "password", "password"
182 u.password, u.password_confirmation = "password", "password"
175 assert u.save
183 assert u.save
176 u = User.new(:firstname => "Similar", :lastname => "User",
184 u = User.new(:firstname => "Similar", :lastname => "User",
177 :mail => "similaruser@somenet.foo")
185 :mail => "similaruser@somenet.foo")
178 u.login = 'NewUser'
186 u.login = 'NewUser'
179 u.password, u.password_confirmation = "password", "password"
187 u.password, u.password_confirmation = "password", "password"
180 assert !u.save
188 assert !u.save
181 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:login]
189 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:login]
182 end
190 end
183
191
184 def test_mail_uniqueness_should_not_be_case_sensitive
192 def test_mail_uniqueness_should_not_be_case_sensitive
185 set_language_if_valid 'en'
193 set_language_if_valid 'en'
186 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
194 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
187 u.login = 'newuser1'
195 u.login = 'newuser1'
188 u.password, u.password_confirmation = "password", "password"
196 u.password, u.password_confirmation = "password", "password"
189 assert u.save
197 assert u.save
190
198
191 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
199 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
192 u.login = 'newuser2'
200 u.login = 'newuser2'
193 u.password, u.password_confirmation = "password", "password"
201 u.password, u.password_confirmation = "password", "password"
194 assert !u.save
202 assert !u.save
195 assert_include "Email #{I18n.translate('activerecord.errors.messages.taken')}", u.errors.full_messages
203 assert_include "Email #{I18n.translate('activerecord.errors.messages.taken')}", u.errors.full_messages
196 end
204 end
197
205
198 def test_update
206 def test_update
199 assert_equal "admin", @admin.login
207 assert_equal "admin", @admin.login
200 @admin.login = "john"
208 @admin.login = "john"
201 assert @admin.save, @admin.errors.full_messages.join("; ")
209 assert @admin.save, @admin.errors.full_messages.join("; ")
202 @admin.reload
210 @admin.reload
203 assert_equal "john", @admin.login
211 assert_equal "john", @admin.login
204 end
212 end
205
213
206 def test_update_should_not_fail_for_legacy_user_with_different_case_logins
214 def test_update_should_not_fail_for_legacy_user_with_different_case_logins
207 u1 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser1@somenet.foo")
215 u1 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser1@somenet.foo")
208 u1.login = 'newuser1'
216 u1.login = 'newuser1'
209 assert u1.save
217 assert u1.save
210
218
211 u2 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser2@somenet.foo")
219 u2 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser2@somenet.foo")
212 u2.login = 'newuser1'
220 u2.login = 'newuser1'
213 assert u2.save(:validate => false)
221 assert u2.save(:validate => false)
214
222
215 user = User.find(u2.id)
223 user = User.find(u2.id)
216 user.firstname = "firstname"
224 user.firstname = "firstname"
217 assert user.save, "Save failed"
225 assert user.save, "Save failed"
218 end
226 end
219
227
220 def test_destroy_should_delete_members_and_roles
228 def test_destroy_should_delete_members_and_roles
221 members = Member.where(:user_id => 2)
229 members = Member.where(:user_id => 2)
222 ms = members.count
230 ms = members.count
223 rs = members.collect(&:roles).flatten.size
231 rs = members.collect(&:roles).flatten.size
224 assert ms > 0
232 assert ms > 0
225 assert rs > 0
233 assert rs > 0
226 assert_difference 'Member.count', - ms do
234 assert_difference 'Member.count', - ms do
227 assert_difference 'MemberRole.count', - rs do
235 assert_difference 'MemberRole.count', - rs do
228 User.find(2).destroy
236 User.find(2).destroy
229 end
237 end
230 end
238 end
231 assert_nil User.find_by_id(2)
239 assert_nil User.find_by_id(2)
232 assert_equal 0, Member.where(:user_id => 2).count
240 assert_equal 0, Member.where(:user_id => 2).count
233 end
241 end
234
242
235 def test_destroy_should_update_attachments
243 def test_destroy_should_update_attachments
236 attachment = Attachment.create!(:container => Project.find(1),
244 attachment = Attachment.create!(:container => Project.find(1),
237 :file => uploaded_test_file("testfile.txt", "text/plain"),
245 :file => uploaded_test_file("testfile.txt", "text/plain"),
238 :author_id => 2)
246 :author_id => 2)
239
247
240 User.find(2).destroy
248 User.find(2).destroy
241 assert_nil User.find_by_id(2)
249 assert_nil User.find_by_id(2)
242 assert_equal User.anonymous, attachment.reload.author
250 assert_equal User.anonymous, attachment.reload.author
243 end
251 end
244
252
245 def test_destroy_should_update_comments
253 def test_destroy_should_update_comments
246 comment = Comment.create!(
254 comment = Comment.create!(
247 :commented => News.create!(:project_id => 1,
255 :commented => News.create!(:project_id => 1,
248 :author_id => 1, :title => 'foo', :description => 'foo'),
256 :author_id => 1, :title => 'foo', :description => 'foo'),
249 :author => User.find(2),
257 :author => User.find(2),
250 :comments => 'foo'
258 :comments => 'foo'
251 )
259 )
252
260
253 User.find(2).destroy
261 User.find(2).destroy
254 assert_nil User.find_by_id(2)
262 assert_nil User.find_by_id(2)
255 assert_equal User.anonymous, comment.reload.author
263 assert_equal User.anonymous, comment.reload.author
256 end
264 end
257
265
258 def test_destroy_should_update_issues
266 def test_destroy_should_update_issues
259 issue = Issue.create!(:project_id => 1, :author_id => 2,
267 issue = Issue.create!(:project_id => 1, :author_id => 2,
260 :tracker_id => 1, :subject => 'foo')
268 :tracker_id => 1, :subject => 'foo')
261
269
262 User.find(2).destroy
270 User.find(2).destroy
263 assert_nil User.find_by_id(2)
271 assert_nil User.find_by_id(2)
264 assert_equal User.anonymous, issue.reload.author
272 assert_equal User.anonymous, issue.reload.author
265 end
273 end
266
274
267 def test_destroy_should_unassign_issues
275 def test_destroy_should_unassign_issues
268 issue = Issue.create!(:project_id => 1, :author_id => 1,
276 issue = Issue.create!(:project_id => 1, :author_id => 1,
269 :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
277 :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
270
278
271 User.find(2).destroy
279 User.find(2).destroy
272 assert_nil User.find_by_id(2)
280 assert_nil User.find_by_id(2)
273 assert_nil issue.reload.assigned_to
281 assert_nil issue.reload.assigned_to
274 end
282 end
275
283
276 def test_destroy_should_update_journals
284 def test_destroy_should_update_journals
277 issue = Issue.create!(:project_id => 1, :author_id => 2,
285 issue = Issue.create!(:project_id => 1, :author_id => 2,
278 :tracker_id => 1, :subject => 'foo')
286 :tracker_id => 1, :subject => 'foo')
279 issue.init_journal(User.find(2), "update")
287 issue.init_journal(User.find(2), "update")
280 issue.save!
288 issue.save!
281
289
282 User.find(2).destroy
290 User.find(2).destroy
283 assert_nil User.find_by_id(2)
291 assert_nil User.find_by_id(2)
284 assert_equal User.anonymous, issue.journals.first.reload.user
292 assert_equal User.anonymous, issue.journals.first.reload.user
285 end
293 end
286
294
287 def test_destroy_should_update_journal_details_old_value
295 def test_destroy_should_update_journal_details_old_value
288 issue = Issue.create!(:project_id => 1, :author_id => 1,
296 issue = Issue.create!(:project_id => 1, :author_id => 1,
289 :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
297 :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
290 issue.init_journal(User.find(1), "update")
298 issue.init_journal(User.find(1), "update")
291 issue.assigned_to_id = nil
299 issue.assigned_to_id = nil
292 assert_difference 'JournalDetail.count' do
300 assert_difference 'JournalDetail.count' do
293 issue.save!
301 issue.save!
294 end
302 end
295 journal_detail = JournalDetail.order('id DESC').first
303 journal_detail = JournalDetail.order('id DESC').first
296 assert_equal '2', journal_detail.old_value
304 assert_equal '2', journal_detail.old_value
297
305
298 User.find(2).destroy
306 User.find(2).destroy
299 assert_nil User.find_by_id(2)
307 assert_nil User.find_by_id(2)
300 assert_equal User.anonymous.id.to_s, journal_detail.reload.old_value
308 assert_equal User.anonymous.id.to_s, journal_detail.reload.old_value
301 end
309 end
302
310
303 def test_destroy_should_update_journal_details_value
311 def test_destroy_should_update_journal_details_value
304 issue = Issue.create!(:project_id => 1, :author_id => 1,
312 issue = Issue.create!(:project_id => 1, :author_id => 1,
305 :tracker_id => 1, :subject => 'foo')
313 :tracker_id => 1, :subject => 'foo')
306 issue.init_journal(User.find(1), "update")
314 issue.init_journal(User.find(1), "update")
307 issue.assigned_to_id = 2
315 issue.assigned_to_id = 2
308 assert_difference 'JournalDetail.count' do
316 assert_difference 'JournalDetail.count' do
309 issue.save!
317 issue.save!
310 end
318 end
311 journal_detail = JournalDetail.order('id DESC').first
319 journal_detail = JournalDetail.order('id DESC').first
312 assert_equal '2', journal_detail.value
320 assert_equal '2', journal_detail.value
313
321
314 User.find(2).destroy
322 User.find(2).destroy
315 assert_nil User.find_by_id(2)
323 assert_nil User.find_by_id(2)
316 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
324 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
317 end
325 end
318
326
319 def test_destroy_should_update_messages
327 def test_destroy_should_update_messages
320 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
328 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
321 message = Message.create!(:board_id => board.id, :author_id => 2,
329 message = Message.create!(:board_id => board.id, :author_id => 2,
322 :subject => 'foo', :content => 'foo')
330 :subject => 'foo', :content => 'foo')
323 User.find(2).destroy
331 User.find(2).destroy
324 assert_nil User.find_by_id(2)
332 assert_nil User.find_by_id(2)
325 assert_equal User.anonymous, message.reload.author
333 assert_equal User.anonymous, message.reload.author
326 end
334 end
327
335
328 def test_destroy_should_update_news
336 def test_destroy_should_update_news
329 news = News.create!(:project_id => 1, :author_id => 2,
337 news = News.create!(:project_id => 1, :author_id => 2,
330 :title => 'foo', :description => 'foo')
338 :title => 'foo', :description => 'foo')
331 User.find(2).destroy
339 User.find(2).destroy
332 assert_nil User.find_by_id(2)
340 assert_nil User.find_by_id(2)
333 assert_equal User.anonymous, news.reload.author
341 assert_equal User.anonymous, news.reload.author
334 end
342 end
335
343
336 def test_destroy_should_delete_private_queries
344 def test_destroy_should_delete_private_queries
337 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PRIVATE)
345 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PRIVATE)
338 query.project_id = 1
346 query.project_id = 1
339 query.user_id = 2
347 query.user_id = 2
340 query.save!
348 query.save!
341
349
342 User.find(2).destroy
350 User.find(2).destroy
343 assert_nil User.find_by_id(2)
351 assert_nil User.find_by_id(2)
344 assert_nil Query.find_by_id(query.id)
352 assert_nil Query.find_by_id(query.id)
345 end
353 end
346
354
347 def test_destroy_should_update_public_queries
355 def test_destroy_should_update_public_queries
348 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PUBLIC)
356 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PUBLIC)
349 query.project_id = 1
357 query.project_id = 1
350 query.user_id = 2
358 query.user_id = 2
351 query.save!
359 query.save!
352
360
353 User.find(2).destroy
361 User.find(2).destroy
354 assert_nil User.find_by_id(2)
362 assert_nil User.find_by_id(2)
355 assert_equal User.anonymous, query.reload.user
363 assert_equal User.anonymous, query.reload.user
356 end
364 end
357
365
358 def test_destroy_should_update_time_entries
366 def test_destroy_should_update_time_entries
359 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today,
367 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today,
360 :activity => TimeEntryActivity.create!(:name => 'foo'))
368 :activity => TimeEntryActivity.create!(:name => 'foo'))
361 entry.project_id = 1
369 entry.project_id = 1
362 entry.user_id = 2
370 entry.user_id = 2
363 entry.save!
371 entry.save!
364
372
365 User.find(2).destroy
373 User.find(2).destroy
366 assert_nil User.find_by_id(2)
374 assert_nil User.find_by_id(2)
367 assert_equal User.anonymous, entry.reload.user
375 assert_equal User.anonymous, entry.reload.user
368 end
376 end
369
377
370 def test_destroy_should_delete_tokens
378 def test_destroy_should_delete_tokens
371 token = Token.create!(:user_id => 2, :value => 'foo')
379 token = Token.create!(:user_id => 2, :value => 'foo')
372
380
373 User.find(2).destroy
381 User.find(2).destroy
374 assert_nil User.find_by_id(2)
382 assert_nil User.find_by_id(2)
375 assert_nil Token.find_by_id(token.id)
383 assert_nil Token.find_by_id(token.id)
376 end
384 end
377
385
378 def test_destroy_should_delete_watchers
386 def test_destroy_should_delete_watchers
379 issue = Issue.create!(:project_id => 1, :author_id => 1,
387 issue = Issue.create!(:project_id => 1, :author_id => 1,
380 :tracker_id => 1, :subject => 'foo')
388 :tracker_id => 1, :subject => 'foo')
381 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
389 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
382
390
383 User.find(2).destroy
391 User.find(2).destroy
384 assert_nil User.find_by_id(2)
392 assert_nil User.find_by_id(2)
385 assert_nil Watcher.find_by_id(watcher.id)
393 assert_nil Watcher.find_by_id(watcher.id)
386 end
394 end
387
395
388 def test_destroy_should_update_wiki_contents
396 def test_destroy_should_update_wiki_contents
389 wiki_content = WikiContent.create!(
397 wiki_content = WikiContent.create!(
390 :text => 'foo',
398 :text => 'foo',
391 :author_id => 2,
399 :author_id => 2,
392 :page => WikiPage.create!(:title => 'Foo',
400 :page => WikiPage.create!(:title => 'Foo',
393 :wiki => Wiki.create!(:project_id => 3,
401 :wiki => Wiki.create!(:project_id => 3,
394 :start_page => 'Start'))
402 :start_page => 'Start'))
395 )
403 )
396 wiki_content.text = 'bar'
404 wiki_content.text = 'bar'
397 assert_difference 'WikiContent::Version.count' do
405 assert_difference 'WikiContent::Version.count' do
398 wiki_content.save!
406 wiki_content.save!
399 end
407 end
400
408
401 User.find(2).destroy
409 User.find(2).destroy
402 assert_nil User.find_by_id(2)
410 assert_nil User.find_by_id(2)
403 assert_equal User.anonymous, wiki_content.reload.author
411 assert_equal User.anonymous, wiki_content.reload.author
404 wiki_content.versions.each do |version|
412 wiki_content.versions.each do |version|
405 assert_equal User.anonymous, version.reload.author
413 assert_equal User.anonymous, version.reload.author
406 end
414 end
407 end
415 end
408
416
409 def test_destroy_should_nullify_issue_categories
417 def test_destroy_should_nullify_issue_categories
410 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
418 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
411
419
412 User.find(2).destroy
420 User.find(2).destroy
413 assert_nil User.find_by_id(2)
421 assert_nil User.find_by_id(2)
414 assert_nil category.reload.assigned_to_id
422 assert_nil category.reload.assigned_to_id
415 end
423 end
416
424
417 def test_destroy_should_nullify_changesets
425 def test_destroy_should_nullify_changesets
418 changeset = Changeset.create!(
426 changeset = Changeset.create!(
419 :repository => Repository::Subversion.create!(
427 :repository => Repository::Subversion.create!(
420 :project_id => 1,
428 :project_id => 1,
421 :url => 'file:///tmp',
429 :url => 'file:///tmp',
422 :identifier => 'tmp'
430 :identifier => 'tmp'
423 ),
431 ),
424 :revision => '12',
432 :revision => '12',
425 :committed_on => Time.now,
433 :committed_on => Time.now,
426 :committer => 'jsmith'
434 :committer => 'jsmith'
427 )
435 )
428 assert_equal 2, changeset.user_id
436 assert_equal 2, changeset.user_id
429
437
430 User.find(2).destroy
438 User.find(2).destroy
431 assert_nil User.find_by_id(2)
439 assert_nil User.find_by_id(2)
432 assert_nil changeset.reload.user_id
440 assert_nil changeset.reload.user_id
433 end
441 end
434
442
435 def test_anonymous_user_should_not_be_destroyable
443 def test_anonymous_user_should_not_be_destroyable
436 assert_no_difference 'User.count' do
444 assert_no_difference 'User.count' do
437 assert_equal false, User.anonymous.destroy
445 assert_equal false, User.anonymous.destroy
438 end
446 end
439 end
447 end
440
448
441 def test_password_change_should_destroy_tokens
449 def test_password_change_should_destroy_tokens
442 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
450 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
443 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
451 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
444
452
445 user = User.find(2)
453 user = User.find(2)
446 user.password, user.password_confirmation = "a new password", "a new password"
454 user.password, user.password_confirmation = "a new password", "a new password"
447 assert user.save
455 assert user.save
448
456
449 assert_nil Token.find_by_id(recovery_token.id)
457 assert_nil Token.find_by_id(recovery_token.id)
450 assert_nil Token.find_by_id(autologin_token.id)
458 assert_nil Token.find_by_id(autologin_token.id)
451 end
459 end
452
460
453 def test_mail_change_should_destroy_tokens
461 def test_mail_change_should_destroy_tokens
454 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
462 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
455 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
463 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
456
464
457 user = User.find(2)
465 user = User.find(2)
458 user.mail = "user@somwehere.com"
466 user.mail = "user@somwehere.com"
459 assert user.save
467 assert user.save
460
468
461 assert_nil Token.find_by_id(recovery_token.id)
469 assert_nil Token.find_by_id(recovery_token.id)
462 assert_equal autologin_token, Token.find_by_id(autologin_token.id)
470 assert_equal autologin_token, Token.find_by_id(autologin_token.id)
463 end
471 end
464
472
465 def test_change_on_other_fields_should_not_destroy_tokens
473 def test_change_on_other_fields_should_not_destroy_tokens
466 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
474 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
467 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
475 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
468
476
469 user = User.find(2)
477 user = User.find(2)
470 user.firstname = "Bobby"
478 user.firstname = "Bobby"
471 assert user.save
479 assert user.save
472
480
473 assert_equal recovery_token, Token.find_by_id(recovery_token.id)
481 assert_equal recovery_token, Token.find_by_id(recovery_token.id)
474 assert_equal autologin_token, Token.find_by_id(autologin_token.id)
482 assert_equal autologin_token, Token.find_by_id(autologin_token.id)
475 end
483 end
476
484
477 def test_validate_login_presence
485 def test_validate_login_presence
478 @admin.login = ""
486 @admin.login = ""
479 assert !@admin.save
487 assert !@admin.save
480 assert_equal 1, @admin.errors.count
488 assert_equal 1, @admin.errors.count
481 end
489 end
482
490
483 def test_validate_mail_notification_inclusion
491 def test_validate_mail_notification_inclusion
484 u = User.new
492 u = User.new
485 u.mail_notification = 'foo'
493 u.mail_notification = 'foo'
486 u.save
494 u.save
487 assert_not_equal [], u.errors[:mail_notification]
495 assert_not_equal [], u.errors[:mail_notification]
488 end
496 end
489
497
490 def test_password
498 def test_password
491 user = User.try_to_login("admin", "admin")
499 user = User.try_to_login("admin", "admin")
492 assert_kind_of User, user
500 assert_kind_of User, user
493 assert_equal "admin", user.login
501 assert_equal "admin", user.login
494 user.password = "hello123"
502 user.password = "hello123"
495 assert user.save
503 assert user.save
496
504
497 user = User.try_to_login("admin", "hello123")
505 user = User.try_to_login("admin", "hello123")
498 assert_kind_of User, user
506 assert_kind_of User, user
499 assert_equal "admin", user.login
507 assert_equal "admin", user.login
500 end
508 end
501
509
502 def test_validate_password_length
510 def test_validate_password_length
503 with_settings :password_min_length => '100' do
511 with_settings :password_min_length => '100' do
504 user = User.new(:firstname => "new100",
512 user = User.new(:firstname => "new100",
505 :lastname => "user100", :mail => "newuser100@somenet.foo")
513 :lastname => "user100", :mail => "newuser100@somenet.foo")
506 user.login = "newuser100"
514 user.login = "newuser100"
507 user.password, user.password_confirmation = "password100", "password100"
515 user.password, user.password_confirmation = "password100", "password100"
508 assert !user.save
516 assert !user.save
509 assert_equal 1, user.errors.count
517 assert_equal 1, user.errors.count
510 end
518 end
511 end
519 end
512
520
513 def test_name_format
521 def test_name_format
514 assert_equal 'John S.', @jsmith.name(:firstname_lastinitial)
522 assert_equal 'John S.', @jsmith.name(:firstname_lastinitial)
515 assert_equal 'Smith, John', @jsmith.name(:lastname_comma_firstname)
523 assert_equal 'Smith, John', @jsmith.name(:lastname_comma_firstname)
516 assert_equal 'J. Smith', @jsmith.name(:firstinitial_lastname)
524 assert_equal 'J. Smith', @jsmith.name(:firstinitial_lastname)
517 assert_equal 'J.-P. Lang', User.new(:firstname => 'Jean-Philippe', :lastname => 'Lang').name(:firstinitial_lastname)
525 assert_equal 'J.-P. Lang', User.new(:firstname => 'Jean-Philippe', :lastname => 'Lang').name(:firstinitial_lastname)
518 end
526 end
519
527
520 def test_name_should_use_setting_as_default_format
528 def test_name_should_use_setting_as_default_format
521 with_settings :user_format => :firstname_lastname do
529 with_settings :user_format => :firstname_lastname do
522 assert_equal 'John Smith', @jsmith.reload.name
530 assert_equal 'John Smith', @jsmith.reload.name
523 end
531 end
524 with_settings :user_format => :username do
532 with_settings :user_format => :username do
525 assert_equal 'jsmith', @jsmith.reload.name
533 assert_equal 'jsmith', @jsmith.reload.name
526 end
534 end
527 with_settings :user_format => :lastname do
535 with_settings :user_format => :lastname do
528 assert_equal 'Smith', @jsmith.reload.name
536 assert_equal 'Smith', @jsmith.reload.name
529 end
537 end
530 end
538 end
531
539
532 def test_today_should_return_the_day_according_to_user_time_zone
540 def test_today_should_return_the_day_according_to_user_time_zone
533 preference = User.find(1).pref
541 preference = User.find(1).pref
534 date = Date.new(2012, 05, 15)
542 date = Date.new(2012, 05, 15)
535 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
543 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
536 Date.stubs(:today).returns(date)
544 Date.stubs(:today).returns(date)
537 Time.stubs(:now).returns(time)
545 Time.stubs(:now).returns(time)
538
546
539 preference.update_attribute :time_zone, 'Baku' # UTC+4
547 preference.update_attribute :time_zone, 'Baku' # UTC+4
540 assert_equal '2012-05-16', User.find(1).today.to_s
548 assert_equal '2012-05-16', User.find(1).today.to_s
541
549
542 preference.update_attribute :time_zone, 'La Paz' # UTC-4
550 preference.update_attribute :time_zone, 'La Paz' # UTC-4
543 assert_equal '2012-05-15', User.find(1).today.to_s
551 assert_equal '2012-05-15', User.find(1).today.to_s
544
552
545 preference.update_attribute :time_zone, ''
553 preference.update_attribute :time_zone, ''
546 assert_equal '2012-05-15', User.find(1).today.to_s
554 assert_equal '2012-05-15', User.find(1).today.to_s
547 end
555 end
548
556
549 def test_time_to_date_should_return_the_date_according_to_user_time_zone
557 def test_time_to_date_should_return_the_date_according_to_user_time_zone
550 preference = User.find(1).pref
558 preference = User.find(1).pref
551 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
559 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
552
560
553 preference.update_attribute :time_zone, 'Baku' # UTC+4
561 preference.update_attribute :time_zone, 'Baku' # UTC+4
554 assert_equal '2012-05-16', User.find(1).time_to_date(time).to_s
562 assert_equal '2012-05-16', User.find(1).time_to_date(time).to_s
555
563
556 preference.update_attribute :time_zone, 'La Paz' # UTC-4
564 preference.update_attribute :time_zone, 'La Paz' # UTC-4
557 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
565 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
558
566
559 preference.update_attribute :time_zone, ''
567 preference.update_attribute :time_zone, ''
560 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
568 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
561 end
569 end
562
570
563 def test_fields_for_order_statement_should_return_fields_according_user_format_setting
571 def test_fields_for_order_statement_should_return_fields_according_user_format_setting
564 with_settings :user_format => 'lastname_comma_firstname' do
572 with_settings :user_format => 'lastname_comma_firstname' do
565 assert_equal ['users.lastname', 'users.firstname', 'users.id'],
573 assert_equal ['users.lastname', 'users.firstname', 'users.id'],
566 User.fields_for_order_statement
574 User.fields_for_order_statement
567 end
575 end
568 end
576 end
569
577
570 def test_fields_for_order_statement_width_table_name_should_prepend_table_name
578 def test_fields_for_order_statement_width_table_name_should_prepend_table_name
571 with_settings :user_format => 'lastname_firstname' do
579 with_settings :user_format => 'lastname_firstname' do
572 assert_equal ['authors.lastname', 'authors.firstname', 'authors.id'],
580 assert_equal ['authors.lastname', 'authors.firstname', 'authors.id'],
573 User.fields_for_order_statement('authors')
581 User.fields_for_order_statement('authors')
574 end
582 end
575 end
583 end
576
584
577 def test_fields_for_order_statement_with_blank_format_should_return_default
585 def test_fields_for_order_statement_with_blank_format_should_return_default
578 with_settings :user_format => '' do
586 with_settings :user_format => '' do
579 assert_equal ['users.firstname', 'users.lastname', 'users.id'],
587 assert_equal ['users.firstname', 'users.lastname', 'users.id'],
580 User.fields_for_order_statement
588 User.fields_for_order_statement
581 end
589 end
582 end
590 end
583
591
584 def test_fields_for_order_statement_with_invalid_format_should_return_default
592 def test_fields_for_order_statement_with_invalid_format_should_return_default
585 with_settings :user_format => 'foo' do
593 with_settings :user_format => 'foo' do
586 assert_equal ['users.firstname', 'users.lastname', 'users.id'],
594 assert_equal ['users.firstname', 'users.lastname', 'users.id'],
587 User.fields_for_order_statement
595 User.fields_for_order_statement
588 end
596 end
589 end
597 end
590
598
591 test ".try_to_login with good credentials should return the user" do
599 test ".try_to_login with good credentials should return the user" do
592 user = User.try_to_login("admin", "admin")
600 user = User.try_to_login("admin", "admin")
593 assert_kind_of User, user
601 assert_kind_of User, user
594 assert_equal "admin", user.login
602 assert_equal "admin", user.login
595 end
603 end
596
604
597 test ".try_to_login with wrong credentials should return nil" do
605 test ".try_to_login with wrong credentials should return nil" do
598 assert_nil User.try_to_login("admin", "foo")
606 assert_nil User.try_to_login("admin", "foo")
599 end
607 end
600
608
601 def test_try_to_login_with_locked_user_should_return_nil
609 def test_try_to_login_with_locked_user_should_return_nil
602 @jsmith.status = User::STATUS_LOCKED
610 @jsmith.status = User::STATUS_LOCKED
603 @jsmith.save!
611 @jsmith.save!
604
612
605 user = User.try_to_login("jsmith", "jsmith")
613 user = User.try_to_login("jsmith", "jsmith")
606 assert_equal nil, user
614 assert_equal nil, user
607 end
615 end
608
616
609 def test_try_to_login_with_locked_user_and_not_active_only_should_return_user
617 def test_try_to_login_with_locked_user_and_not_active_only_should_return_user
610 @jsmith.status = User::STATUS_LOCKED
618 @jsmith.status = User::STATUS_LOCKED
611 @jsmith.save!
619 @jsmith.save!
612
620
613 user = User.try_to_login("jsmith", "jsmith", false)
621 user = User.try_to_login("jsmith", "jsmith", false)
614 assert_equal @jsmith, user
622 assert_equal @jsmith, user
615 end
623 end
616
624
617 test ".try_to_login should fall-back to case-insensitive if user login is not found as-typed" do
625 test ".try_to_login should fall-back to case-insensitive if user login is not found as-typed" do
618 user = User.try_to_login("AdMin", "admin")
626 user = User.try_to_login("AdMin", "admin")
619 assert_kind_of User, user
627 assert_kind_of User, user
620 assert_equal "admin", user.login
628 assert_equal "admin", user.login
621 end
629 end
622
630
623 test ".try_to_login should select the exact matching user first" do
631 test ".try_to_login should select the exact matching user first" do
624 case_sensitive_user = User.generate! do |user|
632 case_sensitive_user = User.generate! do |user|
625 user.password = "admin123"
633 user.password = "admin123"
626 end
634 end
627 # bypass validations to make it appear like existing data
635 # bypass validations to make it appear like existing data
628 case_sensitive_user.update_attribute(:login, 'ADMIN')
636 case_sensitive_user.update_attribute(:login, 'ADMIN')
629
637
630 user = User.try_to_login("ADMIN", "admin123")
638 user = User.try_to_login("ADMIN", "admin123")
631 assert_kind_of User, user
639 assert_kind_of User, user
632 assert_equal "ADMIN", user.login
640 assert_equal "ADMIN", user.login
633 end
641 end
634
642
635 if ldap_configured?
643 if ldap_configured?
636 test "#try_to_login using LDAP with failed connection to the LDAP server" do
644 test "#try_to_login using LDAP with failed connection to the LDAP server" do
637 auth_source = AuthSourceLdap.find(1)
645 auth_source = AuthSourceLdap.find(1)
638 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
646 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
639
647
640 assert_equal nil, User.try_to_login('edavis', 'wrong')
648 assert_equal nil, User.try_to_login('edavis', 'wrong')
641 end
649 end
642
650
643 test "#try_to_login using LDAP" do
651 test "#try_to_login using LDAP" do
644 assert_equal nil, User.try_to_login('edavis', 'wrong')
652 assert_equal nil, User.try_to_login('edavis', 'wrong')
645 end
653 end
646
654
647 test "#try_to_login using LDAP binding with user's account" do
655 test "#try_to_login using LDAP binding with user's account" do
648 auth_source = AuthSourceLdap.find(1)
656 auth_source = AuthSourceLdap.find(1)
649 auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
657 auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
650 auth_source.account_password = ''
658 auth_source.account_password = ''
651 auth_source.save!
659 auth_source.save!
652
660
653 ldap_user = User.new(:mail => 'example1@redmine.org', :firstname => 'LDAP', :lastname => 'user', :auth_source_id => 1)
661 ldap_user = User.new(:mail => 'example1@redmine.org', :firstname => 'LDAP', :lastname => 'user', :auth_source_id => 1)
654 ldap_user.login = 'example1'
662 ldap_user.login = 'example1'
655 ldap_user.save!
663 ldap_user.save!
656
664
657 assert_equal ldap_user, User.try_to_login('example1', '123456')
665 assert_equal ldap_user, User.try_to_login('example1', '123456')
658 assert_nil User.try_to_login('example1', '11111')
666 assert_nil User.try_to_login('example1', '11111')
659 end
667 end
660
668
661 test "#try_to_login using LDAP on the fly registration" do
669 test "#try_to_login using LDAP on the fly registration" do
662 AuthSourceLdap.find(1).update_attribute :onthefly_register, true
670 AuthSourceLdap.find(1).update_attribute :onthefly_register, true
663
671
664 assert_difference('User.count') do
672 assert_difference('User.count') do
665 assert User.try_to_login('edavis', '123456')
673 assert User.try_to_login('edavis', '123456')
666 end
674 end
667
675
668 assert_no_difference('User.count') do
676 assert_no_difference('User.count') do
669 assert User.try_to_login('edavis', '123456')
677 assert User.try_to_login('edavis', '123456')
670 end
678 end
671
679
672 assert_nil User.try_to_login('example1', '11111')
680 assert_nil User.try_to_login('example1', '11111')
673 end
681 end
674
682
675 test "#try_to_login using LDAP on the fly registration and binding with user's account" do
683 test "#try_to_login using LDAP on the fly registration and binding with user's account" do
676 auth_source = AuthSourceLdap.find(1)
684 auth_source = AuthSourceLdap.find(1)
677 auth_source.update_attribute :onthefly_register, true
685 auth_source.update_attribute :onthefly_register, true
678 auth_source = AuthSourceLdap.find(1)
686 auth_source = AuthSourceLdap.find(1)
679 auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
687 auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
680 auth_source.account_password = ''
688 auth_source.account_password = ''
681 auth_source.save!
689 auth_source.save!
682
690
683 assert_difference('User.count') do
691 assert_difference('User.count') do
684 assert User.try_to_login('example1', '123456')
692 assert User.try_to_login('example1', '123456')
685 end
693 end
686
694
687 assert_no_difference('User.count') do
695 assert_no_difference('User.count') do
688 assert User.try_to_login('example1', '123456')
696 assert User.try_to_login('example1', '123456')
689 end
697 end
690
698
691 assert_nil User.try_to_login('example1', '11111')
699 assert_nil User.try_to_login('example1', '11111')
692 end
700 end
693
701
694 else
702 else
695 puts "Skipping LDAP tests."
703 puts "Skipping LDAP tests."
696 end
704 end
697
705
698 def test_create_anonymous
706 def test_create_anonymous
699 AnonymousUser.delete_all
707 AnonymousUser.delete_all
700 anon = User.anonymous
708 anon = User.anonymous
701 assert !anon.new_record?
709 assert !anon.new_record?
702 assert_kind_of AnonymousUser, anon
710 assert_kind_of AnonymousUser, anon
703 end
711 end
704
712
705 def test_ensure_single_anonymous_user
713 def test_ensure_single_anonymous_user
706 AnonymousUser.delete_all
714 AnonymousUser.delete_all
707 anon1 = User.anonymous
715 anon1 = User.anonymous
708 assert !anon1.new_record?
716 assert !anon1.new_record?
709 assert_kind_of AnonymousUser, anon1
717 assert_kind_of AnonymousUser, anon1
710 anon2 = AnonymousUser.create(
718 anon2 = AnonymousUser.create(
711 :lastname => 'Anonymous', :firstname => '',
719 :lastname => 'Anonymous', :firstname => '',
712 :login => '', :status => 0)
720 :login => '', :status => 0)
713 assert_equal 1, anon2.errors.count
721 assert_equal 1, anon2.errors.count
714 end
722 end
715
723
716 def test_rss_key
724 def test_rss_key
717 assert_nil @jsmith.rss_token
725 assert_nil @jsmith.rss_token
718 key = @jsmith.rss_key
726 key = @jsmith.rss_key
719 assert_equal 40, key.length
727 assert_equal 40, key.length
720
728
721 @jsmith.reload
729 @jsmith.reload
722 assert_equal key, @jsmith.rss_key
730 assert_equal key, @jsmith.rss_key
723 end
731 end
724
732
725 def test_rss_key_should_not_be_generated_twice
733 def test_rss_key_should_not_be_generated_twice
726 assert_difference 'Token.count', 1 do
734 assert_difference 'Token.count', 1 do
727 key1 = @jsmith.rss_key
735 key1 = @jsmith.rss_key
728 key2 = @jsmith.rss_key
736 key2 = @jsmith.rss_key
729 assert_equal key1, key2
737 assert_equal key1, key2
730 end
738 end
731 end
739 end
732
740
733 def test_api_key_should_not_be_generated_twice
741 def test_api_key_should_not_be_generated_twice
734 assert_difference 'Token.count', 1 do
742 assert_difference 'Token.count', 1 do
735 key1 = @jsmith.api_key
743 key1 = @jsmith.api_key
736 key2 = @jsmith.api_key
744 key2 = @jsmith.api_key
737 assert_equal key1, key2
745 assert_equal key1, key2
738 end
746 end
739 end
747 end
740
748
741 test "#api_key should generate a new one if the user doesn't have one" do
749 test "#api_key should generate a new one if the user doesn't have one" do
742 user = User.generate!(:api_token => nil)
750 user = User.generate!(:api_token => nil)
743 assert_nil user.api_token
751 assert_nil user.api_token
744
752
745 key = user.api_key
753 key = user.api_key
746 assert_equal 40, key.length
754 assert_equal 40, key.length
747 user.reload
755 user.reload
748 assert_equal key, user.api_key
756 assert_equal key, user.api_key
749 end
757 end
750
758
751 test "#api_key should return the existing api token value" do
759 test "#api_key should return the existing api token value" do
752 user = User.generate!
760 user = User.generate!
753 token = Token.create!(:action => 'api')
761 token = Token.create!(:action => 'api')
754 user.api_token = token
762 user.api_token = token
755 assert user.save
763 assert user.save
756
764
757 assert_equal token.value, user.api_key
765 assert_equal token.value, user.api_key
758 end
766 end
759
767
760 test "#find_by_api_key should return nil if no matching key is found" do
768 test "#find_by_api_key should return nil if no matching key is found" do
761 assert_nil User.find_by_api_key('zzzzzzzzz')
769 assert_nil User.find_by_api_key('zzzzzzzzz')
762 end
770 end
763
771
764 test "#find_by_api_key should return nil if the key is found for an inactive user" do
772 test "#find_by_api_key should return nil if the key is found for an inactive user" do
765 user = User.generate!
773 user = User.generate!
766 user.status = User::STATUS_LOCKED
774 user.status = User::STATUS_LOCKED
767 token = Token.create!(:action => 'api')
775 token = Token.create!(:action => 'api')
768 user.api_token = token
776 user.api_token = token
769 user.save
777 user.save
770
778
771 assert_nil User.find_by_api_key(token.value)
779 assert_nil User.find_by_api_key(token.value)
772 end
780 end
773
781
774 test "#find_by_api_key should return the user if the key is found for an active user" do
782 test "#find_by_api_key should return the user if the key is found for an active user" do
775 user = User.generate!
783 user = User.generate!
776 token = Token.create!(:action => 'api')
784 token = Token.create!(:action => 'api')
777 user.api_token = token
785 user.api_token = token
778 user.save
786 user.save
779
787
780 assert_equal user, User.find_by_api_key(token.value)
788 assert_equal user, User.find_by_api_key(token.value)
781 end
789 end
782
790
783 def test_default_admin_account_changed_should_return_false_if_account_was_not_changed
791 def test_default_admin_account_changed_should_return_false_if_account_was_not_changed
784 user = User.find_by_login("admin")
792 user = User.find_by_login("admin")
785 user.password = "admin"
793 user.password = "admin"
786 assert user.save(:validate => false)
794 assert user.save(:validate => false)
787
795
788 assert_equal false, User.default_admin_account_changed?
796 assert_equal false, User.default_admin_account_changed?
789 end
797 end
790
798
791 def test_default_admin_account_changed_should_return_true_if_password_was_changed
799 def test_default_admin_account_changed_should_return_true_if_password_was_changed
792 user = User.find_by_login("admin")
800 user = User.find_by_login("admin")
793 user.password = "newpassword"
801 user.password = "newpassword"
794 user.save!
802 user.save!
795
803
796 assert_equal true, User.default_admin_account_changed?
804 assert_equal true, User.default_admin_account_changed?
797 end
805 end
798
806
799 def test_default_admin_account_changed_should_return_true_if_account_is_disabled
807 def test_default_admin_account_changed_should_return_true_if_account_is_disabled
800 user = User.find_by_login("admin")
808 user = User.find_by_login("admin")
801 user.password = "admin"
809 user.password = "admin"
802 user.status = User::STATUS_LOCKED
810 user.status = User::STATUS_LOCKED
803 assert user.save(:validate => false)
811 assert user.save(:validate => false)
804
812
805 assert_equal true, User.default_admin_account_changed?
813 assert_equal true, User.default_admin_account_changed?
806 end
814 end
807
815
808 def test_default_admin_account_changed_should_return_true_if_account_does_not_exist
816 def test_default_admin_account_changed_should_return_true_if_account_does_not_exist
809 user = User.find_by_login("admin")
817 user = User.find_by_login("admin")
810 user.destroy
818 user.destroy
811
819
812 assert_equal true, User.default_admin_account_changed?
820 assert_equal true, User.default_admin_account_changed?
813 end
821 end
814
822
815 def test_membership_with_project_should_return_membership
823 def test_membership_with_project_should_return_membership
816 project = Project.find(1)
824 project = Project.find(1)
817
825
818 membership = @jsmith.membership(project)
826 membership = @jsmith.membership(project)
819 assert_kind_of Member, membership
827 assert_kind_of Member, membership
820 assert_equal @jsmith, membership.user
828 assert_equal @jsmith, membership.user
821 assert_equal project, membership.project
829 assert_equal project, membership.project
822 end
830 end
823
831
824 def test_membership_with_project_id_should_return_membership
832 def test_membership_with_project_id_should_return_membership
825 project = Project.find(1)
833 project = Project.find(1)
826
834
827 membership = @jsmith.membership(1)
835 membership = @jsmith.membership(1)
828 assert_kind_of Member, membership
836 assert_kind_of Member, membership
829 assert_equal @jsmith, membership.user
837 assert_equal @jsmith, membership.user
830 assert_equal project, membership.project
838 assert_equal project, membership.project
831 end
839 end
832
840
833 def test_membership_for_non_member_should_return_nil
841 def test_membership_for_non_member_should_return_nil
834 project = Project.find(1)
842 project = Project.find(1)
835
843
836 user = User.generate!
844 user = User.generate!
837 membership = user.membership(1)
845 membership = user.membership(1)
838 assert_nil membership
846 assert_nil membership
839 end
847 end
840
848
841 def test_roles_for_project_with_member_on_public_project_should_return_roles_and_non_member
849 def test_roles_for_project_with_member_on_public_project_should_return_roles_and_non_member
842 roles = @jsmith.roles_for_project(Project.find(1))
850 roles = @jsmith.roles_for_project(Project.find(1))
843 assert_kind_of Role, roles.first
851 assert_kind_of Role, roles.first
844 assert_equal ["Manager"], roles.map(&:name)
852 assert_equal ["Manager"], roles.map(&:name)
845 end
853 end
846
854
847 def test_roles_for_project_with_member_on_private_project_should_return_roles
855 def test_roles_for_project_with_member_on_private_project_should_return_roles
848 Project.find(1).update_attribute :is_public, false
856 Project.find(1).update_attribute :is_public, false
849
857
850 roles = @jsmith.roles_for_project(Project.find(1))
858 roles = @jsmith.roles_for_project(Project.find(1))
851 assert_kind_of Role, roles.first
859 assert_kind_of Role, roles.first
852 assert_equal ["Manager"], roles.map(&:name)
860 assert_equal ["Manager"], roles.map(&:name)
853 end
861 end
854
862
855 def test_roles_for_project_with_non_member_with_public_project_should_return_non_member
863 def test_roles_for_project_with_non_member_with_public_project_should_return_non_member
856 set_language_if_valid 'en'
864 set_language_if_valid 'en'
857 roles = User.find(8).roles_for_project(Project.find(1))
865 roles = User.find(8).roles_for_project(Project.find(1))
858 assert_equal ["Non member"], roles.map(&:name)
866 assert_equal ["Non member"], roles.map(&:name)
859 end
867 end
860
868
861 def test_roles_for_project_with_non_member_with_public_project_and_override_should_return_override_roles
869 def test_roles_for_project_with_non_member_with_public_project_and_override_should_return_override_roles
862 project = Project.find(1)
870 project = Project.find(1)
863 Member.create!(:project => project, :principal => Group.non_member, :role_ids => [1, 2])
871 Member.create!(:project => project, :principal => Group.non_member, :role_ids => [1, 2])
864 roles = User.find(8).roles_for_project(project)
872 roles = User.find(8).roles_for_project(project)
865 assert_equal ["Developer", "Manager"], roles.map(&:name).sort
873 assert_equal ["Developer", "Manager"], roles.map(&:name).sort
866 end
874 end
867
875
868 def test_roles_for_project_with_non_member_with_private_project_should_return_no_roles
876 def test_roles_for_project_with_non_member_with_private_project_should_return_no_roles
869 Project.find(1).update_attribute :is_public, false
877 Project.find(1).update_attribute :is_public, false
870
878
871 roles = User.find(8).roles_for_project(Project.find(1))
879 roles = User.find(8).roles_for_project(Project.find(1))
872 assert_equal [], roles.map(&:name)
880 assert_equal [], roles.map(&:name)
873 end
881 end
874
882
875 def test_roles_for_project_with_non_member_with_private_project_and_override_should_return_no_roles
883 def test_roles_for_project_with_non_member_with_private_project_and_override_should_return_no_roles
876 project = Project.find(1)
884 project = Project.find(1)
877 project.update_attribute :is_public, false
885 project.update_attribute :is_public, false
878 Member.create!(:project => project, :principal => Group.non_member, :role_ids => [1, 2])
886 Member.create!(:project => project, :principal => Group.non_member, :role_ids => [1, 2])
879 roles = User.find(8).roles_for_project(project)
887 roles = User.find(8).roles_for_project(project)
880 assert_equal [], roles.map(&:name).sort
888 assert_equal [], roles.map(&:name).sort
881 end
889 end
882
890
883 def test_roles_for_project_with_anonymous_with_public_project_should_return_anonymous
891 def test_roles_for_project_with_anonymous_with_public_project_should_return_anonymous
884 set_language_if_valid 'en'
892 set_language_if_valid 'en'
885 roles = User.anonymous.roles_for_project(Project.find(1))
893 roles = User.anonymous.roles_for_project(Project.find(1))
886 assert_equal ["Anonymous"], roles.map(&:name)
894 assert_equal ["Anonymous"], roles.map(&:name)
887 end
895 end
888
896
889 def test_roles_for_project_with_anonymous_with_public_project_and_override_should_return_override_roles
897 def test_roles_for_project_with_anonymous_with_public_project_and_override_should_return_override_roles
890 project = Project.find(1)
898 project = Project.find(1)
891 Member.create!(:project => project, :principal => Group.anonymous, :role_ids => [1, 2])
899 Member.create!(:project => project, :principal => Group.anonymous, :role_ids => [1, 2])
892 roles = User.anonymous.roles_for_project(project)
900 roles = User.anonymous.roles_for_project(project)
893 assert_equal ["Developer", "Manager"], roles.map(&:name).sort
901 assert_equal ["Developer", "Manager"], roles.map(&:name).sort
894 end
902 end
895
903
896 def test_roles_for_project_with_anonymous_with_private_project_should_return_no_roles
904 def test_roles_for_project_with_anonymous_with_private_project_should_return_no_roles
897 Project.find(1).update_attribute :is_public, false
905 Project.find(1).update_attribute :is_public, false
898
906
899 roles = User.anonymous.roles_for_project(Project.find(1))
907 roles = User.anonymous.roles_for_project(Project.find(1))
900 assert_equal [], roles.map(&:name)
908 assert_equal [], roles.map(&:name)
901 end
909 end
902
910
903 def test_roles_for_project_with_anonymous_with_private_project_and_override_should_return_no_roles
911 def test_roles_for_project_with_anonymous_with_private_project_and_override_should_return_no_roles
904 project = Project.find(1)
912 project = Project.find(1)
905 project.update_attribute :is_public, false
913 project.update_attribute :is_public, false
906 Member.create!(:project => project, :principal => Group.anonymous, :role_ids => [1, 2])
914 Member.create!(:project => project, :principal => Group.anonymous, :role_ids => [1, 2])
907 roles = User.anonymous.roles_for_project(project)
915 roles = User.anonymous.roles_for_project(project)
908 assert_equal [], roles.map(&:name).sort
916 assert_equal [], roles.map(&:name).sort
909 end
917 end
910
918
911 def test_roles_for_project_should_be_unique
919 def test_roles_for_project_should_be_unique
912 m = Member.new(:user_id => 1, :project_id => 1)
920 m = Member.new(:user_id => 1, :project_id => 1)
913 m.member_roles.build(:role_id => 1)
921 m.member_roles.build(:role_id => 1)
914 m.member_roles.build(:role_id => 1)
922 m.member_roles.build(:role_id => 1)
915 m.save!
923 m.save!
916
924
917 user = User.find(1)
925 user = User.find(1)
918 project = Project.find(1)
926 project = Project.find(1)
919 assert_equal 1, user.roles_for_project(project).size
927 assert_equal 1, user.roles_for_project(project).size
920 assert_equal [1], user.roles_for_project(project).map(&:id)
928 assert_equal [1], user.roles_for_project(project).map(&:id)
921 end
929 end
922
930
923 def test_projects_by_role_for_user_with_role
931 def test_projects_by_role_for_user_with_role
924 user = User.find(2)
932 user = User.find(2)
925 assert_kind_of Hash, user.projects_by_role
933 assert_kind_of Hash, user.projects_by_role
926 assert_equal 2, user.projects_by_role.size
934 assert_equal 2, user.projects_by_role.size
927 assert_equal [1,5], user.projects_by_role[Role.find(1)].collect(&:id).sort
935 assert_equal [1,5], user.projects_by_role[Role.find(1)].collect(&:id).sort
928 assert_equal [2], user.projects_by_role[Role.find(2)].collect(&:id).sort
936 assert_equal [2], user.projects_by_role[Role.find(2)].collect(&:id).sort
929 end
937 end
930
938
931 def test_accessing_projects_by_role_with_no_projects_should_return_an_empty_array
939 def test_accessing_projects_by_role_with_no_projects_should_return_an_empty_array
932 user = User.find(2)
940 user = User.find(2)
933 assert_equal [], user.projects_by_role[Role.find(3)]
941 assert_equal [], user.projects_by_role[Role.find(3)]
934 # should not update the hash
942 # should not update the hash
935 assert_nil user.projects_by_role.values.detect(&:blank?)
943 assert_nil user.projects_by_role.values.detect(&:blank?)
936 end
944 end
937
945
938 def test_projects_by_role_for_user_with_no_role
946 def test_projects_by_role_for_user_with_no_role
939 user = User.generate!
947 user = User.generate!
940 assert_equal({}, user.projects_by_role)
948 assert_equal({}, user.projects_by_role)
941 end
949 end
942
950
943 def test_projects_by_role_for_anonymous
951 def test_projects_by_role_for_anonymous
944 assert_equal({}, User.anonymous.projects_by_role)
952 assert_equal({}, User.anonymous.projects_by_role)
945 end
953 end
946
954
947 def test_valid_notification_options
955 def test_valid_notification_options
948 # without memberships
956 # without memberships
949 assert_equal 5, User.find(7).valid_notification_options.size
957 assert_equal 5, User.find(7).valid_notification_options.size
950 # with memberships
958 # with memberships
951 assert_equal 6, User.find(2).valid_notification_options.size
959 assert_equal 6, User.find(2).valid_notification_options.size
952 end
960 end
953
961
954 def test_valid_notification_options_class_method
962 def test_valid_notification_options_class_method
955 assert_equal 5, User.valid_notification_options.size
963 assert_equal 5, User.valid_notification_options.size
956 assert_equal 5, User.valid_notification_options(User.find(7)).size
964 assert_equal 5, User.valid_notification_options(User.find(7)).size
957 assert_equal 6, User.valid_notification_options(User.find(2)).size
965 assert_equal 6, User.valid_notification_options(User.find(2)).size
958 end
966 end
959
967
960 def test_notified_project_ids_setter_should_coerce_to_unique_integer_array
968 def test_notified_project_ids_setter_should_coerce_to_unique_integer_array
961 @jsmith.notified_project_ids = ["1", "123", "2u", "wrong", "12", 6, 12, -35, ""]
969 @jsmith.notified_project_ids = ["1", "123", "2u", "wrong", "12", 6, 12, -35, ""]
962 assert_equal [1, 123, 2, 12, 6], @jsmith.notified_projects_ids
970 assert_equal [1, 123, 2, 12, 6], @jsmith.notified_projects_ids
963 end
971 end
964
972
965 def test_mail_notification_all
973 def test_mail_notification_all
966 @jsmith.mail_notification = 'all'
974 @jsmith.mail_notification = 'all'
967 @jsmith.notified_project_ids = []
975 @jsmith.notified_project_ids = []
968 @jsmith.save
976 @jsmith.save
969 @jsmith.reload
977 @jsmith.reload
970 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
978 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
971 end
979 end
972
980
973 def test_mail_notification_selected
981 def test_mail_notification_selected
974 @jsmith.mail_notification = 'selected'
982 @jsmith.mail_notification = 'selected'
975 @jsmith.notified_project_ids = [1]
983 @jsmith.notified_project_ids = [1]
976 @jsmith.save
984 @jsmith.save
977 @jsmith.reload
985 @jsmith.reload
978 assert Project.find(1).recipients.include?(@jsmith.mail)
986 assert Project.find(1).recipients.include?(@jsmith.mail)
979 end
987 end
980
988
981 def test_mail_notification_only_my_events
989 def test_mail_notification_only_my_events
982 @jsmith.mail_notification = 'only_my_events'
990 @jsmith.mail_notification = 'only_my_events'
983 @jsmith.notified_project_ids = []
991 @jsmith.notified_project_ids = []
984 @jsmith.save
992 @jsmith.save
985 @jsmith.reload
993 @jsmith.reload
986 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
994 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
987 end
995 end
988
996
989 def test_comments_sorting_preference
997 def test_comments_sorting_preference
990 assert !@jsmith.wants_comments_in_reverse_order?
998 assert !@jsmith.wants_comments_in_reverse_order?
991 @jsmith.pref.comments_sorting = 'asc'
999 @jsmith.pref.comments_sorting = 'asc'
992 assert !@jsmith.wants_comments_in_reverse_order?
1000 assert !@jsmith.wants_comments_in_reverse_order?
993 @jsmith.pref.comments_sorting = 'desc'
1001 @jsmith.pref.comments_sorting = 'desc'
994 assert @jsmith.wants_comments_in_reverse_order?
1002 assert @jsmith.wants_comments_in_reverse_order?
995 end
1003 end
996
1004
997 def test_find_by_mail_should_be_case_insensitive
1005 def test_find_by_mail_should_be_case_insensitive
998 u = User.find_by_mail('JSmith@somenet.foo')
1006 u = User.find_by_mail('JSmith@somenet.foo')
999 assert_not_nil u
1007 assert_not_nil u
1000 assert_equal 'jsmith@somenet.foo', u.mail
1008 assert_equal 'jsmith@somenet.foo', u.mail
1001 end
1009 end
1002
1010
1003 def test_random_password
1011 def test_random_password
1004 u = User.new
1012 u = User.new
1005 u.random_password
1013 u.random_password
1006 assert !u.password.blank?
1014 assert !u.password.blank?
1007 assert !u.password_confirmation.blank?
1015 assert !u.password_confirmation.blank?
1008 end
1016 end
1009
1017
1010 test "#change_password_allowed? should be allowed if no auth source is set" do
1018 test "#change_password_allowed? should be allowed if no auth source is set" do
1011 user = User.generate!
1019 user = User.generate!
1012 assert user.change_password_allowed?
1020 assert user.change_password_allowed?
1013 end
1021 end
1014
1022
1015 test "#change_password_allowed? should delegate to the auth source" do
1023 test "#change_password_allowed? should delegate to the auth source" do
1016 user = User.generate!
1024 user = User.generate!
1017
1025
1018 allowed_auth_source = AuthSource.generate!
1026 allowed_auth_source = AuthSource.generate!
1019 def allowed_auth_source.allow_password_changes?; true; end
1027 def allowed_auth_source.allow_password_changes?; true; end
1020
1028
1021 denied_auth_source = AuthSource.generate!
1029 denied_auth_source = AuthSource.generate!
1022 def denied_auth_source.allow_password_changes?; false; end
1030 def denied_auth_source.allow_password_changes?; false; end
1023
1031
1024 assert user.change_password_allowed?
1032 assert user.change_password_allowed?
1025
1033
1026 user.auth_source = allowed_auth_source
1034 user.auth_source = allowed_auth_source
1027 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
1035 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
1028
1036
1029 user.auth_source = denied_auth_source
1037 user.auth_source = denied_auth_source
1030 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
1038 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
1031 end
1039 end
1032
1040
1033 def test_own_account_deletable_should_be_true_with_unsubscrive_enabled
1041 def test_own_account_deletable_should_be_true_with_unsubscrive_enabled
1034 with_settings :unsubscribe => '1' do
1042 with_settings :unsubscribe => '1' do
1035 assert_equal true, User.find(2).own_account_deletable?
1043 assert_equal true, User.find(2).own_account_deletable?
1036 end
1044 end
1037 end
1045 end
1038
1046
1039 def test_own_account_deletable_should_be_false_with_unsubscrive_disabled
1047 def test_own_account_deletable_should_be_false_with_unsubscrive_disabled
1040 with_settings :unsubscribe => '0' do
1048 with_settings :unsubscribe => '0' do
1041 assert_equal false, User.find(2).own_account_deletable?
1049 assert_equal false, User.find(2).own_account_deletable?
1042 end
1050 end
1043 end
1051 end
1044
1052
1045 def test_own_account_deletable_should_be_false_for_a_single_admin
1053 def test_own_account_deletable_should_be_false_for_a_single_admin
1046 User.delete_all(["admin = ? AND id <> ?", true, 1])
1054 User.delete_all(["admin = ? AND id <> ?", true, 1])
1047
1055
1048 with_settings :unsubscribe => '1' do
1056 with_settings :unsubscribe => '1' do
1049 assert_equal false, User.find(1).own_account_deletable?
1057 assert_equal false, User.find(1).own_account_deletable?
1050 end
1058 end
1051 end
1059 end
1052
1060
1053 def test_own_account_deletable_should_be_true_for_an_admin_if_other_admin_exists
1061 def test_own_account_deletable_should_be_true_for_an_admin_if_other_admin_exists
1054 User.generate! do |user|
1062 User.generate! do |user|
1055 user.admin = true
1063 user.admin = true
1056 end
1064 end
1057
1065
1058 with_settings :unsubscribe => '1' do
1066 with_settings :unsubscribe => '1' do
1059 assert_equal true, User.find(1).own_account_deletable?
1067 assert_equal true, User.find(1).own_account_deletable?
1060 end
1068 end
1061 end
1069 end
1062
1070
1063 test "#allowed_to? for archived project should return false" do
1071 test "#allowed_to? for archived project should return false" do
1064 project = Project.find(1)
1072 project = Project.find(1)
1065 project.archive
1073 project.archive
1066 project.reload
1074 project.reload
1067 assert_equal false, @admin.allowed_to?(:view_issues, project)
1075 assert_equal false, @admin.allowed_to?(:view_issues, project)
1068 end
1076 end
1069
1077
1070 test "#allowed_to? for closed project should return true for read actions" do
1078 test "#allowed_to? for closed project should return true for read actions" do
1071 project = Project.find(1)
1079 project = Project.find(1)
1072 project.close
1080 project.close
1073 project.reload
1081 project.reload
1074 assert_equal false, @admin.allowed_to?(:edit_project, project)
1082 assert_equal false, @admin.allowed_to?(:edit_project, project)
1075 assert_equal true, @admin.allowed_to?(:view_project, project)
1083 assert_equal true, @admin.allowed_to?(:view_project, project)
1076 end
1084 end
1077
1085
1078 test "#allowed_to? for project with module disabled should return false" do
1086 test "#allowed_to? for project with module disabled should return false" do
1079 project = Project.find(1)
1087 project = Project.find(1)
1080 project.enabled_module_names = ["issue_tracking"]
1088 project.enabled_module_names = ["issue_tracking"]
1081 assert_equal true, @admin.allowed_to?(:add_issues, project)
1089 assert_equal true, @admin.allowed_to?(:add_issues, project)
1082 assert_equal false, @admin.allowed_to?(:view_wiki_pages, project)
1090 assert_equal false, @admin.allowed_to?(:view_wiki_pages, project)
1083 end
1091 end
1084
1092
1085 test "#allowed_to? for admin users should return true" do
1093 test "#allowed_to? for admin users should return true" do
1086 project = Project.find(1)
1094 project = Project.find(1)
1087 assert ! @admin.member_of?(project)
1095 assert ! @admin.member_of?(project)
1088 %w(edit_issues delete_issues manage_news add_documents manage_wiki).each do |p|
1096 %w(edit_issues delete_issues manage_news add_documents manage_wiki).each do |p|
1089 assert_equal true, @admin.allowed_to?(p.to_sym, project)
1097 assert_equal true, @admin.allowed_to?(p.to_sym, project)
1090 end
1098 end
1091 end
1099 end
1092
1100
1093 test "#allowed_to? for normal users" do
1101 test "#allowed_to? for normal users" do
1094 project = Project.find(1)
1102 project = Project.find(1)
1095 assert_equal true, @jsmith.allowed_to?(:delete_messages, project) #Manager
1103 assert_equal true, @jsmith.allowed_to?(:delete_messages, project) #Manager
1096 assert_equal false, @dlopper.allowed_to?(:delete_messages, project) #Developper
1104 assert_equal false, @dlopper.allowed_to?(:delete_messages, project) #Developper
1097 end
1105 end
1098
1106
1099 test "#allowed_to? with empty array should return false" do
1107 test "#allowed_to? with empty array should return false" do
1100 assert_equal false, @admin.allowed_to?(:view_project, [])
1108 assert_equal false, @admin.allowed_to?(:view_project, [])
1101 end
1109 end
1102
1110
1103 test "#allowed_to? with multiple projects" do
1111 test "#allowed_to? with multiple projects" do
1104 assert_equal true, @admin.allowed_to?(:view_project, Project.all.to_a)
1112 assert_equal true, @admin.allowed_to?(:view_project, Project.all.to_a)
1105 assert_equal false, @dlopper.allowed_to?(:view_project, Project.all.to_a) #cannot see Project(2)
1113 assert_equal false, @dlopper.allowed_to?(:view_project, Project.all.to_a) #cannot see Project(2)
1106 assert_equal true, @jsmith.allowed_to?(:edit_issues, @jsmith.projects.to_a) #Manager or Developer everywhere
1114 assert_equal true, @jsmith.allowed_to?(:edit_issues, @jsmith.projects.to_a) #Manager or Developer everywhere
1107 assert_equal false, @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects.to_a) #Dev cannot delete_issue_watchers
1115 assert_equal false, @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects.to_a) #Dev cannot delete_issue_watchers
1108 end
1116 end
1109
1117
1110 test "#allowed_to? with with options[:global] should return true if user has one role with the permission" do
1118 test "#allowed_to? with with options[:global] should return true if user has one role with the permission" do
1111 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
1119 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
1112 @anonymous = User.find(6)
1120 @anonymous = User.find(6)
1113 assert_equal true, @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
1121 assert_equal true, @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
1114 assert_equal false, @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
1122 assert_equal false, @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
1115 assert_equal true, @dlopper2.allowed_to?(:add_issues, nil, :global => true)
1123 assert_equal true, @dlopper2.allowed_to?(:add_issues, nil, :global => true)
1116 assert_equal false, @anonymous.allowed_to?(:add_issues, nil, :global => true)
1124 assert_equal false, @anonymous.allowed_to?(:add_issues, nil, :global => true)
1117 assert_equal true, @anonymous.allowed_to?(:view_issues, nil, :global => true)
1125 assert_equal true, @anonymous.allowed_to?(:view_issues, nil, :global => true)
1118 end
1126 end
1119
1127
1120 # this is just a proxy method, the test only calls it to ensure it doesn't break trivially
1128 # this is just a proxy method, the test only calls it to ensure it doesn't break trivially
1121 test "#allowed_to_globally?" do
1129 test "#allowed_to_globally?" do
1122 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
1130 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
1123 @anonymous = User.find(6)
1131 @anonymous = User.find(6)
1124 assert_equal true, @jsmith.allowed_to_globally?(:delete_issue_watchers)
1132 assert_equal true, @jsmith.allowed_to_globally?(:delete_issue_watchers)
1125 assert_equal false, @dlopper2.allowed_to_globally?(:delete_issue_watchers)
1133 assert_equal false, @dlopper2.allowed_to_globally?(:delete_issue_watchers)
1126 assert_equal true, @dlopper2.allowed_to_globally?(:add_issues)
1134 assert_equal true, @dlopper2.allowed_to_globally?(:add_issues)
1127 assert_equal false, @anonymous.allowed_to_globally?(:add_issues)
1135 assert_equal false, @anonymous.allowed_to_globally?(:add_issues)
1128 assert_equal true, @anonymous.allowed_to_globally?(:view_issues)
1136 assert_equal true, @anonymous.allowed_to_globally?(:view_issues)
1129 end
1137 end
1130
1138
1131 def test_notify_about_issue
1139 def test_notify_about_issue
1132 project = Project.find(1)
1140 project = Project.find(1)
1133 author = User.generate!
1141 author = User.generate!
1134 assignee = User.generate!
1142 assignee = User.generate!
1135 member = User.generate!
1143 member = User.generate!
1136 Member.create!(:user => member, :project => project, :role_ids => [1])
1144 Member.create!(:user => member, :project => project, :role_ids => [1])
1137 issue = Issue.generate!(:project => project, :assigned_to => assignee, :author => author)
1145 issue = Issue.generate!(:project => project, :assigned_to => assignee, :author => author)
1138
1146
1139 tests = {
1147 tests = {
1140 author => %w(all only_my_events only_owner selected),
1148 author => %w(all only_my_events only_owner selected),
1141 assignee => %w(all only_my_events only_assigned selected),
1149 assignee => %w(all only_my_events only_assigned selected),
1142 member => %w(all)
1150 member => %w(all)
1143 }
1151 }
1144
1152
1145 tests.each do |user, expected|
1153 tests.each do |user, expected|
1146 User::MAIL_NOTIFICATION_OPTIONS.map(&:first).each do |option|
1154 User::MAIL_NOTIFICATION_OPTIONS.map(&:first).each do |option|
1147 user.mail_notification = option
1155 user.mail_notification = option
1148 assert_equal expected.include?(option), user.notify_about?(issue)
1156 assert_equal expected.include?(option), user.notify_about?(issue)
1149 end
1157 end
1150 end
1158 end
1151 end
1159 end
1152
1160
1153 def test_notify_about_issue_for_previous_assignee
1161 def test_notify_about_issue_for_previous_assignee
1154 assignee = User.generate!(:mail_notification => 'only_assigned')
1162 assignee = User.generate!(:mail_notification => 'only_assigned')
1155 new_assignee = User.generate!(:mail_notification => 'only_assigned')
1163 new_assignee = User.generate!(:mail_notification => 'only_assigned')
1156 issue = Issue.generate!(:assigned_to => assignee)
1164 issue = Issue.generate!(:assigned_to => assignee)
1157
1165
1158 assert assignee.notify_about?(issue)
1166 assert assignee.notify_about?(issue)
1159 assert !new_assignee.notify_about?(issue)
1167 assert !new_assignee.notify_about?(issue)
1160
1168
1161 issue.assigned_to = new_assignee
1169 issue.assigned_to = new_assignee
1162 assert assignee.notify_about?(issue)
1170 assert assignee.notify_about?(issue)
1163 assert new_assignee.notify_about?(issue)
1171 assert new_assignee.notify_about?(issue)
1164
1172
1165 issue.save!
1173 issue.save!
1166 assert !assignee.notify_about?(issue)
1174 assert !assignee.notify_about?(issue)
1167 assert new_assignee.notify_about?(issue)
1175 assert new_assignee.notify_about?(issue)
1168 end
1176 end
1169
1177
1170 def test_notify_about_news
1178 def test_notify_about_news
1171 user = User.generate!
1179 user = User.generate!
1172 news = News.new
1180 news = News.new
1173
1181
1174 User::MAIL_NOTIFICATION_OPTIONS.map(&:first).each do |option|
1182 User::MAIL_NOTIFICATION_OPTIONS.map(&:first).each do |option|
1175 user.mail_notification = option
1183 user.mail_notification = option
1176 assert_equal (option != 'none'), user.notify_about?(news)
1184 assert_equal (option != 'none'), user.notify_about?(news)
1177 end
1185 end
1178 end
1186 end
1179
1187
1180 def test_salt_unsalted_passwords
1188 def test_salt_unsalted_passwords
1181 # Restore a user with an unsalted password
1189 # Restore a user with an unsalted password
1182 user = User.find(1)
1190 user = User.find(1)
1183 user.salt = nil
1191 user.salt = nil
1184 user.hashed_password = User.hash_password("unsalted")
1192 user.hashed_password = User.hash_password("unsalted")
1185 user.save!
1193 user.save!
1186
1194
1187 User.salt_unsalted_passwords!
1195 User.salt_unsalted_passwords!
1188
1196
1189 user.reload
1197 user.reload
1190 # Salt added
1198 # Salt added
1191 assert !user.salt.blank?
1199 assert !user.salt.blank?
1192 # Password still valid
1200 # Password still valid
1193 assert user.check_password?("unsalted")
1201 assert user.check_password?("unsalted")
1194 assert_equal user, User.try_to_login(user.login, "unsalted")
1202 assert_equal user, User.try_to_login(user.login, "unsalted")
1195 end
1203 end
1196
1204
1197 if Object.const_defined?(:OpenID)
1205 if Object.const_defined?(:OpenID)
1198 def test_setting_identity_url
1206 def test_setting_identity_url
1199 normalized_open_id_url = 'http://example.com/'
1207 normalized_open_id_url = 'http://example.com/'
1200 u = User.new( :identity_url => 'http://example.com/' )
1208 u = User.new( :identity_url => 'http://example.com/' )
1201 assert_equal normalized_open_id_url, u.identity_url
1209 assert_equal normalized_open_id_url, u.identity_url
1202 end
1210 end
1203
1211
1204 def test_setting_identity_url_without_trailing_slash
1212 def test_setting_identity_url_without_trailing_slash
1205 normalized_open_id_url = 'http://example.com/'
1213 normalized_open_id_url = 'http://example.com/'
1206 u = User.new( :identity_url => 'http://example.com' )
1214 u = User.new( :identity_url => 'http://example.com' )
1207 assert_equal normalized_open_id_url, u.identity_url
1215 assert_equal normalized_open_id_url, u.identity_url
1208 end
1216 end
1209
1217
1210 def test_setting_identity_url_without_protocol
1218 def test_setting_identity_url_without_protocol
1211 normalized_open_id_url = 'http://example.com/'
1219 normalized_open_id_url = 'http://example.com/'
1212 u = User.new( :identity_url => 'example.com' )
1220 u = User.new( :identity_url => 'example.com' )
1213 assert_equal normalized_open_id_url, u.identity_url
1221 assert_equal normalized_open_id_url, u.identity_url
1214 end
1222 end
1215
1223
1216 def test_setting_blank_identity_url
1224 def test_setting_blank_identity_url
1217 u = User.new( :identity_url => 'example.com' )
1225 u = User.new( :identity_url => 'example.com' )
1218 u.identity_url = ''
1226 u.identity_url = ''
1219 assert u.identity_url.blank?
1227 assert u.identity_url.blank?
1220 end
1228 end
1221
1229
1222 def test_setting_invalid_identity_url
1230 def test_setting_invalid_identity_url
1223 u = User.new( :identity_url => 'this is not an openid url' )
1231 u = User.new( :identity_url => 'this is not an openid url' )
1224 assert u.identity_url.blank?
1232 assert u.identity_url.blank?
1225 end
1233 end
1226 else
1234 else
1227 puts "Skipping openid tests."
1235 puts "Skipping openid tests."
1228 end
1236 end
1229 end
1237 end
General Comments 0
You need to be logged in to leave comments. Login now