##// END OF EJS Templates
Adds named scopes for users index....
Jean-Philippe Lang -
r7961:f52410be1922
parent child
Show More
@@ -1,240 +1,234
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 UsersController < ApplicationController
18 class UsersController < ApplicationController
19 layout 'admin'
19 layout 'admin'
20
20
21 before_filter :require_admin, :except => :show
21 before_filter :require_admin, :except => :show
22 before_filter :find_user, :only => [:show, :edit, :update, :destroy, :edit_membership, :destroy_membership]
22 before_filter :find_user, :only => [:show, :edit, :update, :destroy, :edit_membership, :destroy_membership]
23 accept_api_auth :index, :show, :create, :update, :destroy
23 accept_api_auth :index, :show, :create, :update, :destroy
24
24
25 helper :sort
25 helper :sort
26 include SortHelper
26 include SortHelper
27 helper :custom_fields
27 helper :custom_fields
28 include CustomFieldsHelper
28 include CustomFieldsHelper
29
29
30 def index
30 def index
31 sort_init 'login', 'asc'
31 sort_init 'login', 'asc'
32 sort_update %w(login firstname lastname mail admin created_on last_login_on)
32 sort_update %w(login firstname lastname mail admin created_on last_login_on)
33
33
34 case params[:format]
34 case params[:format]
35 when 'xml', 'json'
35 when 'xml', 'json'
36 @offset, @limit = api_offset_and_limit
36 @offset, @limit = api_offset_and_limit
37 else
37 else
38 @limit = per_page_option
38 @limit = per_page_option
39 end
39 end
40
40
41 scope = User
41 @status = params[:status] || 1
42 scope = scope.in_group(params[:group_id].to_i) if params[:group_id].present?
43
42
44 @status = params[:status] ? params[:status].to_i : 1
43 scope = User.logged.status(@status)
45 c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
44 scope = scope.like(params[:name]) if params[:name].present?
45 scope = scope.in_group(params[:group_id]) if params[:group_id].present?
46
46
47 unless params[:name].blank?
47 @user_count = scope.count
48 name = "%#{params[:name].strip.downcase}%"
49 c << ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ? OR LOWER(mail) LIKE ?", name, name, name, name]
50 end
51
52 @user_count = scope.count(:conditions => c.conditions)
53 @user_pages = Paginator.new self, @user_count, @limit, params['page']
48 @user_pages = Paginator.new self, @user_count, @limit, params['page']
54 @offset ||= @user_pages.current.offset
49 @offset ||= @user_pages.current.offset
55 @users = scope.find :all,
50 @users = scope.find :all,
56 :order => sort_clause,
51 :order => sort_clause,
57 :conditions => c.conditions,
58 :limit => @limit,
52 :limit => @limit,
59 :offset => @offset
53 :offset => @offset
60
54
61 respond_to do |format|
55 respond_to do |format|
62 format.html {
56 format.html {
63 @groups = Group.all.sort
57 @groups = Group.all.sort
64 render :layout => !request.xhr?
58 render :layout => !request.xhr?
65 }
59 }
66 format.api
60 format.api
67 end
61 end
68 end
62 end
69
63
70 def show
64 def show
71 # show projects based on current user visibility
65 # show projects based on current user visibility
72 @memberships = @user.memberships.all(:conditions => Project.visible_condition(User.current))
66 @memberships = @user.memberships.all(:conditions => Project.visible_condition(User.current))
73
67
74 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
68 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
75 @events_by_day = events.group_by(&:event_date)
69 @events_by_day = events.group_by(&:event_date)
76
70
77 unless User.current.admin?
71 unless User.current.admin?
78 if !@user.active? || (@user != User.current && @memberships.empty? && events.empty?)
72 if !@user.active? || (@user != User.current && @memberships.empty? && events.empty?)
79 render_404
73 render_404
80 return
74 return
81 end
75 end
82 end
76 end
83
77
84 respond_to do |format|
78 respond_to do |format|
85 format.html { render :layout => 'base' }
79 format.html { render :layout => 'base' }
86 format.api
80 format.api
87 end
81 end
88 end
82 end
89
83
90 def new
84 def new
91 @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
85 @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
92 @auth_sources = AuthSource.find(:all)
86 @auth_sources = AuthSource.find(:all)
93 end
87 end
94
88
95 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
89 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
96 def create
90 def create
97 @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
91 @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
98 @user.safe_attributes = params[:user]
92 @user.safe_attributes = params[:user]
99 @user.admin = params[:user][:admin] || false
93 @user.admin = params[:user][:admin] || false
100 @user.login = params[:user][:login]
94 @user.login = params[:user][:login]
101 @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
95 @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
102
96
103 # TODO: Similar to My#account
97 # TODO: Similar to My#account
104 @user.pref.attributes = params[:pref]
98 @user.pref.attributes = params[:pref]
105 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
99 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
106
100
107 if @user.save
101 if @user.save
108 @user.pref.save
102 @user.pref.save
109 @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
103 @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
110
104
111 Mailer.deliver_account_information(@user, params[:user][:password]) if params[:send_information]
105 Mailer.deliver_account_information(@user, params[:user][:password]) if params[:send_information]
112
106
113 respond_to do |format|
107 respond_to do |format|
114 format.html {
108 format.html {
115 flash[:notice] = l(:notice_successful_create)
109 flash[:notice] = l(:notice_successful_create)
116 redirect_to(params[:continue] ?
110 redirect_to(params[:continue] ?
117 {:controller => 'users', :action => 'new'} :
111 {:controller => 'users', :action => 'new'} :
118 {:controller => 'users', :action => 'edit', :id => @user}
112 {:controller => 'users', :action => 'edit', :id => @user}
119 )
113 )
120 }
114 }
121 format.api { render :action => 'show', :status => :created, :location => user_url(@user) }
115 format.api { render :action => 'show', :status => :created, :location => user_url(@user) }
122 end
116 end
123 else
117 else
124 @auth_sources = AuthSource.find(:all)
118 @auth_sources = AuthSource.find(:all)
125 # Clear password input
119 # Clear password input
126 @user.password = @user.password_confirmation = nil
120 @user.password = @user.password_confirmation = nil
127
121
128 respond_to do |format|
122 respond_to do |format|
129 format.html { render :action => 'new' }
123 format.html { render :action => 'new' }
130 format.api { render_validation_errors(@user) }
124 format.api { render_validation_errors(@user) }
131 end
125 end
132 end
126 end
133 end
127 end
134
128
135 def edit
129 def edit
136 @auth_sources = AuthSource.find(:all)
130 @auth_sources = AuthSource.find(:all)
137 @membership ||= Member.new
131 @membership ||= Member.new
138 end
132 end
139
133
140 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
134 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
141 def update
135 def update
142 @user.admin = params[:user][:admin] if params[:user][:admin]
136 @user.admin = params[:user][:admin] if params[:user][:admin]
143 @user.login = params[:user][:login] if params[:user][:login]
137 @user.login = params[:user][:login] if params[:user][:login]
144 if params[:user][:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?)
138 if params[:user][:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?)
145 @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
139 @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
146 end
140 end
147 @user.safe_attributes = params[:user]
141 @user.safe_attributes = params[:user]
148 # Was the account actived ? (do it before User#save clears the change)
142 # Was the account actived ? (do it before User#save clears the change)
149 was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
143 was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
150 # TODO: Similar to My#account
144 # TODO: Similar to My#account
151 @user.pref.attributes = params[:pref]
145 @user.pref.attributes = params[:pref]
152 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
146 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
153
147
154 if @user.save
148 if @user.save
155 @user.pref.save
149 @user.pref.save
156 @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
150 @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
157
151
158 if was_activated
152 if was_activated
159 Mailer.deliver_account_activated(@user)
153 Mailer.deliver_account_activated(@user)
160 elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
154 elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
161 Mailer.deliver_account_information(@user, params[:user][:password])
155 Mailer.deliver_account_information(@user, params[:user][:password])
162 end
156 end
163
157
164 respond_to do |format|
158 respond_to do |format|
165 format.html {
159 format.html {
166 flash[:notice] = l(:notice_successful_update)
160 flash[:notice] = l(:notice_successful_update)
167 redirect_to :back
161 redirect_to :back
168 }
162 }
169 format.api { head :ok }
163 format.api { head :ok }
170 end
164 end
171 else
165 else
172 @auth_sources = AuthSource.find(:all)
166 @auth_sources = AuthSource.find(:all)
173 @membership ||= Member.new
167 @membership ||= Member.new
174 # Clear password input
168 # Clear password input
175 @user.password = @user.password_confirmation = nil
169 @user.password = @user.password_confirmation = nil
176
170
177 respond_to do |format|
171 respond_to do |format|
178 format.html { render :action => :edit }
172 format.html { render :action => :edit }
179 format.api { render_validation_errors(@user) }
173 format.api { render_validation_errors(@user) }
180 end
174 end
181 end
175 end
182 rescue ::ActionController::RedirectBackError
176 rescue ::ActionController::RedirectBackError
183 redirect_to :controller => 'users', :action => 'edit', :id => @user
177 redirect_to :controller => 'users', :action => 'edit', :id => @user
184 end
178 end
185
179
186 verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
180 verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
187 def destroy
181 def destroy
188 @user.destroy
182 @user.destroy
189 respond_to do |format|
183 respond_to do |format|
190 format.html { redirect_to(users_url) }
184 format.html { redirect_to(users_url) }
191 format.api { head :ok }
185 format.api { head :ok }
192 end
186 end
193 end
187 end
194
188
195 def edit_membership
189 def edit_membership
196 @membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
190 @membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
197 @membership.save if request.post?
191 @membership.save if request.post?
198 respond_to do |format|
192 respond_to do |format|
199 if @membership.valid?
193 if @membership.valid?
200 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
194 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
201 format.js {
195 format.js {
202 render(:update) {|page|
196 render(:update) {|page|
203 page.replace_html "tab-content-memberships", :partial => 'users/memberships'
197 page.replace_html "tab-content-memberships", :partial => 'users/memberships'
204 page.visual_effect(:highlight, "member-#{@membership.id}")
198 page.visual_effect(:highlight, "member-#{@membership.id}")
205 }
199 }
206 }
200 }
207 else
201 else
208 format.js {
202 format.js {
209 render(:update) {|page|
203 render(:update) {|page|
210 page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', ')))
204 page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', ')))
211 }
205 }
212 }
206 }
213 end
207 end
214 end
208 end
215 end
209 end
216
210
217 def destroy_membership
211 def destroy_membership
218 @membership = Member.find(params[:membership_id])
212 @membership = Member.find(params[:membership_id])
219 if request.post? && @membership.deletable?
213 if request.post? && @membership.deletable?
220 @membership.destroy
214 @membership.destroy
221 end
215 end
222 respond_to do |format|
216 respond_to do |format|
223 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
217 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
224 format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
218 format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
225 end
219 end
226 end
220 end
227
221
228 private
222 private
229
223
230 def find_user
224 def find_user
231 if params[:id] == 'current'
225 if params[:id] == 'current'
232 require_login || return
226 require_login || return
233 @user = User.current
227 @user = User.current
234 else
228 else
235 @user = User.find(params[:id])
229 @user = User.find(params[:id])
236 end
230 end
237 rescue ActiveRecord::RecordNotFound
231 rescue ActiveRecord::RecordNotFound
238 render_404
232 render_404
239 end
233 end
240 end
234 end
@@ -1,61 +1,61
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 module UsersHelper
18 module UsersHelper
19 def users_status_options_for_select(selected)
19 def users_status_options_for_select(selected)
20 user_count_by_status = User.count(:group => 'status').to_hash
20 user_count_by_status = User.count(:group => 'status').to_hash
21 options_for_select([[l(:label_all), ''],
21 options_for_select([[l(:label_all), ''],
22 ["#{l(:status_active)} (#{user_count_by_status[1].to_i})", 1],
22 ["#{l(:status_active)} (#{user_count_by_status[1].to_i})", '1'],
23 ["#{l(:status_registered)} (#{user_count_by_status[2].to_i})", 2],
23 ["#{l(:status_registered)} (#{user_count_by_status[2].to_i})", '2'],
24 ["#{l(:status_locked)} (#{user_count_by_status[3].to_i})", 3]], selected)
24 ["#{l(:status_locked)} (#{user_count_by_status[3].to_i})", '3']], selected.to_s)
25 end
25 end
26
26
27 # Options for the new membership projects combo-box
27 # Options for the new membership projects combo-box
28 def options_for_membership_project_select(user, projects)
28 def options_for_membership_project_select(user, projects)
29 options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
29 options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
30 options << project_tree_options_for_select(projects) do |p|
30 options << project_tree_options_for_select(projects) do |p|
31 {:disabled => (user.projects.include?(p))}
31 {:disabled => (user.projects.include?(p))}
32 end
32 end
33 options
33 options
34 end
34 end
35
35
36 def user_mail_notification_options(user)
36 def user_mail_notification_options(user)
37 user.valid_notification_options.collect {|o| [l(o.last), o.first]}
37 user.valid_notification_options.collect {|o| [l(o.last), o.first]}
38 end
38 end
39
39
40 def change_status_link(user)
40 def change_status_link(user)
41 url = {:controller => 'users', :action => 'update', :id => user, :page => params[:page], :status => params[:status], :tab => nil}
41 url = {:controller => 'users', :action => 'update', :id => user, :page => params[:page], :status => params[:status], :tab => nil}
42
42
43 if user.locked?
43 if user.locked?
44 link_to l(:button_unlock), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock'
44 link_to l(:button_unlock), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock'
45 elsif user.registered?
45 elsif user.registered?
46 link_to l(:button_activate), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock'
46 link_to l(:button_activate), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock'
47 elsif user != User.current
47 elsif user != User.current
48 link_to l(:button_lock), url.merge(:user => {:status => User::STATUS_LOCKED}), :method => :put, :class => 'icon icon-lock'
48 link_to l(:button_lock), url.merge(:user => {:status => User::STATUS_LOCKED}), :method => :put, :class => 'icon icon-lock'
49 end
49 end
50 end
50 end
51
51
52 def user_settings_tabs
52 def user_settings_tabs
53 tabs = [{:name => 'general', :partial => 'users/general', :label => :label_general},
53 tabs = [{:name => 'general', :partial => 'users/general', :label => :label_general},
54 {:name => 'memberships', :partial => 'users/memberships', :label => :label_project_plural}
54 {:name => 'memberships', :partial => 'users/memberships', :label => :label_project_plural}
55 ]
55 ]
56 if Group.all.any?
56 if Group.all.any?
57 tabs.insert 1, {:name => 'groups', :partial => 'users/groups', :label => :label_group_plural}
57 tabs.insert 1, {:name => 'groups', :partial => 'users/groups', :label => :label_group_plural}
58 end
58 end
59 tabs
59 tabs
60 end
60 end
61 end
61 end
@@ -1,636 +1,646
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 # Account statuses
23 # Account statuses
24 STATUS_ANONYMOUS = 0
24 STATUS_ANONYMOUS = 0
25 STATUS_ACTIVE = 1
25 STATUS_ACTIVE = 1
26 STATUS_REGISTERED = 2
26 STATUS_REGISTERED = 2
27 STATUS_LOCKED = 3
27 STATUS_LOCKED = 3
28
28
29 # Different ways of displaying/sorting users
29 # Different ways of displaying/sorting users
30 USER_FORMATS = {
30 USER_FORMATS = {
31 :firstname_lastname => {:string => '#{firstname} #{lastname}', :order => %w(firstname lastname id)},
31 :firstname_lastname => {:string => '#{firstname} #{lastname}', :order => %w(firstname lastname id)},
32 :firstname => {:string => '#{firstname}', :order => %w(firstname id)},
32 :firstname => {:string => '#{firstname}', :order => %w(firstname id)},
33 :lastname_firstname => {:string => '#{lastname} #{firstname}', :order => %w(lastname firstname id)},
33 :lastname_firstname => {:string => '#{lastname} #{firstname}', :order => %w(lastname firstname id)},
34 :lastname_coma_firstname => {:string => '#{lastname}, #{firstname}', :order => %w(lastname firstname id)},
34 :lastname_coma_firstname => {:string => '#{lastname}, #{firstname}', :order => %w(lastname firstname id)},
35 :username => {:string => '#{login}', :order => %w(login id)},
35 :username => {:string => '#{login}', :order => %w(login id)},
36 }
36 }
37
37
38 MAIL_NOTIFICATION_OPTIONS = [
38 MAIL_NOTIFICATION_OPTIONS = [
39 ['all', :label_user_mail_option_all],
39 ['all', :label_user_mail_option_all],
40 ['selected', :label_user_mail_option_selected],
40 ['selected', :label_user_mail_option_selected],
41 ['only_my_events', :label_user_mail_option_only_my_events],
41 ['only_my_events', :label_user_mail_option_only_my_events],
42 ['only_assigned', :label_user_mail_option_only_assigned],
42 ['only_assigned', :label_user_mail_option_only_assigned],
43 ['only_owner', :label_user_mail_option_only_owner],
43 ['only_owner', :label_user_mail_option_only_owner],
44 ['none', :label_user_mail_option_none]
44 ['none', :label_user_mail_option_none]
45 ]
45 ]
46
46
47 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
47 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
48 :after_remove => Proc.new {|user, group| group.user_removed(user)}
48 :after_remove => Proc.new {|user, group| group.user_removed(user)}
49 has_many :changesets, :dependent => :nullify
49 has_many :changesets, :dependent => :nullify
50 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
50 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
51 has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'"
51 has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'"
52 has_one :api_token, :class_name => 'Token', :conditions => "action='api'"
52 has_one :api_token, :class_name => 'Token', :conditions => "action='api'"
53 belongs_to :auth_source
53 belongs_to :auth_source
54
54
55 # Active non-anonymous users scope
55 # Active non-anonymous users scope
56 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
56 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
57 named_scope :logged, :conditions => "#{User.table_name}.status <> #{STATUS_ANONYMOUS}"
58 named_scope :status, lambda {|arg| arg.blank? ? {} : {:conditions => {:status => arg.to_i}} }
59 named_scope :like, lambda {|arg|
60 if arg.blank?
61 {}
62 else
63 pattern = "%#{arg.to_s.strip.downcase}%"
64 {:conditions => ["LOWER(login) LIKE :p OR LOWER(firstname) LIKE :p OR LOWER(lastname) LIKE :p OR LOWER(mail) LIKE :p", {:p => pattern}]}
65 end
66 }
57
67
58 acts_as_customizable
68 acts_as_customizable
59
69
60 attr_accessor :password, :password_confirmation
70 attr_accessor :password, :password_confirmation
61 attr_accessor :last_before_login_on
71 attr_accessor :last_before_login_on
62 # Prevents unauthorized assignments
72 # Prevents unauthorized assignments
63 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
73 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
64
74
65 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
75 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
66 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
76 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
67 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
77 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
68 # Login must contain lettres, numbers, underscores only
78 # Login must contain lettres, numbers, underscores only
69 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
79 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
70 validates_length_of :login, :maximum => 30
80 validates_length_of :login, :maximum => 30
71 validates_length_of :firstname, :lastname, :maximum => 30
81 validates_length_of :firstname, :lastname, :maximum => 30
72 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_blank => true
82 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_blank => true
73 validates_length_of :mail, :maximum => 60, :allow_nil => true
83 validates_length_of :mail, :maximum => 60, :allow_nil => true
74 validates_confirmation_of :password, :allow_nil => true
84 validates_confirmation_of :password, :allow_nil => true
75 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
85 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
76 validate :validate_password_length
86 validate :validate_password_length
77
87
78 before_create :set_mail_notification
88 before_create :set_mail_notification
79 before_save :update_hashed_password
89 before_save :update_hashed_password
80 before_destroy :remove_references_before_destroy
90 before_destroy :remove_references_before_destroy
81
91
82 named_scope :in_group, lambda {|group|
92 named_scope :in_group, lambda {|group|
83 group_id = group.is_a?(Group) ? group.id : group.to_i
93 group_id = group.is_a?(Group) ? group.id : group.to_i
84 { :conditions => ["#{User.table_name}.id IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id] }
94 { :conditions => ["#{User.table_name}.id IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id] }
85 }
95 }
86 named_scope :not_in_group, lambda {|group|
96 named_scope :not_in_group, lambda {|group|
87 group_id = group.is_a?(Group) ? group.id : group.to_i
97 group_id = group.is_a?(Group) ? group.id : group.to_i
88 { :conditions => ["#{User.table_name}.id NOT IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id] }
98 { :conditions => ["#{User.table_name}.id NOT IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id] }
89 }
99 }
90
100
91 def set_mail_notification
101 def set_mail_notification
92 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
102 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
93 true
103 true
94 end
104 end
95
105
96 def update_hashed_password
106 def update_hashed_password
97 # update hashed_password if password was set
107 # update hashed_password if password was set
98 if self.password && self.auth_source_id.blank?
108 if self.password && self.auth_source_id.blank?
99 salt_password(password)
109 salt_password(password)
100 end
110 end
101 end
111 end
102
112
103 def reload(*args)
113 def reload(*args)
104 @name = nil
114 @name = nil
105 @projects_by_role = nil
115 @projects_by_role = nil
106 super
116 super
107 end
117 end
108
118
109 def mail=(arg)
119 def mail=(arg)
110 write_attribute(:mail, arg.to_s.strip)
120 write_attribute(:mail, arg.to_s.strip)
111 end
121 end
112
122
113 def identity_url=(url)
123 def identity_url=(url)
114 if url.blank?
124 if url.blank?
115 write_attribute(:identity_url, '')
125 write_attribute(:identity_url, '')
116 else
126 else
117 begin
127 begin
118 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
128 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
119 rescue OpenIdAuthentication::InvalidOpenId
129 rescue OpenIdAuthentication::InvalidOpenId
120 # Invlaid url, don't save
130 # Invlaid url, don't save
121 end
131 end
122 end
132 end
123 self.read_attribute(:identity_url)
133 self.read_attribute(:identity_url)
124 end
134 end
125
135
126 # Returns the user that matches provided login and password, or nil
136 # Returns the user that matches provided login and password, or nil
127 def self.try_to_login(login, password)
137 def self.try_to_login(login, password)
128 # Make sure no one can sign in with an empty password
138 # Make sure no one can sign in with an empty password
129 return nil if password.to_s.empty?
139 return nil if password.to_s.empty?
130 user = find_by_login(login)
140 user = find_by_login(login)
131 if user
141 if user
132 # user is already in local database
142 # user is already in local database
133 return nil if !user.active?
143 return nil if !user.active?
134 if user.auth_source
144 if user.auth_source
135 # user has an external authentication method
145 # user has an external authentication method
136 return nil unless user.auth_source.authenticate(login, password)
146 return nil unless user.auth_source.authenticate(login, password)
137 else
147 else
138 # authentication with local password
148 # authentication with local password
139 return nil unless user.check_password?(password)
149 return nil unless user.check_password?(password)
140 end
150 end
141 else
151 else
142 # user is not yet registered, try to authenticate with available sources
152 # user is not yet registered, try to authenticate with available sources
143 attrs = AuthSource.authenticate(login, password)
153 attrs = AuthSource.authenticate(login, password)
144 if attrs
154 if attrs
145 user = new(attrs)
155 user = new(attrs)
146 user.login = login
156 user.login = login
147 user.language = Setting.default_language
157 user.language = Setting.default_language
148 if user.save
158 if user.save
149 user.reload
159 user.reload
150 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
160 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
151 end
161 end
152 end
162 end
153 end
163 end
154 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
164 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
155 user
165 user
156 rescue => text
166 rescue => text
157 raise text
167 raise text
158 end
168 end
159
169
160 # Returns the user who matches the given autologin +key+ or nil
170 # Returns the user who matches the given autologin +key+ or nil
161 def self.try_to_autologin(key)
171 def self.try_to_autologin(key)
162 tokens = Token.find_all_by_action_and_value('autologin', key)
172 tokens = Token.find_all_by_action_and_value('autologin', key)
163 # Make sure there's only 1 token that matches the key
173 # Make sure there's only 1 token that matches the key
164 if tokens.size == 1
174 if tokens.size == 1
165 token = tokens.first
175 token = tokens.first
166 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
176 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
167 token.user.update_attribute(:last_login_on, Time.now)
177 token.user.update_attribute(:last_login_on, Time.now)
168 token.user
178 token.user
169 end
179 end
170 end
180 end
171 end
181 end
172
182
173 def self.name_formatter(formatter = nil)
183 def self.name_formatter(formatter = nil)
174 USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
184 USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
175 end
185 end
176
186
177 # Returns an array of fields names than can be used to make an order statement for users
187 # Returns an array of fields names than can be used to make an order statement for users
178 # according to how user names are displayed
188 # according to how user names are displayed
179 # Examples:
189 # Examples:
180 #
190 #
181 # User.fields_for_order_statement => ['users.login', 'users.id']
191 # User.fields_for_order_statement => ['users.login', 'users.id']
182 # User.fields_for_order_statement('authors') => ['authors.login', 'authors.id']
192 # User.fields_for_order_statement('authors') => ['authors.login', 'authors.id']
183 def self.fields_for_order_statement(table=nil)
193 def self.fields_for_order_statement(table=nil)
184 table ||= table_name
194 table ||= table_name
185 name_formatter[:order].map {|field| "#{table}.#{field}"}
195 name_formatter[:order].map {|field| "#{table}.#{field}"}
186 end
196 end
187
197
188 # Return user's full name for display
198 # Return user's full name for display
189 def name(formatter = nil)
199 def name(formatter = nil)
190 f = self.class.name_formatter(formatter)
200 f = self.class.name_formatter(formatter)
191 if formatter
201 if formatter
192 eval('"' + f[:string] + '"')
202 eval('"' + f[:string] + '"')
193 else
203 else
194 @name ||= eval('"' + f[:string] + '"')
204 @name ||= eval('"' + f[:string] + '"')
195 end
205 end
196 end
206 end
197
207
198 def active?
208 def active?
199 self.status == STATUS_ACTIVE
209 self.status == STATUS_ACTIVE
200 end
210 end
201
211
202 def registered?
212 def registered?
203 self.status == STATUS_REGISTERED
213 self.status == STATUS_REGISTERED
204 end
214 end
205
215
206 def locked?
216 def locked?
207 self.status == STATUS_LOCKED
217 self.status == STATUS_LOCKED
208 end
218 end
209
219
210 def activate
220 def activate
211 self.status = STATUS_ACTIVE
221 self.status = STATUS_ACTIVE
212 end
222 end
213
223
214 def register
224 def register
215 self.status = STATUS_REGISTERED
225 self.status = STATUS_REGISTERED
216 end
226 end
217
227
218 def lock
228 def lock
219 self.status = STATUS_LOCKED
229 self.status = STATUS_LOCKED
220 end
230 end
221
231
222 def activate!
232 def activate!
223 update_attribute(:status, STATUS_ACTIVE)
233 update_attribute(:status, STATUS_ACTIVE)
224 end
234 end
225
235
226 def register!
236 def register!
227 update_attribute(:status, STATUS_REGISTERED)
237 update_attribute(:status, STATUS_REGISTERED)
228 end
238 end
229
239
230 def lock!
240 def lock!
231 update_attribute(:status, STATUS_LOCKED)
241 update_attribute(:status, STATUS_LOCKED)
232 end
242 end
233
243
234 # Returns true if +clear_password+ is the correct user's password, otherwise false
244 # Returns true if +clear_password+ is the correct user's password, otherwise false
235 def check_password?(clear_password)
245 def check_password?(clear_password)
236 if auth_source_id.present?
246 if auth_source_id.present?
237 auth_source.authenticate(self.login, clear_password)
247 auth_source.authenticate(self.login, clear_password)
238 else
248 else
239 User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password
249 User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password
240 end
250 end
241 end
251 end
242
252
243 # Generates a random salt and computes hashed_password for +clear_password+
253 # Generates a random salt and computes hashed_password for +clear_password+
244 # The hashed password is stored in the following form: SHA1(salt + SHA1(password))
254 # The hashed password is stored in the following form: SHA1(salt + SHA1(password))
245 def salt_password(clear_password)
255 def salt_password(clear_password)
246 self.salt = User.generate_salt
256 self.salt = User.generate_salt
247 self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
257 self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
248 end
258 end
249
259
250 # Does the backend storage allow this user to change their password?
260 # Does the backend storage allow this user to change their password?
251 def change_password_allowed?
261 def change_password_allowed?
252 return true if auth_source_id.blank?
262 return true if auth_source_id.blank?
253 return auth_source.allow_password_changes?
263 return auth_source.allow_password_changes?
254 end
264 end
255
265
256 # Generate and set a random password. Useful for automated user creation
266 # Generate and set a random password. Useful for automated user creation
257 # Based on Token#generate_token_value
267 # Based on Token#generate_token_value
258 #
268 #
259 def random_password
269 def random_password
260 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
270 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
261 password = ''
271 password = ''
262 40.times { |i| password << chars[rand(chars.size-1)] }
272 40.times { |i| password << chars[rand(chars.size-1)] }
263 self.password = password
273 self.password = password
264 self.password_confirmation = password
274 self.password_confirmation = password
265 self
275 self
266 end
276 end
267
277
268 def pref
278 def pref
269 self.preference ||= UserPreference.new(:user => self)
279 self.preference ||= UserPreference.new(:user => self)
270 end
280 end
271
281
272 def time_zone
282 def time_zone
273 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
283 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
274 end
284 end
275
285
276 def wants_comments_in_reverse_order?
286 def wants_comments_in_reverse_order?
277 self.pref[:comments_sorting] == 'desc'
287 self.pref[:comments_sorting] == 'desc'
278 end
288 end
279
289
280 # Return user's RSS key (a 40 chars long string), used to access feeds
290 # Return user's RSS key (a 40 chars long string), used to access feeds
281 def rss_key
291 def rss_key
282 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
292 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
283 token.value
293 token.value
284 end
294 end
285
295
286 # Return user's API key (a 40 chars long string), used to access the API
296 # Return user's API key (a 40 chars long string), used to access the API
287 def api_key
297 def api_key
288 token = self.api_token || self.create_api_token(:action => 'api')
298 token = self.api_token || self.create_api_token(:action => 'api')
289 token.value
299 token.value
290 end
300 end
291
301
292 # Return an array of project ids for which the user has explicitly turned mail notifications on
302 # Return an array of project ids for which the user has explicitly turned mail notifications on
293 def notified_projects_ids
303 def notified_projects_ids
294 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
304 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
295 end
305 end
296
306
297 def notified_project_ids=(ids)
307 def notified_project_ids=(ids)
298 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
308 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
299 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
309 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
300 @notified_projects_ids = nil
310 @notified_projects_ids = nil
301 notified_projects_ids
311 notified_projects_ids
302 end
312 end
303
313
304 def valid_notification_options
314 def valid_notification_options
305 self.class.valid_notification_options(self)
315 self.class.valid_notification_options(self)
306 end
316 end
307
317
308 # Only users that belong to more than 1 project can select projects for which they are notified
318 # Only users that belong to more than 1 project can select projects for which they are notified
309 def self.valid_notification_options(user=nil)
319 def self.valid_notification_options(user=nil)
310 # Note that @user.membership.size would fail since AR ignores
320 # Note that @user.membership.size would fail since AR ignores
311 # :include association option when doing a count
321 # :include association option when doing a count
312 if user.nil? || user.memberships.length < 1
322 if user.nil? || user.memberships.length < 1
313 MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
323 MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
314 else
324 else
315 MAIL_NOTIFICATION_OPTIONS
325 MAIL_NOTIFICATION_OPTIONS
316 end
326 end
317 end
327 end
318
328
319 # Find a user account by matching the exact login and then a case-insensitive
329 # Find a user account by matching the exact login and then a case-insensitive
320 # version. Exact matches will be given priority.
330 # version. Exact matches will be given priority.
321 def self.find_by_login(login)
331 def self.find_by_login(login)
322 # force string comparison to be case sensitive on MySQL
332 # force string comparison to be case sensitive on MySQL
323 type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : ''
333 type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : ''
324
334
325 # First look for an exact match
335 # First look for an exact match
326 user = first(:conditions => ["#{type_cast} login = ?", login])
336 user = first(:conditions => ["#{type_cast} login = ?", login])
327 # Fail over to case-insensitive if none was found
337 # Fail over to case-insensitive if none was found
328 user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase])
338 user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase])
329 end
339 end
330
340
331 def self.find_by_rss_key(key)
341 def self.find_by_rss_key(key)
332 token = Token.find_by_value(key)
342 token = Token.find_by_value(key)
333 token && token.user.active? ? token.user : nil
343 token && token.user.active? ? token.user : nil
334 end
344 end
335
345
336 def self.find_by_api_key(key)
346 def self.find_by_api_key(key)
337 token = Token.find_by_action_and_value('api', key)
347 token = Token.find_by_action_and_value('api', key)
338 token && token.user.active? ? token.user : nil
348 token && token.user.active? ? token.user : nil
339 end
349 end
340
350
341 # Makes find_by_mail case-insensitive
351 # Makes find_by_mail case-insensitive
342 def self.find_by_mail(mail)
352 def self.find_by_mail(mail)
343 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
353 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
344 end
354 end
345
355
346 def to_s
356 def to_s
347 name
357 name
348 end
358 end
349
359
350 # Returns the current day according to user's time zone
360 # Returns the current day according to user's time zone
351 def today
361 def today
352 if time_zone.nil?
362 if time_zone.nil?
353 Date.today
363 Date.today
354 else
364 else
355 Time.now.in_time_zone(time_zone).to_date
365 Time.now.in_time_zone(time_zone).to_date
356 end
366 end
357 end
367 end
358
368
359 def logged?
369 def logged?
360 true
370 true
361 end
371 end
362
372
363 def anonymous?
373 def anonymous?
364 !logged?
374 !logged?
365 end
375 end
366
376
367 # Return user's roles for project
377 # Return user's roles for project
368 def roles_for_project(project)
378 def roles_for_project(project)
369 roles = []
379 roles = []
370 # No role on archived projects
380 # No role on archived projects
371 return roles unless project && project.active?
381 return roles unless project && project.active?
372 if logged?
382 if logged?
373 # Find project membership
383 # Find project membership
374 membership = memberships.detect {|m| m.project_id == project.id}
384 membership = memberships.detect {|m| m.project_id == project.id}
375 if membership
385 if membership
376 roles = membership.roles
386 roles = membership.roles
377 else
387 else
378 @role_non_member ||= Role.non_member
388 @role_non_member ||= Role.non_member
379 roles << @role_non_member
389 roles << @role_non_member
380 end
390 end
381 else
391 else
382 @role_anonymous ||= Role.anonymous
392 @role_anonymous ||= Role.anonymous
383 roles << @role_anonymous
393 roles << @role_anonymous
384 end
394 end
385 roles
395 roles
386 end
396 end
387
397
388 # Return true if the user is a member of project
398 # Return true if the user is a member of project
389 def member_of?(project)
399 def member_of?(project)
390 !roles_for_project(project).detect {|role| role.member?}.nil?
400 !roles_for_project(project).detect {|role| role.member?}.nil?
391 end
401 end
392
402
393 # Returns a hash of user's projects grouped by roles
403 # Returns a hash of user's projects grouped by roles
394 def projects_by_role
404 def projects_by_role
395 return @projects_by_role if @projects_by_role
405 return @projects_by_role if @projects_by_role
396
406
397 @projects_by_role = Hash.new {|h,k| h[k]=[]}
407 @projects_by_role = Hash.new {|h,k| h[k]=[]}
398 memberships.each do |membership|
408 memberships.each do |membership|
399 membership.roles.each do |role|
409 membership.roles.each do |role|
400 @projects_by_role[role] << membership.project if membership.project
410 @projects_by_role[role] << membership.project if membership.project
401 end
411 end
402 end
412 end
403 @projects_by_role.each do |role, projects|
413 @projects_by_role.each do |role, projects|
404 projects.uniq!
414 projects.uniq!
405 end
415 end
406
416
407 @projects_by_role
417 @projects_by_role
408 end
418 end
409
419
410 # Returns true if user is arg or belongs to arg
420 # Returns true if user is arg or belongs to arg
411 def is_or_belongs_to?(arg)
421 def is_or_belongs_to?(arg)
412 if arg.is_a?(User)
422 if arg.is_a?(User)
413 self == arg
423 self == arg
414 elsif arg.is_a?(Group)
424 elsif arg.is_a?(Group)
415 arg.users.include?(self)
425 arg.users.include?(self)
416 else
426 else
417 false
427 false
418 end
428 end
419 end
429 end
420
430
421 # Return true if the user is allowed to do the specified action on a specific context
431 # Return true if the user is allowed to do the specified action on a specific context
422 # Action can be:
432 # Action can be:
423 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
433 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
424 # * a permission Symbol (eg. :edit_project)
434 # * a permission Symbol (eg. :edit_project)
425 # Context can be:
435 # Context can be:
426 # * a project : returns true if user is allowed to do the specified action on this project
436 # * a project : returns true if user is allowed to do the specified action on this project
427 # * an array of projects : returns true if user is allowed on every project
437 # * an array of projects : returns true if user is allowed on every project
428 # * nil with options[:global] set : check if user has at least one role allowed for this action,
438 # * nil with options[:global] set : check if user has at least one role allowed for this action,
429 # or falls back to Non Member / Anonymous permissions depending if the user is logged
439 # or falls back to Non Member / Anonymous permissions depending if the user is logged
430 def allowed_to?(action, context, options={}, &block)
440 def allowed_to?(action, context, options={}, &block)
431 if context && context.is_a?(Project)
441 if context && context.is_a?(Project)
432 # No action allowed on archived projects
442 # No action allowed on archived projects
433 return false unless context.active?
443 return false unless context.active?
434 # No action allowed on disabled modules
444 # No action allowed on disabled modules
435 return false unless context.allows_to?(action)
445 return false unless context.allows_to?(action)
436 # Admin users are authorized for anything else
446 # Admin users are authorized for anything else
437 return true if admin?
447 return true if admin?
438
448
439 roles = roles_for_project(context)
449 roles = roles_for_project(context)
440 return false unless roles
450 return false unless roles
441 roles.detect {|role|
451 roles.detect {|role|
442 (context.is_public? || role.member?) &&
452 (context.is_public? || role.member?) &&
443 role.allowed_to?(action) &&
453 role.allowed_to?(action) &&
444 (block_given? ? yield(role, self) : true)
454 (block_given? ? yield(role, self) : true)
445 }
455 }
446 elsif context && context.is_a?(Array)
456 elsif context && context.is_a?(Array)
447 # Authorize if user is authorized on every element of the array
457 # Authorize if user is authorized on every element of the array
448 context.map do |project|
458 context.map do |project|
449 allowed_to?(action, project, options, &block)
459 allowed_to?(action, project, options, &block)
450 end.inject do |memo,allowed|
460 end.inject do |memo,allowed|
451 memo && allowed
461 memo && allowed
452 end
462 end
453 elsif options[:global]
463 elsif options[:global]
454 # Admin users are always authorized
464 # Admin users are always authorized
455 return true if admin?
465 return true if admin?
456
466
457 # authorize if user has at least one role that has this permission
467 # authorize if user has at least one role that has this permission
458 roles = memberships.collect {|m| m.roles}.flatten.uniq
468 roles = memberships.collect {|m| m.roles}.flatten.uniq
459 roles << (self.logged? ? Role.non_member : Role.anonymous)
469 roles << (self.logged? ? Role.non_member : Role.anonymous)
460 roles.detect {|role|
470 roles.detect {|role|
461 role.allowed_to?(action) &&
471 role.allowed_to?(action) &&
462 (block_given? ? yield(role, self) : true)
472 (block_given? ? yield(role, self) : true)
463 }
473 }
464 else
474 else
465 false
475 false
466 end
476 end
467 end
477 end
468
478
469 # Is the user allowed to do the specified action on any project?
479 # Is the user allowed to do the specified action on any project?
470 # See allowed_to? for the actions and valid options.
480 # See allowed_to? for the actions and valid options.
471 def allowed_to_globally?(action, options, &block)
481 def allowed_to_globally?(action, options, &block)
472 allowed_to?(action, nil, options.reverse_merge(:global => true), &block)
482 allowed_to?(action, nil, options.reverse_merge(:global => true), &block)
473 end
483 end
474
484
475 safe_attributes 'login',
485 safe_attributes 'login',
476 'firstname',
486 'firstname',
477 'lastname',
487 'lastname',
478 'mail',
488 'mail',
479 'mail_notification',
489 'mail_notification',
480 'language',
490 'language',
481 'custom_field_values',
491 'custom_field_values',
482 'custom_fields',
492 'custom_fields',
483 'identity_url'
493 'identity_url'
484
494
485 safe_attributes 'status',
495 safe_attributes 'status',
486 'auth_source_id',
496 'auth_source_id',
487 :if => lambda {|user, current_user| current_user.admin?}
497 :if => lambda {|user, current_user| current_user.admin?}
488
498
489 safe_attributes 'group_ids',
499 safe_attributes 'group_ids',
490 :if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
500 :if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
491
501
492 # Utility method to help check if a user should be notified about an
502 # Utility method to help check if a user should be notified about an
493 # event.
503 # event.
494 #
504 #
495 # TODO: only supports Issue events currently
505 # TODO: only supports Issue events currently
496 def notify_about?(object)
506 def notify_about?(object)
497 case mail_notification
507 case mail_notification
498 when 'all'
508 when 'all'
499 true
509 true
500 when 'selected'
510 when 'selected'
501 # user receives notifications for created/assigned issues on unselected projects
511 # user receives notifications for created/assigned issues on unselected projects
502 if object.is_a?(Issue) && (object.author == self || is_or_belongs_to?(object.assigned_to))
512 if object.is_a?(Issue) && (object.author == self || is_or_belongs_to?(object.assigned_to))
503 true
513 true
504 else
514 else
505 false
515 false
506 end
516 end
507 when 'none'
517 when 'none'
508 false
518 false
509 when 'only_my_events'
519 when 'only_my_events'
510 if object.is_a?(Issue) && (object.author == self || is_or_belongs_to?(object.assigned_to))
520 if object.is_a?(Issue) && (object.author == self || is_or_belongs_to?(object.assigned_to))
511 true
521 true
512 else
522 else
513 false
523 false
514 end
524 end
515 when 'only_assigned'
525 when 'only_assigned'
516 if object.is_a?(Issue) && is_or_belongs_to?(object.assigned_to)
526 if object.is_a?(Issue) && is_or_belongs_to?(object.assigned_to)
517 true
527 true
518 else
528 else
519 false
529 false
520 end
530 end
521 when 'only_owner'
531 when 'only_owner'
522 if object.is_a?(Issue) && object.author == self
532 if object.is_a?(Issue) && object.author == self
523 true
533 true
524 else
534 else
525 false
535 false
526 end
536 end
527 else
537 else
528 false
538 false
529 end
539 end
530 end
540 end
531
541
532 def self.current=(user)
542 def self.current=(user)
533 @current_user = user
543 @current_user = user
534 end
544 end
535
545
536 def self.current
546 def self.current
537 @current_user ||= User.anonymous
547 @current_user ||= User.anonymous
538 end
548 end
539
549
540 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
550 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
541 # one anonymous user per database.
551 # one anonymous user per database.
542 def self.anonymous
552 def self.anonymous
543 anonymous_user = AnonymousUser.find(:first)
553 anonymous_user = AnonymousUser.find(:first)
544 if anonymous_user.nil?
554 if anonymous_user.nil?
545 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
555 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
546 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
556 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
547 end
557 end
548 anonymous_user
558 anonymous_user
549 end
559 end
550
560
551 # Salts all existing unsalted passwords
561 # Salts all existing unsalted passwords
552 # It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password))
562 # It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password))
553 # This method is used in the SaltPasswords migration and is to be kept as is
563 # This method is used in the SaltPasswords migration and is to be kept as is
554 def self.salt_unsalted_passwords!
564 def self.salt_unsalted_passwords!
555 transaction do
565 transaction do
556 User.find_each(:conditions => "salt IS NULL OR salt = ''") do |user|
566 User.find_each(:conditions => "salt IS NULL OR salt = ''") do |user|
557 next if user.hashed_password.blank?
567 next if user.hashed_password.blank?
558 salt = User.generate_salt
568 salt = User.generate_salt
559 hashed_password = User.hash_password("#{salt}#{user.hashed_password}")
569 hashed_password = User.hash_password("#{salt}#{user.hashed_password}")
560 User.update_all("salt = '#{salt}', hashed_password = '#{hashed_password}'", ["id = ?", user.id] )
570 User.update_all("salt = '#{salt}', hashed_password = '#{hashed_password}'", ["id = ?", user.id] )
561 end
571 end
562 end
572 end
563 end
573 end
564
574
565 protected
575 protected
566
576
567 def validate_password_length
577 def validate_password_length
568 # Password length validation based on setting
578 # Password length validation based on setting
569 if !password.nil? && password.size < Setting.password_min_length.to_i
579 if !password.nil? && password.size < Setting.password_min_length.to_i
570 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
580 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
571 end
581 end
572 end
582 end
573
583
574 private
584 private
575
585
576 # Removes references that are not handled by associations
586 # Removes references that are not handled by associations
577 # Things that are not deleted are reassociated with the anonymous user
587 # Things that are not deleted are reassociated with the anonymous user
578 def remove_references_before_destroy
588 def remove_references_before_destroy
579 return if self.id.nil?
589 return if self.id.nil?
580
590
581 substitute = User.anonymous
591 substitute = User.anonymous
582 Attachment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
592 Attachment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
583 Comment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
593 Comment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
584 Issue.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
594 Issue.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
585 Issue.update_all 'assigned_to_id = NULL', ['assigned_to_id = ?', id]
595 Issue.update_all 'assigned_to_id = NULL', ['assigned_to_id = ?', id]
586 Journal.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
596 Journal.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
587 JournalDetail.update_all ['old_value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]
597 JournalDetail.update_all ['old_value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]
588 JournalDetail.update_all ['value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]
598 JournalDetail.update_all ['value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]
589 Message.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
599 Message.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
590 News.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
600 News.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
591 # Remove private queries and keep public ones
601 # Remove private queries and keep public ones
592 Query.delete_all ['user_id = ? AND is_public = ?', id, false]
602 Query.delete_all ['user_id = ? AND is_public = ?', id, false]
593 Query.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
603 Query.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
594 TimeEntry.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
604 TimeEntry.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
595 Token.delete_all ['user_id = ?', id]
605 Token.delete_all ['user_id = ?', id]
596 Watcher.delete_all ['user_id = ?', id]
606 Watcher.delete_all ['user_id = ?', id]
597 WikiContent.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
607 WikiContent.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
598 WikiContent::Version.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
608 WikiContent::Version.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
599 end
609 end
600
610
601 # Return password digest
611 # Return password digest
602 def self.hash_password(clear_password)
612 def self.hash_password(clear_password)
603 Digest::SHA1.hexdigest(clear_password || "")
613 Digest::SHA1.hexdigest(clear_password || "")
604 end
614 end
605
615
606 # Returns a 128bits random salt as a hex string (32 chars long)
616 # Returns a 128bits random salt as a hex string (32 chars long)
607 def self.generate_salt
617 def self.generate_salt
608 ActiveSupport::SecureRandom.hex(16)
618 ActiveSupport::SecureRandom.hex(16)
609 end
619 end
610
620
611 end
621 end
612
622
613 class AnonymousUser < User
623 class AnonymousUser < User
614
624
615 def validate_on_create
625 def validate_on_create
616 # There should be only one AnonymousUser in the database
626 # There should be only one AnonymousUser in the database
617 errors.add :base, 'An anonymous user already exists.' if AnonymousUser.find(:first)
627 errors.add :base, 'An anonymous user already exists.' if AnonymousUser.find(:first)
618 end
628 end
619
629
620 def available_custom_fields
630 def available_custom_fields
621 []
631 []
622 end
632 end
623
633
624 # Overrides a few properties
634 # Overrides a few properties
625 def logged?; false end
635 def logged?; false end
626 def admin; false end
636 def admin; false end
627 def name(*args); I18n.t(:label_user_anonymous) end
637 def name(*args); I18n.t(:label_user_anonymous) end
628 def mail; nil end
638 def mail; nil end
629 def time_zone; nil end
639 def time_zone; nil end
630 def rss_key; nil end
640 def rss_key; nil end
631
641
632 # Anonymous user can not be destroyed
642 # Anonymous user can not be destroyed
633 def destroy
643 def destroy
634 false
644 false
635 end
645 end
636 end
646 end
@@ -1,312 +1,320
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 require 'users_controller'
19 require 'users_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class UsersController; def rescue_action(e) raise e end; end
22 class UsersController; def rescue_action(e) raise e end; end
23
23
24 class UsersControllerTest < ActionController::TestCase
24 class UsersControllerTest < ActionController::TestCase
25 include Redmine::I18n
25 include Redmine::I18n
26
26
27 fixtures :users, :projects, :members, :member_roles, :roles, :auth_sources, :custom_fields, :custom_values, :groups_users
27 fixtures :users, :projects, :members, :member_roles, :roles, :auth_sources, :custom_fields, :custom_values, :groups_users
28
28
29 def setup
29 def setup
30 @controller = UsersController.new
30 @controller = UsersController.new
31 @request = ActionController::TestRequest.new
31 @request = ActionController::TestRequest.new
32 @response = ActionController::TestResponse.new
32 @response = ActionController::TestResponse.new
33 User.current = nil
33 User.current = nil
34 @request.session[:user_id] = 1 # admin
34 @request.session[:user_id] = 1 # admin
35 end
35 end
36
36
37 def test_index
37 def test_index
38 get :index
38 get :index
39 assert_response :success
39 assert_response :success
40 assert_template 'index'
40 assert_template 'index'
41 end
41 end
42
42
43 def test_index
43 def test_index
44 get :index
44 get :index
45 assert_response :success
45 assert_response :success
46 assert_template 'index'
46 assert_template 'index'
47 assert_not_nil assigns(:users)
47 assert_not_nil assigns(:users)
48 # active users only
48 # active users only
49 assert_nil assigns(:users).detect {|u| !u.active?}
49 assert_nil assigns(:users).detect {|u| !u.active?}
50 end
50 end
51
51
52 def test_index_with_status_filter
53 get :index, :status => 3
54 assert_response :success
55 assert_template 'index'
56 assert_not_nil assigns(:users)
57 assert_equal [3], assigns(:users).map(&:status).uniq
58 end
59
52 def test_index_with_name_filter
60 def test_index_with_name_filter
53 get :index, :name => 'john'
61 get :index, :name => 'john'
54 assert_response :success
62 assert_response :success
55 assert_template 'index'
63 assert_template 'index'
56 users = assigns(:users)
64 users = assigns(:users)
57 assert_not_nil users
65 assert_not_nil users
58 assert_equal 1, users.size
66 assert_equal 1, users.size
59 assert_equal 'John', users.first.firstname
67 assert_equal 'John', users.first.firstname
60 end
68 end
61
69
62 def test_index_with_group_filter
70 def test_index_with_group_filter
63 get :index, :group_id => '10'
71 get :index, :group_id => '10'
64 assert_response :success
72 assert_response :success
65 assert_template 'index'
73 assert_template 'index'
66 users = assigns(:users)
74 users = assigns(:users)
67 assert users.any?
75 assert users.any?
68 assert_equal([], (users - Group.find(10).users))
76 assert_equal([], (users - Group.find(10).users))
69 end
77 end
70
78
71 def test_show
79 def test_show
72 @request.session[:user_id] = nil
80 @request.session[:user_id] = nil
73 get :show, :id => 2
81 get :show, :id => 2
74 assert_response :success
82 assert_response :success
75 assert_template 'show'
83 assert_template 'show'
76 assert_not_nil assigns(:user)
84 assert_not_nil assigns(:user)
77
85
78 assert_tag 'li', :content => /Phone number/
86 assert_tag 'li', :content => /Phone number/
79 end
87 end
80
88
81 def test_show_should_not_display_hidden_custom_fields
89 def test_show_should_not_display_hidden_custom_fields
82 @request.session[:user_id] = nil
90 @request.session[:user_id] = nil
83 UserCustomField.find_by_name('Phone number').update_attribute :visible, false
91 UserCustomField.find_by_name('Phone number').update_attribute :visible, false
84 get :show, :id => 2
92 get :show, :id => 2
85 assert_response :success
93 assert_response :success
86 assert_template 'show'
94 assert_template 'show'
87 assert_not_nil assigns(:user)
95 assert_not_nil assigns(:user)
88
96
89 assert_no_tag 'li', :content => /Phone number/
97 assert_no_tag 'li', :content => /Phone number/
90 end
98 end
91
99
92 def test_show_should_not_fail_when_custom_values_are_nil
100 def test_show_should_not_fail_when_custom_values_are_nil
93 user = User.find(2)
101 user = User.find(2)
94
102
95 # Create a custom field to illustrate the issue
103 # Create a custom field to illustrate the issue
96 custom_field = CustomField.create!(:name => 'Testing', :field_format => 'text')
104 custom_field = CustomField.create!(:name => 'Testing', :field_format => 'text')
97 custom_value = user.custom_values.build(:custom_field => custom_field).save!
105 custom_value = user.custom_values.build(:custom_field => custom_field).save!
98
106
99 get :show, :id => 2
107 get :show, :id => 2
100 assert_response :success
108 assert_response :success
101 end
109 end
102
110
103 def test_show_inactive
111 def test_show_inactive
104 @request.session[:user_id] = nil
112 @request.session[:user_id] = nil
105 get :show, :id => 5
113 get :show, :id => 5
106 assert_response 404
114 assert_response 404
107 end
115 end
108
116
109 def test_show_should_not_reveal_users_with_no_visible_activity_or_project
117 def test_show_should_not_reveal_users_with_no_visible_activity_or_project
110 @request.session[:user_id] = nil
118 @request.session[:user_id] = nil
111 get :show, :id => 9
119 get :show, :id => 9
112 assert_response 404
120 assert_response 404
113 end
121 end
114
122
115 def test_show_inactive_by_admin
123 def test_show_inactive_by_admin
116 @request.session[:user_id] = 1
124 @request.session[:user_id] = 1
117 get :show, :id => 5
125 get :show, :id => 5
118 assert_response 200
126 assert_response 200
119 assert_not_nil assigns(:user)
127 assert_not_nil assigns(:user)
120 end
128 end
121
129
122 def test_show_displays_memberships_based_on_project_visibility
130 def test_show_displays_memberships_based_on_project_visibility
123 @request.session[:user_id] = 1
131 @request.session[:user_id] = 1
124 get :show, :id => 2
132 get :show, :id => 2
125 assert_response :success
133 assert_response :success
126 memberships = assigns(:memberships)
134 memberships = assigns(:memberships)
127 assert_not_nil memberships
135 assert_not_nil memberships
128 project_ids = memberships.map(&:project_id)
136 project_ids = memberships.map(&:project_id)
129 assert project_ids.include?(2) #private project admin can see
137 assert project_ids.include?(2) #private project admin can see
130 end
138 end
131
139
132 def test_show_current_should_require_authentication
140 def test_show_current_should_require_authentication
133 @request.session[:user_id] = nil
141 @request.session[:user_id] = nil
134 get :show, :id => 'current'
142 get :show, :id => 'current'
135 assert_response 302
143 assert_response 302
136 end
144 end
137
145
138 def test_show_current
146 def test_show_current
139 @request.session[:user_id] = 2
147 @request.session[:user_id] = 2
140 get :show, :id => 'current'
148 get :show, :id => 'current'
141 assert_response :success
149 assert_response :success
142 assert_template 'show'
150 assert_template 'show'
143 assert_equal User.find(2), assigns(:user)
151 assert_equal User.find(2), assigns(:user)
144 end
152 end
145
153
146 def test_new
154 def test_new
147 get :new
155 get :new
148
156
149 assert_response :success
157 assert_response :success
150 assert_template :new
158 assert_template :new
151 assert assigns(:user)
159 assert assigns(:user)
152 end
160 end
153
161
154 def test_create
162 def test_create
155 Setting.bcc_recipients = '1'
163 Setting.bcc_recipients = '1'
156
164
157 assert_difference 'User.count' do
165 assert_difference 'User.count' do
158 assert_difference 'ActionMailer::Base.deliveries.size' do
166 assert_difference 'ActionMailer::Base.deliveries.size' do
159 post :create,
167 post :create,
160 :user => {
168 :user => {
161 :firstname => 'John',
169 :firstname => 'John',
162 :lastname => 'Doe',
170 :lastname => 'Doe',
163 :login => 'jdoe',
171 :login => 'jdoe',
164 :password => 'secret',
172 :password => 'secret',
165 :password_confirmation => 'secret',
173 :password_confirmation => 'secret',
166 :mail => 'jdoe@gmail.com',
174 :mail => 'jdoe@gmail.com',
167 :mail_notification => 'none'
175 :mail_notification => 'none'
168 },
176 },
169 :send_information => '1'
177 :send_information => '1'
170 end
178 end
171 end
179 end
172
180
173 user = User.first(:order => 'id DESC')
181 user = User.first(:order => 'id DESC')
174 assert_redirected_to :controller => 'users', :action => 'edit', :id => user.id
182 assert_redirected_to :controller => 'users', :action => 'edit', :id => user.id
175
183
176 assert_equal 'John', user.firstname
184 assert_equal 'John', user.firstname
177 assert_equal 'Doe', user.lastname
185 assert_equal 'Doe', user.lastname
178 assert_equal 'jdoe', user.login
186 assert_equal 'jdoe', user.login
179 assert_equal 'jdoe@gmail.com', user.mail
187 assert_equal 'jdoe@gmail.com', user.mail
180 assert_equal 'none', user.mail_notification
188 assert_equal 'none', user.mail_notification
181 assert user.check_password?('secret')
189 assert user.check_password?('secret')
182
190
183 mail = ActionMailer::Base.deliveries.last
191 mail = ActionMailer::Base.deliveries.last
184 assert_not_nil mail
192 assert_not_nil mail
185 assert_equal [user.mail], mail.bcc
193 assert_equal [user.mail], mail.bcc
186 assert mail.body.include?('secret')
194 assert mail.body.include?('secret')
187 end
195 end
188
196
189 def test_create_with_failure
197 def test_create_with_failure
190 assert_no_difference 'User.count' do
198 assert_no_difference 'User.count' do
191 post :create, :user => {}
199 post :create, :user => {}
192 end
200 end
193
201
194 assert_response :success
202 assert_response :success
195 assert_template 'new'
203 assert_template 'new'
196 end
204 end
197
205
198 def test_edit
206 def test_edit
199 get :edit, :id => 2
207 get :edit, :id => 2
200
208
201 assert_response :success
209 assert_response :success
202 assert_template 'edit'
210 assert_template 'edit'
203 assert_equal User.find(2), assigns(:user)
211 assert_equal User.find(2), assigns(:user)
204 end
212 end
205
213
206 def test_update
214 def test_update
207 ActionMailer::Base.deliveries.clear
215 ActionMailer::Base.deliveries.clear
208 put :update, :id => 2, :user => {:firstname => 'Changed', :mail_notification => 'only_assigned'}, :pref => {:hide_mail => '1', :comments_sorting => 'desc'}
216 put :update, :id => 2, :user => {:firstname => 'Changed', :mail_notification => 'only_assigned'}, :pref => {:hide_mail => '1', :comments_sorting => 'desc'}
209
217
210 user = User.find(2)
218 user = User.find(2)
211 assert_equal 'Changed', user.firstname
219 assert_equal 'Changed', user.firstname
212 assert_equal 'only_assigned', user.mail_notification
220 assert_equal 'only_assigned', user.mail_notification
213 assert_equal true, user.pref[:hide_mail]
221 assert_equal true, user.pref[:hide_mail]
214 assert_equal 'desc', user.pref[:comments_sorting]
222 assert_equal 'desc', user.pref[:comments_sorting]
215 assert ActionMailer::Base.deliveries.empty?
223 assert ActionMailer::Base.deliveries.empty?
216 end
224 end
217
225
218 def test_update_with_failure
226 def test_update_with_failure
219 assert_no_difference 'User.count' do
227 assert_no_difference 'User.count' do
220 put :update, :id => 2, :user => {:firstname => ''}
228 put :update, :id => 2, :user => {:firstname => ''}
221 end
229 end
222
230
223 assert_response :success
231 assert_response :success
224 assert_template 'edit'
232 assert_template 'edit'
225 end
233 end
226
234
227 def test_update_with_group_ids_should_assign_groups
235 def test_update_with_group_ids_should_assign_groups
228 put :update, :id => 2, :user => {:group_ids => ['10']}
236 put :update, :id => 2, :user => {:group_ids => ['10']}
229
237
230 user = User.find(2)
238 user = User.find(2)
231 assert_equal [10], user.group_ids
239 assert_equal [10], user.group_ids
232 end
240 end
233
241
234 def test_update_with_activation_should_send_a_notification
242 def test_update_with_activation_should_send_a_notification
235 u = User.new(:firstname => 'Foo', :lastname => 'Bar', :mail => 'foo.bar@somenet.foo', :language => 'fr')
243 u = User.new(:firstname => 'Foo', :lastname => 'Bar', :mail => 'foo.bar@somenet.foo', :language => 'fr')
236 u.login = 'foo'
244 u.login = 'foo'
237 u.status = User::STATUS_REGISTERED
245 u.status = User::STATUS_REGISTERED
238 u.save!
246 u.save!
239 ActionMailer::Base.deliveries.clear
247 ActionMailer::Base.deliveries.clear
240 Setting.bcc_recipients = '1'
248 Setting.bcc_recipients = '1'
241
249
242 put :update, :id => u.id, :user => {:status => User::STATUS_ACTIVE}
250 put :update, :id => u.id, :user => {:status => User::STATUS_ACTIVE}
243 assert u.reload.active?
251 assert u.reload.active?
244 mail = ActionMailer::Base.deliveries.last
252 mail = ActionMailer::Base.deliveries.last
245 assert_not_nil mail
253 assert_not_nil mail
246 assert_equal ['foo.bar@somenet.foo'], mail.bcc
254 assert_equal ['foo.bar@somenet.foo'], mail.bcc
247 assert mail.body.include?(ll('fr', :notice_account_activated))
255 assert mail.body.include?(ll('fr', :notice_account_activated))
248 end
256 end
249
257
250 def test_update_with_password_change_should_send_a_notification
258 def test_update_with_password_change_should_send_a_notification
251 ActionMailer::Base.deliveries.clear
259 ActionMailer::Base.deliveries.clear
252 Setting.bcc_recipients = '1'
260 Setting.bcc_recipients = '1'
253
261
254 put :update, :id => 2, :user => {:password => 'newpass', :password_confirmation => 'newpass'}, :send_information => '1'
262 put :update, :id => 2, :user => {:password => 'newpass', :password_confirmation => 'newpass'}, :send_information => '1'
255 u = User.find(2)
263 u = User.find(2)
256 assert u.check_password?('newpass')
264 assert u.check_password?('newpass')
257
265
258 mail = ActionMailer::Base.deliveries.last
266 mail = ActionMailer::Base.deliveries.last
259 assert_not_nil mail
267 assert_not_nil mail
260 assert_equal [u.mail], mail.bcc
268 assert_equal [u.mail], mail.bcc
261 assert mail.body.include?('newpass')
269 assert mail.body.include?('newpass')
262 end
270 end
263
271
264 test "put :update with a password change to an AuthSource user switching to Internal authentication" do
272 test "put :update with a password change to an AuthSource user switching to Internal authentication" do
265 # Configure as auth source
273 # Configure as auth source
266 u = User.find(2)
274 u = User.find(2)
267 u.auth_source = AuthSource.find(1)
275 u.auth_source = AuthSource.find(1)
268 u.save!
276 u.save!
269
277
270 put :update, :id => u.id, :user => {:auth_source_id => '', :password => 'newpass'}, :password_confirmation => 'newpass'
278 put :update, :id => u.id, :user => {:auth_source_id => '', :password => 'newpass'}, :password_confirmation => 'newpass'
271
279
272 assert_equal nil, u.reload.auth_source
280 assert_equal nil, u.reload.auth_source
273 assert u.check_password?('newpass')
281 assert u.check_password?('newpass')
274 end
282 end
275
283
276 def test_destroy
284 def test_destroy
277 assert_difference 'User.count', -1 do
285 assert_difference 'User.count', -1 do
278 delete :destroy, :id => 2
286 delete :destroy, :id => 2
279 end
287 end
280 assert_redirected_to '/users'
288 assert_redirected_to '/users'
281 assert_nil User.find_by_id(2)
289 assert_nil User.find_by_id(2)
282 end
290 end
283
291
284 def test_destroy_should_not_accept_get_requests
292 def test_destroy_should_not_accept_get_requests
285 assert_no_difference 'User.count' do
293 assert_no_difference 'User.count' do
286 get :destroy, :id => 2
294 get :destroy, :id => 2
287 end
295 end
288 assert_response 405
296 assert_response 405
289 end
297 end
290
298
291 def test_destroy_should_be_denied_for_non_admin_users
299 def test_destroy_should_be_denied_for_non_admin_users
292 @request.session[:user_id] = 3
300 @request.session[:user_id] = 3
293
301
294 assert_no_difference 'User.count' do
302 assert_no_difference 'User.count' do
295 get :destroy, :id => 2
303 get :destroy, :id => 2
296 end
304 end
297 assert_response 403
305 assert_response 403
298 end
306 end
299
307
300 def test_edit_membership
308 def test_edit_membership
301 post :edit_membership, :id => 2, :membership_id => 1,
309 post :edit_membership, :id => 2, :membership_id => 1,
302 :membership => { :role_ids => [2]}
310 :membership => { :role_ids => [2]}
303 assert_redirected_to :action => 'edit', :id => '2', :tab => 'memberships'
311 assert_redirected_to :action => 'edit', :id => '2', :tab => 'memberships'
304 assert_equal [2], Member.find(1).role_ids
312 assert_equal [2], Member.find(1).role_ids
305 end
313 end
306
314
307 def test_destroy_membership
315 def test_destroy_membership
308 post :destroy_membership, :id => 2, :membership_id => 1
316 post :destroy_membership, :id => 2, :membership_id => 1
309 assert_redirected_to :action => 'edit', :id => '2', :tab => 'memberships'
317 assert_redirected_to :action => 'edit', :id => '2', :tab => 'memberships'
310 assert_nil Member.find_by_id(1)
318 assert_nil Member.find_by_id(1)
311 end
319 end
312 end
320 end
General Comments 0
You need to be logged in to leave comments. Login now