Merge branch 'more-password-stuff'

This commit is contained in:
Adriaan de Groot 2019-11-08 13:34:06 +01:00
commit 6a142d9edb
5 changed files with 63 additions and 27 deletions

View File

@ -31,19 +31,15 @@
#include <memory> #include <memory>
PasswordCheck::PasswordCheck() PasswordCheck::PasswordCheck()
: m_message() : m_weight( 0 )
, m_message()
, m_accept( []( const QString& ) { return true; } ) , m_accept( []( const QString& ) { return true; } )
{ {
} }
PasswordCheck::PasswordCheck( const QString& m, AcceptFunc a ) PasswordCheck::PasswordCheck( MessageFunc m, AcceptFunc a, Weight weight )
: m_message( [m]() { return m; } ) : m_weight( weight )
, m_accept( a ) , m_message( m )
{
}
PasswordCheck::PasswordCheck( MessageFunc m, AcceptFunc a )
: m_message( m )
, m_accept( a ) , m_accept( a )
{ {
} }
@ -59,7 +55,8 @@ DEFINE_CHECK_FUNC( minLength )
{ {
cDebug() << Logger::SubEntry << "minLength set to" << minLength; cDebug() << Logger::SubEntry << "minLength set to" << minLength;
checks.push_back( PasswordCheck( []() { return QCoreApplication::translate( "PWQ", "Password is too short" ); }, checks.push_back( PasswordCheck( []() { return QCoreApplication::translate( "PWQ", "Password is too short" ); },
[minLength]( const QString& s ) { return s.length() >= minLength; } ) ); [minLength]( const QString& s ) { return s.length() >= minLength; },
PasswordCheck::Weight( 10 ) ) );
} }
} }
@ -74,7 +71,8 @@ DEFINE_CHECK_FUNC( maxLength )
{ {
cDebug() << Logger::SubEntry << "maxLength set to" << maxLength; cDebug() << Logger::SubEntry << "maxLength set to" << maxLength;
checks.push_back( PasswordCheck( []() { return QCoreApplication::translate( "PWQ", "Password is too long" ); }, checks.push_back( PasswordCheck( []() { return QCoreApplication::translate( "PWQ", "Password is too long" ); },
[maxLength]( const QString& s ) { return s.length() <= maxLength; } ) ); [maxLength]( const QString& s ) { return s.length() <= maxLength; },
PasswordCheck::Weight( 10 ) ) );
} }
} }
@ -363,7 +361,8 @@ DEFINE_CHECK_FUNC( libpwquality )
cDebug() << "Password strength" << r << "too low"; cDebug() << "Password strength" << r << "too low";
} }
return r >= settings->arbitrary_minimum_strength; return r >= settings->arbitrary_minimum_strength;
} ) ); },
PasswordCheck::Weight( 100 ) ) );
} }
} }
#endif #endif

View File

@ -38,11 +38,18 @@ public:
using AcceptFunc = std::function< bool( const QString& ) >; using AcceptFunc = std::function< bool( const QString& ) >;
using MessageFunc = std::function< QString() >; using MessageFunc = std::function< QString() >;
/** Generate a @p message if @p filter returns true */ using Weight = size_t;
PasswordCheck( MessageFunc message, AcceptFunc filter );
/** Yields @p message if @p filter returns true */ /** @brief Generate a @p message if @p filter returns true
PasswordCheck( const QString& message, AcceptFunc filter ); *
/** Null check, always returns empty */ * When @p filter returns true on the proposed password, the
* password is accepted (by this check). If false, then the
* @p message will be shown to the user.
*
* @p weight is used to order the checks (low-weight goes first).
*/
PasswordCheck( MessageFunc message, AcceptFunc filter, Weight weight = 1000 );
/** @brief Null check, always accepts, no message */
PasswordCheck(); PasswordCheck();
/** Applies this check to the given password string @p s /** Applies this check to the given password string @p s
@ -52,7 +59,11 @@ public:
*/ */
QString filter( const QString& s ) const { return m_accept( s ) ? QString() : m_message(); } QString filter( const QString& s ) const { return m_accept( s ) ? QString() : m_message(); }
Weight weight() const { return m_weight; }
bool operator<( const PasswordCheck& other ) const { return weight() < other.weight(); }
private: private:
Weight m_weight;
MessageFunc m_message; MessageFunc m_message;
AcceptFunc m_accept; AcceptFunc m_accept;
}; };

View File

