Fixed a bug in the main run loop whereby the objects array wasn't freed until shutdown, which sometimes meant objects got destroyed after the application, which is bad when the objects use the application to find services, etc. (This doesn't happen with services since services are not allowed to store references to the application, either directly or indirectly through references to objects or service instances.) Fixed the logout code to wait until the user tries to do something that requires login before clearing the logging out flag. This fixes a bug with HTTP whereby if a user logged out then his UA wouldn't ever be sent a 401 so wouldn't ever lose the authentication info, because browsers send the authentication info regardless of whether a page needs it or not. Also took the opportunity to remove a potential bug in the input validator code: store the admin message instead of a reference to the user object which might end up with a lifetime longer than expected.

This commit is contained in:
ian%hixie.ch 2001-12-01 04:37:26 +00:00
parent 0324923ec4
commit 09282c5818
3 changed files with 75 additions and 23 deletions

View File

@ -76,6 +76,12 @@ sub run {
# reference to us and we wouldn't want a memory leak...
$self->defaultOutput(undef);
} while ($self->input->next());
# clear the objects hash here, so that objects are removed before
# us, otherwise they can't refer back to us during shutdown.
# don't need to do the same to services as services should never
# use the application object during shutdown. (They shouldn't be
# able to. If they can, there is a circular reference.)
$self->objects([]);
$self->input(undef); # shutdown the input service instance
$self->dump(5, 'PLIF application completed normally.');
}

View File

@ -46,10 +46,25 @@ sub provides {
$class->SUPER::provides($service));
}
sub objectProvides {
my $class = shift;
my($service) = @_;
return ($service eq 'user.login.deniedUserHandle' or $class->SUPER::objectProvides($service));
}
sub objectInit {
my $self = shift;
my($app, $user) = @_;
$self->SUPER::objectInit(@_);
$self->user($user);
}
# input.verify
sub verifyInput {
my $self = shift;
my($app) = @_;
# clear internal flags
$self->userAdminMessage('');
# let's see if there are any protocol-specific user authenticators
my @result = $app->getSelectingServiceList('input.verify.user.'.$app->input->defaultOutputProtocol)->authenticateUser($app);
if (not @result) {
@ -59,14 +74,29 @@ sub verifyInput {
# now let's see what that gave us
if (@result) {
# horrah, somebody knew what to do!
if ((defined($result[0])) and ($result[0]->checkLogin())) {
if (defined($result[0])) {
my $canLogin = $result[0]->checkLogin();
if ($canLogin) {
if ($canLogin != 0) {
# can log in and not logged out
$app->addObject($result[0]); # they will have returned a user object
} else {
# hmm, so apparently user is not authentic
$self->errorState(\@result);
# logged out (0E0 is true but numerically equal to 0)
# flag user internally so we know to log them out
# if they try to do something that requires login
# (note: we can't store a reference to an object
# ourselves, so create an object to hold the
# reference for us)
$app->addObject($self->objectCreate($app, $result[0]));
}
} else {
# hmm, so apparently user is not allowed to log in
$self->dump(2, 'user '.($result[0]->userID).' tried logging in but their account is disabled');
$self->userAdminMessage($result[0]->adminMessage);
return $self; # supports user.login (reportInputVerificationError)
}
}
}
return; # nope, nothing to see here... (no error, anyway)
}
@ -85,12 +115,7 @@ sub authenticateUser {
sub reportInputVerificationError {
my $self = shift;
my($app) = @_;
my $message = '';
if (defined($self->errorState) and defined($self->errorState->[0])) {
$message = $self->errorState->[0]->adminMessage;
}
$self->errorState(undef);
$app->output->loginFailed(1, $message); # 1 means 'unknown username/password'
$app->output->loginFailed(1, $self->userAdminMessage); # 1 means 'unknown username/password'
}
# dispatcher.commands
@ -109,6 +134,12 @@ sub cmdLoginLogout {
if (defined($user)) {
$user->logout();
$app->removeObject($user);
# flag user internally so we know to log them out
# if they try to do something that requires login
# (note: we can't store a reference to an object
# ourselves, so create an object to hold the
# reference for us)
$self->addObject($self->objectCreate($app, $user));
}
$app->noCommand();
}
@ -162,13 +193,19 @@ sub hasRight {
sub requireLogin {
my $self = shift;
my($app) = @_;
my $deniedUser = $app->getObject('user.login.deniedUserHandle');
if (defined($deniedUser)) {
$deniedUser->user->loggedOut();
$app->removeObject($deniedUser);
} else {
my $address = $app->input->address;
if (defined($address) and not defined($app->getService('user.factory')->getUserByContactDetails($app, $app->input->protocol, $address))) {
my($user, $password) = $self->createUser($app, $app->input->protocol, $address);
$self->sendPassword($app, $user, $app->input->protocol, $password);
} else {
$app->output->loginFailed(0, '');
return;
}
}
$app->output->loginFailed(0, '');
}
# dispatcher.output.generic

View File

@ -108,7 +108,6 @@ sub objectProvides {
sub objectInit {
my $self = shift;
my($app, $userID, $mode, $password, $adminMessage, $newFieldID, $newFieldValue, $newFieldPassword, $fields, $groups, $rights) = @_;
$self->{'_DIRTY'} = {}; # make sure propertySet is happy
$self->SUPER::objectInit(@_);
$self->userID($userID);
@ -295,15 +294,25 @@ sub logout {
}
}
sub loggedOut {
my $self = shift;
if ($self->mode == 1) {
$self->mode(0); # clear flag
}
}
sub checkLogin {
my $self = shift;
# check to see if the account is disabled
my $enabled = $self->mode == 0;
# if user is logging out, clear flag
if ($self->mode == 1) {
$self->mode(0);
# user is logging out
return '0E0'; # true but zero
} elsif ($self->mode == 0) {
# account is in normal state
return 1; # true
} else {
# account is disabled
return 0; # false
}
return $enabled;
}
sub joinGroup {