Skip to content

Commit

Permalink
Merge pull request #2439 from drgrice1/user-list-password-edit
Browse files Browse the repository at this point in the history
Add editing user passwords to the add user and edit user pages.
  • Loading branch information
pstaabp authored Jul 2, 2024
2 parents de57ea3 + 09f051d commit f146c11
Show file tree
Hide file tree
Showing 24 changed files with 364 additions and 290 deletions.
22 changes: 13 additions & 9 deletions bin/addcourse
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ use WeBWorK::DB;
use WeBWorK::File::Classlist;
use WeBWorK::Utils qw(runtime_use cryptPassword);
use WeBWorK::Utils::CourseManagement qw(addCourse);
use WeBWorK::File::Classlist qw(parse_classlist);

sub usage_error {
warn "@_\n";
Expand Down Expand Up @@ -144,14 +145,17 @@ if ($users) {
# Set the default status if the status field is not set.
$record{status} = $ce->{statuses}{Enrolled}{abbrevs}[0] unless $record{status};

# Set the password if the password field is empty.
if (!defined $record{password} || $record{password} !~ /\S/) {
if (defined $record{student_id} && $record{student_id} =~ /\S/) {
# Use the student ID if it is non-empty.
$record{password} = cryptPassword($record{student_id});
} else {
# An empty password field in the database disables password login.
$record{password} = '';
# Determine what to use for the password (if anything).
if (!$record{password}) {
if (defined $record{unencrypted_password} && $record{unencrypted_password} =~ /\S/) {
$record{password} = cryptPassword($record{unencrypted_password});
} elsif ($ce->{fallback_password_source}
&& { user_id => 1, first_name => 1, last_name => 1, student_id => 1 }
->{ $ce->{fallback_password_source} }
&& $record{ $ce->{fallback_password_source} }
&& $record{ $ce->{fallback_password_source} } =~ /\S/)
{
$record{password} = cryptPassword($record{ $ce->{fallback_password_source} });
}
}

Expand All @@ -165,7 +169,7 @@ if ($users) {
push @users,
[
$userClass->new(%record),
$passwordClass->new(user_id => $user_id, password => $record{password}),
$record{password} ? $passwordClass->new(user_id => $user_id, password => $record{password}) : undef,
$permissionClass->new(
user_id => $user_id,
permission => defined $professors{$user_id}
Expand Down
39 changes: 21 additions & 18 deletions bin/importClassList.pl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
# Artistic License for more details.
################################################################################

use strict;
use warnings;

BEGIN {
use Mojo::File qw(curfile);
use Env qw(WEBWORK_ROOT);
Expand All @@ -29,9 +32,7 @@ BEGIN
use WeBWorK::DB qw(check_user_id);
use WeBWorK::File::Classlist;
use WeBWorK::Utils qw(cryptPassword);

use strict;
use warnings;
use WeBWorK::File::Classlist qw(parse_classlist);

if ((scalar(@ARGV) != 2)) {
print "\nSyntax is: importClassList.pl course_id path_to_classlist_file.lst\n\n";
Expand All @@ -48,16 +49,14 @@ BEGIN
courseName => $courseID
});

my $db = new WeBWorK::DB($ce->{dbLayout});
my $db = WeBWorK::DB($ce->{dbLayout})->new;

my $createNew = 1; # Always set to true, so add new users
my $replaceExisting = "none"; # Always set to "none" so no existing accounts are changed
my @replaceList = (); # Empty list
my (@replaced, @added, @skipped);

# This was copied with MINOR changes from lib/WeBWorK/ContentGenerator/Instructor/UserList.pm
# FIXME REFACTOR this belongs in a utility class so that addcourse can use it!
# (we need a whole suite of higher-level import/export functions somewhere)
# This was copied with MINOR changes from lib/WeBWorK/ContentGenerator/Instructor/UserList.pm.
sub importUsersFromCSV {
my ($fileName, $createNew, $replaceExisting, @replaceList) = @_;

Expand Down Expand Up @@ -108,14 +107,17 @@ sub importUsersFromCSV {
$record{status} = $default_status_abbrev
unless defined $record{status} and $record{status} ne "";

# set password from student ID if password field is "empty"
if (not defined $record{password} or $record{password} eq "") {
if (defined $record{student_id} and $record{student_id} =~ /\S/) {
# crypt the student ID and use that
$record{password} = cryptPassword($record{student_id});
} else {
# an empty password field in the database disables password login
$record{password} = "";
# Determine what to use for the password (if anything).
if (!$record{password}) {
if (defined $record{unencrypted_password} && $record{unencrypted_password} =~ /\S/) {
$record{password} = cryptPassword($record{unencrypted_password});
} elsif ($ce->{fallback_password_source}
&& { user_id => 1, first_name => 1, last_name => 1, student_id => 1 }
->{ $ce->{fallback_password_source} }
&& $record{ $ce->{fallback_password_source} }
&& $record{ $ce->{fallback_password_source} } =~ /\S/)
{
$record{password} = cryptPassword($record{ $ce->{fallback_password_source} });
}
}

Expand All @@ -125,18 +127,18 @@ sub importUsersFromCSV {

my $User = $db->newUser(%record);
my $PermissionLevel = $db->newPermissionLevel(user_id => $user_id, permission => $record{permission});
my $Password = $db->newPassword(user_id => $user_id, password => $record{password});
my $Password = $record{password} ? $db->newPassword(user_id => $user_id, password => $record{password}) : undef;

# DBFIXME use REPLACE
if (exists $allUserIDs{$user_id}) {
$db->putUser($User);
$db->putPermissionLevel($PermissionLevel);
$db->putPassword($Password);
$db->putPassword($Password) if $Password;
push @replaced, $user_id;
} else {
$db->addUser($User);
$db->addPermissionLevel($PermissionLevel);
$db->addPassword($Password);
$db->addPassword($Password) if $Password;
push @added, $user_id;
}
}
Expand All @@ -145,6 +147,7 @@ sub importUsersFromCSV {
print("Skipped:\n\t", join("\n\t", @skipped), "\n\n");
print("Replaced:\n\t", join("\n\t", @replaced), "\n\n");

return;
}

importUsersFromCSV($fileName, $createNew, $replaceExisting, @replaceList);
Expand Down
14 changes: 14 additions & 0 deletions conf/defaults.config
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,20 @@ $authen{admin_module} = ['WeBWorK::Authen::Basic_TheLastOption'];
# invalid or missing permission levels.
$default_permission_level = $userRoles{student};

# This sets the default fallback source to use for a user's password when a user
# account is created in a course. That is the source that will be shown by
# default in a select menu in the user interface, and the source that will be
# used by the importClassList.pl and addcourse scripts. It can be one of
# 'user_id', 'first_name', 'last_name', or 'student_id', or can be set to '' (or
# anything not listed before). If a user is created and no password is
# explicitly provided, then this source will used for the password (assuming
# that source value is also set). If this is not one of the allowed sources,
# and if a password is not explicitly provided, then the user will be created
# without a password and will not be able to sign in with username and password.
# Note that this is only used at the time that a user is initially created in a
# course, and not when editing passwords at a later time.
$fallback_password_source = '';

################################################################################
# Session options
################################################################################
Expand Down
4 changes: 4 additions & 0 deletions htdocs/js/UserList/userlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,8 @@
)();
});
}

for (const deleteCheck of document.querySelectorAll('input[name$="password_delete"]')) {
new bootstrap.Tooltip(deleteCheck, { placement: 'top', fallbackPlacements: [] });
}
})();
28 changes: 28 additions & 0 deletions htdocs/js/UserList/userlist.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.edit-classlist-table-container {
table.table {
border-collapse: separate;
border-spacing: 0;

.table-group-divider {
tr:first-child {
th,
td {
border-top: calc(var(--bs-border-width) * 2) solid;
}
}
}

th,
td {
border-right: var(--bs-border-width) solid var(--bs-table-border-color);
}

th:first-child,
td:first-child {
position: sticky;
z-index: 1;
left: 0;
border-left: var(--bs-border-width) solid var(--bs-table-border-color);
}
}
}
20 changes: 15 additions & 5 deletions lib/WeBWorK/ContentGenerator/Instructor/AddUsers.pm
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ sub initialize ($c) {
# FIXME: Handle errors if user already exists as well as all other errors that could occur (including errors
# when adding the permission, adding the password, and assigning sets to the users).
for my $i (1 .. $numberOfStudents) {
my $new_user_id = trim_spaces($c->param("new_user_id_$i"));
my $new_user_id = trim_spaces($c->param("user_id_$i"));
next unless $new_user_id;

my $newUser = $db->newUser;
Expand Down Expand Up @@ -81,10 +81,20 @@ sub initialize ($c) {
$newPermissionLevel->permission($c->param("permission_$i"));
$db->addPermissionLevel($newPermissionLevel);

my $newPassword = $db->newPassword;
$newPassword->user_id($new_user_id);
$newPassword->password(cryptPassword($c->param("student_id_$i")));
$db->addPassword($newPassword);
my $password =
$c->param("password_$i") =~ /\S/ ? $c->param("password_$i")
: ($c->param('fallback_password_source')
&& $c->param($c->param('fallback_password_source') . "_$i")
&& $c->param($c->param('fallback_password_source') . "_$i") =~ /\S/)
? $c->param($c->param('fallback_password_source') . "_$i")
: undef;

if (defined $password) {
my $newPassword = $db->newPassword;
$newPassword->user_id($new_user_id);
$newPassword->password(cryptPassword($password));
$db->addPassword($newPassword);
}

push(
@{ $c->{studentEntryReport} },
Expand Down
Loading

0 comments on commit f146c11

Please sign in to comment.