Merge branch 'calamares' into work-3.3

This commit is contained in:
Adriaan de Groot 2022-05-09 15:03:41 +02:00
commit bbeba5c9ab
20 changed files with 273 additions and 35 deletions

View File

@ -8,6 +8,25 @@ contributors are listed. Note that Calamares does not have a historical
changelog -- this log starts with version 3.2.0. The release notes on the
website will have to do for older versions.
# 3.2.58 (unreleased) #
This release contains contributions from (alphabetically by first name):
- Enrique Medina Gremaldos
## Core ##
- Internal improvements to translations-setup means that Catalan (in the
Valencian dialect), Occitan (Lenga d'Oc) and Serbian (in Latin script)
are all better supported. Thanks Enrique.
## Modules ##
- *users* module now has a structured *user* key with settings specific
to the user (shell, in particular). This maintains backwards compatibility
with the *userShell* key.
- *users* module now has lists of forbidden login- and host-names, to
avoid settings that will mess up the install (e.g. using a login-name
that is one of the system's reserved names). #1944
# 3.2.57 (2022-05-04) #
This release contains contributions from (alphabetically by first name):

View File

@ -818,6 +818,7 @@ class DMgreetd(DisplayManager):
def desktop_environment_setup(self, default_desktop_environment):
with open(self.environments_path(), 'w') as envs_file:
envs_file.write(default_desktop_environment.desktop_file)
envs_file.write("\n")
def greeter_setup(self):
pass

View File

@ -201,10 +201,9 @@ Config::setLoginName( const QString& login )
}
const QStringList&
Config::forbiddenLoginNames()
Config::forbiddenLoginNames() const
{
static QStringList forbidden { "root" };
return forbidden;
return m_forbiddenLoginNames;
}
QString
@ -220,13 +219,6 @@ Config::loginNameStatus() const
{
return tr( "Your username is too long." );
}
for ( const QString& badName : forbiddenLoginNames() )
{
if ( 0 == QString::compare( badName, m_loginName, Qt::CaseSensitive ) )
{
return tr( "'%1' is not allowed as username." ).arg( badName );
}
}
QRegExp validateFirstLetter( "^[a-z_]" );
if ( validateFirstLetter.indexIn( m_loginName ) != 0 )
@ -238,6 +230,12 @@ Config::loginNameStatus() const
return tr( "Only lowercase letters, numbers, underscore and hyphen are allowed." );
}
// Although we've made the list lower-case, and the RE above forces lower-case, still pass the flag
if ( forbiddenLoginNames().contains( m_loginName, Qt::CaseInsensitive ) )
{
return tr( "'%1' is not allowed as username." ).arg( m_loginName );
}
return QString();
}
@ -268,10 +266,9 @@ Config::setHostName( const QString& host )
}
const QStringList&
Config::forbiddenHostNames()
Config::forbiddenHostNames() const
{
static QStringList forbidden { "localhost" };
return forbidden;
return m_forbiddenHostNames;
}
QString
@ -291,12 +288,11 @@ Config::hostnameStatus() const
{
return tr( "Your hostname is too long." );
}
for ( const QString& badName : forbiddenHostNames() )
// "LocalHost" is just as forbidden as "localhost"
if ( forbiddenHostNames().contains( m_hostname, Qt::CaseInsensitive ) )
{
if ( 0 == QString::compare( badName, m_hostname, Qt::CaseSensitive ) )
{
return tr( "'%1' is not allowed as hostname." ).arg( badName );
}
return tr( "'%1' is not allowed as hostname." ).arg( m_hostname );
}
if ( !HOSTNAME_RX.exactMatch( m_hostname ) )
@ -881,16 +877,41 @@ copyLegacy( const QVariantMap& source, const QString& sourceKey, QVariantMap& ta
}
}
/** @brief Tidy up a list of names
*
* Remove duplicates, apply lowercase, sort.
*/
static void
tidy( QStringList& l )
{
std::for_each( l.begin(), l.end(), []( QString& s ) { s = s.toLower(); } );
l.sort();
l.removeDuplicates();
}
void
Config::setConfigurationMap( const QVariantMap& configurationMap )
{
QString shell( QLatin1String( "/bin/bash" ) ); // as if it's not set at all
if ( configurationMap.contains( "userShell" ) )
// Handle *user* key and subkeys and legacy settings
{
shell = CalamaresUtils::getString( configurationMap, "userShell" );
bool ok = false; // Ignored
QVariantMap userSettings = CalamaresUtils::getSubMap( configurationMap, "user", ok );
// TODO:3.3: Remove calls to copyLegacy
copyLegacy( configurationMap, "userShell", userSettings, "shell" );
QString shell( QLatin1String( "/bin/bash" ) ); // as if it's not set at all
if ( userSettings.contains( "shell" ) )
{
shell = CalamaresUtils::getString( userSettings, "shell" );
}
// Now it might be explicitly set to empty, which is ok
setUserShell( shell );
m_forbiddenLoginNames = CalamaresUtils::getStringList( userSettings, "forbidden_names" );
m_forbiddenLoginNames << QStringLiteral( "root" ) << QStringLiteral( "nobody" );
tidy( m_forbiddenLoginNames );
}
// Now it might be explicitly set to empty, which is ok
setUserShell( shell );
setAutoLoginGroup( either< QString, const QString& >(
CalamaresUtils::getString, configurationMap, "autologinGroup", "autoLoginGroup", QString() ) );
@ -911,6 +932,10 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
m_writeEtcHosts = CalamaresUtils::getBool( hostnameSettings, "writeHostsFile", true );
m_hostnameTemplate
= CalamaresUtils::getString( hostnameSettings, "template", QStringLiteral( "${first}-${product}" ) );
m_forbiddenHostNames = CalamaresUtils::getStringList( hostnameSettings, "forbidden_names" );
m_forbiddenHostNames << QStringLiteral( "localhost" );
tidy( m_forbiddenHostNames );
}
setConfigurationDefaultGroups( configurationMap, m_defaultGroups );

View File

@ -252,8 +252,8 @@ public:
bool isReady() const;
static const QStringList& forbiddenLoginNames();
static const QStringList& forbiddenHostNames();
const QStringList& forbiddenLoginNames() const;
const QStringList& forbiddenHostNames() const;
public Q_SLOTS:
/** @brief Sets the user's shell if possible
@ -347,6 +347,9 @@ private:
bool m_writeEtcHosts = false;
QString m_hostnameTemplate;
QStringList m_forbiddenHostNames;
QStringList m_forbiddenLoginNames;
PasswordCheckList m_passwordChecks;
};

View File

@ -53,6 +53,9 @@ private Q_SLOTS:
void testAutoLogin_data();
void testAutoLogin();
void testUserYAML_data();
void testUserYAML();
};
UserTests::UserTests() {}
@ -298,7 +301,8 @@ UserTests::testHostSuggestions_data()
QTest::newRow( "full " ) << QStringLiteral( "${name}" ) << QStringLiteral( "chuckyeager" );
QTest::newRow( "login+ " ) << QStringLiteral( "${login}-${first}" ) << QStringLiteral( "bill-chuck" );
// This is a bit dodgy: assumes CPU architecture of the testing host
QTest::newRow( " cpu " ) << QStringLiteral( "${cpu}X" ) << QStringLiteral( "x8664X" ); // Assume we don't test on non-amd64
QTest::newRow( " cpu " ) << QStringLiteral( "${cpu}X" )
<< QStringLiteral( "x8664X" ); // Assume we don't test on non-amd64
// These have X X in the template to indicate that they are bogus. Mostly we want
// to see what the template engine does for these.
QTest::newRow( "@prod " ) << QStringLiteral( "X${product}X" ) << QString();
@ -315,10 +319,11 @@ UserTests::testHostSuggestions()
QFETCH( QString, templateString );
QFETCH( QString, result );
if ( templateString.startsWith('X') && templateString.endsWith('X'))
if ( templateString.startsWith( 'X' ) && templateString.endsWith( 'X' ) )
{
QEXPECT_FAIL( "", "Test is too host-specific", Continue );
cWarning() << Logger::SubEntry << "Next test" << templateString << "->" << makeHostnameSuggestion( templateString, fullName, login );
cWarning() << Logger::SubEntry << "Next test" << templateString << "->"
<< makeHostnameSuggestion( templateString, fullName, login );
}
QCOMPARE( makeHostnameSuggestion( templateString, fullName, login ), result );
}
@ -453,6 +458,58 @@ UserTests::testAutoLogin()
QCOMPARE( c.autoLoginGroup(), autoLoginGroupName );
}
void
UserTests::testUserYAML_data()
{
QTest::addColumn< QString >( "filename" );
QTest::addColumn< QString >( "shell" );
QTest::newRow( "old, unset " ) << "tests/7ao-shell.conf"
<< "/bin/bash";
QTest::newRow( "old, empty " ) << "tests/7bo-shell.conf"
<< "";
QTest::newRow( "old, relative" ) << "tests/7co-shell.conf"
<< "/bin/ls"; // Setting is ignored
QTest::newRow( "old, invalid " ) << "tests/7do-shell.conf"
<< "";
QTest::newRow( "old, absolute" ) << "tests/7eo-shell.conf"
<< "/usr/bin/dash";
QTest::newRow( "new, unset " ) << "tests/7an-shell.conf"
<< "/bin/bash";
QTest::newRow( "new, empty " ) << "tests/7bn-shell.conf"
<< "";
QTest::newRow( "new, relative" ) << "tests/7cn-shell.conf"
<< "/bin/ls"; // Setting is ignored
QTest::newRow( "new, invalid " ) << "tests/7dn-shell.conf"
<< "";
QTest::newRow( "new, absolute" ) << "tests/7en-shell.conf"
<< "/usr/bin/dash";
}
void
UserTests::testUserYAML()
{
Config c;
c.setUserShell( QStringLiteral( "/bin/ls" ) );
QFETCH( QString, filename );
QFETCH( QString, shell );
// BUILD_AS_TEST is the source-directory path
QFile fi( QString( "%1/%2" ).arg( BUILD_AS_TEST, filename ) );
QVERIFY( fi.exists() );
bool ok = false;
const auto map = CalamaresUtils::loadYaml( fi, &ok );
QVERIFY( ok );
QVERIFY( map.count() > 0 );
QCOMPARE( c.userShell(), QStringLiteral( "/bin/ls" ) );
c.setConfigurationMap( map );
QCOMPARE( c.userShell(), shell );
}
QTEST_GUILESS_MAIN( UserTests )

View File

@ -0,0 +1,8 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
# Unset (bogus needed to keep it valid YAML)
user:
# shell: /usr/bin/dash
bogus: true

View File

@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
# Unset (bogus needed to keep it valid YAML)
# userShell: /usr/bin/dash
bogus: true

View File

@ -0,0 +1,8 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
# Explicitly empty
user:
shell: ""

View File

@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
# Explicitly empty
userShell: ""

View File

@ -0,0 +1,8 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
# Non-absolute path is ignored
user:
shell: dash

View File

@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
# Non-absolute path is ignored
userShell: dash

View File

@ -0,0 +1,8 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
# Invalid setting (should be string), won't pass validation
user:
shell: [1]

View File

@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
# Invalid setting (should be string), won't pass validation
userShell: [1]

View File

@ -0,0 +1,8 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
# Explicitly set with full path
user:
shell: /usr/bin/dash

View File

@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
# Explicitly set with full path
userShell: /usr/bin/dash

View File

@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
# Explicitly set with full path
user:
shell: /usr/bin/new
bogus: true
userShell: /usr/bin/old

View File

@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
# Explicitly set with full path
user:
shell: /usr/bin/new
bogus: true
# userShell: /usr/bin/old

View File

@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
# Explicitly set with full path
user:
# shell: /usr/bin/new
bogus: true
userShell: /usr/bin/old

View File

@ -140,13 +140,30 @@ allowWeakPasswords: false
# to be unchecked.
allowWeakPasswordsDefault: false
# Shell to be used for the regular user of the target system.
# There are three possible kinds of settings:
# - unset (i.e. commented out, the default), act as if set to /bin/bash
# - empty (explicit), don't pass shell information to useradd at all
# and rely on a correct configuration file in /etc/default/useradd
# - set, non-empty, use that path as shell. No validation is done
# that the shell actually exists or is executable.
# User settings
#
# The user can enter a username, but there are some other
# hidden settings for the user which are configurable in Calamares.
#
# Key *user* has the following sub-keys:
#
# - *shell* Shell to be used for the regular user of the target system.
# There are three possible kinds of settings:
# - unset (i.e. commented out, the default), act as if set to /bin/bash
# - empty (explicit), don't pass shell information to useradd at all
# and rely on a correct configuration file in /etc/default/useradd
# - set, non-empty, use that path as shell. No validation is done
# that the shell actually exists or is executable.
# - *forbidden_names* Login names that may not be used. This list always
# contains "root" and "nobody", but may be extended to list other special
# names for a given distro (eg. "video", or "mysql" might not be a valid
# end-user login name).
user:
shell: /bin/bash
forbidden_names: [ root ]
# TODO:3.3: Remove this setting
#
# This is the legacy setting for user.shell
userShell: /bin/bash
# Hostname settings
@ -186,10 +203,14 @@ userShell: /bin/bash
# `${key}` values to something that will fit in a hostname, but does not
# apply the same to literal text in the template. Do not use invalid
# characters in the literal text, or no suggeston will be done.
# - *forbidden_names* lists hostnames that may not be used. This list
# always contains "localhost", but may list others that are unsuitable
# or broken in special ways.
hostname:
location: EtcFile
writeHostsFile: true
template: "derp-${cpu}"
forbidden_names: [ localhost ]
# TODO:3.3: Remove this setting
#

View File

@ -8,6 +8,12 @@ type: object
properties:
# User shell, should be path to /bin/sh or so
userShell: { type: string }
user:
additionalProperties: false
type: object
properties:
shell: { type: string } # Overrides userShell
forbidden_names: { type: array, items: { type: string } }
# Group settings
defaultGroups:
type: array
@ -47,6 +53,7 @@ properties:
location: { type: string, enum: [ None, EtcFile, Hostnamed, Transient ] }
writeHostsFile: { type: boolean, default: true }
template: { type: string, default: "${first}-${product}" }
forbidden_names: { type: array, items: { type: string } }
# Legacy Hostname setting
setHostname: { type: string, enum: [ None, EtcFile, Hostnamed ] }
writeHostsFile: { type: boolean, default: true }