Skip to content

Commit 07c17ea

Browse files
committed
expand masquerade capabilities to root account admins
closes #4332 * Allow :become_user to be granted to any root account admin role, not just site admin roles. * Adjust policy on User objects to properly grant :become_user: * You can always become yourself (stop masquerading) * Site admins can become any user besides other site admins * Root account admins can only become users that are not account admins, and that belong to accounts that this root account admin has permissions to * Adjust masquerading code to check for :become_user on the user object itself, rather than checking just on the site admin account * This means we have to figure out the target user before checking permissions * Because the permission check already checks for becoming another site admin user, that special case was removed in the masquerading code * Special case the UI to not show the "become" link for the current user (i.e. you can't become yourself, and you can't become the user that you already are) Change-Id: I69bc855b8ee24098b9a63b0b1c8d7edf2063b625 Reviewed-on: https://gerrit.instructure.com/4614 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Jon Jensen <jon@instructure.com>
1 parent 1503a7e commit 07c17ea

8 files changed

Lines changed: 124 additions & 29 deletions

File tree

app/controllers/users_controller.rb

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,8 @@ def index
191191
end
192192

193193
def masquerade
194-
if user_is_site_admin?(@real_current_user || @current_user, :become_user)
195-
@user = User.find_by_id(params[:user_id])
194+
@user = User.find_by_id(params[:user_id])
195+
if (authorized_action(@user, @real_current_user || @current_user, :become_user))
196196
if request.post?
197197
if @user == @real_current_user
198198
session[:become_user_id] = nil
@@ -203,9 +203,6 @@ def masquerade
203203
session[:masquerade_return_to] = nil
204204
return return_to(return_url, request.referer)
205205
end
206-
return
207-
else
208-
render_unauthorized_action
209206
end
210207
end
211208

app/models/role_override.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ def self.known_role_types
477477
},
478478
:become_user => {
479479
:label => lambda { t('permissions.become_user', "Become other users") },
480-
:account_only => :site_admin,
480+
:account_only => :root,
481481
:true_for => %w(AccountAdmin),
482482
:available_to => %w(AccountAdmin AccountMembership),
483483
},

app/models/user.rb

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ def courses_with_grades
690690

691691
set_policy do
692692
given { |user| user == self }
693-
set { can :rename and can :read and can :manage and can :manage_content and can :manage_files and can :manage_calendar }
693+
set { can :rename and can :read and can :manage and can :manage_content and can :manage_files and can :manage_calendar and can :become_user }
694694

695695
given {|user| self.courses.any?{|c| c.user_is_teacher?(user)}}
696696
set { can :rename and can :create_user_notes and can :read_user_notes}
@@ -718,7 +718,26 @@ def courses_with_grades
718718
)
719719
end
720720
set { can :manage_user_details and can :manage_logins and can :rename and can :view_statistics and can :create_user_notes and can :read_user_notes and can :delete_user_notes}
721-
721+
722+
given do |user|
723+
user && ((
724+
# or, if the user we are given can masquerade in *all* of this user's accounts
725+
# (to prevent an account admin from masquerading and gaining access to another root account)
726+
(self.associated_accounts.all?{|a| a.grants_right?(user, nil, :become_user) } && !self.associated_accounts.empty?)
727+
) && (
728+
# account admins can't masquerade as other account admins
729+
self.account_users.empty? || (
730+
# unless they're a site admin
731+
Account.site_admin.grants_right?(user, nil, :become_user) &&
732+
# and not wanting to become a site admin
733+
!Account.site_admin_user?(self)
734+
)
735+
) || (
736+
# only site admins can masquerade as users that don't belong to any account
737+
self.associated_accounts.empty? && Account.site_admin.grants_right?(user, nil, :become_user)
738+
))
739+
end
740+
set { can :become_user }
722741
end
723742

724743
def self.infer_id(obj)

app/views/role_overrides/index.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@
128128
<td><%= t 'headers.account_permissions', "Account Permissions" %></td>
129129
<td colspan='<%= @role_types.size %>'></td>
130130
</tr>
131-
<%= render :partial => 'permission', :collection => permissions.select { |k, p| p[:account_only] == true } %>
131+
<%= render :partial => 'permission', :collection => permissions.select { |k, p| p[:account_only] == true || p[:account_only] == :root && @context.root_account? } %>
132132
<tr class='ui-widget-header'>
133133
<td><%= t 'headers.course_permissions', "Course & Account Permissions" %></td>
134134
<td colspan='<%= @role_types.size %>'></td>

