From 3eb87a14e133b266ab8d8c634dda3af312474f1f Mon Sep 17 00:00:00 2001 From: Nippy Date: Sat, 19 Sep 2026 18:04:27 +0200 Subject: new user settings --- .../raveusers/RaveUsersJobs.cpp | 294 +++++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100644 raveos-calamares-module/raveusers/RaveUsersJobs.cpp (limited to 'raveos-calamares-module/raveusers/RaveUsersJobs.cpp') diff --git a/raveos-calamares-module/raveusers/RaveUsersJobs.cpp b/raveos-calamares-module/raveusers/RaveUsersJobs.cpp new file mode 100644 index 0000000..1923c38 --- /dev/null +++ b/raveos-calamares-module/raveusers/RaveUsersJobs.cpp @@ -0,0 +1,294 @@ +#include "RaveUsersJobs.h" + +#include "RaveUsersBackend.h" + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "utils/Logger.h" +#include "utils/Permissions.h" +#include "utils/System.h" + +#include +#include +#include +#include + +#include + +using WriteMode = Calamares::System::WriteMode; + +static QDir +targetRootDir() +{ + Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); + return QDir( gs->value( "rootMountPoint" ).toString() ); +} + +RaveUsersCreateUserJob::RaveUsersCreateUserJob( const RaveUsersBackend* backend ) + : m_backend( backend ) +{ +} + +QString +RaveUsersCreateUserJob::prettyName() const +{ + return tr( "Create user %1" ).arg( m_backend->username() ); +} + +QString +RaveUsersCreateUserJob::prettyStatusMessage() const +{ + return tr( "Creating user %1…", "@status" ).arg( m_backend->username() ); +} + +Calamares::JobResult +RaveUsersCreateUserJob::exec() +{ + const QString login = m_backend->username(); + + QStringList useraddCommand { "useradd", "-m", "-U" }; + const QString shell = m_backend->userShell(); + if ( !shell.isEmpty() ) + { + useraddCommand << "-s" << shell; + } + + useraddCommand << "-c" << login; + if ( m_backend->homeUMask() >= 0 ) + { + useraddCommand << "-K" << QStringLiteral( "UMASK=%1" ).arg( m_backend->homeUMask(), 3, 8, QChar( '0' ) ); + } + useraddCommand << login; + + auto result = Calamares::System::instance()->targetEnvCommand( useraddCommand ); + if ( result.getExitCode() ) + { + cError() << "useradd failed" << result.getExitCode(); + return result.explainProcess( useraddCommand, std::chrono::seconds( 10 ) ); + } + + const QStringList groups = m_backend->groupsForThisUser(); + if ( !groups.isEmpty() ) + { + QStringList usermodCommand { "usermod", "-aG", groups.join( ',' ), login }; + result = Calamares::System::instance()->targetEnvCommand( usermodCommand ); + if ( result.getExitCode() ) + { + cError() << "usermod failed" << result.getExitCode(); + return result.explainProcess( usermodCommand, std::chrono::seconds( 10 ) ); + } + } + + const QString userGroup = QStringLiteral( "%1:%2" ).arg( login, login ); + const QString homeDir = QStringLiteral( "/home/%1" ).arg( login ); + result = Calamares::System::instance()->targetEnvCommand( { "chown", "-R", userGroup, homeDir } ); + if ( result.getExitCode() ) + { + cError() << "chown failed" << result.getExitCode(); + return result.explainProcess( "chown", std::chrono::seconds( 10 ) ); + } + + return Calamares::JobResult::ok(); +} + +RaveUsersSetPasswordJob::RaveUsersSetPasswordJob( const QString& userName, const QString& password ) + : m_userName( userName ) + , m_password( password ) +{ +} + +QString +RaveUsersSetPasswordJob::prettyName() const +{ + return tr( "Set password for user %1" ).arg( m_userName ); +} + +QString +RaveUsersSetPasswordJob::prettyStatusMessage() const +{ + return tr( "Setting password for user %1…", "@status" ).arg( m_userName ); +} + +static QString +makeSha512Salt( int length = 16 ) +{ + static const QString chars = QStringLiteral( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./" ); + QString salt; + salt.reserve( length ); + for ( int i = 0; i < length; ++i ) + { + salt += chars.at( QRandomGenerator::global()->bounded( chars.size() ) ); + } + return QStringLiteral( "$6$" ) + salt + QLatin1Char( '$' ); +} + +Calamares::JobResult +RaveUsersSetPasswordJob::exec() +{ + QDir destDir = targetRootDir(); + if ( !destDir.exists() ) + { + return Calamares::JobResult::error( tr( "Bad destination system path." ), + tr( "rootMountPoint is %1" ).arg( destDir.absolutePath() ) ); + } + + if ( m_userName == QLatin1String( "root" ) && m_password.isEmpty() ) + { + int ec = Calamares::System::instance()->targetEnvCall( { "usermod", "-p", "!", m_userName } ); + if ( ec ) + { + return Calamares::JobResult::error( tr( "Cannot disable root account." ), + tr( "usermod terminated with error code %1." ).arg( ec ) ); + } + return Calamares::JobResult::ok(); + } + + const QString salt = makeSha512Salt(); + const QByteArray hash = crypt( m_password.toUtf8().constData(), salt.toUtf8().constData() ); + if ( hash.isEmpty() ) + { + return Calamares::JobResult::error( tr( "Cannot hash password." ) ); + } + + int ec = Calamares::System::instance()->targetEnvCall( + { "usermod", "-p", QString::fromLatin1( hash ), m_userName } ); + if ( ec ) + { + return Calamares::JobResult::error( tr( "Cannot set password for user %1." ).arg( m_userName ), + tr( "usermod terminated with error code %1." ).arg( ec ) ); + } + + return Calamares::JobResult::ok(); +} + +RaveUsersSetHostnameJob::RaveUsersSetHostnameJob( const QString& hostname ) + : m_hostname( hostname ) +{ +} + +QString +RaveUsersSetHostnameJob::prettyName() const +{ + return tr( "Set hostname %1" ).arg( m_hostname ); +} + +Calamares::JobResult +RaveUsersSetHostnameJob::exec() +{ + if ( !Calamares::System::instance()->createTargetFile( QStringLiteral( "/etc/hostname" ), + ( m_hostname + '\n' ).toUtf8(), + WriteMode::Overwrite ) ) + { + return Calamares::JobResult::error( tr( "Cannot write hostname to target system" ) ); + } + + const QString hosts = QStringLiteral( R"(# Standard host addresses +127.0.0.1 localhost +::1 localhost ip6-localhost ip6-loopback +ff02::1 ip6-allnodes +ff02::2 ip6-allrouters +# This host address +127.0.1.1 %1 +)" ) + .arg( m_hostname ); + + if ( !Calamares::System::instance()->createTargetFile( + QStringLiteral( "/etc/hosts" ), hosts.toUtf8(), WriteMode::Overwrite ) ) + { + return Calamares::JobResult::error( tr( "Cannot write hosts file to target system" ) ); + } + + return Calamares::JobResult::ok(); +} + +RaveUsersSetupSudoJob::RaveUsersSetupSudoJob( const QString& group, bool configureWithGroup ) + : m_group( group ) + , m_configureWithGroup( configureWithGroup ) +{ +} + +QString +RaveUsersSetupSudoJob::prettyName() const +{ + return tr( "Configuring sudo users…", "@status" ); +} + +Calamares::JobResult +RaveUsersSetupSudoJob::exec() +{ + if ( m_group.isEmpty() ) + { + return Calamares::JobResult::ok(); + } + + const QString designator = m_configureWithGroup ? QStringLiteral( "(ALL:ALL)" ) : QStringLiteral( "(ALL)" ); + const QString line = QChar( '%' ) + QStringLiteral( "%1 ALL=%2 ALL\n" ).arg( m_group, designator ); + + auto fileResult = Calamares::System::instance()->createTargetFile( + QStringLiteral( "/etc/sudoers.d/10-installer" ), line.toUtf8().constData(), WriteMode::Overwrite ); + + if ( fileResult ) + { + if ( !Calamares::Permissions::apply( fileResult.path(), 0440 ) ) + { + return Calamares::JobResult::error( tr( "Cannot chmod sudoers file." ) ); + } + } + else + { + return Calamares::JobResult::error( tr( "Cannot create sudoers file for writing." ) ); + } + + return Calamares::JobResult::ok(); +} + +RaveUsersSetupGroupsJob::RaveUsersSetupGroupsJob( const QStringList& groups ) + : m_groups( groups ) +{ +} + +QString +RaveUsersSetupGroupsJob::prettyName() const +{ + return tr( "Preparing groups…", "@status" ); +} + +Calamares::JobResult +RaveUsersSetupGroupsJob::exec() +{ + QDir targetRoot = targetRootDir(); + + QSet< QString > existing; + QFile groupFile( targetRoot.absoluteFilePath( "etc/group" ) ); + if ( groupFile.open( QIODevice::ReadOnly | QIODevice::Text ) ) + { + while ( !groupFile.atEnd() ) + { + const QString line = QString::fromLocal8Bit( groupFile.readLine() ); + if ( line.startsWith( '#' ) ) + { + continue; + } + const int idx = line.indexOf( ':' ); + if ( idx > 0 ) + { + existing.insert( line.left( idx ) ); + } + } + } + + for ( const QString& group : m_groups ) + { + if ( group.isEmpty() || existing.contains( group ) ) + { + continue; + } + QStringList cmd { "groupadd", "--system", group }; + if ( Calamares::System::instance()->targetEnvCall( cmd ) ) + { + cWarning() << "groupadd failed for" << group; + } + } + + return Calamares::JobResult::ok(); +} -- cgit v1.3