Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package yast2-users for openSUSE:Factory checked in at 2023-06-14 16:28:33 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/yast2-users (Old) and /work/SRC/openSUSE:Factory/.yast2-users.new.15902 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "yast2-users" Wed Jun 14 16:28:33 2023 rev:256 rq:1092599 version:4.6.2 Changes: -------- --- /work/SRC/openSUSE:Factory/yast2-users/yast2-users.changes 2023-04-04 21:17:16.067984403 +0200 +++ /work/SRC/openSUSE:Factory/.yast2-users.new.15902/yast2-users.changes 2023-06-14 16:28:35.130190183 +0200 @@ -1,0 +2,10 @@ +Wed Jun 7 16:22:59 UTC 2023 - Imobach Gonzalez Sosa <[email protected]> + +- Write the users when using AutoYaST on an installed system + (bsc#1211753). +- Move Ruby classes in Yast::Users (SSHAuthorizedKeysFile and + SSHAuthorizedKeyring) to the Y2Users module to remove duplication + Yast::Users Perl module. +- 4.6.2 + +------------------------------------------------------------------- Old: ---- yast2-users-4.6.1.tar.bz2 New: ---- yast2-users-4.6.2.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ yast2-users.spec ++++++ --- /var/tmp/diff_new_pack.egPkCR/_old 2023-06-14 16:28:37.658205723 +0200 +++ /var/tmp/diff_new_pack.egPkCR/_new 2023-06-14 16:28:37.710206042 +0200 @@ -17,7 +17,7 @@ Name: yast2-users -Version: 4.6.1 +Version: 4.6.2 Release: 0 Summary: YaST2 - User and Group Configuration License: GPL-2.0-only ++++++ yast2-users-4.6.1.tar.bz2 -> yast2-users-4.6.2.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.6.1/package/yast2-users.changes new/yast2-users-4.6.2/package/yast2-users.changes --- old/yast2-users-4.6.1/package/yast2-users.changes 2023-04-03 13:48:38.000000000 +0200 +++ new/yast2-users-4.6.2/package/yast2-users.changes 2023-06-12 16:19:35.000000000 +0200 @@ -1,4 +1,14 @@ ------------------------------------------------------------------- +Wed Jun 7 16:22:59 UTC 2023 - Imobach Gonzalez Sosa <[email protected]> + +- Write the users when using AutoYaST on an installed system + (bsc#1211753). +- Move Ruby classes in Yast::Users (SSHAuthorizedKeysFile and + SSHAuthorizedKeyring) to the Y2Users module to remove duplication + Yast::Users Perl module. +- 4.6.2 + +------------------------------------------------------------------- Thu Mar 23 14:04:54 UTC 2023 - Ancor Gonzalez Sosa <[email protected]> - Stop mangling the value of "Create as Btrfs Subvolume" for new diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.6.1/package/yast2-users.spec new/yast2-users-4.6.2/package/yast2-users.spec --- old/yast2-users-4.6.1/package/yast2-users.spec 2023-04-03 13:48:38.000000000 +0200 +++ new/yast2-users-4.6.2/package/yast2-users.spec 2023-06-12 16:19:35.000000000 +0200 @@ -17,7 +17,7 @@ Name: yast2-users -Version: 4.6.1 +Version: 4.6.2 Release: 0 Summary: YaST2 - User and Group Configuration License: GPL-2.0-only diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.6.1/src/lib/users/clients/auto.rb new/yast2-users-4.6.2/src/lib/users/clients/auto.rb --- old/yast2-users-4.6.1/src/lib/users/clients/auto.rb 2023-04-03 13:48:38.000000000 +0200 +++ new/yast2-users-4.6.2/src/lib/users/clients/auto.rb 2023-06-12 16:19:35.000000000 +0200 @@ -22,6 +22,11 @@ require "y2users" require "y2users/autoinst/reader" require "y2issues" +require "y2users/config_merger" +require "y2users/config_manager" +require "y2users/autoinst/reader" +require "y2users/users_module/reader" +require "y2users/linux/writer" Yast.import "Users" Yast.import "Linuxrc" @@ -90,15 +95,33 @@ end # @note This code is not executed during autoinstallation (instead, the - # users_finish is used). However, it is used when running ayast_setup. + # users_finish is used). However, it is used when running ayast_setup + # or using the AutoYaST UI. + # + # When working on an already installed system, the process of detecting + # which users/groups changed is tricky: + # + # * The approach followed by [Y2Users::UsersModule::Reader](https://github.com/yast/yast-users/blob/414b6c7373068c367c0a01be20a1399fbd0ef470/src/lib/y2users/users_module/reader.rb#L103), + # checking the content of `org_user`, does not work because it is defined + # only if the user was modified using the AutoYaST UI. + # * Directly comparing the users/groups from `system_config` and + # `target_config` does not work because passwords are missing from the + # `target_config` users. + # + # To overcome these limitations, we only consider those users/groups + # which 'modified' property is not nil, although it does not guarantee + # that they changed at all. # # @return [Boolean] true if configuration was changed; false otherwise. def write - Yast::Users.SetWriteOnly(true) - progress_orig = Yast::Progress.set(false) - ret = Yast::Users.Write == "" - Yast::Progress.set(progress_orig) - ret + system_config = Y2Users::ConfigManager.instance.system(force_read: true) + new_config = system_config.copy + _, target_config = Y2Users::UsersModule::Reader.new.read + remove_unchanged_elements(target_config) + Y2Users::ConfigMerger.new(new_config, target_config).merge + writer = Y2Users::Linux::Writer.new(new_config, system_config) + issues = writer.write + issues.empty? end def modified? @@ -149,6 +172,23 @@ root_user end + + # Clean users and groups that have not changed according to the 'modified' attributes + # + # @param config [Y2Users::Config] Configuration to clean + def remove_unchanged_elements(config) + all_users = Yast::Users.GetUsers("uid", "local").values + + Yast::Users.GetUsers("uid", "system").values + uids = all_users.select { |u| u["modified"] }.map { |u| u["uid"] } + users = config.users.reject { |u| uids.include?(u.name) } + + all_groups = Yast::Users.GetGroups("cn", "local").values + + Yast::Users.GetGroups("cn", "system").values + gids = all_groups.select { |g| g["modified"] }.map { |g| g["cn"] } + groups = config.groups.reject { |g| gids.include?(g.name) } + + (users + groups).each { |e| config.detach(e) } + end end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.6.1/src/lib/users/ssh_authorized_keyring.rb new/yast2-users-4.6.2/src/lib/users/ssh_authorized_keyring.rb --- old/yast2-users-4.6.1/src/lib/users/ssh_authorized_keyring.rb 2023-04-03 13:48:38.000000000 +0200 +++ new/yast2-users-4.6.2/src/lib/users/ssh_authorized_keyring.rb 2023-06-12 16:19:35.000000000 +0200 @@ -19,220 +19,218 @@ require "yast" require "users/ssh_authorized_keys_file" -module Yast - module Users - # Read, write and store SSH authorized keys. - # - # This class manages authorized keys for a home directory in the system. - class SSHAuthorizedKeyring - include Logger - - # @return [Array<String>] List of authorized keys - attr_reader :keys - # @return [String] Home directory path - attr_reader :home - - # Base class to use in file/directory problems - class PathError < StandardError - # @return [String] Path - attr_reader :path - - # Constructor - # - # @param path [String] Path - def initialize(path) - @path = path - super(default_message) - end - - # @return [String] Error message - def message - "#{super}: #{path}" - end - - # Returns the default message to be used - # - # Derived classes should implement it. - # - # @return [String] Default message - def default_message - "Path error" - end - end - - # The home directory does not exist. - class HomeDoesNotExist < PathError - # @return [String] Default error message - def default_message - "Home directory does not exist" - end - end - - # The user's SSH configuration directory could not be created. - class CouldNotCreateSSHDirectory < PathError - # @return [String] Default error message - def default_message - "SSH directory could not be created" - end - end - - # The user's SSH configuration directory is a link (potentially insecure). - class NotRegularSSHDirectory < PathError - # @return [String] Default error message - def default_message - "SSH directory is not a regular directory" - end - end - - # The authorized_keys is not a regular file (potentially insecure). - class NotRegularAuthorizedKeysFile < PathError - # @return [String] Default error message - def default_message - "authorized_keys is not a regular file" - end - end +module Y2Users + # Read, write and store SSH authorized keys. + # + # This class manages authorized keys for a home directory in the system. + class SSHAuthorizedKeyring + include Yast::Logger + + # @return [Array<String>] List of authorized keys + attr_reader :keys + # @return [String] Home directory path + attr_reader :home + + # Base class to use in file/directory problems + class PathError < StandardError + # @return [String] Yast::Path + attr_reader :path # Constructor # - # @param home [String] path to the user home where keys will be written - # @param keys [Array<String>] List of authorized keys, empty by default - def initialize(home, keys = []) - @keys = keys - @old_keys = keys - @home = home + # @param path [String] Yast::Path + def initialize(path) + @path = path + super(default_message) + end + + # @return [String] Error message + def message + "#{super}: #{path}" end - # Add/register a keys + # Returns the default message to be used # - # This method does not make any change to the system. For that, - # see #write_keys. + # Derived classes should implement it. # - # @return [Array<String>] Registered authorized keys - def add_keys(new_keys) - @keys |= new_keys.compact + # @return [String] Default message + def default_message + "Yast::Path error" end + end - # Determines if the keyring is empty - # - # @return [Boolean] +true+ if it's empty; +false+ otherwise - def empty? - keys.empty? + # The home directory does not exist. + class HomeDoesNotExist < PathError + # @return [String] Default error message + def default_message + "Home directory does not exist" end + end - # Determines if the keyring has changed - # - # @return [Boolean] +true+ if it has changed; +false+ otherwise - def changed? - @keys != @old_keys + # The user's SSH configuration directory could not be created. + class CouldNotCreateSSHDirectory < PathError + # @return [String] Default error message + def default_message + "SSH directory could not be created" end + end - # Read keys from a given home directory and add them to the keyring - # - # @return [Array<String>] List of authorized keys - def read_keys - path = authorized_keys_path - @old_keys = FileUtils::Exists(path) ? SSHAuthorizedKeysFile.new(path).keys : [] - log.info "Read #{@old_keys.size} keys from #{path}" - @keys = @old_keys.dup + # The user's SSH configuration directory is a link (potentially insecure). + class NotRegularSSHDirectory < PathError + # @return [String] Default error message + def default_message + "SSH directory is not a regular directory" end + end - # Write user keys to the given file - # - # If SSH_DIR does not exist in the given directory, it will be - # created inheriting owner/group and setting permissions to SSH_DIR_PERM. - def write_keys - return unless changed? + # The authorized_keys is not a regular file (potentially insecure). + class NotRegularAuthorizedKeysFile < PathError + # @return [String] Default error message + def default_message + "authorized_keys is not a regular file" + end + end - remove_authorized_keys_file + # Constructor + # + # @param home [String] path to the user home where keys will be written + # @param keys [Array<String>] List of authorized keys, empty by default + def initialize(home, keys = []) + @keys = keys + @old_keys = keys + @home = home + end - return if keys.empty? + # Add/register a keys + # + # This method does not make any change to the system. For that, + # see #write_keys. + # + # @return [Array<String>] Registered authorized keys + def add_keys(new_keys) + @keys |= new_keys.compact + end - if !FileUtils::Exists(home) - log.error("Home directory '#{home}' does not exist!") - raise HomeDoesNotExist, home - end + # Determines if the keyring is empty + # + # @return [Boolean] +true+ if it's empty; +false+ otherwise + def empty? + keys.empty? + end - user = FileUtils::GetOwnerUserID(home) - group = FileUtils::GetOwnerGroupID(home) - create_ssh_dir(user, group) - write_file(user, group) - end + # Determines if the keyring has changed + # + # @return [Boolean] +true+ if it has changed; +false+ otherwise + def changed? + @keys != @old_keys + end - private + # Read keys from a given home directory and add them to the keyring + # + # @return [Array<String>] List of authorized keys + def read_keys + path = authorized_keys_path + @old_keys = Yast::FileUtils::Exists(path) ? Y2Users::SSHAuthorizedKeysFile.new(path).keys : [] + log.info "Read #{@old_keys.size} keys from #{path}" + @keys = @old_keys.dup + end - # @return [String] Relative path to the SSH directory inside users' home - SSH_DIR = ".ssh".freeze - # @return [String] Authorized keys file name - AUTHORIZED_KEYS_FILE = "authorized_keys".freeze - # @return [String] Permissions to be set on SSH_DIR directory - SSH_DIR_PERMS = "0700".freeze + # Write user keys to the given file + # + # If SSH_DIR does not exist in the given directory, it will be + # created inheriting owner/group and setting permissions to SSH_DIR_PERM. + def write_keys + return unless changed? - # Determine the path to the user's SSH directory - # - # @return [String] Path to the user's SSH directory - # - # @see SSH_DIR - def ssh_dir_path - @ssh_dir_path ||= File.join(home, SSH_DIR) - end + remove_authorized_keys_file - # Determine the path to the user's authorized keys file - # - # @return [String] Path to authorized keys file - # - # @see SSH_DIR - # @see AUTHORIZED_KEYS_FILE - # - # @see #ssh_dir_path - def authorized_keys_path - @authorized_keys_path ||= File.join(ssh_dir_path, AUTHORIZED_KEYS_FILE) + return if keys.empty? + + if !Yast::FileUtils::Exists(home) + log.error("Home directory '#{home}' does not exist!") + raise HomeDoesNotExist, home end - # Find or creates the SSH directory - # - # This method sets up the SSH directory (usually .ssh). Although only 1 - # level is needed (as SSH directory lives under $HOME/.ssh), this code - # should support changing SSH_DIR to something like `.config/ssh`. - # - # @param user [Fixnum] Users's UID - # @param group [Fixnum] Group's GID - # @return [String] Returns the path to the first created directory - # - # @raise NotRegularSSHDirectory - # @raise CouldNotCreateSSHDirectory - def create_ssh_dir(user, group) - if FileUtils::Exists(ssh_dir_path) - raise NotRegularSSHDirectory, ssh_dir_path unless FileUtils::IsDirectory(ssh_dir_path) - - return ssh_dir_path - end - ret = SCR.Execute(Path.new(".target.mkdir"), ssh_dir_path) - log.info("Creating SSH directory: #{ret}") - raise CouldNotCreateSSHDirectory, ssh_dir_path unless ret + user = Yast::FileUtils::GetOwnerUserID(home) + group = Yast::FileUtils::GetOwnerGroupID(home) + create_ssh_dir(user, group) + write_file(user, group) + end - FileUtils::Chown("#{user}:#{group}", ssh_dir_path, false) && - FileUtils::Chmod(SSH_DIR_PERMS, ssh_dir_path, false) - end + private - # Write authorized keys file - # - # @param owner [Fixnum] Users's UID - # @param group [Fixnum] Group's GID - def write_file(owner, group) - file = SSHAuthorizedKeysFile.new(authorized_keys_path) - file.keys = keys - log.info "Writing #{keys.size} keys in #{authorized_keys_path}" - file.save && FileUtils::Chown("#{owner}:#{group}", authorized_keys_path, false) - rescue SSHAuthorizedKeysFile::NotRegularFile - raise NotRegularAuthorizedKeysFile, authorized_keys_path - end - - # Remove the authorized keys file - def remove_authorized_keys_file - return unless FileUtils::Exists(authorized_keys_path) + # @return [String] Relative path to the SSH directory inside users' home + SSH_DIR = ".ssh".freeze + # @return [String] Authorized keys file name + AUTHORIZED_KEYS_FILE = "authorized_keys".freeze + # @return [String] Permissions to be set on SSH_DIR directory + SSH_DIR_PERMS = "0700".freeze - SCR.Execute(Path.new(".target.remove"), authorized_keys_path) - end + # Determine the path to the user's SSH directory + # + # @return [String] Yast::Path to the user's SSH directory + # + # @see SSH_DIR + def ssh_dir_path + @ssh_dir_path ||= File.join(home, SSH_DIR) + end + + # Determine the path to the user's authorized keys file + # + # @return [String] Yast::Path to authorized keys file + # + # @see SSH_DIR + # @see AUTHORIZED_KEYS_FILE + # + # @see #ssh_dir_path + def authorized_keys_path + @authorized_keys_path ||= File.join(ssh_dir_path, AUTHORIZED_KEYS_FILE) + end + + # Find or creates the SSH directory + # + # This method sets up the SSH directory (usually .ssh). Although only 1 + # level is needed (as SSH directory lives under $HOME/.ssh), this code + # should support changing SSH_DIR to something like `.config/ssh`. + # + # @param user [Fixnum] Users's UID + # @param group [Fixnum] Group's GID + # @return [String] Returns the path to the first created directory + # + # @raise NotRegularSSHDirectory + # @raise CouldNotCreateSSHDirectory + def create_ssh_dir(user, group) + if Yast::FileUtils::Exists(ssh_dir_path) + raise NotRegularSSHDirectory, ssh_dir_path unless Yast::FileUtils::IsDirectory(ssh_dir_path) + + return ssh_dir_path + end + ret = Yast::SCR.Execute(Yast::Path.new(".target.mkdir"), ssh_dir_path) + log.info("Creating SSH directory: #{ret}") + raise CouldNotCreateSSHDirectory, ssh_dir_path unless ret + + Yast::FileUtils::Chown("#{user}:#{group}", ssh_dir_path, false) && + Yast::FileUtils::Chmod(SSH_DIR_PERMS, ssh_dir_path, false) + end + + # Write authorized keys file + # + # @param owner [Fixnum] Users's UID + # @param group [Fixnum] Group's GID + def write_file(owner, group) + file = Y2Users::SSHAuthorizedKeysFile.new(authorized_keys_path) + file.keys = keys + log.info "Writing #{keys.size} keys in #{authorized_keys_path}" + file.save && Yast::FileUtils::Chown("#{owner}:#{group}", authorized_keys_path, false) + rescue Y2Users::SSHAuthorizedKeysFile::NotRegularFile + raise NotRegularAuthorizedKeysFile, authorized_keys_path + end + + # Remove the authorized keys file + def remove_authorized_keys_file + return unless Yast::FileUtils::Exists(authorized_keys_path) + + Yast::SCR.Execute(Yast::Path.new(".target.remove"), authorized_keys_path) end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.6.1/src/lib/users/ssh_authorized_keys_file.rb new/yast2-users-4.6.2/src/lib/users/ssh_authorized_keys_file.rb --- old/yast2-users-4.6.1/src/lib/users/ssh_authorized_keys_file.rb 2023-04-03 13:48:38.000000000 +0200 +++ new/yast2-users-4.6.2/src/lib/users/ssh_authorized_keys_file.rb 2023-06-12 16:19:35.000000000 +0200 @@ -20,111 +20,111 @@ Yast.import "FileUtils" -module Yast - module Users - # Represents a `authorized_keys` SSH file. - # - # @example Adding a key to a file - # path = "/home/user/.ssh/authorized_keys" - # file = SSHAuthorizedKeysFile.new(path) #=> #<SSHAuthorizedKeysFile:...> - # file.add_key("ssh-rsa 123ABC") #=> true - # file.save #=> true - # - # @example Creating a new authorized_keys file - # path = "/home/user/.ssh/authorized_keys2" - # file = SSHAuthorizedKeysFile.new(path) #=> #<SSHAuthorizedKeysFile:...> - # file.keys = ["ssh-rsa 123ABC"] #=> true - # file.save #=> true - # - # @see man sshd(8) - class SSHAuthorizedKeysFile - include Logger - - # @return [Pathname,String] Path to the file - attr_reader :path - - # authorized_keys exists but it's not a regular file - class NotRegularFile < StandardError; end - - # Constructor - # - # @param path [Pathname,String] Path to the file - def initialize(path) - @path = path.to_s - end +module Y2Users + # Represents a `authorized_keys` SSH file. + # + # @example Adding a key to a file + # path = "/home/user/.ssh/authorized_keys" + # file = SSHAuthorizedKeysFile.new(path) #=> #<SSHAuthorizedKeysFile:...> + # file.add_key("ssh-rsa 123ABC") #=> true + # file.save #=> true + # + # @example Creating a new authorized_keys file + # path = "/home/user/.ssh/authorized_keys2" + # file = SSHAuthorizedKeysFile.new(path) #=> #<SSHAuthorizedKeysFile:...> + # file.keys = ["ssh-rsa 123ABC"] #=> true + # file.save #=> true + # + # @see man sshd(8) + class SSHAuthorizedKeysFile + include Yast::Logger - # Return the authorized keys present in the file - # - # @return [Array<String>] Array of keys - def keys - return @keys if @keys - - content = SCR.Read(Path.new(".target.string"), path) - self.keys = content.nil? ? [] : content.split("\n") - @keys - end + # @return [Pathname,String] Path to the file + attr_reader :path - # Validate and add a key to the keyring - # - # The key is validated before adding it to the keyring. - # - # @param key [String] String that represents the key - # @return [Boolean] +true+ if the key was added; +false+ otherwise - def add_key(key) - new_key = key.strip - - # Ignores comments or empty lines given as key - return if new_key.empty? || new_key.start_with?("#") - - if valid_key?(new_key) - keys << new_key - true - else - log.warn("The key '#{key}' does not look like a valid SSH key") - false - end - end + # authorized_keys exists but it's not a regular file + class NotRegularFile < StandardError; end - # Set the authorized keys in the file - # - # It won't write the new keys to the file. For that, check the #save - # method. - # - # @param new_keys [Array<String>] SSH authorized keys - # @return [Array<String>] SSH authorized keys - # - # @see #save - # @see #keys - def keys=(new_keys) - @keys = [] - new_keys.each { |k| add_key(k) } - keys - end + # Constructor + # + # @param path [Pathname,String] Path to the file + def initialize(path) + @path = path.to_s + end - # https://github.com/puppetlabs/puppet/blob/master/lib/puppet/type/ssh_authorized_key.rb#L138 - AUTHORIZED_KEYS_REGEX = - /\A(?<env>(.+)\s+)?(?<type>(ssh|ecdsa)-\S+)\s+(?<key>[^ ]+)\s*(?<comment>.*)\z/.freeze - - # Determine is a string qualifies like a valid key - # - # @param key [String] SSH authorized keys - # @return [Boolean] +true+ if it's valid; +false+ otherwise - def valid_key?(key) - AUTHORIZED_KEYS_REGEX.match(key) + # Return the authorized keys present in the file + # + # @return [Array<String>] Array of keys + def keys + return @keys if @keys + + content = Yast::SCR.Read(Yast::Path.new(".target.string"), path) + self.keys = content.nil? ? [] : content.split("\n") + @keys + end + + # Validate and add a key to the keyring + # + # The key is validated before adding it to the keyring. + # + # @param key [String] String that represents the key + # @return [Boolean] +true+ if the key was added; +false+ otherwise + def add_key(key) + new_key = key.strip + + # Ignores comments or empty lines given as key + return if new_key.empty? || new_key.start_with?("#") + + if valid_key?(new_key) + keys << new_key + true + else + log.warn("The key '#{key}' does not look like a valid SSH key") + false end + end + + # Set the authorized keys in the file + # + # It won't write the new keys to the file. For that, check the #save + # method. + # + # @param new_keys [Array<String>] SSH authorized keys + # @return [Array<String>] SSH authorized keys + # + # @see #save + # @see #keys + def keys=(new_keys) + @keys = [] + new_keys.each { |k| add_key(k) } + keys + end - # Write keys to the file - # - # @return [Boolean] +true+ if file was written; +false+ otherwise. - def save - if FileUtils::Exists(path) - raise NotRegularFile unless FileUtils::IsFile(path) - else - SCR.Execute(Path.new(".target.bash"), "umask 0077 && /usr/bin/touch #{path.shellescape}") - end - content = keys.join("\n") + "\n" - SCR.Write(Path.new(".target.string"), path, content) + # https://github.com/puppetlabs/puppet/blob/master/lib/puppet/type/ssh_authorized_key.rb#L138 + AUTHORIZED_KEYS_REGEX = + /\A(?<env>(.+)\s+)?(?<type>(ssh|ecdsa)-\S+)\s+(?<key>[^ ]+)\s*(?<comment>.*)\z/.freeze + + # Determine is a string qualifies like a valid key + # + # @param key [String] SSH authorized keys + # @return [Boolean] +true+ if it's valid; +false+ otherwise + def valid_key?(key) + AUTHORIZED_KEYS_REGEX.match(key) + end + + # Write keys to the file + # + # @return [Boolean] +true+ if file was written; +false+ otherwise. + def save + if Yast::FileUtils::Exists(path) + raise NotRegularFile unless Yast::FileUtils::IsFile(path) + else + Yast::SCR.Execute( + Yast::Path.new(".target.bash"), "umask 0077 && /usr/bin/touch #{path.shellescape}" + ) end + content = keys.join("\n") + "\n" + Yast::SCR.Write(Yast::Path.new(".target.string"), path, content) end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.6.1/src/lib/y2users/linux/reader.rb new/yast2-users-4.6.2/src/lib/y2users/linux/reader.rb --- old/yast2-users-4.6.1/src/lib/y2users/linux/reader.rb 2023-04-03 13:48:38.000000000 +0200 +++ new/yast2-users-4.6.2/src/lib/y2users/linux/reader.rb 2023-06-12 16:19:35.000000000 +0200 @@ -82,13 +82,13 @@ # Reads users authorized keys # - # @see Yast::Users::SSHAuthorizedKeyring#read_keys + # @see Y2Users::SSHAuthorizedKeyring#read_keys # @return [Array<Y2Users::User>] def read_authorized_keys(config) config.users.each do |user| next unless user.home - user.authorized_keys = Yast::Users::SSHAuthorizedKeyring.new(user.home.path).read_keys + user.authorized_keys = Y2Users::SSHAuthorizedKeyring.new(user.home.path).read_keys end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.6.1/src/lib/y2users/linux/set_auth_keys_action.rb new/yast2-users-4.6.2/src/lib/y2users/linux/set_auth_keys_action.rb --- old/yast2-users-4.6.1/src/lib/y2users/linux/set_auth_keys_action.rb 2023-04-03 13:48:38.000000000 +0200 +++ new/yast2-users-4.6.2/src/lib/y2users/linux/set_auth_keys_action.rb 2023-06-12 16:19:35.000000000 +0200 @@ -54,11 +54,11 @@ # # Issues are generated when the authorized keys cannot be set. def run_action - keyring = Yast::Users::SSHAuthorizedKeyring.new(user.home.path, previous_keys) + keyring = Y2Users::SSHAuthorizedKeyring.new(user.home.path, previous_keys) keyring.add_keys(user.authorized_keys) keyring.write_keys true - rescue Yast::Users::SSHAuthorizedKeyring::PathError => e + rescue Y2Users::SSHAuthorizedKeyring::PathError => e issues << Y2Issues::Issue.new( # TRANSLATORS: %s is a placeholder for a username format(_("Error writing authorized keys for '%s'"), user.name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.6.1/src/modules/SSHAuthorizedKeys.rb new/yast2-users-4.6.2/src/modules/SSHAuthorizedKeys.rb --- old/yast2-users-4.6.1/src/modules/SSHAuthorizedKeys.rb 2023-04-03 13:48:38.000000000 +0200 +++ new/yast2-users-4.6.2/src/modules/SSHAuthorizedKeys.rb 2023-06-12 16:19:35.000000000 +0200 @@ -35,27 +35,27 @@ # Read keys from a given home directory # - # @see Yast::Users::SSHAuthorizedKeyring#read_keys + # @see Y2Y2Users::SSHAuthorizedKeyring#read_keys def read_keys(home) - keyring = Users::SSHAuthorizedKeyring.new(home) + keyring = Y2Users::SSHAuthorizedKeyring.new(home) keyring.read_keys end # Write keys to a given home directory # - # @see Yast::Users::SSHAuthorizedKeyring#write_keys + # @see Y2Y2Users::SSHAuthorizedKeyring#write_keys def write_keys(home, keys) - keyring = Users::SSHAuthorizedKeyring.new(home) + keyring = Y2Users::SSHAuthorizedKeyring.new(home) keyring.add_keys(keys) keyring.write_keys - rescue Users::SSHAuthorizedKeyring::HomeDoesNotExist => e + rescue Y2Users::SSHAuthorizedKeyring::HomeDoesNotExist => e log.warn(e.message) Report.Warning( # TRANSLATORS: '%s' is a directory path format(_("Home directory '%s' does not exist\n" \ "so authorized keys will not be written."), e.path) ) - rescue Users::SSHAuthorizedKeyring::NotRegularSSHDirectory => e + rescue Y2Users::SSHAuthorizedKeyring::NotRegularSSHDirectory => e log.warn(e.message) Report.Warning( # TRANSLATORS: '%s' is a directory path @@ -63,7 +63,7 @@ "a security issue so authorized keys will not\n" \ "be written."), e.path) ) - rescue Users::SSHAuthorizedKeyring::NotRegularAuthorizedKeysFile => e + rescue Y2Users::SSHAuthorizedKeyring::NotRegularAuthorizedKeysFile => e log.warn(e.message) Report.Warning( # TRANSLATORS: '%s' is a directory path @@ -71,7 +71,7 @@ "a security issue so authorized keys will not\n" \ "be written."), e.path) ) - rescue Users::SSHAuthorizedKeyring::CouldNotCreateSSHDirectory => e + rescue Y2Users::SSHAuthorizedKeyring::CouldNotCreateSSHDirectory => e log.warn(e.message) Report.Warning( Message.UnableToCreateDirectory(e.path) + "\n" + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.6.1/test/lib/users/clients/auto_test.rb new/yast2-users-4.6.2/test/lib/users/clients/auto_test.rb --- old/yast2-users-4.6.1/test/lib/users/clients/auto_test.rb 2023-04-03 13:48:38.000000000 +0200 +++ new/yast2-users-4.6.2/test/lib/users/clients/auto_test.rb 2023-06-12 16:19:35.000000000 +0200 @@ -208,5 +208,79 @@ subject.run end end + + context "Write" do + let(:func) { "Write" } + let(:reader) do + instance_double( + Y2Users::UsersModule::Reader, read: [Y2Users::Config.new, target_config] + ) + end + let(:writer) { instance_double(Y2Users::Linux::Writer, write: []) } + let(:system_config) { Y2Users::Config.new } + let(:target_config) do + Y2Users::Config.new.tap do |config| + config.attach(user) + config.attach(root) + config.attach(users_group) + config.attach(others_group) + end + end + let(:root) { Y2Users::User.new("root").tap { |u| u.uid = 0 } } + let(:user) { Y2Users::User.new("foo") } + let(:users_group) { Y2Users::Group.new("users") } + let(:others_group) { Y2Users::Group.new("others") } + + before do + allow(Y2Users::ConfigManager.instance).to receive(:system) + .and_return(system_config) + allow(Y2Users::Linux::Writer).to receive(:new).and_return(writer) + allow(Y2Users::UsersModule::Reader).to receive(:new).and_return(reader) + allow(Yast::Users).to receive(:GetUsers).with("uid", "local") + .and_return("foo" => { "uid" => "foo", "modified" => true }) + allow(Yast::Users).to receive(:GetUsers).with("uid", "system") + .and_return("root" => { "uid" => "root", "modified" => false }) + allow(Yast::Users).to receive(:GetGroups).with("cn", "local") + .and_return("foo" => { "cn" => "users", "modified" => true }) + allow(Yast::Users).to receive(:GetGroups).with("cn", "system") + .and_return("root" => { "cn" => "others", "modified" => false }) + end + + it "writes the users defined in the Users module" do + expect(Y2Users::Linux::Writer).to receive(:new) do |new_config, _| + names = new_config.users.map(&:name) + expect(names).to include("foo") + expect(names).to_not include("root") + writer + end + subject.run + end + + it "writes the groups defined in the Users module" do + expect(Y2Users::Linux::Writer).to receive(:new) do |new_config, _| + names = new_config.groups.map(&:name) + expect(names).to include("users") + expect(names).to_not include("others") + writer + end + subject.run + end + + context "when there are not issues" do + it "returns true" do + expect(subject.run).to eq(true) + end + end + + context "when any issue was found" do + before do + allow(writer).to receive(:write).and_return([double("issue")]) + end + + it "returns false" do + expect(subject.run).to eq(false) + end + end + end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.6.1/test/lib/users/ssh_authorized_keyring_test.rb new/yast2-users-4.6.2/test/lib/users/ssh_authorized_keyring_test.rb --- old/yast2-users-4.6.1/test/lib/users/ssh_authorized_keyring_test.rb 2023-04-03 13:48:38.000000000 +0200 +++ new/yast2-users-4.6.2/test/lib/users/ssh_authorized_keyring_test.rb 2023-06-12 16:19:35.000000000 +0200 @@ -21,8 +21,8 @@ require "users/ssh_authorized_keyring" require "tmpdir" -describe Yast::Users::SSHAuthorizedKeyring do - subject(:keyring) { Yast::Users::SSHAuthorizedKeyring.new(home) } +describe Y2Users::SSHAuthorizedKeyring do + subject(:keyring) { Y2Users::SSHAuthorizedKeyring.new(home) } let(:home) { FIXTURES_PATH.join("home", "user1").to_s } def authorized_keys_from_home(path) @@ -130,7 +130,7 @@ describe "#write_keys" do let(:tmpdir) { Dir.mktmpdir } let(:home) { File.join(tmpdir, "/home/user") } - let(:file) { Yast::Users::SSHAuthorizedKeysFile.new(authorized_keys_path) } + let(:file) { Y2Users::SSHAuthorizedKeysFile.new(authorized_keys_path) } let(:ssh_dir) { File.join(home, ".ssh") } let(:key) { "ssh-rsa 123ABC" } let(:authorized_keys_path) { File.join(home, ".ssh", "authorized_keys") } @@ -138,7 +138,7 @@ before do FileUtils.mkdir_p(ssh_dir) - allow(Yast::Users::SSHAuthorizedKeysFile).to receive(:new).and_return(file) + allow(Y2Users::SSHAuthorizedKeysFile).to receive(:new).and_return(file) end after { FileUtils.rm_rf(tmpdir) if File.exist?(tmpdir) } @@ -211,20 +211,20 @@ let(:home_dir_exists) { false } it "raises a HomeDoesNotExist exception and does not write authorized_keys" do - expect(Yast::Users::SSHAuthorizedKeysFile).to_not receive(:new) + expect(Y2Users::SSHAuthorizedKeysFile).to_not receive(:new) expect { keyring.write_keys } - .to raise_error(Yast::Users::SSHAuthorizedKeyring::HomeDoesNotExist) + .to raise_error(Y2Users::SSHAuthorizedKeyring::HomeDoesNotExist) end end context "when SSH directory could not be created" do it "raises a CouldNotCreateSSHDirectory exception and does not write authorized_keys" do - expect(Yast::Users::SSHAuthorizedKeysFile).to_not receive(:new) + expect(Y2Users::SSHAuthorizedKeysFile).to_not receive(:new) expect(Yast::SCR).to receive(:Execute) .with(Yast::Path.new(".target.mkdir"), anything) .and_return(false) expect { keyring.write_keys } - .to raise_error(Yast::Users::SSHAuthorizedKeyring::CouldNotCreateSSHDirectory) + .to raise_error(Y2Users::SSHAuthorizedKeyring::CouldNotCreateSSHDirectory) end end @@ -234,9 +234,9 @@ it "raises a NotRegularSSHDirectory and does not write authorized_keys" do allow(Yast::FileUtils).to receive(:IsDirectory).with(ssh_dir) .and_return(false) - expect(Yast::Users::SSHAuthorizedKeysFile).to_not receive(:new) + expect(Y2Users::SSHAuthorizedKeysFile).to_not receive(:new) expect { keyring.write_keys } - .to raise_error(Yast::Users::SSHAuthorizedKeyring::NotRegularSSHDirectory) + .to raise_error(Y2Users::SSHAuthorizedKeyring::NotRegularSSHDirectory) end end @@ -282,10 +282,10 @@ it "raises a NotRegularAuthorizedKeysFile" do allow(file).to receive(:save) - .and_raise(Yast::Users::SSHAuthorizedKeysFile::NotRegularFile) + .and_raise(Y2Users::SSHAuthorizedKeysFile::NotRegularFile) expect { keyring.write_keys } - .to raise_error(Yast::Users::SSHAuthorizedKeyring::NotRegularAuthorizedKeysFile) + .to raise_error(Y2Users::SSHAuthorizedKeyring::NotRegularAuthorizedKeysFile) end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.6.1/test/lib/users/ssh_authorized_keys_file_test.rb new/yast2-users-4.6.2/test/lib/users/ssh_authorized_keys_file_test.rb --- old/yast2-users-4.6.1/test/lib/users/ssh_authorized_keys_file_test.rb 2023-04-03 13:48:38.000000000 +0200 +++ new/yast2-users-4.6.2/test/lib/users/ssh_authorized_keys_file_test.rb 2023-06-12 16:19:35.000000000 +0200 @@ -20,8 +20,8 @@ require "users/ssh_authorized_keys_file" require "tmpdir" -describe Yast::Users::SSHAuthorizedKeysFile do - subject(:file) { Yast::Users::SSHAuthorizedKeysFile.new(path) } +describe Y2Users::SSHAuthorizedKeysFile do + subject(:file) { described_class.new(path) } let(:path) { FIXTURES_PATH.join("home", "user1", ".ssh", "authorized_keys") } describe "#keys" do @@ -117,7 +117,7 @@ expect(Yast::SCR).to_not receive(:Write) .with(Yast::Path.new(".target.string"), anything) file.keys = [key0, key1] - expect { file.save }.to raise_error(Yast::Users::SSHAuthorizedKeysFile::NotRegularFile) + expect { file.save }.to raise_error(Y2Users::SSHAuthorizedKeysFile::NotRegularFile) end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.6.1/test/lib/y2users/linux/set_auth_keys_action_test.rb new/yast2-users-4.6.2/test/lib/y2users/linux/set_auth_keys_action_test.rb --- old/yast2-users-4.6.1/test/lib/y2users/linux/set_auth_keys_action_test.rb 2023-04-03 13:48:38.000000000 +0200 +++ new/yast2-users-4.6.2/test/lib/y2users/linux/set_auth_keys_action_test.rb 2023-06-12 16:19:35.000000000 +0200 @@ -37,21 +37,21 @@ describe "#perform" do it "calls SSHAuthorizedKeyring#write_keys" do - obj = double(Yast::Users::SSHAuthorizedKeyring) + obj = double(Y2Users::SSHAuthorizedKeyring) expect(obj).to receive(:write_keys) expect(obj).to receive(:add_keys).with(["test"]) - expect(Yast::Users::SSHAuthorizedKeyring).to receive(:new).with("/home/test", []) + expect(Y2Users::SSHAuthorizedKeyring).to receive(:new).with("/home/test", []) .and_return(obj) subject.perform end it "returns result without success and with issues if cmd failed" do - obj = double(Yast::Users::SSHAuthorizedKeyring) + obj = double(Y2Users::SSHAuthorizedKeyring) expect(obj).to receive(:add_keys).with(["test"]) expect(obj).to receive(:write_keys) - .and_raise(Yast::Users::SSHAuthorizedKeyring::PathError, "/home/test") - expect(Yast::Users::SSHAuthorizedKeyring).to receive(:new).with("/home/test", []) + .and_raise(Y2Users::SSHAuthorizedKeyring::PathError, "/home/test") + expect(Y2Users::SSHAuthorizedKeyring).to receive(:new).with("/home/test", []) .and_return(obj) result = action.perform diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.6.1/test/ssh_authorized_keys_test.rb new/yast2-users-4.6.2/test/ssh_authorized_keys_test.rb --- old/yast2-users-4.6.1/test/ssh_authorized_keys_test.rb 2023-04-03 13:48:38.000000000 +0200 +++ new/yast2-users-4.6.2/test/ssh_authorized_keys_test.rb 2023-06-12 16:19:35.000000000 +0200 @@ -34,16 +34,16 @@ let(:authorized_keys_path) { File.join("ssh_dir", ".authorized_keys") } let(:key) { double("key") } let(:keys) { [key] } - let(:keyring) { instance_double(Yast::Users::SSHAuthorizedKeyring, add_keys: []) } + let(:keyring) { instance_double(Y2Users::SSHAuthorizedKeyring, add_keys: []) } before do - allow(Yast::Users::SSHAuthorizedKeyring).to receive(:new).and_return(keyring) + allow(Y2Users::SSHAuthorizedKeyring).to receive(:new).and_return(keyring) end describe "#write_keys" do context "when home directory does not exists" do let(:exception) do - Yast::Users::SSHAuthorizedKeyring::HomeDoesNotExist.new(home) + Y2Users::SSHAuthorizedKeyring::HomeDoesNotExist.new(home) end it "shows an error message" do @@ -56,7 +56,7 @@ context "SSH directory is not a directory" do let(:exception) do - Yast::Users::SSHAuthorizedKeyring::NotRegularSSHDirectory.new(ssh_dir) + Y2Users::SSHAuthorizedKeyring::NotRegularSSHDirectory.new(ssh_dir) end it "shows an error message" do @@ -69,7 +69,7 @@ context "SSH directory could not be created" do let(:exception) do - Yast::Users::SSHAuthorizedKeyring::CouldNotCreateSSHDirectory.new(ssh_dir) + Y2Users::SSHAuthorizedKeyring::CouldNotCreateSSHDirectory.new(ssh_dir) end it "shows an error message" do @@ -82,7 +82,7 @@ context "authorized_keys exists but it's not a regular file" do let(:exception) do - Yast::Users::SSHAuthorizedKeyring::NotRegularAuthorizedKeysFile.new(authorized_keys_path) + Y2Users::SSHAuthorizedKeyring::NotRegularAuthorizedKeysFile.new(authorized_keys_path) end it "shows an error message" do