@ -144,6 +144,11 @@ UsersPage::retranslate()
"use this computer, you can create multiple " "use this computer, you can create multiple "
"accounts after installation.</small>" ) ); "accounts after installation.</small>" ) );
} }
// Re-do password checks (with output messages) as well.
// .. the password-checking methods get their values from the text boxes,
// not from their parameters.
onPasswordTextChanged( QString() );
onRootPasswordTextChanged( QString() );
} }
@ -222,6 +227,8 @@ void
UsersPage::onActivate() UsersPage::onActivate()
{ {
ui->textBoxFullName->setFocus(); ui->textBoxFullName->setFocus();
onPasswordTextChanged( QString() );
onRootPasswordTextChanged( QString() );
} }
@ -407,14 +414,7 @@ UsersPage::validateHostnameText( const QString& textRef )
bool bool
UsersPage::checkPasswordAcceptance( const QString& pw1, const QString& pw2, QLabel* badge, QLabel* message ) UsersPage::checkPasswordAcceptance( const QString& pw1, const QString& pw2, QLabel* badge, QLabel* message )
{ {
if ( pw1.isEmpty() && pw2.isEmpty() ) if ( pw1 != pw2 )
{
// Not exactly labelOk() because we also don't want a checkmark OK
badge->clear();
message->clear();
return false;
}
else if ( pw1 != pw2 )
{ {
labelError( badge, message, tr( "Your passwords do not match!" ) ); labelError( badge, message, tr( "Your passwords do not match!" ) );
return false; return false;
@ -424,6 +424,12 @@ UsersPage::checkPasswordAcceptance( const QString& pw1, const QString& pw2, QLab
bool failureIsFatal = ui->checkBoxValidatePassword->isChecked(); bool failureIsFatal = ui->checkBoxValidatePassword->isChecked();
bool failureFound = false; bool failureFound = false;
if ( m_passwordChecksChanged )
{
std::sort( m_passwordChecks.begin(), m_passwordChecks.end() );
m_passwordChecksChanged = false;
}
for ( auto pc : m_passwordChecks ) for ( auto pc : m_passwordChecks )
{ {
QString s = pc.filter( pw1 ); QString s = pc.filter( pw1 );
@ -502,6 +508,8 @@ UsersPage::setReusePasswordDefault( bool checked )
void void
UsersPage::addPasswordCheck( const QString& key, const QVariant& value ) UsersPage::addPasswordCheck( const QString& key, const QVariant& value )
{ {
m_passwordChecksChanged = true;
if ( key == "minLength" ) if ( key == "minLength" )
{ {
add_check_minLength( m_passwordChecks, value ); add_check_minLength( m_passwordChecks, value );
@ -510,6 +518,16 @@ UsersPage::addPasswordCheck( const QString& key, const QVariant& value )
{ {
add_check_maxLength( m_passwordChecks, value ); add_check_maxLength( m_passwordChecks, value );
} }
else if ( key == "nonempty" )
{
if ( value.toBool() )
{
m_passwordChecks.push_back( PasswordCheck(
[]() { return QCoreApplication::translate( "PWQ", "Password is empty" ); },
[]( const QString& s ) { return !s.isEmpty(); },
PasswordCheck::Weight( 1 ) ) );
}
}
#ifdef CHECK_PWQUALITY #ifdef CHECK_PWQUALITY
else if ( key == "libpwquality" ) else if ( key == "libpwquality" )
{ {

View File

@ -90,6 +90,7 @@ private:
Ui::Page_UserSetup* ui; Ui::Page_UserSetup* ui;
PasswordCheckList m_passwordChecks; PasswordCheckList m_passwordChecks;
bool m_passwordChecksChanged = false;
bool m_readyFullName; bool m_readyFullName;
bool m_readyUsername; bool m_readyUsername;

View File

@ -58,8 +58,14 @@ setRootPassword: true
doReusePassword: true doReusePassword: true
# These are optional password-requirements that a distro can enforce # These are optional password-requirements that a distro can enforce
# on the user. The values given in this sample file disable each check, # on the user. The values given in this sample file set only very weak
# as if the check was not listed at all. # validation settings.
#
# - nonempty rejects empty passwords
# - there are no length validations
# - libpwquality (if it is enabled at all) has no length of class
# restrictions, although it will still reject palindromes and
# dictionary words with these settings.
# #
# Checks may be listed multiple times; each is checked separately, # Checks may be listed multiple times; each is checked separately,
# and no effort is done to ensure that the checks are consistent # and no effort is done to ensure that the checks are consistent
@ -84,6 +90,7 @@ doReusePassword: true
# (That will show the box *Allow weak passwords* in the user- # (That will show the box *Allow weak passwords* in the user-
# interface, and check it by default). # interface, and check it by default).
passwordRequirements: passwordRequirements:
nonempty: true
minLength: -1 # Password at least this many characters minLength: -1 # Password at least this many characters
maxLength: -1 # Password at most this many characters maxLength: -1 # Password at most this many characters
libpwquality: libpwquality: