Skip to content

Commit b937168

Browse files
authored
Merge pull request #2718 from drgrice1/manage-otp-remove-large-course-restrictions
Remove the filtering of large courses on the course admin "Manage OTP Secrets" page and fix security vulnerability.
2 parents abe7125 + d81c523 commit b937168

File tree

5 files changed

+44
-51
lines changed

5 files changed

+44
-51
lines changed

lib/WeBWorK/ContentGenerator/CourseAdmin.pm

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2403,30 +2403,25 @@ sub do_save_lti_course_map ($c) {
24032403

24042404
# Form to copy or reset OTP secrets.
24052405
sub manage_otp_secrets_form ($c) {
2406-
my $courses = {};
2407-
my $dbs = {};
2408-
my $skipped_courses = [];
2409-
my $show_all_courses = $c->param('show_all_courses') || 0;
2406+
my $courses = {};
2407+
my $dbs = {};
24102408

24112409
# Create course data first, since it is used in all cases and initializes course db references.
24122410
for my $courseID (listCourses($c->ce)) {
24132411
my $ce = WeBWorK::CourseEnvironment->new({ courseName => $courseID });
2414-
$dbs->{$courseID} = WeBWorK::DB->new($ce);
2415-
2416-
# By default ignore courses larger than 200 users, as this can cause a large load building menus.
2417-
my @users = $dbs->{$courseID}->listUsers;
2418-
if ($show_all_courses || scalar @users < 200) {
2419-
$courses->{$courseID} = \@users;
2420-
} else {
2421-
push(@$skipped_courses, $courseID);
2422-
}
2412+
$dbs->{$courseID} = WeBWorK::DB->new($ce);
2413+
$courses->{$courseID} = [ $dbs->{$courseID}->listUsers ];
24232414
}
24242415

2425-
# Process the confirmed rest or copy actions here.
2416+
# Process the confirmed reset or copy actions here.
24262417
if ($c->param('otp_confirm_reset')) {
24272418
my $total = 0;
24282419
my $courseID = $c->param('sourceResetCourseID');
24292420
for my $user ($c->param('otp_reset_row')) {
2421+
if ($courseID eq $c->ce->{courseName} && $user eq $c->param('user')) {
2422+
$c->addbadmessage($c->maketext('You may not reset your own OTP secret!'));
2423+
next;
2424+
}
24302425
my $password = $dbs->{$courseID}->getPassword($user);
24312426
if ($password && $password->otp_secret) {
24322427
$password->otp_secret('');
@@ -2443,6 +2438,11 @@ sub manage_otp_secrets_form ($c) {
24432438
my $total = 0;
24442439
for my $row ($c->param('otp_copy_row')) {
24452440
my ($s_course, $s_user, $d_course, $d_user) = split(':', $row);
2441+
if ($d_course eq $c->ce->{courseName} && $d_user eq $c->param('user')) {
2442+
$c->addbadmessage(
2443+
$c->maketext('You cannot overwrite your OTP secret with one from another course or user!'));
2444+
next;
2445+
}
24462446
my $s_password = $dbs->{$s_course}->getPassword($s_user);
24472447
if ($s_password && $s_password->otp_secret) {
24482448
# Password may not be defined if using external auth, so create new password record if not.
@@ -2461,11 +2461,7 @@ sub manage_otp_secrets_form ($c) {
24612461
}
24622462
}
24632463

2464-
return $c->include(
2465-
'ContentGenerator/CourseAdmin/manage_otp_secrets_form',
2466-
courses => $courses,
2467-
skipped_courses => $skipped_courses
2468-
);
2464+
return $c->include('ContentGenerator/CourseAdmin/manage_otp_secrets_form', courses => $courses);
24692465
}
24702466

24712467
# Deals with both single and multiple copy confirmation.
@@ -2550,17 +2546,24 @@ sub copy_otp_secrets_confirm ($c) {
25502546
$dbs{$d_course} = WeBWorK::DB->new($dest_ce);
25512547
}
25522548

2553-
my $d_user_password = $dbs{$d_course}->getPassword($d_user);
2554-
if (!defined $d_user_password) {
2555-
# Just because there is no password record, the user could still exist when using external auth.
2556-
unless ($dbs{$d_course}->existsUser($d_user)) {
2557-
$dest_error = 'warning';
2558-
$error_message = $c->maketext('User does not exist - Skipping');
2559-
$skip = 1;
2549+
if ($d_course eq $c->ce->{courseName} && $d_user eq $c->param('user')) {
2550+
$dest_error = 'danger';
2551+
$error_message =
2552+
$c->maketext('You cannot overwrite your OTP secret with one from another course or user!');
2553+
$skip = 1;
2554+
} else {
2555+
my $d_user_password = $dbs{$d_course}->getPassword($d_user);
2556+
if (!defined $d_user_password) {
2557+
# Just because there is no password record, the user could still exist when using external auth.
2558+
unless ($dbs{$d_course}->existsUser($d_user)) {
2559+
$dest_error = 'warning';
2560+
$error_message = $c->maketext('User does not exist - Skipping');
2561+
$skip = 1;
2562+
}
2563+
} elsif ($d_user_password->otp_secret) {
2564+
$dest_error = 'danger';
2565+
$error_message = $c->maketext('OTP Secret is not empty - Overwritting');
25602566
}
2561-
} elsif ($d_user_password->otp_secret) {
2562-
$dest_error = 'danger';
2563-
$error_message = $c->maketext('OTP Secret is not empty - Overwritting');
25642567
}
25652568

25662569
push(
@@ -2599,8 +2602,14 @@ sub reset_otp_secrets_confirm ($c) {
25992602
my $db = WeBWorK::DB->new($ce);
26002603
my @rows;
26012604
for my $user (@dest_users) {
2602-
my $password = $db->getPassword($user);
2603-
my $error = $password && $password->otp_secret ? '' : $c->maketext('OTP Secret is empty - Skipping');
2605+
my $error = '';
2606+
2607+
if ($source_course eq $c->ce->{courseName} && $user eq $c->param('user')) {
2608+
$error = $c->maketext('You may not reset your own OTP secret!');
2609+
} else {
2610+
my $password = $db->getPassword($user);
2611+
$error = $c->maketext('OTP Secret is empty - Skipping') unless $password && $password->otp_secret;
2612+
}
26042613

26052614
push(
26062615
@rows,

templates/ContentGenerator/CourseAdmin/copy_otp_secrets_confirm.html.ep

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
</tbody>
4141
</table>
4242
</div>
43-
<%= $c->hidden_fields('subDisplay', 'show_all_courses') =%>
43+
<%= $c->hidden_fields('subDisplay') =%>
4444
% if ($total > 0) {
4545
% my $skipped = @$action_rows - $total;
4646
<%= content 'hidden-rows' %>

templates/ContentGenerator/CourseAdmin/manage_otp_secrets_form.html.ep

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,6 @@
88
%
99
<h2><%= maketext('Manage OTP Secrets') %> <%= $c->helpMacro('AdminManageOTPSecrets') %></h2>
1010
<%= form_for current_route, method => 'POST', begin =%>
11-
% if (@$skipped_courses) {
12-
<div class="mb-3">
13-
<div class="alert alert-warning mb-2">
14-
<%= maketext('The following large courses are not shown: [_1]', join(', ', @$skipped_courses)); =%>
15-
</div>
16-
<p>
17-
<%= submit_button maketext('Show All Courses'),
18-
name => 'show_all_courses', class => 'btn btn-primary' =%>
19-
</p>
20-
</div>
21-
% }
2211
<div>
2312
<ul class="nav nav-tabs mb-2" role="tablist">
2413
% for my $tab ('single', 'multiple', 'reset') {
@@ -147,7 +136,7 @@
147136
disabled => undef,
148137
selected => undef,
149138
],
150-
sort (@course_keys, @$skipped_courses)
139+
sort @course_keys
151140
],
152141
id => 'destMultipleCourseID',
153142
class => 'form-select',
@@ -195,7 +184,7 @@
195184
</div>
196185
</div>
197186
</div>
198-
<%= $c->hidden_fields('subDisplay', 'show_all_courses') =%>
187+
<%= $c->hidden_fields('subDisplay') =%>
199188
<%= hidden_field action => $active_tab, id => 'current_action' =%>
200189
<div class="mb-3">
201190
<%= submit_button $active_tab eq 'single'

templates/ContentGenerator/CourseAdmin/reset_otp_secrets_confirm.html.ep

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
</tbody>
2929
</table>
3030
</div>
31-
<%= $c->hidden_fields('subDisplay', 'show_all_courses', 'sourceResetCourseID') =%>
31+
<%= $c->hidden_fields('subDisplay', 'sourceResetCourseID') =%>
3232
% if ($total > 0) {
3333
% my $skipped = @$action_rows - $total;
3434
<%= content 'hidden-rows' %>

templates/HelpFiles/AdminManageOTPSecrets.html.ep

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,3 @@
1919
<%= maketext('If resetting secrets, choose a source course ID, then choose one (or more) users whose secrets '
2020
. 'will be reset. Note, the user(s) will have to setup two factor authentication afterwards.') =%>
2121
</p>
22-
<p class="mb-0">
23-
<%= maketext('By default, courses larger than 200 users will not be shown, as this can cause extra CPU usage '
24-
. 'when generating the user menus. If any large course is skipped, a message will inform you of which '
25-
. 'courses were skipped, and a button to show all courses can be used to include those courses.') =%>
26-
</p>

0 commit comments

Comments
 (0)