From 40d7d1baac94703063d6ef57c4d515d17373fd68 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 28 Jul 2020 10:21:23 +0200 Subject: [PATCH] [users] Move login validation to Config object - add a loginNameStatus which is a QString (empty if things are ok) stating what's wrong with the loginName, if anything. --- src/modules/users/Config.cpp | 58 ++++++++++++++++++++++++++++++--- src/modules/users/Config.h | 6 ++++ src/modules/users/UsersPage.cpp | 57 +++++++++----------------------- src/modules/users/UsersPage.h | 2 +- 4 files changed, 77 insertions(+), 46 deletions(-) diff --git a/src/modules/users/Config.cpp b/src/modules/users/Config.cpp index d4fdb16c5..03ae60b2f 100644 --- a/src/modules/users/Config.cpp +++ b/src/modules/users/Config.cpp @@ -28,6 +28,10 @@ #include #include +#include + +static const QRegExp USERNAME_RX( "^[a-z_][a-z0-9_-]*[$]?$" ); +static constexpr const int USERNAME_MAX_LENGTH = 31; Config::Config( QObject* parent ) : QObject( parent ) @@ -49,7 +53,7 @@ Config::setUserShell( const QString& shell ) } static inline void -setGS( const QString& key, const QString& group ) +insertInGlobalStorage( const QString& key, const QString& group ) { auto* gs = Calamares::JobQueue::instance()->globalStorage(); if ( !gs || group.isEmpty() ) @@ -62,14 +66,14 @@ setGS( const QString& key, const QString& group ) void Config::setAutologinGroup( const QString& group ) { - setGS( QStringLiteral( "autologinGroup" ), group ); + insertInGlobalStorage( QStringLiteral( "autologinGroup" ), group ); emit autologinGroupChanged( group ); } void Config::setSudoersGroup( const QString& group ) { - setGS( QStringLiteral( "sudoersGroup" ), group ); + insertInGlobalStorage( QStringLiteral( "sudoersGroup" ), group ); emit sudoersGroupChanged( group ); } @@ -82,7 +86,53 @@ Config::setLoginName( const QString& login ) m_customLoginName = !login.isEmpty(); m_loginName = login; emit loginNameChanged( login ); + emit loginNameStatusChanged( loginNameStatus() ); + } +} + +const QStringList& +Config::forbiddenLoginNames() +{ + static QStringList forbidden { "root" }; + return forbidden; +} + +QString +Config::loginNameStatus() const +{ + // An empty login is "ok", even if it isn't really + if ( m_loginName.isEmpty() ) + { + return QString(); + } + + QRegExpValidator validateEntireLoginName( USERNAME_RX ); + QRegExpValidator validateFirstLetter( QRegExp( "[a-z_].*" ) ); // anchors are implicit in QRegExpValidator + int pos = -1; + + if ( m_loginName.length() > USERNAME_MAX_LENGTH ) + { + return tr( "Your username is too long." ); + } + QString login( m_loginName ); // make a copy because validate() doesn't take const& + if ( validateFirstLetter.validate( login, pos ) == QValidator::Invalid ) + { + return tr( "Your username must start with a lowercase letter or underscore." ); + } + if ( validateEntireLoginName.validate( login, pos ) == QValidator::Invalid ) + { + return tr( "Only lowercase letters, numbers, underscore and hyphen are allowed." ); } + + for ( const QString& badName : forbiddenLoginNames() ) + { + if ( 0 == QString::compare( badName, m_loginName, Qt::CaseSensitive ) ) + { + return tr( "'%1' is not allowed as user name." ).arg( badName ); + } + } + + return QString(); } void @@ -133,7 +183,6 @@ guessProductName() static QString makeLoginNameSuggestion( const QStringList& parts ) { - static const QRegExp USERNAME_RX( "^[a-z_][a-z0-9_-]*[$]?$" ); if ( parts.isEmpty() || parts.first().isEmpty() ) { return QString(); @@ -199,6 +248,7 @@ Config::setFullName( const QString& name ) { m_loginName = login; emit loginNameChanged( login ); + emit loginNameStatusChanged( loginNameStatus() ); } } if ( !m_customHostName ) diff --git a/src/modules/users/Config.h b/src/modules/users/Config.h index 6a5ff0105..91a494619 100644 --- a/src/modules/users/Config.h +++ b/src/modules/users/Config.h @@ -35,6 +35,7 @@ class Config : public QObject Q_PROPERTY( QString fullName READ fullName WRITE setFullName NOTIFY fullNameChanged ) Q_PROPERTY( QString loginName READ loginName WRITE setLoginName NOTIFY loginNameChanged ) + Q_PROPERTY( QString loginNameStatus READ loginNameStatus NOTIFY loginNameStatusChanged ) Q_PROPERTY( QString hostName READ hostName WRITE setHostName NOTIFY hostNameChanged ) @@ -60,10 +61,14 @@ public: QString fullName() const { return m_fullName; } /// The login name of the user QString loginName() const { return m_loginName; } + /// Status message about login -- empty for "ok" + QString loginNameStatus() const; /// The host name (name for the system) QString hostName() const { return m_hostName; } + static const QStringList& forbiddenLoginNames(); + public Q_SLOTS: /** @brief Sets the user's shell if possible * @@ -94,6 +99,7 @@ signals: void sudoersGroupChanged( const QString& ); void fullNameChanged( const QString& ); void loginNameChanged( const QString& ); + void loginNameStatusChanged( const QString& ); void hostNameChanged( const QString& ); private: diff --git a/src/modules/users/UsersPage.cpp b/src/modules/users/UsersPage.cpp index ee2719091..cc940502e 100644 --- a/src/modules/users/UsersPage.cpp +++ b/src/modules/users/UsersPage.cpp @@ -46,7 +46,6 @@ #include #include -static const QRegExp USERNAME_RX( "^[a-z_][a-z0-9_-]*[$]?$" ); static const QRegExp HOSTNAME_RX( "^[a-zA-Z0-9][-a-zA-Z0-9_]*$" ); static constexpr const int USERNAME_MAX_LENGTH = 31; static constexpr const int HOSTNAME_MIN_LENGTH = 2; @@ -129,7 +128,7 @@ UsersPage::UsersPage( Config* config, QWidget* parent ) connect( ui->textBoxLoginName, &QLineEdit::textEdited, config, &Config::setLoginName ); connect( config, &Config::loginNameChanged, ui->textBoxLoginName, &QLineEdit::setText ); - connect( config, &Config::loginNameChanged, this, &UsersPage::validateUsernameText ); + connect( config, &Config::loginNameStatusChanged, this, &UsersPage::reportLoginNameStatus ); setWriteRootPassword( true ); ui->checkBoxReusePassword->setChecked( true ); @@ -277,55 +276,31 @@ UsersPage::onFullNameTextEdited( const QString& textRef ) checkReady( isReady() ); } - void -UsersPage::validateUsernameText( const QString& textRef ) +UsersPage::reportLoginNameStatus( const QString& status ) { - QString text( textRef ); - QRegExpValidator val_whole( USERNAME_RX ); - QRegExpValidator val_start( QRegExp( "[a-z_].*" ) ); // anchors are implicit in QRegExpValidator - int pos = -1; - - if ( text.isEmpty() ) - { - ui->labelUsernameError->clear(); - ui->labelUsername->clear(); - m_readyUsername = false; - } - else if ( text.length() > USERNAME_MAX_LENGTH ) - { - labelError( ui->labelUsername, ui->labelUsernameError, tr( "Your username is too long." ) ); - m_readyUsername = false; - } - else if ( val_start.validate( text, pos ) == QValidator::Invalid ) - { - labelError( ui->labelUsername, - ui->labelUsernameError, - tr( "Your username must start with a lowercase letter or underscore." ) ); - m_readyUsername = false; - } - else if ( val_whole.validate( text, pos ) == QValidator::Invalid ) + if ( status.isEmpty() ) { - labelError( ui->labelUsername, - ui->labelUsernameError, - tr( "Only lowercase letters, numbers, underscore and hyphen are allowed." ) ); - m_readyUsername = false; - } - else if ( 0 == QString::compare( "root", text, Qt::CaseSensitive ) ) - { - labelError( ui->labelUsername, ui->labelUsernameError, tr( "'root' is not allowed as user name." ) ); - m_readyUsername = false; + if ( m_config->loginName().isEmpty() ) + { + ui->labelUsernameError->clear(); + ui->labelUsername->clear(); + m_readyUsername = false; + } + else + { + labelOk( ui->labelUsername, ui->labelUsernameError ); + m_readyUsername = true; + } } else { - labelOk( ui->labelUsername, ui->labelUsernameError ); - m_readyUsername = true; + labelError( ui->labelUsername, ui->labelUsernameError, status ); + m_readyUsername = false; } - emit checkReady( isReady() ); } - void UsersPage::validateHostnameText( const QString& textRef ) { diff --git a/src/modules/users/UsersPage.h b/src/modules/users/UsersPage.h index ac5701b2d..7cf83100c 100644 --- a/src/modules/users/UsersPage.h +++ b/src/modules/users/UsersPage.h @@ -72,7 +72,7 @@ public: protected slots: void onFullNameTextEdited( const QString& ); - void validateUsernameText( const QString& ); + void reportLoginNameStatus( const QString& ); void validateHostnameText( const QString& ); void onPasswordTextChanged( const QString& ); void onRootPasswordTextChanged( const QString& );