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 2021-07-25 20:09:10 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/yast2-users (Old) and /work/SRC/openSUSE:Factory/.yast2-users.new.1899 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "yast2-users" Sun Jul 25 20:09:10 2021 rev:245 rq:907749 version:4.4.5 Changes: -------- --- /work/SRC/openSUSE:Factory/yast2-users/yast2-users.changes 2021-07-21 19:07:14.495381317 +0200 +++ /work/SRC/openSUSE:Factory/.yast2-users.new.1899/yast2-users.changes 2021-07-25 20:09:15.419444111 +0200 @@ -1,0 +2,6 @@ +Tue Jul 20 15:09:28 UTC 2021 - David Diaz <dgonza...@suse.com> + +- Do not rewrite authorized_keys unless it is needed (bsc#1188361). +- 4.4.5 + +------------------------------------------------------------------- Old: ---- yast2-users-4.4.4.tar.bz2 New: ---- yast2-users-4.4.5.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ yast2-users.spec ++++++ --- /var/tmp/diff_new_pack.Go1cKq/_old 2021-07-25 20:09:15.891443585 +0200 +++ /var/tmp/diff_new_pack.Go1cKq/_new 2021-07-25 20:09:15.895443581 +0200 @@ -17,7 +17,7 @@ Name: yast2-users -Version: 4.4.4 +Version: 4.4.5 Release: 0 Summary: YaST2 - User and Group Configuration License: GPL-2.0-only ++++++ yast2-users-4.4.4.tar.bz2 -> yast2-users-4.4.5.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.4.4/package/yast2-users.changes new/yast2-users-4.4.5/package/yast2-users.changes --- old/yast2-users-4.4.4/package/yast2-users.changes 2021-07-19 10:28:28.000000000 +0200 +++ new/yast2-users-4.4.5/package/yast2-users.changes 2021-07-22 14:27:47.000000000 +0200 @@ -1,4 +1,10 @@ ------------------------------------------------------------------- +Tue Jul 20 15:09:28 UTC 2021 - David Diaz <dgonza...@suse.com> + +- Do not rewrite authorized_keys unless it is needed (bsc#1188361). +- 4.4.5 + +------------------------------------------------------------------- Fri Jul 16 13:13:26 UTC 2021 - David Diaz <dgonza...@suse.com> - Use a more reliable way of knowing if a user is being diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.4.4/package/yast2-users.spec new/yast2-users-4.4.5/package/yast2-users.spec --- old/yast2-users-4.4.4/package/yast2-users.spec 2021-07-19 10:28:28.000000000 +0200 +++ new/yast2-users-4.4.5/package/yast2-users.spec 2021-07-22 14:27:47.000000000 +0200 @@ -17,7 +17,7 @@ Name: yast2-users -Version: 4.4.4 +Version: 4.4.5 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.4.4/src/lib/users/ssh_authorized_keyring.rb new/yast2-users-4.4.5/src/lib/users/ssh_authorized_keyring.rb --- old/yast2-users-4.4.4/src/lib/users/ssh_authorized_keyring.rb 2021-07-19 10:28:28.000000000 +0200 +++ new/yast2-users-4.4.5/src/lib/users/ssh_authorized_keyring.rb 2021-07-22 14:27:47.000000000 +0200 @@ -98,6 +98,7 @@ # @param keys [Array<String>] List of authorized keys, empty by default def initialize(home, keys = []) @keys = keys + @old_keys = keys @home = home end @@ -108,7 +109,7 @@ # # @return [Array<String>] Registered authorized keys def add_keys(new_keys) - keys.concat(new_keys) + @keys |= new_keys.compact end # Determines if the keyring is empty @@ -118,14 +119,21 @@ keys.empty? end + # Determines if the keyring has changed + # + # @return [Boolean] +true+ if it has changed; +false+ otherwise + def changed? + @keys != @old_keys + 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 - @keys = FileUtils::Exists(path) ? SSHAuthorizedKeysFile.new(path).keys : [] - log.info "Read #{@keys.size} keys from #{path}" - @keys + @old_keys = FileUtils::Exists(path) ? SSHAuthorizedKeysFile.new(path).keys : [] + log.info "Read #{@old_keys.size} keys from #{path}" + @keys = @old_keys.dup end # Write user keys to the given file @@ -133,13 +141,17 @@ # 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? + remove_authorized_keys_file + return if keys.empty? if !FileUtils::Exists(home) log.error("Home directory '#{home}' does not exist!") raise HomeDoesNotExist, home end + user = FileUtils::GetOwnerUserID(home) group = FileUtils::GetOwnerGroupID(home) create_ssh_dir(user, group) @@ -154,8 +166,6 @@ AUTHORIZED_KEYS_FILE = "authorized_keys".freeze # @return [String] Permissions to be set on SSH_DIR directory SSH_DIR_PERMS = "0700".freeze - # @return [String] Permissions to be set on `authorized_keys` file - AUTHORIZED_KEYS_PERMS = "0600".freeze # Determine the path to the user's SSH directory # diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.4.4/src/lib/users/ssh_authorized_keys_file.rb new/yast2-users-4.4.5/src/lib/users/ssh_authorized_keys_file.rb --- old/yast2-users-4.4.4/src/lib/users/ssh_authorized_keys_file.rb 2021-07-19 10:28:28.000000000 +0200 +++ new/yast2-users-4.4.5/src/lib/users/ssh_authorized_keys_file.rb 2021-07-22 14:27:47.000000000 +0200 @@ -72,6 +72,10 @@ # @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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.4.4/src/modules/Users.pm new/yast2-users-4.4.5/src/modules/Users.pm --- old/yast2-users-4.4.4/src/modules/Users.pm 2021-07-19 10:28:28.000000000 +0200 +++ new/yast2-users-4.4.5/src/modules/Users.pm 2021-07-22 14:27:47.000000000 +0200 @@ -5939,7 +5939,8 @@ "gidNumber" => $gid, "homeDirectory" => $user->{"homeDirectory"} || $user->{"home"} || $existing{"homeDirectory"} || $home, "type" => $type, - "modified" => "imported" + "modified" => "imported", + "authorized_keys" => $user->{"authorized_keys"} || $existing{"authorized_keys"} || [] ); } } @@ -5961,7 +5962,8 @@ "grouplist" => \%grouplist, "homeDirectory" => $user->{"homeDirectory"} || $user->{"home"} || $home, "type" => $type, - "modified" => "imported" + "modified" => "imported", + "authorized_keys" => $user->{"authorized_keys"} || [] ); } my %translated = ( @@ -5992,14 +5994,6 @@ $ret{"shadowLastChange"} = LastChangeIsNow (); } - # Import authorized keys from profile (FATE#319471) - if (defined($user->{"authorized_keys"})) { - $ret{"authorized_keys"} = $user->{"authorized_keys"}, - } else { - my @authorized_keys = (); - $ret{"authorized_keys"} = \@authorized_keys; - } - # AutoYaST-imported users don't go through AddUser(). This means we have # to replicate some of that logic here: # diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.4.4/test/lib/users/ssh_authorized_keyring_test.rb new/yast2-users-4.4.5/test/lib/users/ssh_authorized_keyring_test.rb --- old/yast2-users-4.4.4/test/lib/users/ssh_authorized_keyring_test.rb 2021-07-19 10:28:28.000000000 +0200 +++ new/yast2-users-4.4.5/test/lib/users/ssh_authorized_keyring_test.rb 2021-07-22 14:27:47.000000000 +0200 @@ -25,6 +25,51 @@ subject(:keyring) { Yast::Users::SSHAuthorizedKeyring.new(home) } let(:home) { FIXTURES_PATH.join("home", "user1").to_s } + def authorized_keys_from_home(path) + file_path = File.join(path, ".ssh", "authorized_keys") + File.read(file_path).lines.map(&:strip).grep(/ssh-/) + end + + describe "#add_keys" do + let(:known_key) { keyring.keys.first } + let(:new_key) { "ssh-rsa 123ABC" } + + before { keyring.read_keys } + + it "adds only keys not already present in the keyring" do + keyring.add_keys([known_key, new_key]) + + expect(keyring.keys).to include(new_key) + expect(keyring.keys).to include(known_key) + expect(keyring.keys).to eq(keyring.keys.uniq) + end + + it "preserves the keys order" do + keyring.add_keys([new_key, known_key]) + + expect(keyring.keys.first).to eq(known_key) + expect(keyring.keys.last).to eq(new_key) + end + end + + describe "#changed?" do + before { keyring.read_keys } + + context "when new keys has been added" do + before { keyring.add_keys(["ssh-rsa 123ABC"]) } + + it "returns true" do + expect(keyring.changed?).to eq(true) + end + end + + context "when no new keys has been added" do + it "returns false" do + expect(keyring.changed?).to eq(false) + end + end + end + describe "#empty?" do context "when keyring is empty" do it "returns true" do @@ -45,7 +90,7 @@ context "if some keys are present in the given home directory" do let(:expected_keys) { authorized_keys_from(home) } - it "returns true" do + it "returns read keys" do expect(keyring.read_keys).to eq(expected_keys) end @@ -85,122 +130,163 @@ describe "#write_keys" do let(:tmpdir) { Dir.mktmpdir } let(:home) { File.join(tmpdir, "/home/user") } - let(:file) { double("file", save: true) } + let(:file) { Yast::Users::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") } - before { FileUtils.mkdir_p(home) } + before do + FileUtils.mkdir_p(ssh_dir) + + allow(Yast::Users::SSHAuthorizedKeysFile).to receive(:new).and_return(file) + end + after { FileUtils.rm_rf(tmpdir) if File.exist?(tmpdir) } - context "if no keys are registered for the given home" do + context "when there are not registered keys for the given home" do it "does not try to write the keys" do expect(file).to_not receive(:save) + keyring.write_keys end end - context "if some keys are registered for the given home" do + context "when there are registered keys for the given home" do let(:uid) { 1001 } let(:gid) { 101 } let(:home_dir_exists) { true } let(:ssh_dir_exists) { false } before do - allow(Yast::SCR).to receive(:Execute).and_call_original allow(Yast::SCR).to receive(:Read).and_call_original + allow(Yast::SCR).to receive(:Execute).and_call_original + allow(Yast::SCR).to receive(:Execute) + .with(Yast::Path.new(".target.remove"), authorized_keys_path) + allow(Yast::FileUtils).to receive(:Exists).and_call_original allow(Yast::FileUtils).to receive(:Exists).with(ssh_dir).and_return(ssh_dir_exists) allow(Yast::FileUtils).to receive(:Exists).with(home).and_return(home_dir_exists) - allow(Yast::FileUtils).to receive(:IsDirectory).with(ssh_dir) - .and_return(true) + allow(Yast::FileUtils).to receive(:IsDirectory).with(ssh_dir).and_return(true) + allow(Yast::FileUtils).to receive(:Chmod).and_call_original + + # Load some authorized_keys + FileUtils.cp_r( + FIXTURES_PATH.join("home", "user1", ".ssh", "authorized_keys"), + authorized_keys_path + ) + keyring.read_keys keyring.add_keys([key]) end - it "writes the keys" do - keyring.write_keys - expect(File).to exist(authorized_keys_path) - end + context "but no new keys are added" do + let(:key) { keyring.keys.first } - it "SSH directory and authorized_keys inherits owner/group from home" do - allow(Yast::FileUtils).to receive(:GetOwnerUserID).with(home).and_return(uid) - allow(Yast::FileUtils).to receive(:GetOwnerGroupID).with(home).and_return(gid) - expect(Yast::FileUtils).to receive(:Chown).with("#{uid}:#{gid}", ssh_dir, false) - expect(Yast::FileUtils).to receive(:Chown) - .with("#{uid}:#{gid}", authorized_keys_path, false) + it "does not write keys again" do + expect(file).to_not receive(:save) - keyring.write_keys + keyring.write_keys + end end - it "sets SSH directory permissions to 0700" do - keyring.write_keys - mode = File.stat(ssh_dir).mode.to_s(8) - expect(mode).to eq("40700") - end + context "but new keys are added" do + it "writes the keys" do + expect(file).to receive(:save) - it "sets authorized_keys permissions to 0600" do - keyring.write_keys - mode = File.stat(authorized_keys_path).mode.to_s(8) - expect(mode).to eq("100600") - end + keyring.write_keys + + expect(File).to exist(authorized_keys_path) + end - context "when home directory does not exist" do - let(:home_dir_exists) { false } + it "SSH directory and authorized_keys inherits owner/group from home" do + allow(Yast::FileUtils).to receive(:GetOwnerUserID).with(home).and_return(uid) + allow(Yast::FileUtils).to receive(:GetOwnerGroupID).with(home).and_return(gid) + expect(Yast::FileUtils).to receive(:Chown).with("#{uid}:#{gid}", ssh_dir, false) + expect(Yast::FileUtils).to receive(:Chown) + .with("#{uid}:#{gid}", authorized_keys_path, false) - it "raises a HomeDoesNotExist exception and does not write authorized_keys" do - expect(Yast::Users::SSHAuthorizedKeysFile).to_not receive(:new) - expect { keyring.write_keys } - .to raise_error(Yast::Users::SSHAuthorizedKeyring::HomeDoesNotExist) + keyring.write_keys 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(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) + context "when home directory does not exist" do + 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 { keyring.write_keys } + .to raise_error(Yast::Users::SSHAuthorizedKeyring::HomeDoesNotExist) + end end - end - context "when SSH directory is not a regular directory" do - let(:ssh_dir_exists) { true } + 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(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) + end + end + + context "when SSH directory is not a regular directory" do + let(:ssh_dir_exists) { true } - 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 { keyring.write_keys } - .to raise_error(Yast::Users::SSHAuthorizedKeyring::NotRegularSSHDirectory) + 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 { keyring.write_keys } + .to raise_error(Yast::Users::SSHAuthorizedKeyring::NotRegularSSHDirectory) + end end - end - context "when SSH directory already exists" do - let(:ssh_dir_exists) { true } + context "when SSH directory already exists" do + let(:ssh_dir_exists) { true } - it "does not create the directory" do - allow(Yast::FileUtils).to receive(:IsDirectory).with(ssh_dir) - .and_return(true) - expect(Yast::SCR).to_not receive(:Execute) - .with(Yast::Path.new(".target.mkdir"), anything) - keyring.write_keys + it "does not create the directory" do + allow(Yast::FileUtils).to receive(:IsDirectory).with(ssh_dir) + .and_return(true) + expect(Yast::SCR).to_not receive(:Execute) + .with(Yast::Path.new(".target.mkdir"), anything) + keyring.write_keys + end + + it "does not set the SSH directory permissions" do + expect(Yast::FileUtils).to_not receive(:Chmod).with(anything, ssh_dir, false) + + keyring.write_keys + end + end + + context "when SSH directory does not exists yet" do + it "creates the directory" do + expect(Yast::SCR).to receive(:Execute) + .with(Yast::Path.new(".target.mkdir"), ssh_dir) + + keyring.write_keys + end + + it "sets the SSH directory permissions to 0700" do + FileUtils.rm_r(ssh_dir) # Force to remove the mock, if exists + + expect(Yast::FileUtils).to receive(:Chmod).with("0700", ssh_dir, false) + + keyring.write_keys + mode = File.stat(ssh_dir).mode.to_s(8) + expect(mode).to eq("40700") + end end - end - context "when authorized_keys is not a regular file" do - let(:ssh_dir_exists) { true } - let(:file) { double("file") } - - it "raises a NotRegularAuthorizedKeysFile" do - allow(Yast::Users::SSHAuthorizedKeysFile).to receive(:new).and_return(file) - allow(file).to receive(:keys=) - allow(file).to receive(:save) - .and_raise(Yast::Users::SSHAuthorizedKeysFile::NotRegularFile) + context "when authorized_keys is not a regular file" do + let(:ssh_dir_exists) { true } - expect { keyring.write_keys } - .to raise_error(Yast::Users::SSHAuthorizedKeyring::NotRegularAuthorizedKeysFile) + it "raises a NotRegularAuthorizedKeysFile" do + allow(file).to receive(:save) + .and_raise(Yast::Users::SSHAuthorizedKeysFile::NotRegularFile) + + expect { keyring.write_keys } + .to raise_error(Yast::Users::SSHAuthorizedKeyring::NotRegularAuthorizedKeysFile) + end end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-users-4.4.4/test/lib/users/ssh_authorized_keys_file_test.rb new/yast2-users-4.4.5/test/lib/users/ssh_authorized_keys_file_test.rb --- old/yast2-users-4.4.4/test/lib/users/ssh_authorized_keys_file_test.rb 2021-07-19 10:28:28.000000000 +0200 +++ new/yast2-users-4.4.5/test/lib/users/ssh_authorized_keys_file_test.rb 2021-07-22 14:27:47.000000000 +0200 @@ -18,6 +18,7 @@ # you may find current contact information at www.suse.com require_relative "../../test_helper" require "users/ssh_authorized_keys_file" +require "tmpdir" describe Yast::Users::SSHAuthorizedKeysFile do subject(:file) { Yast::Users::SSHAuthorizedKeysFile.new(path) } @@ -66,29 +67,34 @@ let(:key1) { "ssh-rsa 456DEF" } let(:expected_content) { "ssh-dsa 123ABC\nssh-rsa 456DEF\n" } - let(:path) { "/tmp/home/user" } + let(:tmpdir) { Dir.mktmpdir } + let(:ssh_dir) { File.join(tmpdir, "/home/user/.ssh/") } + let(:path) { File.join(ssh_dir, "authorized_keys") } let(:dir) { File.dirname(path) } let(:file_exists) { false } - before do - allow(Yast::FileUtils).to receive(:Exists).with(dir) - .and_return(dir_exists) - allow(Yast::FileUtils).to receive(:Exists).with(path) - .and_return(file_exists) - end + after { FileUtils.rm_rf(tmpdir) if File.exist?(tmpdir) } context "if the directory exists" do - let(:dir_exists) { true } + before { FileUtils.mkdir_p(ssh_dir) } - it "creates the file with the registered keys and returns true" do - expect(Yast::SCR).to receive(:Write) - .with(Yast::Path.new(".target.string"), path, expected_content) - file.keys = [key0, key1] - file.save + context "and the file does not exist" do + it "creates the file with the registered keys and returns true" do + expect(Yast::SCR).to receive(:Write) + .with(Yast::Path.new(".target.string"), path, expected_content) + file.keys = [key0, key1] + file.save + end + + it "sets permissions to 0600" do + file.save + mode = File.stat(path).mode.to_s(8) + expect(mode).to eq("100600") + end end context "and the file exists and it is a regular one" do - let(:file_exists) { true } + before { FileUtils.touch(path) } it "updates the file with the registered keys and returns true" do allow(Yast::FileUtils).to receive(:IsFile).with(path) @@ -102,7 +108,7 @@ end context "and the file exists but it is not a regular one" do - let(:file_exists) { true } + before { FileUtils.mkdir(path) } it "raises NotRegularFile exception and does not update the file" do allow(Yast::FileUtils).to receive(:IsFile).with(path) @@ -117,8 +123,6 @@ end context "if the directory does not exist" do - let(:dir_exists) { false } - it "returns false" do file.keys = [key0, key1] expect(file.save).to eq(false) @@ -127,23 +131,65 @@ end describe "#add_key" do - let(:key) { "ssh-dsa 123ABC" } - - context "when the contains keys" do - let(:path) { FIXTURES_PATH.join("home", "user1", ".ssh", "authorized_keys") } + context "when a valid key is given" do + let(:key) { "ssh-dsa 123ABC" } it "adds the new key" do file.add_key(key) + expect(file.keys).to include(key) end end - context "when the file does not contain keys" do - let(:path) { FIXTURES_PATH.join("home", "user2", ".ssh", "authorized_keys") } + context "when a not valid key is given" do + let(:key) { "AAA-DSA 123ABC" } + + it "does not add the new key" do + file.add_key(key) + + expect(file.keys).to_not include(key) + end + + it "logs an error" do + expect(subject.log).to receive(:warn).with(/.*#{key}.*does not look.*valid.*/) + + file.add_key(key) + end + end + + context "when given key actually represents an empty line" do + let(:key) { " " } + + it "ignores it" do + expect(file).to_not receive(:valid_key?).with(key) + + file.add_key(key) + + expect(file.keys).to_not include(key) + end + + it "does not logs an error" do + expect(subject.log).to_not receive(:warn).with(/.*#{key}.*/) + + file.add_key(key) + end + end + + context "when given key actually is a comment" do + let(:key) { "# Just a comment, not a key " } + + it "ignores it" do + expect(file).to_not receive(:valid_key?).with(key) + + file.add_key(key) + + expect(file.keys).to_not include(key) + end + + it "does not logs an error" do + expect(subject.log).to_not receive(:warn).with(/.*#{key}.*/) - it "adds the new key" do file.add_key(key) - expect(file.keys).to eq([key]) end end end