commit
43f9f2e2f4
@ -71,4 +71,7 @@ calamares_add_test(
|
||||
SOURCES
|
||||
Tests.cpp
|
||||
Config.cpp
|
||||
CheckPWQuality.cpp
|
||||
LIBRARIES
|
||||
${USER_EXTRA_LIB}
|
||||
)
|
||||
|
@ -22,7 +22,6 @@
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
#ifdef HAVE_LIBPWQUALITY
|
||||
#include <pwquality.h>
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "utils/String.h"
|
||||
#include "utils/Variant.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QFile>
|
||||
#include <QRegExp>
|
||||
|
||||
@ -36,6 +37,22 @@ static const QRegExp HOSTNAME_RX( "^[a-zA-Z0-9][-a-zA-Z0-9_]*$" );
|
||||
static constexpr const int HOSTNAME_MIN_LENGTH = 2;
|
||||
static constexpr const int HOSTNAME_MAX_LENGTH = 63;
|
||||
|
||||
const NamedEnumTable< HostNameAction >&
|
||||
hostNameActionNames()
|
||||
{
|
||||
// *INDENT-OFF*
|
||||
// clang-format off
|
||||
static const NamedEnumTable< HostNameAction > names {
|
||||
{ QStringLiteral( "none" ), HostNameAction::None },
|
||||
{ QStringLiteral( "etcfile" ), HostNameAction::EtcHostname },
|
||||
{ QStringLiteral( "hostnamed" ), HostNameAction::SystemdHostname }
|
||||
};
|
||||
// clang-format on
|
||||
// *INDENT-ON*
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
Config::Config( QObject* parent )
|
||||
: QObject( parent )
|
||||
{
|
||||
@ -343,6 +360,106 @@ Config::setAutoLogin( bool b )
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Config::setReuseUserPasswordForRoot( bool reuse )
|
||||
{
|
||||
if ( reuse != m_reuseUserPasswordForRoot )
|
||||
{
|
||||
m_reuseUserPasswordForRoot = reuse;
|
||||
emit reuseUserPasswordForRootChanged( reuse );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Config::setRequireStrongPasswords( bool strong )
|
||||
{
|
||||
if ( strong != m_requireStrongPasswords )
|
||||
{
|
||||
m_requireStrongPasswords = strong;
|
||||
emit requireStrongPasswordsChanged( strong );
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Config::isPasswordAcceptable( const QString& password, QString& message )
|
||||
{
|
||||
bool failureIsFatal = requireStrongPasswords();
|
||||
|
||||
for ( auto pc : m_passwordChecks )
|
||||
{
|
||||
QString s = pc.filter( password );
|
||||
|
||||
if ( !s.isEmpty() )
|
||||
{
|
||||
message = s;
|
||||
return !failureIsFatal;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Config::setUserPassword( const QString& s )
|
||||
{
|
||||
m_userPassword = s;
|
||||
// TODO: check new password status
|
||||
emit userPasswordChanged( s );
|
||||
}
|
||||
|
||||
void
|
||||
Config::setUserPasswordSecondary( const QString& s )
|
||||
{
|
||||
m_userPasswordSecondary = s;
|
||||
// TODO: check new password status
|
||||
emit userPasswordSecondaryChanged( s );
|
||||
}
|
||||
|
||||
void
|
||||
Config::setRootPassword( const QString& s )
|
||||
{
|
||||
if ( writeRootPassword() )
|
||||
{
|
||||
m_rootPassword = s;
|
||||
// TODO: check new password status
|
||||
emit rootPasswordChanged( s );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Config::setRootPasswordSecondary( const QString& s )
|
||||
{
|
||||
if ( writeRootPassword() )
|
||||
{
|
||||
m_rootPasswordSecondary = s;
|
||||
// TODO: check new password status
|
||||
emit rootPasswordSecondaryChanged( s );
|
||||
}
|
||||
}
|
||||
|
||||
QString Config::rootPassword() const
|
||||
{
|
||||
if ( writeRootPassword() )
|
||||
{
|
||||
if ( reuseUserPasswordForRoot() )
|
||||
return userPassword();
|
||||
return m_rootPassword;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString Config::rootPasswordSecondary() const
|
||||
{
|
||||
if ( writeRootPassword() )
|
||||
{
|
||||
if ( reuseUserPasswordForRoot() )
|
||||
return userPasswordSecondary();
|
||||
return m_rootPasswordSecondary;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
|
||||
STATICTEST void
|
||||
setConfigurationDefaultGroups( const QVariantMap& map, QStringList& defaultGroups )
|
||||
{
|
||||
@ -357,6 +474,74 @@ setConfigurationDefaultGroups( const QVariantMap& map, QStringList& defaultGroup
|
||||
}
|
||||
}
|
||||
|
||||
STATICTEST HostNameActions
|
||||
getHostNameActions( const QVariantMap& configurationMap )
|
||||
{
|
||||
HostNameAction setHostName = HostNameAction::EtcHostname;
|
||||
QString hostnameActionString = CalamaresUtils::getString( configurationMap, "setHostname" );
|
||||
if ( !hostnameActionString.isEmpty() )
|
||||
{
|
||||
bool ok = false;
|
||||
setHostName = hostNameActionNames().find( hostnameActionString, ok );
|
||||
if ( !ok )
|
||||
{
|
||||
setHostName = HostNameAction::EtcHostname; // Rather than none
|
||||
}
|
||||
}
|
||||
|
||||
HostNameAction writeHosts = CalamaresUtils::getBool( configurationMap, "writeHostsFile", true )
|
||||
? HostNameAction::WriteEtcHosts
|
||||
: HostNameAction::None;
|
||||
return setHostName | writeHosts;
|
||||
}
|
||||
|
||||
/** @brief Process entries in the passwordRequirements config entry
|
||||
*
|
||||
* Called once for each item in the config entry, which should
|
||||
* be a key-value pair. What makes sense as a value depends on
|
||||
* the key. Supported keys are documented in users.conf.
|
||||
*
|
||||
* @return if the check was added, returns @c true
|
||||
*/
|
||||
STATICTEST bool
|
||||
addPasswordCheck( const QString& key, const QVariant& value, PasswordCheckList& passwordChecks )
|
||||
{
|
||||
if ( key == "minLength" )
|
||||
{
|
||||
add_check_minLength( passwordChecks, value );
|
||||
}
|
||||
else if ( key == "maxLength" )
|
||||
{
|
||||
add_check_maxLength( passwordChecks, value );
|
||||
}
|
||||
else if ( key == "nonempty" )
|
||||
{
|
||||
if ( value.toBool() )
|
||||
{
|
||||
passwordChecks.push_back(
|
||||
PasswordCheck( []() { return QCoreApplication::translate( "PWQ", "Password is empty" ); },
|
||||
[]( const QString& s ) { return !s.isEmpty(); },
|
||||
PasswordCheck::Weight( 1 ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
cDebug() << "nonempty check is mentioned but set to false";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#ifdef CHECK_PWQUALITY
|
||||
else if ( key == "libpwquality" )
|
||||
{
|
||||
add_check_libpwquality( passwordChecks, value );
|
||||
}
|
||||
#endif // CHECK_PWQUALITY
|
||||
else
|
||||
{
|
||||
cWarning() << "Unknown password-check key" << key;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Config::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
@ -372,9 +557,25 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
setAutologinGroup( CalamaresUtils::getString( configurationMap, "autologinGroup" ) );
|
||||
setSudoersGroup( CalamaresUtils::getString( configurationMap, "sudoersGroup" ) );
|
||||
|
||||
m_hostNameActions = getHostNameActions( configurationMap );
|
||||
|
||||
setConfigurationDefaultGroups( configurationMap, m_defaultGroups );
|
||||
m_doAutoLogin = CalamaresUtils::getBool( configurationMap, "doAutologin", false );
|
||||
|
||||
m_writeRootPassword = CalamaresUtils::getBool( configurationMap, "setRootPassword", true );
|
||||
Calamares::JobQueue::instance()->globalStorage()->insert( "setRootPassword", m_writeRootPassword );
|
||||
|
||||
m_reuseUserPasswordForRoot = CalamaresUtils::getBool( configurationMap, "doReusePassword", false );
|
||||
|
||||
m_permitWeakPasswords = CalamaresUtils::getBool( configurationMap, "allowWeakPasswords", false );
|
||||
m_requireStrongPasswords
|
||||
= !m_permitWeakPasswords || !CalamaresUtils::getBool( configurationMap, "allowWeakPasswordsDefault", false );
|
||||
|
||||
// If the value doesn't exist, or isn't a map, this gives an empty map -- no problem
|
||||
auto pr_checks( configurationMap.value( "passwordRequirements" ).toMap() );
|
||||
for ( decltype( pr_checks )::const_iterator i = pr_checks.constBegin(); i != pr_checks.constEnd(); ++i )
|
||||
{
|
||||
addPasswordCheck( i.key(), i.value(), m_passwordChecks );
|
||||
}
|
||||
std::sort( m_passwordChecks.begin(), m_passwordChecks.end() );
|
||||
}
|
||||
|
@ -21,9 +21,25 @@
|
||||
#ifndef USERS_CONFIG_H
|
||||
#define USERS_CONFIG_H
|
||||
|
||||
#include "CheckPWQuality.h"
|
||||
|
||||
#include "utils/NamedEnum.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariantMap>
|
||||
|
||||
enum HostNameAction
|
||||
{
|
||||
None = 0x0,
|
||||
EtcHostname = 0x1, // Write to /etc/hostname directly
|
||||
SystemdHostname = 0x2, // Set via hostnamed(1)
|
||||
WriteEtcHosts = 0x4 // Write /etc/hosts (127.0.1.1 is this host)
|
||||
};
|
||||
Q_DECLARE_FLAGS( HostNameActions, HostNameAction )
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS( HostNameActions )
|
||||
|
||||
const NamedEnumTable< HostNameAction >& hostNameActionNames();
|
||||
|
||||
class Config : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -41,6 +57,22 @@ class Config : public QObject
|
||||
|
||||
Q_PROPERTY( QString hostName READ hostName WRITE setHostName NOTIFY hostNameChanged )
|
||||
Q_PROPERTY( QString hostNameStatus READ hostNameStatus NOTIFY hostNameStatusChanged )
|
||||
Q_PROPERTY( HostNameActions hostNameActions READ hostNameActions CONSTANT )
|
||||
|
||||
Q_PROPERTY( QString userPassword READ userPassword WRITE setUserPassword NOTIFY userPasswordChanged )
|
||||
Q_PROPERTY( QString userPasswordSecondary READ userPasswordSecondary WRITE setUserPasswordSecondary NOTIFY
|
||||
userPasswordSecondaryChanged )
|
||||
Q_PROPERTY( QString rootPassword READ rootPassword WRITE setRootPassword NOTIFY rootPasswordChanged )
|
||||
Q_PROPERTY( QString rootPasswordSecondary READ rootPasswordSecondary WRITE setRootPasswordSecondary NOTIFY
|
||||
rootPasswordSecondaryChanged )
|
||||
|
||||
Q_PROPERTY( bool writeRootPassword READ writeRootPassword CONSTANT )
|
||||
Q_PROPERTY( bool reuseUserPasswordForRoot READ reuseUserPasswordForRoot WRITE setReuseUserPasswordForRoot NOTIFY
|
||||
reuseUserPasswordForRootChanged )
|
||||
|
||||
Q_PROPERTY( bool permitWeakPasswords READ permitWeakPasswords CONSTANT )
|
||||
Q_PROPERTY( bool requireStrongPasswords READ requireStrongPasswords WRITE setRequireStrongPasswords NOTIFY
|
||||
requireStrongPasswordsChanged )
|
||||
|
||||
public:
|
||||
Config( QObject* parent = nullptr );
|
||||
@ -71,14 +103,42 @@ public:
|
||||
QString hostName() const { return m_hostName; }
|
||||
/// Status message about hostname -- empty for "ok"
|
||||
QString hostNameStatus() const;
|
||||
/// How to write the hostname
|
||||
HostNameActions hostNameActions() const { return m_hostNameActions; }
|
||||
|
||||
/// Should the user be automatically logged-in?
|
||||
bool doAutoLogin() const { return m_doAutoLogin; }
|
||||
/// Should the root password be written (if false, no password is set and the root account is disabled for login)
|
||||
bool writeRootPassword() const { return m_writeRootPassword; }
|
||||
/// Should the user's password be used for root, too? (if root is written at all)
|
||||
bool reuseUserPasswordForRoot() const { return m_reuseUserPasswordForRoot; }
|
||||
/// Show UI to change the "require strong password" setting?
|
||||
bool permitWeakPasswords() const { return m_permitWeakPasswords; }
|
||||
/// Current setting for "require strong password"?
|
||||
bool requireStrongPasswords() const { return m_requireStrongPasswords; }
|
||||
|
||||
const QStringList& defaultGroups() const { return m_defaultGroups; }
|
||||
|
||||
/** @brief Checks if the password is acceptable.
|
||||
*
|
||||
* If all is well, sets @p message to empty and returns @c true.
|
||||
* If there are warnings, but acceptable, sets @p message to something
|
||||
* non-empty and returns @c true. This happens if requireStrongPasswords
|
||||
* is turned off (by config or user).
|
||||
* If the password is not acceptable, sets @p message to something
|
||||
* non-empty and returns @c false.
|
||||
*/
|
||||
bool isPasswordAcceptable( const QString& password, QString& message );
|
||||
|
||||
// The user enters a password (and again in a separate UI element)
|
||||
QString userPassword() const { return m_userPassword; }
|
||||
QString userPasswordSecondary() const { return m_userPasswordSecondary; }
|
||||
// The root password **may** be entered in the UI, or may be suppressed
|
||||
// entirely when writeRootPassword is off, or may be equal to
|
||||
// the user password when reuseUserPasswordForRoot is on.
|
||||
QString rootPassword() const;
|
||||
QString rootPasswordSecondary() const;
|
||||
|
||||
static const QStringList& forbiddenLoginNames();
|
||||
static const QStringList& forbiddenHostNames();
|
||||
|
||||
@ -109,6 +169,16 @@ public Q_SLOTS:
|
||||
/// Sets the autologin flag
|
||||
void setAutoLogin( bool b );
|
||||
|
||||
/// Set to true to use the user password, unchanged, for root too
|
||||
void setReuseUserPasswordForRoot( bool reuse );
|
||||
/// Change setting for "require strong password"
|
||||
void setRequireStrongPasswords( bool strong );
|
||||
|
||||
void setUserPassword( const QString& );
|
||||
void setUserPasswordSecondary( const QString& );
|
||||
void setRootPassword( const QString& );
|
||||
void setRootPasswordSecondary( const QString& );
|
||||
|
||||
signals:
|
||||
void userShellChanged( const QString& );
|
||||
void autologinGroupChanged( const QString& );
|
||||
@ -119,6 +189,13 @@ signals:
|
||||
void hostNameChanged( const QString& );
|
||||
void hostNameStatusChanged( const QString& );
|
||||
void autoLoginChanged( bool );
|
||||
void reuseUserPasswordForRootChanged( bool );
|
||||
void requireStrongPasswordsChanged( bool );
|
||||
void userPasswordChanged( const QString& );
|
||||
void userPasswordSecondaryChanged( const QString& );
|
||||
void rootPasswordChanged( const QString& );
|
||||
void rootPasswordSecondaryChanged( const QString& );
|
||||
|
||||
|
||||
private:
|
||||
QStringList m_defaultGroups;
|
||||
@ -128,11 +205,25 @@ private:
|
||||
QString m_fullName;
|
||||
QString m_loginName;
|
||||
QString m_hostName;
|
||||
|
||||
QString m_userPassword;
|
||||
QString m_userPasswordSecondary; // enter again to be sure
|
||||
QString m_rootPassword;
|
||||
QString m_rootPasswordSecondary;
|
||||
|
||||
bool m_doAutoLogin = false;
|
||||
|
||||
bool m_writeRootPassword = true;
|
||||
bool m_reuseUserPasswordForRoot = false;
|
||||
|
||||
bool m_permitWeakPasswords = false;
|
||||
bool m_requireStrongPasswords = true;
|
||||
|
||||
bool m_customLoginName = false;
|
||||
bool m_customHostName = false;
|
||||
|
||||
HostNameActions m_hostNameActions;
|
||||
PasswordCheckList m_passwordChecks;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -27,13 +27,13 @@
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QtDBus/QDBusConnection>
|
||||
#include <QtDBus/QDBusInterface>
|
||||
#include <QtDBus/QDBusReply>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusReply>
|
||||
|
||||
using WriteMode = CalamaresUtils::System::WriteMode;
|
||||
|
||||
SetHostNameJob::SetHostNameJob( const QString& hostname, Actions a )
|
||||
SetHostNameJob::SetHostNameJob( const QString& hostname, HostNameActions a )
|
||||
: Calamares::Job()
|
||||
, m_hostname( hostname )
|
||||
, m_actions( a )
|
||||
@ -138,7 +138,7 @@ SetHostNameJob::exec()
|
||||
return Calamares::JobResult::error( tr( "Internal Error" ) );
|
||||
}
|
||||
|
||||
if ( m_actions & Action::EtcHostname )
|
||||
if ( m_actions & HostNameAction::EtcHostname )
|
||||
{
|
||||
if ( !setFileHostname( m_hostname ) )
|
||||
{
|
||||
@ -147,7 +147,7 @@ SetHostNameJob::exec()
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_actions & Action::WriteEtcHosts )
|
||||
if ( m_actions & HostNameAction::WriteEtcHosts )
|
||||
{
|
||||
if ( !writeFileEtcHosts( m_hostname ) )
|
||||
{
|
||||
@ -156,7 +156,7 @@ SetHostNameJob::exec()
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_actions & Action::SystemdHostname )
|
||||
if ( m_actions & HostNameAction::SystemdHostname )
|
||||
{
|
||||
// Does its own logging
|
||||
setSystemdHostname( m_hostname );
|
||||
|
@ -21,23 +21,15 @@
|
||||
#ifndef SETHOSTNAMEJOB_CPP_H
|
||||
#define SETHOSTNAMEJOB_CPP_H
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include "Job.h"
|
||||
|
||||
class SetHostNameJob : public Calamares::Job
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Action
|
||||
{
|
||||
None = 0x0,
|
||||
EtcHostname = 0x1, // Write to /etc/hostname directly
|
||||
SystemdHostname = 0x2, // Set via hostnamed(1)
|
||||
WriteEtcHosts = 0x4 // Write /etc/hosts (127.0.1.1 is this host)
|
||||
};
|
||||
Q_DECLARE_FLAGS( Actions, Action )
|
||||
|
||||
|
||||
SetHostNameJob( const QString& hostname, Actions a );
|
||||
SetHostNameJob( const QString& hostname, HostNameActions a );
|
||||
QString prettyName() const override;
|
||||
QString prettyDescription() const override;
|
||||
QString prettyStatusMessage() const override;
|
||||
@ -45,9 +37,7 @@ public:
|
||||
|
||||
private:
|
||||
const QString m_hostname;
|
||||
const Actions m_actions;
|
||||
const HostNameActions m_actions;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS( SetHostNameJob::Actions )
|
||||
|
||||
#endif // SETHOSTNAMEJOB_CPP_H
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
// Implementation details
|
||||
extern void setConfigurationDefaultGroups( const QVariantMap& map, QStringList& defaultGroups );
|
||||
extern HostNameActions getHostNameActions( const QVariantMap& configurationMap );
|
||||
extern bool addPasswordCheck( const QString& key, const QVariant& value, PasswordCheckList& passwordChecks );
|
||||
|
||||
/** @brief Test Config object methods and internals
|
||||
*
|
||||
@ -40,6 +42,9 @@ private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void testDefaultGroups();
|
||||
void testHostActions_data();
|
||||
void testHostActions();
|
||||
void testPasswordChecks();
|
||||
};
|
||||
|
||||
UserTests::UserTests() {}
|
||||
@ -105,6 +110,52 @@ UserTests::testDefaultGroups()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
UserTests::testHostActions_data()
|
||||
{
|
||||
QTest::addColumn< bool >( "set" );
|
||||
QTest::addColumn< QString >( "string" );
|
||||
QTest::addColumn< int >( "result" );
|
||||
|
||||
QTest::newRow( "unset " ) << false << QString() << int( HostNameAction::EtcHostname );
|
||||
QTest::newRow( "empty " ) << true << QString() << int( HostNameAction::EtcHostname );
|
||||
QTest::newRow( "bad " ) << true << QString( "derp" ) << int( HostNameAction::EtcHostname );
|
||||
QTest::newRow( "none " ) << true << QString( "none" ) << int( HostNameAction::None );
|
||||
QTest::newRow( "systemd" ) << true << QString( "Hostnamed" ) << int( HostNameAction::SystemdHostname );
|
||||
}
|
||||
|
||||
void
|
||||
UserTests::testHostActions()
|
||||
{
|
||||
QFETCH( bool, set );
|
||||
QFETCH( QString, string );
|
||||
QFETCH( int, result );
|
||||
|
||||
QVariantMap m;
|
||||
if ( set )
|
||||
{
|
||||
m.insert( "setHostname", string );
|
||||
}
|
||||
QCOMPARE( getHostNameActions( m ), HostNameActions( result ) | HostNameAction::WriteEtcHosts ); // write bits default to true
|
||||
m.insert( "writeHostsFile", false );
|
||||
QCOMPARE( getHostNameActions( m ), HostNameActions( result ) );
|
||||
m.insert( "writeHostsFile", true );
|
||||
QCOMPARE( getHostNameActions( m ), HostNameActions( result ) | HostNameAction::WriteEtcHosts );
|
||||
}
|
||||
|
||||
void
|
||||
UserTests::testPasswordChecks()
|
||||
{
|
||||
{
|
||||
PasswordCheckList l;
|
||||
QCOMPARE( l.length(), 0 );
|
||||
QVERIFY( !addPasswordCheck( "nonempty", QVariant(false), l ) ); // a silly setting
|
||||
QCOMPARE( l.length(), 0 );
|
||||
QVERIFY( addPasswordCheck( "nonempty", QVariant(true), l ) );
|
||||
QCOMPARE( l.length(), 1 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QTEST_GUILESS_MAIN( UserTests )
|
||||
|
||||
|
@ -50,7 +50,7 @@ enum class Badness
|
||||
|
||||
/** Add an error message and pixmap to a label. */
|
||||
static inline void
|
||||
labelError( QLabel* pix, QLabel* label, const QString& message, Badness bad = Badness::Fatal )
|
||||
labelError( QLabel* pix, QLabel* label, const QString& message, Badness bad )
|
||||
{
|
||||
label->setText( message );
|
||||
pix->setPixmap( CalamaresUtils::defaultPixmap( ( bad == Badness::Fatal ) ? CalamaresUtils::StatusError
|
||||
@ -88,7 +88,7 @@ labelStatus( QLabel* pix, QLabel* label, const QString& value, const QString& st
|
||||
}
|
||||
else
|
||||
{
|
||||
labelError( pix, label, status );
|
||||
labelError( pix, label, status, Badness::Fatal );
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
@ -105,6 +105,12 @@ UsersPage::UsersPage( Config* config, QWidget* parent )
|
||||
{
|
||||
ui->setupUi( this );
|
||||
|
||||
ui->checkBoxReusePassword->setVisible( m_config->writeRootPassword() );
|
||||
ui->checkBoxReusePassword->setChecked( m_config->reuseUserPasswordForRoot() );
|
||||
|
||||
ui->checkBoxValidatePassword->setVisible( m_config->permitWeakPasswords() );
|
||||
ui->checkBoxValidatePassword->setChecked( m_config->requireStrongPasswords() );
|
||||
|
||||
// Connect signals and slots
|
||||
connect( ui->textBoxUserPassword, &QLineEdit::textChanged, this, &UsersPage::onPasswordTextChanged );
|
||||
connect( ui->textBoxUserVerifiedPassword, &QLineEdit::textChanged, this, &UsersPage::onPasswordTextChanged );
|
||||
@ -115,21 +121,7 @@ UsersPage::UsersPage( Config* config, QWidget* parent )
|
||||
onRootPasswordTextChanged( ui->textBoxRootPassword->text() );
|
||||
checkReady( isReady() );
|
||||
} );
|
||||
connect( ui->checkBoxReusePassword, &QCheckBox::stateChanged, this, [this]( const int checked ) {
|
||||
/* When "reuse" is checked, hide the fields for explicitly
|
||||
* entering the root password. However, if we're going to
|
||||
* disable the root password anyway, hide them all regardless of
|
||||
* the checkbox -- so when writeRoot is false, checked needs
|
||||
* to be true, to hide them all.
|
||||
*/
|
||||
const bool visible = m_config->writeRootPassword() ? !checked : false;
|
||||
ui->labelChooseRootPassword->setVisible( visible );
|
||||
ui->labelRootPassword->setVisible( visible );
|
||||
ui->labelRootPasswordError->setVisible( visible );
|
||||
ui->textBoxRootPassword->setVisible( visible );
|
||||
ui->textBoxVerifiedRootPassword->setVisible( visible );
|
||||
checkReady( isReady() );
|
||||
} );
|
||||
connect( ui->checkBoxReusePassword, &QCheckBox::stateChanged, this, &UsersPage::onReuseUserPasswordChanged );
|
||||
|
||||
connect( ui->textBoxFullName, &QLineEdit::textEdited, config, &Config::setFullName );
|
||||
connect( config, &Config::fullNameChanged, this, &UsersPage::onFullNameTextEdited );
|
||||
@ -147,13 +139,25 @@ UsersPage::UsersPage( Config* config, QWidget* parent )
|
||||
} );
|
||||
connect( config, &Config::autoLoginChanged, ui->checkBoxDoAutoLogin, &QCheckBox::setChecked );
|
||||
|
||||
ui->checkBoxReusePassword->setVisible( m_config->writeRootPassword() );
|
||||
ui->checkBoxReusePassword->setChecked( true );
|
||||
ui->checkBoxValidatePassword->setChecked( true );
|
||||
if ( m_config->writeRootPassword() )
|
||||
{
|
||||
connect( ui->checkBoxReusePassword, &QCheckBox::stateChanged, this, [this]( int checked ) {
|
||||
m_config->setReuseUserPasswordForRoot( checked != Qt::Unchecked );
|
||||
} );
|
||||
connect( config, &Config::reuseUserPasswordForRootChanged, ui->checkBoxReusePassword, &QCheckBox::setChecked );
|
||||
}
|
||||
|
||||
setPasswordCheckboxVisible( false );
|
||||
if ( m_config->permitWeakPasswords() )
|
||||
{
|
||||
connect( ui->checkBoxValidatePassword, &QCheckBox::stateChanged, this, [this]( int checked ) {
|
||||
m_config->setRequireStrongPasswords( checked != Qt::Unchecked );
|
||||
} );
|
||||
connect( config, &Config::requireStrongPasswordsChanged, ui->checkBoxValidatePassword, &QCheckBox::setChecked );
|
||||
}
|
||||
|
||||
CALAMARES_RETRANSLATE_SLOT( &UsersPage::retranslate );
|
||||
|
||||
onReuseUserPasswordChanged( m_config->reuseUserPasswordForRoot() );
|
||||
}
|
||||
|
||||
UsersPage::~UsersPage()
|
||||
@ -195,32 +199,6 @@ UsersPage::isReady() const
|
||||
return readyFields;
|
||||
}
|
||||
|
||||
QString
|
||||
UsersPage::getRootPassword() const
|
||||
{
|
||||
if ( m_config->writeRootPassword() )
|
||||
{
|
||||
if ( ui->checkBoxReusePassword->isChecked() )
|
||||
{
|
||||
return ui->textBoxUserPassword->text();
|
||||
}
|
||||
else
|
||||
{
|
||||
return ui->textBoxRootPassword->text();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
QPair< QString, QString >
|
||||
UsersPage::getUserPassword() const
|
||||
{
|
||||
return QPair< QString, QString >( m_config->loginName(), ui->textBoxUserPassword->text() );
|
||||
}
|
||||
|
||||
void
|
||||
UsersPage::fillGlobalStorage() const
|
||||
{
|
||||
@ -274,43 +252,26 @@ UsersPage::checkPasswordAcceptance( const QString& pw1, const QString& pw2, QLab
|
||||
{
|
||||
if ( pw1 != pw2 )
|
||||
{
|
||||
labelError( badge, message, tr( "Your passwords do not match!" ) );
|
||||
labelError( badge, message, tr( "Your passwords do not match!" ), Badness::Fatal );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool failureIsFatal = ui->checkBoxValidatePassword->isChecked();
|
||||
bool failureFound = false;
|
||||
|
||||
if ( m_passwordChecksChanged )
|
||||
QString s;
|
||||
bool ok = m_config->isPasswordAcceptable( pw1, s );
|
||||
if ( !ok )
|
||||
{
|
||||
std::sort( m_passwordChecks.begin(), m_passwordChecks.end() );
|
||||
m_passwordChecksChanged = false;
|
||||
labelError( badge, message, s, Badness::Fatal );
|
||||
}
|
||||
|
||||
for ( auto pc : m_passwordChecks )
|
||||
else if ( !s.isEmpty() )
|
||||
{
|
||||
QString s = pc.filter( pw1 );
|
||||
|
||||
if ( !s.isEmpty() )
|
||||
{
|
||||
labelError( badge, message, s, failureIsFatal ? Badness::Fatal : Badness::Warning );
|
||||
failureFound = true;
|
||||
if ( failureIsFatal )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
labelError( badge, message, s, Badness::Warning );
|
||||
}
|
||||
|
||||
if ( !failureFound )
|
||||
else
|
||||
{
|
||||
labelOk( badge, message );
|
||||
}
|
||||
|
||||
// Here, if failureFound is true then we've found **warnings**,
|
||||
// which is ok to continue but the user should know.
|
||||
return true;
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
|
||||
@ -335,58 +296,24 @@ UsersPage::onRootPasswordTextChanged( const QString& )
|
||||
emit checkReady( isReady() );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UsersPage::setPasswordCheckboxVisible( bool visible )
|
||||
UsersPage::onReuseUserPasswordChanged( const int checked )
|
||||
{
|
||||
ui->checkBoxValidatePassword->setVisible( visible );
|
||||
}
|
||||
|
||||
void
|
||||
UsersPage::setValidatePasswordDefault( bool checked )
|
||||
{
|
||||
ui->checkBoxValidatePassword->setChecked( checked );
|
||||
emit checkReady( isReady() );
|
||||
}
|
||||
|
||||
void
|
||||
UsersPage::setReusePasswordDefault( bool checked )
|
||||
{
|
||||
ui->checkBoxReusePassword->setChecked( checked );
|
||||
emit checkReady( isReady() );
|
||||
}
|
||||
|
||||
void
|
||||
UsersPage::addPasswordCheck( const QString& key, const QVariant& value )
|
||||
{
|
||||
m_passwordChecksChanged = true;
|
||||
|
||||
if ( key == "minLength" )
|
||||
{
|
||||
add_check_minLength( m_passwordChecks, value );
|
||||
}
|
||||
else if ( key == "maxLength" )
|
||||
{
|
||||
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
|
||||
else if ( key == "libpwquality" )
|
||||
{
|
||||
add_check_libpwquality( m_passwordChecks, value );
|
||||
}
|
||||
#endif // CHECK_PWQUALITY
|
||||
else
|
||||
{
|
||||
cWarning() << "Unknown password-check key" << key;
|
||||
}
|
||||
/* When "reuse" is checked, hide the fields for explicitly
|
||||
* entering the root password. However, if we're going to
|
||||
* disable the root password anyway, hide them all regardless of
|
||||
* the checkbox -- so when writeRoot is false, visible needs
|
||||
* to be false, to hide them all.
|
||||
*
|
||||
* In principle this is only connected when writeRootPassword is @c true,
|
||||
* but it is **always** called at least once in the constructor
|
||||
* to set up initial visibility.
|
||||
*/
|
||||
const bool visible = m_config->writeRootPassword() ? !checked : false;
|
||||
ui->labelChooseRootPassword->setVisible( visible );
|
||||
ui->labelRootPassword->setVisible( visible );
|
||||
ui->labelRootPasswordError->setVisible( visible );
|
||||
ui->textBoxRootPassword->setVisible( visible );
|
||||
ui->textBoxVerifiedRootPassword->setVisible( visible );
|
||||
checkReady( isReady() );
|
||||
}
|
||||
|
@ -24,8 +24,6 @@
|
||||
#ifndef USERSPAGE_H
|
||||
#define USERSPAGE_H
|
||||
|
||||
#include "CheckPWQuality.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class Config;
|
||||
@ -50,29 +48,13 @@ public:
|
||||
|
||||
void onActivate();
|
||||
|
||||
void setPasswordCheckboxVisible( bool visible );
|
||||
void setValidatePasswordDefault( bool checked );
|
||||
void setReusePasswordDefault( bool checked );
|
||||
|
||||
/** @brief Process entries in the passwordRequirements config entry
|
||||
*
|
||||
* Called once for each item in the config entry, which should
|
||||
* be a key-value pair. What makes sense as a value depends on
|
||||
* the key. Supported keys are documented in users.conf.
|
||||
*/
|
||||
void addPasswordCheck( const QString& key, const QVariant& value );
|
||||
|
||||
///@brief Root password, depends on settings, may be empty
|
||||
QString getRootPassword() const;
|
||||
///@brief User name and password
|
||||
QPair< QString, QString > getUserPassword() const;
|
||||
|
||||
protected slots:
|
||||
void onFullNameTextEdited( const QString& );
|
||||
void reportLoginNameStatus( const QString& );
|
||||
void reportHostNameStatus( const QString& );
|
||||
void onPasswordTextChanged( const QString& );
|
||||
void onRootPasswordTextChanged( const QString& );
|
||||
void onReuseUserPasswordChanged( const int );
|
||||
|
||||
signals:
|
||||
void checkReady( bool );
|
||||
@ -91,9 +73,6 @@ private:
|
||||
Ui::Page_UserSetup* ui;
|
||||
Config* m_config;
|
||||
|
||||
PasswordCheckList m_passwordChecks;
|
||||
bool m_passwordChecksChanged = false;
|
||||
|
||||
bool m_readyFullName;
|
||||
bool m_readyUsername;
|
||||
bool m_readyHostname;
|
||||
|
@ -34,28 +34,9 @@
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DEFINITION( UsersViewStepFactory, registerPlugin< UsersViewStep >(); )
|
||||
|
||||
static const NamedEnumTable< SetHostNameJob::Action >&
|
||||
hostnameActions()
|
||||
{
|
||||
using Action = SetHostNameJob::Action;
|
||||
|
||||
// *INDENT-OFF*
|
||||
// clang-format off
|
||||
static const NamedEnumTable< Action > names {
|
||||
{ QStringLiteral( "none" ), Action::None },
|
||||
{ QStringLiteral( "etcfile" ), Action::EtcHostname },
|
||||
{ QStringLiteral( "hostnamed" ), Action::SystemdHostname }
|
||||
};
|
||||
// clang-format on
|
||||
// *INDENT-ON*
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
UsersViewStep::UsersViewStep( QObject* parent )
|
||||
: Calamares::ViewStep( parent )
|
||||
, m_widget( nullptr )
|
||||
, m_actions( SetHostNameJob::Action::None )
|
||||
, m_config( new Config( this ) )
|
||||
{
|
||||
emit nextStatusChanged( true );
|
||||
@ -151,14 +132,14 @@ UsersViewStep::onLeave()
|
||||
m_config->doAutoLogin(),
|
||||
m_config->defaultGroups() );
|
||||
|
||||
auto userPW = m_widget->getUserPassword();
|
||||
j = new SetPasswordJob( userPW.first, userPW.second );
|
||||
j = new SetPasswordJob( m_config->loginName(), m_config->userPassword() );
|
||||
m_jobs.append( Calamares::job_ptr( j ) );
|
||||
|
||||
j = new SetPasswordJob( "root", m_widget->getRootPassword() );
|
||||
j = new SetPasswordJob( "root", m_config->rootPassword() );
|
||||
m_jobs.append( Calamares::job_ptr( j ) );
|
||||
|
||||
j = new SetHostNameJob( m_config->hostName(), m_actions );
|
||||
// TODO: Config object should create jobs
|
||||
j = new SetHostNameJob( m_config->hostName(), m_config->hostNameActions() );
|
||||
m_jobs.append( Calamares::job_ptr( j ) );
|
||||
|
||||
m_widget->fillGlobalStorage();
|
||||
@ -168,42 +149,5 @@ UsersViewStep::onLeave()
|
||||
void
|
||||
UsersViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
// Create the widget, after all .. as long as writing configuration to the UI is needed
|
||||
(void)this->widget();
|
||||
using CalamaresUtils::getBool;
|
||||
|
||||
m_widget->setReusePasswordDefault( getBool( configurationMap, "doReusePassword", false ) );
|
||||
|
||||
if ( configurationMap.contains( "passwordRequirements" )
|
||||
&& configurationMap.value( "passwordRequirements" ).type() == QVariant::Map )
|
||||
{
|
||||
auto pr_checks( configurationMap.value( "passwordRequirements" ).toMap() );
|
||||
|
||||
for ( decltype( pr_checks )::const_iterator i = pr_checks.constBegin(); i != pr_checks.constEnd(); ++i )
|
||||
{
|
||||
m_widget->addPasswordCheck( i.key(), i.value() );
|
||||
}
|
||||
}
|
||||
|
||||
m_widget->setPasswordCheckboxVisible( getBool( configurationMap, "allowWeakPasswords", false ) );
|
||||
m_widget->setValidatePasswordDefault( !getBool( configurationMap, "allowWeakPasswordsDefault", false ) );
|
||||
|
||||
using Action = SetHostNameJob::Action;
|
||||
|
||||
QString hostnameActionString = CalamaresUtils::getString( configurationMap, "setHostname" );
|
||||
if ( hostnameActionString.isEmpty() )
|
||||
{
|
||||
hostnameActionString = QStringLiteral( "EtcFile" );
|
||||
}
|
||||
bool ok = false;
|
||||
auto hostnameAction = hostnameActions().find( hostnameActionString, ok );
|
||||
if ( !ok )
|
||||
{
|
||||
hostnameAction = Action::EtcHostname;
|
||||
}
|
||||
|
||||
Action hostsfileAction = getBool( configurationMap, "writeHostsFile", true ) ? Action::WriteEtcHosts : Action::None;
|
||||
m_actions = hostsfileAction | hostnameAction;
|
||||
|
||||
m_config->setConfigurationMap( configurationMap );
|
||||
}
|
||||
|
@ -20,8 +20,6 @@
|
||||
#ifndef USERSPAGEPLUGIN_H
|
||||
#define USERSPAGEPLUGIN_H
|
||||
|
||||
#include "SetHostNameJob.h"
|
||||
|
||||
#include "DllMacro.h"
|
||||
#include "utils/PluginFactory.h"
|
||||
#include "viewpages/ViewStep.h"
|
||||
@ -61,8 +59,6 @@ private:
|
||||
UsersPage* m_widget;
|
||||
QList< Calamares::job_ptr > m_jobs;
|
||||
|
||||
SetHostNameJob::Actions m_actions;
|
||||
|
||||
Config* m_config;
|
||||
};
|
||||
|
||||
|
@ -138,4 +138,5 @@ setHostname: EtcFile
|
||||
|
||||
# Should /etc/hosts be written with a hostname for this machine
|
||||
# (also adds localhost and some ipv6 standard entries).
|
||||
# Defaults to *true*.
|
||||
writeHostsFile: true
|
||||
|
Loading…
Reference in New Issue
Block a user