From b06ad40bcd43f31d6d20b4946159abdedac3a748 Mon Sep 17 00:00:00 2001 From: "ian%hixie.ch" Date: Sat, 4 Oct 2003 12:09:38 +0000 Subject: [PATCH] Bug 159567: improvements to user management. Based on a patch by Adam Di Carlo. Improvements include: 'deleteuser' admin command. 'changepassword' admin command. Better error messages when the two new passwords don't match. Edits to the INSTALL file. Also removes the recently added 'sleep' statement on quit since it wasn't helping. --- webtools/mozbot/INSTALL | 6 ++- webtools/mozbot/mozbot.pl | 87 ++++++++++++++++++++++++++++++++------- 2 files changed, 77 insertions(+), 16 deletions(-) diff --git a/webtools/mozbot/INSTALL b/webtools/mozbot/INSTALL index 20ebcfafb6cd..6d4d6e1c2d8d 100644 --- a/webtools/mozbot/INSTALL +++ b/webtools/mozbot/INSTALL @@ -399,7 +399,7 @@ BTW, is the hash in which all the username/password pairs are kept): /msg mozbot vars Admin users '+*arah|li*a' -..., I.e. the username added would be "arah|li" and the password would +..., i.e. the username added would be "arah|li" and the password would be "a". This is not a bug, it's a feature. It means you can include any character, including "'", "|", and so on, in the key, without fear of it being interpreted as a delimiter. @@ -410,6 +410,8 @@ syntax: /msg mozbot vars Admin users '-admin' That's it. No need to say what the value is, since each key in a hash -has to be unique. +has to be unique. (Although, in this particular case, it should be +noted that the preferred way to remove users is actually the +'deleteuser' command.) -- end -- diff --git a/webtools/mozbot/mozbot.pl b/webtools/mozbot/mozbot.pl index 45f937928e89..4f5d227f5e29 100755 --- a/webtools/mozbot/mozbot.pl +++ b/webtools/mozbot/mozbot.pl @@ -2380,6 +2380,7 @@ sub Help { $result->{'shutup'} = 'Clears the output queue (you actually have to say \'shutup please\' or nothing will happen).'; $result->{'restart'} = 'Shuts the bot down completely then restarts it, so that any source changes take effect.'; $result->{'cycle'} = 'Makes the bot disconnect from the server then try to reconnect.'; + $result->{'changepassword'} = 'Change a user\'s password: changepassword ', $result->{'vars'} = 'Manage variables: vars [ [ [\'\']]], say \'vars\' for more details.'; $result->{'join'} = 'Makes the bot attempt to join a channel. The same effect can be achieved using /invite. Syntax: join '; $result->{'part'} = 'Makes the bot leave a channel. The same effect can be achieved using /kick. Syntax: part '; @@ -2388,6 +2389,7 @@ sub Help { $result->{'reload'} = 'Unloads and then loads a module: reload '; $result->{'bless'} = 'Sets the \'admin\' flag on a registered user. Syntax: bless '; $result->{'unbless'} = 'Resets the \'admin\' flag on a registered user. Syntax: unbless '; + $result->{'deleteuser'} = 'Deletes a user from the bot. Syntax: deleteuser ', } return $result; } @@ -2412,23 +2414,28 @@ sub Told { $self->directSay($event, "You have not been added as a user yet. Try the \'newuser\' command (see \'help newuser\' for details)."); } } - } elsif ($message =~ /^\s*password\s+($variablepattern)\s+($variablepattern)\s+\2\s*$/osi) { + } elsif ($message =~ /^\s*password\s+($variablepattern)\s+($variablepattern)\s+($variablepattern)\s*$/osi) { if (not $event->{'channel'}) { if ($authenticatedUsers{$event->{'user'}}) { - if (&::checkPassword($1, $users{$authenticatedUsers{$event->{'user'}}})) { + if ($2 ne $3) { + $self->say($event, 'New passwords did not match. Try again.'); + } elsif (&::checkPassword($1, $users{$authenticatedUsers{$event->{'user'}}})) { $users{$authenticatedUsers{$event->{'user'}}} = &::newPassword($2); + delete($authenticatedUsers{$event->{'user'}}); $self->say($event, 'Password changed. Please reauthenticate.'); $self->saveConfig(); } else { + delete($authenticatedUsers{$event->{'user'}}); $self->say($event, 'That is not your current password. Please reauthenticate.'); } - delete($authenticatedUsers{$event->{'user'}}); } } - } elsif ($message =~ /^\s*new\s*user\s+($variablepattern)\s+($variablepattern)\s+\2\s*$/osi) { + } elsif ($message =~ /^\s*new\s*user\s+($variablepattern)\s+($variablepattern)\s+($variablepattern)\s*$/osi) { if (not $event->{'channel'}) { if (defined($users{$1})) { $self->say($event, 'That user already exists in my list, you can\'t add them again!'); + } elsif ( $2 ne $3 ) { + $self->say($event, 'New passwords did not match. Try again.'); } elsif ($1) { $users{$1} = &::newPassword($2); $userFlags{$1} = 0; @@ -2451,6 +2458,53 @@ sub Told { } elsif ($message =~ /^\s*restart/osi) { $self->say($event, 'If you really want me to restart, use the magic word.'); $self->schedule($event, 7, 1, 'i.e., please.'); + } elsif ($message =~ /^\s*delete\s*user\s+($variablepattern)\s*$/osi) { + if (not defined($users{$1})) { + $self->say($event, "I don't know of a user called '$1', sorry."); + } else { + # check user is not last admin + my $doomedUser = $1; + my $count; + if (($userFlags{$doomedUser} & 1) == 1) { + # deleting an admin. Count how many are left. + $count = 0; + foreach my $user (keys %users) { + ++$count if ($user ne $doomedUser and + ($userFlags{$user} & 1) == 1); + } + } else { + # not deleting an admin. We know there is an admin in there, it's + # the user doing the deleting. So we're safe. + $count = 1; + } + if ($count) { + $self->deleteUser($doomedUser); + $self->say($event, "User '$doomedUser' deleted."); + } else { + $self->say($event, "Can't delete user '$doomedUser', that would leave you with no admins!"); + } + } + } elsif ($message =~ /^\s*change\s*password\s+($variablepattern)\s+($variablepattern)\s+($variablepattern)\s*$/osi) { + if (not defined($users{$1})) { + $self->say($event, "I don't know of a user called '$1', sorry."); + } elsif ($2 ne $3) { + $self->say($event, 'New passwords did not match. Try again.'); + } else { + $users{$1} = &::newPassword($2); + my $count = 0; + foreach my $user (keys %authenticatedUsers) { + if ($authenticatedUsers{$user} eq $1) { + delete($authenticatedUsers{$user}); + ++$count; + } + } + if ($count) { + $self->say($event, "Password changed for user '$1'. They must reauthenticate."); + } else { + $self->say($event, "Password changed for user '$1'."); + } + $self->saveConfig(); + } } elsif ($message =~ /^\s*(?:shutup,?\s+please)\s*[?!.]*\s*$/osi) { my $lost = @msgqueue; @msgqueue = (); @@ -2518,16 +2572,8 @@ sub Authed { if ($self->isAdmin($event)) { foreach (keys %userFlags) { if ((($userFlags{$_} & 2) == 2) and ($authenticatedUsers{$event->{'user'}} ne $_)) { - delete($userFlags{$_}); - delete($users{$_}); - # if they authenticated, remove the entry to prevent dangling links - foreach my $user (keys %authenticatedUsers) { - if ($authenticatedUsers{$user} eq $_) { - delete($authenticatedUsers{$user}); - } - } + $self->deleteUser($_); $self->directSay($event, "Temporary administrator '$_' removed from user list."); - $self->saveConfig(); } } } @@ -2583,7 +2629,6 @@ sub Restart { my $self = shift; my ($event, $reason) = @_; $event->{'bot'}->quit($reason); - sleep 1; # wait one second to give the quit message a chance # Note that `exec' will not call our `END' blocks, nor will it # call any `DESTROY' methods in our objects. So we fork a child to # do that first. @@ -2821,6 +2866,20 @@ sub ReloadModule { $self->LoadModule(@_); } +sub deleteUser { + my $self = shift; + my ($who) = @_; + delete($userFlags{$who}); + delete($users{$who}); + # if they authenticated, remove the entry to prevent dangling links + foreach my $user (keys %authenticatedUsers) { + if ($authenticatedUsers{$user} eq $who) { + delete($authenticatedUsers{$user}); + } + } + $self->saveConfig(); +} + ################################ # Startup (aka main) #