app/views/users/_name.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
<% if can_manage_admin_users || (!@enrollments.any?(&:admin?) && !@user.creation_sis_batch_id && can_do(@context, @current_user, :manage_students)) %>
4545
<td class="links" colspan="2" style="text-align: right; font-size: 0.8em; padding-top: 10px;">
4646
<a href="#" class="edit_user_link"><%= t('edit', 'Edit') %></a>
47-
<% if current_user_is_site_admin?(:become_user) %>
47+
<% if @user.id != @current_user.id && can_do(@user, @real_current_user || @current_user, :become_user) %>
4848
|
4949
<% if @context && !@context.is_a?(Account) %>
5050
<a href="<%= context_url(@context, :context_url, :become_user_id => @user.id) %>"> <%= t('become', 'Become') %></a>

app/views/users/masquerade.html.erb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99
<% else %>
1010
<span class="warning-text"><%= t('are_you_sure_start', 'Are you sure you want to masquerade as this user?') %></span>
1111
<% link_text = "Masquerade as user" %>
12+
<p><%= mt 'details', <<-HEREDOC
13+
Masquerading is essentially logging in as this user without a password.
14+
You will be able to take any action as if you were this user, and from other users' points of views,
15+
it will be as if this user performed them. However, audit logs record the fact that
16+
**you** were the one that actually performed the actions on behalf of this user.
17+
HEREDOC
18+
%></p>
1219
<% end %>
1320

1421
<div style="float: right">

lib/authentication_methods.rb

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -84,49 +84,44 @@ def load_user
8484
@current_user = nil
8585
end
8686

87-
if @current_user && %w(become_user_id me become_teacher become_student).any? { |k| params.key?(k) } && Account.site_admin.grants_right?(@current_user, session, :become_user)
88-
request_become_user_id = nil
87+
if @current_user && %w(become_user_id me become_teacher become_student).any? { |k| params.key?(k) }
88+
request_become_user = nil
8989
if params[:become_user_id]
90-
request_become_user_id = params[:become_user_id]
90+
request_become_user = User.find_by_id(params[:become_user_id])
9191
elsif params.keys.include?('me')
92-
request_become_user_id = nil
92+
request_become_user = @current_user
9393
elsif params.keys.include?('become_teacher')
9494
course = Course.find(params[:course_id] || params[:id]) rescue nil
9595
teacher = course.teachers.first if course
9696
if teacher
97-
request_become_user_id = teacher.id
97+
request_become_user = teacher
9898
else
9999
flash[:error] = I18n.t('lib.auth.errors.teacher_not_found', "No teacher found")
100100
end
101101
elsif params.keys.include?('become_student')
102102
course = Course.find(params[:course_id] || params[:id]) rescue nil
103103
student = course.students.first if course
104104
if student
105-
request_become_user_id = student.id
105+
request_become_user = student
106106
else
107107
flash[:error] = I18n.t('lib.auth.errors.student_not_found', "No student found")
108108
end
109109
end
110-
111-
if request_become_user_id != session[:become_user_id]
110+
111+
if request_become_user && request_become_user.id != session[:become_user_id].to_i && request_become_user.grants_right?(@current_user, session, :become_user)
112112
params_without_become = params.dup
113113
params_without_become.delete_if {|k,v| [ 'become_user_id', 'become_teacher', 'become_student', 'me' ].include? k }
114114
params_without_become[:only_path] = true
115115
session[:masquerade_return_to] = url_for(params_without_become)
116-
return redirect_to user_masquerade_url(request_become_user_id || @current_user.id)
116+
return redirect_to user_masquerade_url(request_become_user.id)
117117
end
118118
end
119119

120-
if session[:become_user_id] && Account.site_admin.grants_right?(@current_user, session, :become_user)
121-
@real_current_user = @current_user
122-
@current_user = User.find(session[:become_user_id]) rescue @current_user
123-
if @current_user.id != @real_current_user.id && Account.site_admin_user?(@current_user)
124-
# we can't let even site admins impersonate other site admins, since
125-
# they may have different permissions.
126-
logger.warn "#{@real_current_user.name}(#{@real_current_user.id}) attempting to impersonate another site admin (#{@current_user.name}). No dice."
127-
flash.now[:error] = I18n.t('lib.auth.errors.admin_impersonation_disallowed', "You can't impersonate other site admins. Sorry.")
128-
@current_user = @real_current_user
129-
else
120+
if session[:become_user_id]
121+
user = User.find_by_id(session[:become_user_id])
122+
if user && user.grants_right?(@current_user, session, :become_user)
123+
@real_current_user = @current_user
124+
@current_user = user
130125
logger.warn "#{@real_current_user.name}(#{@real_current_user.id}) impersonating #{@current_user.name} on page #{request.url}"
131126
end
132127
end

spec/models/user_spec.rb

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,4 +297,81 @@
297297
@user1.submissions.map(&:id).should be_include(s3.id)
298298
end
299299
end
300+
301+
context "permissions" do
302+
it "should grant become_user to self" do
303+
@user = user_with_pseudonym(:username => 'nobody1@example.com')
304+
@user.grants_right?(@user, nil, :become_user).should be_true
305+
end
306+
307+
it "should not grant become_user to other users" do
308+
@user1 = user_with_pseudonym(:username => 'nobody1@example.com')
309+
@user2 = user_with_pseudonym(:username => 'nobody2@example.com')
310+
@user1.grants_right?(@user2, nil, :become_user).should be_false
311+
@user2.grants_right?(@user1, nil, :become_user).should be_false
312+
end
313+
314+
it "should grant become_user to site and account admins" do
315+
user = user_with_pseudonym(:username => 'nobody1@example.com')
316+
@admin = user_with_pseudonym(:username => 'nobody2@example.com')
317+
@site_admin = user_with_pseudonym(:username => 'nobody3@example.com')
318+
Account.site_admin.add_user(@site_admin)
319+
Account.default.add_user(@admin)
320+
user.grants_right?(@site_admin, nil, :become_user).should be_true
321+
@admin.grants_right?(@site_admin, nil, :become_user).should be_true
322+
user.grants_right?(@admin, nil, :become_user).should be_true
323+
@admin.grants_right?(@admin, nil, :become_user).should be_true
324+
@admin.grants_right?(user, nil, :become_user).should be_false
325+
@site_admin.grants_right?(@site_admin, nil, :become_user).should be_true
326+
@site_admin.grants_right?(user, nil, :become_user).should be_false
327+
@site_admin.grants_right?(@admin, nil, :become_user).should be_false
328+
end
329+
330+
it "should not grant become_user to other site admins" do
331+
@site_admin1 = user_with_pseudonym(:username => 'nobody1@example.com')
332+
@site_admin2 = user_with_pseudonym(:username => 'nobody2@example.com')
333+
Account.site_admin.add_user(@site_admin1)
334+
Account.site_admin.add_user(@site_admin2)
335+
@site_admin1.grants_right?(@site_admin2, nil, :become_user).should be_false
336+
@site_admin2.grants_right?(@site_admin1, nil, :become_user).should be_false
337+
end
338+
339+
it "should not grant become_user to other account admins" do
340+
@admin1 = user_with_pseudonym(:username => 'nobody1@example.com')
341+
@admin2 = user_with_pseudonym(:username => 'nobody2@example.com')
342+
Account.default.add_user(@admin1)
343+
Account.default.add_user(@admin2)
344+
@admin1.grants_right?(@admin2, nil, :become_user).should be_false
345+
@admin2.grants_right?(@admin1, nil, :become_user).should be_false
346+
end
347+
348+
it "should grant become_user for users in multiple accounts to site admins but not account admins" do
349+
user = user_with_pseudonym(:username => 'nobody1@example.com')
350+
@account2 = Account.create!
351+
user.pseudonyms.create!(:unique_id => 'nobodyelse@example.com', :account => @account2)
352+
@admin = user_with_pseudonym(:username => 'nobody2@example.com')
353+
@site_admin = user_with_pseudonym(:username => 'nobody3@example.com')
354+
Account.default.add_user(@admin)
355+
Account.site_admin.add_user(@site_admin)
356+
user.grants_right?(@admin, nil, :become_user).should be_false
357+
user.grants_right?(@site_admin, nil, :become_user).should be_true
358+
@account2.add_user(@admin)
359+
user.grants_right?(@admin, nil, :become_user).should be_true
360+
end
361+
362+
it "should not grant become_user for dis-associated users" do
363+
@user1 = user_model
364+
@user2 = user_model
365+
@user1.grants_right?(@user2, nil, :become_user).should be_false
366+
@user2.grants_right?(@user1, nil, :become_user).should be_false
367+
end
368+
369+
it "should grant become_user for dis-associated users to site admins" do
370+
user = user_model
371+
@site_admin = user_model
372+
Account.site_admin.add_user(@site_admin)
373+
user.grants_right?(@site_admin, nil, :become_user).should be_true
374+
@site_admin.grants_right?(user, nil, :become_user).should be_false
375+
end
376+
end
300377
end

0 commit comments

Comments
 (0)