Permalink
Browse files

Item14414: Add support for enable/disable passwords.

 - Htpasswor now loads "commented" accounts but marks them disabled.
 - Login will reject any disabled account
 - USERINFO macro displays enabled/disabled state.
  • Loading branch information...
1 parent 8a31d27 commit a3ed1e8a01866c336bab7cf607045b4f5845ed92 @gac410 gac410 committed Jun 19, 2017
@@ -1585,6 +1585,28 @@ sub passwordError {
=begin TML
+---++ ObjectMethod userEnabled( $login, $enabled ) -> $boolean
+
+Finds if the password is enabled for the given user.
+
+Returns 1 on success, undef on failure.
+
+=cut
+
+sub userEnabled {
+ my ( $this, $login, $enabled ) = @_;
+
+ # If we don't have a PasswordManager and use TemplateLogin, always allow login
+ return 1
+ if ( $Foswiki::cfg{PasswordManager} eq 'none'
+ && $Foswiki::cfg{LoginManager} eq
+ 'Foswiki::LoginManager::TemplateLogin' );
+
+ return $this->{passwords}->userEnabled( $login, $enabled );
+}
+
+=begin TML
+
---++ ObjectMethod validateRegistrationField($field, $value ) -> $string
This method is called for every field submitted during registration. It is also used
@@ -1788,7 +1810,7 @@ sub _expandUserList {
__END__
Foswiki - The Free and Open Source Wiki, http://foswiki.org/
-Copyright (C) 2008-2010 Foswiki Contributors. Foswiki Contributors
+Copyright (C) 2008-2017 Foswiki Contributors. Foswiki Contributors
are listed in the AUTHORS file in the root of this distribution.
NOTE: Please extend that file, not this notice.
@@ -327,26 +327,37 @@ crypt-md5::crypt-md5@example.com
DONE
$this->assert( close($fh) );
- foreach
- my $algo ( 'apache-md5', 'htdigest-md5', 'crypt', 'sha1', 'crypt-md5' )
- {
+ foreach my $algo ( 'apache-md5', 'crypt', 'sha1', 'crypt-md5' ) {
$Foswiki::cfg{Htpasswd}{Encoding} = $algo;
$impl = Foswiki::Users::HtPasswdUser->new( $this->{session} );
+ $impl->ClearCache() if $impl->can('ClearCache');
- foreach my $user ( 'crypt', 'apache-md5', 'sha1', 'htdigest-md5',
- 'crypt-md5' )
- {
+ foreach my $user ( 'crypt', 'apache-md5', 'sha1', 'crypt-md5' ) {
$this->assert( !$impl->checkPassword( $user, '' ) );
+ $this->assert( $impl->userEnabled($user) );
}
+ $impl->finish();
}
+ $impl->finish();
+
+ # SMELL: With autodetect disabled, htdigest algorithm can't handle passwords
+ # stored with other formats.
+ $Foswiki::cfg{Htpasswd}{AutoDetect} = 0;
+ $Foswiki::cfg{Htpasswd}{Encoding} = 'htdigest-md5';
+ $impl = Foswiki::Users::HtPasswdUser->new( $this->{session} );
+ $impl->ClearCache() if $impl->can('ClearCache');
+ $this->assert( !$impl->checkPassword( 'htdigest-md5', '' ) );
+ $this->assert( $impl->userEnabled('htdigest-md5') );
+
# Verify that each algorithm can reset an empty password entry
# But need to autodetect to not corrupt existing entries
$Foswiki::cfg{Htpasswd}{AutoDetect} = 1;
foreach
my $user ( 'crypt', 'apache-md5', 'sha1', 'htdigest-md5', 'crypt-md5' )
{
$Foswiki::cfg{Htpasswd}{Encoding} = $user;
+ $impl->finish();
$impl = Foswiki::Users::HtPasswdUser->new( $this->{session} );
my $added = $impl->setPassword( $user, "pw$user", 1 );
@@ -357,12 +368,58 @@ DONE
# Verify that the passwords were reset
$Foswiki::cfg{Htpasswd}{AutoDetect} = 1;
+ $impl->finish();
$impl = Foswiki::Users::HtPasswdUser->new( $this->{session} );
+ $impl->ClearCache() if $impl->can('ClearCache');
foreach my $user ( 'crypt', 'apache-md5', 'sha1', 'crypt-md5' ) {
$this->assert( $impl->checkPassword( $user, "pw$user" ),
"Failure for $user" );
}
+ # Make sure file is detected as modified.
+ sleep 2;
+
+ open( $fh, '>:encoding(utf-8)', "$Foswiki::cfg{TempfileDir}/junkpasswd" )
+ || die "Unable to open \n $! \n\n ";
+ print $fh <<'DONE';
+#crypt::crypt@example.com
+#apache-md5::apache-md5@example.com
+#sha1::sha1@example.com
+#htdigest-md5:MyNewRealm::htdigest-md5@example.com
+#crypt-md5::crypt-md5@example.com
+DONE
+ $this->assert( close($fh) );
+
+ # Verify that disabled entries are properly detected.
+ foreach
+ my $algo ( 'apache-md5', 'crypt', 'sha1', 'crypt-md5', 'htdigest-md5' )
+ {
+ $Foswiki::cfg{Htpasswd}{Encoding} = $algo;
+ $impl = Foswiki::Users::HtPasswdUser->new( $this->{session} );
+ $impl->ClearCache() if $impl->can('ClearCache');
+
+ foreach my $user ( 'crypt', 'apache-md5', 'sha1',
+ 'crypt-md5', 'htdigest-md5' )
+ {
+ $this->assert( !$impl->checkPassword( $user, '' ) );
+ $this->assert( !$impl->userEnabled($user) );
+ }
+ $impl->finish();
+ }
+
+ foreach
+ my $user ( 'crypt', 'apache-md5', 'sha1', 'htdigest-md5', 'crypt-md5' )
+ {
+ $Foswiki::cfg{Htpasswd}{Encoding} = $user;
+ $impl->finish();
+ $impl = Foswiki::Users::HtPasswdUser->new( $this->{session} );
+
+ my $enabled = $impl->userEnabled( $user, 1 );
+ $this->assert($enabled);
+ }
+
+ #dumpFile();
+
return;
}
@@ -1,4 +1,4 @@
-%META:TOPICINFO{author="ProjectContributor" date="1456368117" format="1.1" version="1"}%
+%META:TOPICINFO{author="ProjectContributor" date="1497836887" format="1.1" version="1"}%
%META:TOPICPARENT{name="Macros"}%
---+ USERINFO -- retrieve details about a user
---++ Parameters
@@ -13,6 +13,7 @@ Format tokens that can be used in =format=:
| =$groups= (*) | Comma separated list of group membership. Currently only expands for users |
| =$isadmin= (*) | Has admin privileges (expands to =true= or =false=) |
| =$isgroup= | Is a group (expands to =true= or =false=) | |
+ | =$isenabled= | Returns true if the account is enabled. | |
%T% Tokens flagged '(*)' are considered private and are hidden from other users by default.%BR%
The [[FormatTokens][standard format tokens]] are also supported.
---++ Examples
@@ -528,6 +528,18 @@ sub loadSession {
# We should have a user at this point; or $defaultUser if there
# was no better information available.
+ if ( defined $this->{_cgisession}
+ && $pwchecker
+ && !$pwchecker->userEnabled($authUser) )
+ {
+ $this->{_cgisession}->delete();
+ $this->{_cgisession}->flush();
+ $this->{_cgisession} = undef;
+ $this->_delSessionCookieFromResponse();
+
+ $authUser = $this->redirectToLoggedOutUrl( $authUser, $defaultUser );
+ }
+
# is this a logout?
if (
( $authUser && $authUser ne $Foswiki::cfg{DefaultUserLogin} )
@@ -1604,7 +1616,7 @@ sub removeUserSessions {
__END__
Foswiki - The Free and Open Source Wiki, http://foswiki.org/
-Copyright (C) 2008-2015 Foswiki Contributors. Foswiki Contributors
+Copyright (C) 2008-2017 Foswiki Contributors. Foswiki Contributors
are listed in the AUTHORS file in the root of this distribution.
NOTE: Please extend that file, not this notice.
@@ -195,93 +195,111 @@ sub login {
my $error = '';
if ($loginName) {
- my $validation = $users->checkPassword( $loginName, $loginPass );
- $error = $users->passwordError($loginName);
-
- if ( !$validation
- && $Foswiki::cfg{TemplateLogin}{AllowLoginUsingEmailAddress}
- && ( $loginName =~ $Foswiki::regex{emailAddrRegex} ) )
- {
-
- # try email addresses if it is one
- my $cuidList = $users->findUserByEmail($loginName);
- foreach my $cuid (@$cuidList) {
- my $login = $users->getLoginName($cuid);
-
- $validation = $users->checkPassword( $login, $loginPass );
- if ($validation) {
- $loginName = $login;
- last;
- }
- }
- }
-
- if ($validation) {
- # SUCCESS our user is authenticated. Note that we may already
- # have been logged in by the userLoggedIn call in loadSession,
- # because the username-password URL params are the same as
- # the params passed to this script, and they will be used
- # in loadSession if no other user info is available.
- $this->userLoggedIn($loginName);
+ if ( !$users->userEnabled($loginName) ) {
+ $session->{response}->status(200);
$session->logger->log(
{
level => 'info',
action => 'login',
webTopic => $web . '.' . $topic,
- extra => "AUTHENTICATION SUCCESS - $loginName - "
+ extra => "AUTHENTICATION DENIED - $loginName - Disabled",
}
);
+ $banner = $session->templates->expandTemplate('DISABLED_USER');
+ }
+ else {
+
+ my $validation = $users->checkPassword( $loginName, $loginPass );
+ $error = $users->passwordError($loginName);
- # remove the sudo param - its only to tell TemplateLogin
- # that we're using BaseMapper..
- $query->delete('sudo');
+ if ( !$validation
+ && $Foswiki::cfg{TemplateLogin}{AllowLoginUsingEmailAddress}
+ && ( $loginName =~ $Foswiki::regex{emailAddrRegex} ) )
+ {
- $this->{_cgisession}->param( 'VALIDATION', $validation )
- if $this->{_cgisession};
- if ( !$origurl || $origurl eq $query->url() ) {
- $origurl = $session->getScriptUrl( 0, 'view', $web, $topic );
+ # try email addresses if it is one
+ my $cuidList = $users->findUserByEmail($loginName);
+ foreach my $cuid (@$cuidList) {
+ my $login = $users->getLoginName($cuid);
+
+ $validation = $users->checkPassword( $login, $loginPass );
+ if ($validation) {
+ $loginName = $login;
+ last;
+ }
+ }
}
- else {
- # Unpack params encoded in the origurl and restore them
- # to the query. If they were left in the query string they
- # would be lost if we redirect with passthrough.
- # First extract the params, ignoring any trailing fragment.
- if ( $origurl =~ s/\?([^#]*)// ) {
- foreach my $pair ( split( /[&;]/, $1 ) ) {
- if ( $pair =~ m/(.*?)=(.*)/ ) {
- $query->param( $1, TAINT($2) );
+ if ($validation) {
+
+ # SUCCESS our user is authenticated. Note that we may already
+ # have been logged in by the userLoggedIn call in loadSession,
+ # because the username-password URL params are the same as
+ # the params passed to this script, and they will be used
+ # in loadSession if no other user info is available.
+ $this->userLoggedIn($loginName);
+ $session->logger->log(
+ {
+ level => 'info',
+ action => 'login',
+ webTopic => $web . '.' . $topic,
+ extra => "AUTHENTICATION SUCCESS - $loginName - "
+ }
+ );
+
+ # remove the sudo param - its only to tell TemplateLogin
+ # that we're using BaseMapper..
+ $query->delete('sudo');
+
+ $this->{_cgisession}->param( 'VALIDATION', $validation )
+ if $this->{_cgisession};
+ if ( !$origurl || $origurl eq $query->url() ) {
+ $origurl =
+ $session->getScriptUrl( 0, 'view', $web, $topic );
+ }
+ else {
+
+ # Unpack params encoded in the origurl and restore them
+ # to the query. If they were left in the query string they
+ # would be lost if we redirect with passthrough.
+ # First extract the params, ignoring any trailing fragment.
+ if ( $origurl =~ s/\?([^#]*)// ) {
+ foreach my $pair ( split( /[&;]/, $1 ) ) {
+ if ( $pair =~ m/(.*?)=(.*)/ ) {
+ $query->param( $1, TAINT($2) );
+ }
}
}
+
+ # Restore the action too
+ $query->action($origaction) if $origaction;
}
- # Restore the action too
- $query->action($origaction) if $origaction;
+ # Restore the method used on origUrl so if it was a GET, we
+ # get another GET.
+ $query->method($origmethod);
+ $session->redirect( $origurl, 1 );
+ return;
}
+ else {
- # Restore the method used on origUrl so if it was a GET, we
- # get another GET.
- $query->method($origmethod);
- $session->redirect( $origurl, 1 );
- return;
- }
- else {
-
- # Tasks:Item1029 After much discussion, the 403 code is not
- # used for authentication failures. RFC states: "Authorization
- # will not help and the request SHOULD NOT be repeated" which
- # is not the situation here.
- $session->{response}->status(200);
- $session->logger->log(
- {
- level => 'info',
- action => 'login',
- webTopic => $web . '.' . $topic,
- extra => "AUTHENTICATION FAILURE - $loginName - ",
- }
- );
- $banner = $session->templates->expandTemplate('UNRECOGNISED_USER');
+ # Tasks:Item1029 After much discussion, the 403 code is not
+ # used for authentication failures. RFC states: "Authorization
+ # will not help and the request SHOULD NOT be repeated" which
+ # is not the situation here.
+ $session->{response}->status(200);
+ $session->logger->log(
+ {
+ level => 'info',
+ action => 'login',
+ webTopic => $web . '.' . $topic,
+ extra => "AUTHENTICATION FAILURE - $loginName - ",
+ }
+ );
+ $banner =
+ $session->templates->expandTemplate('UNRECOGNISED_USER');
+ }
}
}
else {
@@ -340,7 +358,7 @@ sub login {
__END__
Module of Foswiki - The Free and Open Source Wiki, http://foswiki.org/
-Copyright (C) 2008-2015 Foswiki Contributors. All Rights Reserved.
+Copyright (C) 2008-2017 Foswiki Contributors. All Rights Reserved.
Foswiki Contributors are listed in the AUTHORS file in the root
of this distribution. NOTE: Please extend that file, not this notice.
@@ -89,6 +89,11 @@ my %USERINFO_tokens = (
my ( $this, $user ) = @_;
return $this->{users}->isGroup($user) ? 'true' : 'false';
+ },
+ isenabled => sub {
+ my ( $this, $user ) = @_;
+
+ return $this->{users}->userEnabled($user) ? 'true' : 'false';
}
);
my $USERINFO_tokenregex = join( '|', keys %USERINFO_tokens );
Oops, something went wrong.

0 comments on commit a3ed1e8

Please sign in to comment.